freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject incubator-freemarker git commit: Continued work on the FM2 to FM3 converter
Date Sun, 25 Jun 2017 22:56:28 GMT
Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 2b5cf6f68 -> 50b4a93e7


Continued work on the FM2 to FM3 converter


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/50b4a93e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/50b4a93e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/50b4a93e

Branch: refs/heads/3
Commit: 50b4a93e769c7e782707dc12992fdc0c423b3f09
Parents: 2b5cf6f
Author: ddekany <ddekany@apache.org>
Authored: Mon Jun 26 00:56:00 2017 +0200
Committer: ddekany <ddekany@apache.org>
Committed: Mon Jun 26 00:56:00 2017 +0200

----------------------------------------------------------------------
 .../core/FM2ASTToFM3SourceConverter.java        | 666 ++++++++++++-------
 .../converter/FM2ToFM3ConverterTest.java        |  25 +
 2 files changed, 451 insertions(+), 240 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/50b4a93e/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
index 0cb2d35..20fa2b8 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -27,6 +27,7 @@ import org.apache.freemarker.converter.ConverterException;
 import org.apache.freemarker.converter.ConverterUtils;
 import org.apache.freemarker.core.NamingConvention;
 import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util._ClassUtil;
 import org.apache.freemarker.core.util._NullArgumentException;
 import org.apache.freemarker.core.util._StringUtil;
 
@@ -116,8 +117,11 @@ public class FM2ASTToFM3SourceConverter {
     private void printNode(TemplateObject node) throws ConverterException {
         if (node instanceof TemplateElement) {
             printTemplateElement((TemplateElement) node);
+        } else if (node instanceof Expression) {
+            printExp((Expression) node);
         } else {
-            printExpressionNode(node);
+            throw new UnexpectedNodeContentException(node, "Unhandled node class",
+                    _ClassUtil.getShortClassNameOfObject(node));
         }
     }
 
@@ -128,277 +132,454 @@ public class FM2ASTToFM3SourceConverter {
             print(getOnlyParam(node, ParameterRole.CONTENT, String.class));
         } else if (node instanceof DollarVariable) {
             printWithParamsLeadingSkippedTokens("${", node);
-            printNode(getOnlyParam(node, ParameterRole.CONTENT, TemplateObject.class));
+            printNode(getOnlyParam(node, ParameterRole.CONTENT, Expression.class));
             printWithParamsTrailingSkippedTokens("}", node, 0);
-        } else if (node instanceof IfBlock) {
-            printChildrenElements(node);
+        } else if (node instanceof Comment) {
             print(tagBeginChar);
-            print("/#if");
-            printEndTagSkippedTokens(node);
+            print("#--");
+            print(getOnlyParam(node, ParameterRole.CONTENT, String.class));
+            print("--");
             print(tagEndChar);
+        } else {
+            printDir(node);
+        }
+    }
+
+    /**
+     * Prints a directive
+     */
+    private void printDir(TemplateElement node) throws ConverterException {
+        if (node instanceof IfBlock) {
+            printDirIfElseElseIfContainer((IfBlock) node);
         } else if (node instanceof ConditionalBlock) {
-            assertParamCount(node, 2);
-            TemplateObject conditionExp = getParam(node, 0, ParameterRole.CONDITION, TemplateObject.class);
-            int nodeSubtype = getParam(node, 1, ParameterRole.AST_NODE_SUBTYPE, Integer.class);
+            printDirIfOrElseOrElseIf((ConditionalBlock) node);
+        } else if (node instanceof UnifiedCall) {
+            printDirCustom((UnifiedCall) node);
+        } else {
+            throw new ConverterException("Unhandled AST TemplateElement class: " + node.getClass().getName());
+        }
+    }
 
-            print(tagBeginChar);
-            String tagStart;
-            if (nodeSubtype == ConditionalBlock.TYPE_IF) {
-                tagStart = "#if";
-            } else if (nodeSubtype == ConditionalBlock.TYPE_ELSE) {
-                tagStart = "#else";
-            } else if (nodeSubtype == ConditionalBlock.TYPE_ELSE_IF) {
-                tagStart = "#elseIf";
-            } else {
-                throw new UnexpectedNodeContentException(node, "Unhandled subtype, {}.",
nodeSubtype);
-            }
-            print(tagStart);
-            if (conditionExp != null) {
-                printWithParamsLeadingSkippedTokens(tagStart.length() + 1, node);
-                printNode(conditionExp);
-            }
-            printStartTagSkippedTokens(node, conditionExp, true);
-            print(tagEndChar);
+    private void printDirCustom(UnifiedCall node) throws ConverterException {
+        print(tagBeginChar);
+        print('@');
 
-            printChildrenElements(node);
+        Expression callee = getParam(node, 0, ParameterRole.CALLEE, Expression.class);
+        printExp(callee);
 
-            if (!(node.getParentElement() instanceof IfBlock)) {
-                print(tagBeginChar);
-                print("/#if");
-                printEndTagSkippedTokens(node);
-                print(tagEndChar);
-            }
-        } else if (node instanceof UnifiedCall) {
-            print(tagBeginChar);
-            print('@');
+        Expression lastPrintedExp = callee;
+        int paramIdx = 1;
+        int paramCount = node.getParameterCount();
 
-            TemplateObject callee = getParam(node, 0, ParameterRole.CALLEE, TemplateObject.class);
-            printExpressionNode(callee);
+        // Print positional arguments:
+        while (paramIdx < paramCount && node.getParameterRole(paramIdx) == ParameterRole.ARGUMENT_VALUE)
{
+            Expression argValue = getParam(node, paramIdx, ParameterRole.ARGUMENT_VALUE,
Expression.class);
 
-            TemplateObject lastPrintedExp = callee;
-            int paramIdx = 1;
-            int paramCount = node.getParameterCount();
+            printParameterSeparatorSource(lastPrintedExp, argValue);
+            printExp(argValue);
 
-            // Print positional arguments:
-            while (paramIdx < paramCount && node.getParameterRole(paramIdx) ==
ParameterRole.ARGUMENT_VALUE) {
-                TemplateObject argValue = getParam(node, paramIdx, ParameterRole.ARGUMENT_VALUE,
TemplateObject.class);
+            lastPrintedExp = argValue;
+            paramIdx++;
+        }
 
-                printParameterSeparatorSource(lastPrintedExp, argValue);
-                printExpressionNode(argValue);
+        // Print named arguments:
+        while (paramIdx < paramCount
+                && node.getParameterRole(paramIdx) == ParameterRole.ARGUMENT_NAME)
{
+            Expression argValue = getParam(node, paramIdx + 1, ParameterRole.ARGUMENT_VALUE,
Expression.class);
 
-                lastPrintedExp = argValue;
-                paramIdx++;
-            }
+            printParameterSeparatorSource(lastPrintedExp, argValue); // Prints something
like " someArgName="
+            printExp(argValue);
 
-            // Print named arguments:
-            while (paramIdx < paramCount
-                    && node.getParameterRole(paramIdx) == ParameterRole.ARGUMENT_NAME)
{
-                TemplateObject argValue = getParam(node, paramIdx + 1, ParameterRole.ARGUMENT_VALUE,
-                        TemplateObject.class);
+            lastPrintedExp = argValue;
+            paramIdx += 2;
+        }
 
-                printParameterSeparatorSource(lastPrintedExp, argValue); // Prints something
like " someArgName="
-                printExpressionNode(argValue);
+        // Print loop variables:
+        int pos = getEndPositionExclusive(lastPrintedExp);
+        boolean beforeFirstLoopVar = true;
+        while (paramIdx < paramCount) {
+            String sep = readExpWSAndSeparator(pos, beforeFirstLoopVar ? ';' : ',', false);
+            assertNodeContent(sep.length() != 0, node,
+                    "Can't find loop variable separator", null);
+            printWithConvertedExpComments(sep);
+            pos += sep.length();
+
+            String loopVarName = getParam(node, paramIdx, ParameterRole.TARGET_LOOP_VARIABLE,
String.class);
+            print(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
+            String identifierInSrc = readIdentifier(pos);
+            assertNodeContent(identifierInSrc.length() != 0, node,
+                    "Can't find loop variable identifier in source", null);
+            pos += identifierInSrc.length(); // skip loop var name
+
+            beforeFirstLoopVar = false;
+            paramIdx++;
+        }
 
-                lastPrintedExp = argValue;
-                paramIdx += 2;
-            }
+        int startTagEndPos = printStartTagSkippedTokens(node, pos, false);
+        print(tagEndChar);
 
-            // Print loop variables:
-            int pos = getEndPositionExclusive(lastPrintedExp);
-            boolean beforeFirstLoopVar = true;
-            while (paramIdx < paramCount) {
-                String sep = readExpWSAndSeparator(pos, beforeFirstLoopVar ? ';' : ',', false);
-                assertNodeContent(sep.length() != 0, node,
-                        "Can't find loop variable separator", null);
-                print(sep);
-                pos += sep.length();
-
-                String loopVarName = getParam(node, paramIdx, ParameterRole.TARGET_LOOP_VARIABLE,
String.class);
-                print(_StringUtil.toFTLTopLevelIdentifierReference(loopVarName));
-                String identifierInSrc = readIdentifier(pos);
-                assertNodeContent(identifierInSrc.length() != 0, node,
-                        "Can't find loop variable identifier in source", null);
-                pos += identifierInSrc.length(); // skip loop var name
-
-                beforeFirstLoopVar = false;
-                paramIdx++;
-            }
+        int elementEndPos = getEndPositionInclusive(node);
+        {
+            char c = src.charAt(elementEndPos);
+            assertNodeContent(c == tagEndChar, node,
+                    "tagEndChar expected, found '{}'", c);
+        }
+        if (startTagEndPos != elementEndPos) { // We have an end-tag
+            assertNodeContent(src.charAt(startTagEndPos - 1) != '/', node,
+                    "Not expected \"/\" at the end of the start tag", null);
+            printChildrenElements(node);
 
-            int startTagEndPos = printStartTagSkippedTokens(node, pos, false);
+            print(tagBeginChar);
+            print("/@");
+            int nameStartPos = elementEndPos; // Not 1 less; consider the case of </@>
+            while (nameStartPos >= 2 && !src.startsWith("/@", nameStartPos - 2))
{
+                nameStartPos--;
+            }
+            assertNodeContent(nameStartPos >= 2, node,
+                    "Couldn't extract name from end-tag.", null);
+            // Also prints ignored WS after name, for now:
+            printWithConvertedExpComments(src.substring(nameStartPos, elementEndPos));
             print(tagEndChar);
+        } else { // We don't have end-tag
+            assertNodeContent(src.charAt(startTagEndPos - 1) == '/', node,
+                    "Expected \"/\" at the end of the start tag", null);
+            assertNodeContent(node.getChildCount() == 0, node,
+                    "Expected no children", null);
+        }
+    }
 
-            int elementEndPos = getEndPositionInclusive(node);
-            {
-                char c = src.charAt(elementEndPos);
-                assertNodeContent(c == tagEndChar, node,
-                        "tagEndChar expected, found '{}'", c);
-            }
-            if (startTagEndPos != elementEndPos) { // We have an end-tag
-                assertNodeContent(src.charAt(startTagEndPos - 1) != '/', node,
-                        "Not expected \"/\" at the end of the start tag", null);
-                printChildrenElements(node);
-
-                print(tagBeginChar);
-                print("/@");
-                int nameStartPos = elementEndPos; // Not 1 less; consider the case of </@>
-                while (nameStartPos >= 2 && !src.startsWith("/@", nameStartPos
- 2)) {
-                    nameStartPos--;
-                }
-                assertNodeContent(nameStartPos >= 2, node,
-                        "Couldn't extract name from end-tag.", null);
-                print(src.substring(nameStartPos, elementEndPos)); // Also prints ignored
WS after name, for now
-                print(tagEndChar);
-            } else { // We don't have end-tag
-                assertNodeContent(src.charAt(startTagEndPos - 1) == '/', node,
-                        "Expected \"/\" at the end of the start tag", null);
-                assertNodeContent(node.getChildCount() == 0, node,
-                        "Expected no children", null);
-            }
-        } else if (node instanceof Comment) {
+    private void printDirIfOrElseOrElseIf(ConditionalBlock node) throws ConverterException
{
+        assertParamCount(node, 2);
+        Expression conditionExp = getParam(node, 0, ParameterRole.CONDITION, Expression.class);
+        int nodeSubtype = getParam(node, 1, ParameterRole.AST_NODE_SUBTYPE, Integer.class);
+
+        print(tagBeginChar);
+        String tagStart;
+        if (nodeSubtype == ConditionalBlock.TYPE_IF) {
+            tagStart = "#if";
+        } else if (nodeSubtype == ConditionalBlock.TYPE_ELSE) {
+            tagStart = "#else";
+        } else if (nodeSubtype == ConditionalBlock.TYPE_ELSE_IF) {
+            tagStart = "#elseIf";
+        } else {
+            throw new UnexpectedNodeContentException(node, "Unhandled subtype, {}.", nodeSubtype);
+        }
+        print(tagStart);
+        if (conditionExp != null) {
+            printWithParamsLeadingSkippedTokens(tagStart.length() + 1, node);
+            printNode(conditionExp);
+        }
+        printStartTagSkippedTokens(node, conditionExp, true);
+        print(tagEndChar);
+
+        printChildrenElements(node);
+
+        if (!(node.getParentElement() instanceof IfBlock)) {
             print(tagBeginChar);
-            print("#--");
-            print(getOnlyParam(node, ParameterRole.CONTENT, String.class));
-            print("--");
+            print("/#if");
+            printEndTagSkippedTokens(node);
             print(tagEndChar);
-        } else {
-            throw new ConverterException("Unhandled AST TemplateElement class: " + node.getClass().getName());
         }
     }
 
-    private void printExpressionNode(TemplateObject node) throws ConverterException {
+    private void printDirIfElseElseIfContainer(IfBlock node) throws ConverterException {
+        printChildrenElements(node);
+        print(tagBeginChar);
+        print("/#if");
+        printEndTagSkippedTokens(node);
+        print(tagEndChar);
+    }
+
+    /**
+     * Prints an expression
+     */
+    private void printExp(Expression node) throws ConverterException {
         if (node instanceof Identifier) {
-            print(FTLUtil.escapeIdentifier(((Identifier) node).getName()));
+            printExpIdentifier((Identifier) node);
         } else if (node instanceof NumberLiteral) {
-            print(getSrcSectionInclEnd(
-                    node.getBeginColumn(), node.getBeginLine(),
-                    node.getEndColumn(), node.getEndLine()));
+            printExpNumericalLiteral((NumberLiteral) node);
         } else if (node instanceof BooleanLiteral) {
-            print(node.getCanonicalForm());
+            printExpBooleanLiteral((BooleanLiteral) node);
         } else if (node instanceof StringLiteral) {
-            boolean rawString = false;
-            char quote;
-            {
-                int pos = getStartPosition(node);
-                quote = src.charAt(pos);
-                while ((quote == '\\' || quote == '{' /* 2.3.26 bug workaround */ || quote
== 'r')
-                        && pos < src.length()) {
-                    pos++;
-                    if (quote == 'r') {
-                        rawString = true;
-                    }
-                    quote = src.charAt(pos);
-                }
-                if (quote != '\'' && quote != '"') {
-                    throw new UnexpectedNodeContentException(node, "Unexpected string quote
character: {}", quote);
+            printExpStringLiteral((StringLiteral) node);
+        } else if (node instanceof ListLiteral) {
+            printExpListLiteral((ListLiteral) node);
+        } else if (node instanceof HashLiteral) {
+            printExpHashLiteral((HashLiteral) node);
+        } else if (node instanceof Range) {
+            printExpRange((Range) node);
+        } else if (node instanceof AddConcatExpression) {
+            printExpAddConcat((AddConcatExpression) node);
+        } else if (node instanceof ArithmeticExpression) {
+            printExpArithmetic((ArithmeticExpression) node);
+        } else if (node instanceof UnaryPlusMinusExpression) {
+            printExpUnaryPlusMinus((UnaryPlusMinusExpression) node);
+        } else if (node instanceof ParentheticalExpression) {
+            printExpParenthetical((ParentheticalExpression) node);
+        } else if (node instanceof MethodCall) {
+            printExpMethodCall((MethodCall) node);
+        } else if (node instanceof DynamicKeyName) {
+            printExpDynamicKeyName((DynamicKeyName) node);
+        } else if (node instanceof BuiltIn) {
+            printExpBuiltIn((BuiltIn) node);
+        } else if (node instanceof Dot) {
+            printExpDot((Dot) node);
+        } else {
+            throw new ConverterException("Unhandled AST node class: " + node.getClass().getName());
+        }
+    }
+
+    private void printExpDot(Dot node) throws ConverterException {
+        assertParamCount(node, 2);
+        Expression lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class);
+        String rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, String.class);
+        printNode(lho);
+        printWithConvertedExpComments(
+                readExpWSAndSeparator(getEndPositionExclusive(lho), '.', false));
+        print(FTLUtil.escapeIdentifier(rho));
+    }
+
+    private void printExpDynamicKeyName(DynamicKeyName node) throws ConverterException {
+        assertParamCount(node, 2);
+
+        Expression hashExp = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class);
+        printExp(hashExp);
+
+        Expression keyExp = getParam(node, 1, ParameterRole.ENCLOSED_OPERAND, Expression.class);
+        printParameterSeparatorSource(hashExp, keyExp);
+        printExp(keyExp);
+
+        printWithParamsTrailingSkippedTokens("]", node, 1);
+    }
+
+    private void printExpRange(Range node) throws ConverterException {
+        assertParamCount(node, 2);
+
+        Expression lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class);
+        Expression rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, Expression.class);
+
+        printExp(lho);
+
+        printWithConvertedExpComments(src.substring(
+                getEndPositionExclusive(lho),
+                rho != null ? getStartPosition(rho) : getEndPositionExclusive(node)));
+
+        if (rho != null) {
+            printExp(rho);
+        }
+    }
+
+    private void printExpHashLiteral(HashLiteral node) throws ConverterException {
+        int openCharPos = getStartPosition(node);
+        int closeCharPos = getEndPositionInclusive(node);
+        assertNodeContent(src.charAt(openCharPos) == '{', node,
+                "Expected '{'", null);
+        assertNodeContent(src.charAt(closeCharPos) == '}', node,
+                "Expected '}'", null);
+
+        int paramCnt = node.getParameterCount();
+        if (paramCnt == 0) {
+            print('{');
+            printWithConvertedExpComments(src.substring(openCharPos + 1, closeCharPos));
+            print('}');
+        } else {
+            printWithParamsLeadingSkippedTokens("{", node);
+            Expression prevValue = null;
+            for (int paramIdx = 0; paramIdx < paramCnt; paramIdx += 2) {
+                Expression key = getParam(node, paramIdx, ParameterRole.ITEM_KEY, Expression.class);
+                Expression value = getParam(node, paramIdx + 1, ParameterRole.ITEM_VALUE,
Expression.class);
+
+                if (prevValue != null) {
+                    printParameterSeparatorSource(prevValue, key);
                 }
+                printExp(key);
+                printParameterSeparatorSource(key, value);
+                printExp(value);
+
+                prevValue = value;
             }
-            if (rawString) {
-                print('r');
-            }
-            print(quote);
+            printWithParamsTrailingSkippedTokens("}", node, node.getParameterCount() - 1);
+        }
+    }
 
-            int parameterCount = node.getParameterCount();
-            if (parameterCount == 0) {
-                if (!rawString) {
-                    print(FTLUtil.escapeStringLiteralPart(((StringLiteral) node).getAsString(),
quote));
-                } else {
-                    print(((StringLiteral) node).getAsString());
-                }
-            } else {
-                // Not really a literal; contains interpolations
-                for (int paramIdx = 0; paramIdx < parameterCount; paramIdx++) {
-                    Object param = getParam(node, paramIdx, ParameterRole.VALUE_PART, Object.class);
-                    if (param instanceof String) {
-                        print(FTLUtil.escapeStringLiteralPart((String) param));
-                    } else {
-                        assertNodeContent(param instanceof Interpolation, node,
-                                "Unexpected parameter type: {}", param.getClass().getName());
-
-                        // We print the interpolation, the cut it out from the output, then
put it back escaped:
-                        int interpStartPos = out.length();
-                        printNode((TemplateElement) param);
-                        int interpEndPos = out.length();
-                        String interp = out.substring(interpStartPos, interpEndPos);
-                        out.setLength(interpStartPos + 2); // +2 to keep the "${"
-                        String inerpInside = interp.substring(2, interp.length() - 1);
-                        print(FTLUtil.escapeStringLiteralPart(inerpInside, quote)); // For
now we escape as FTL2
-                        print(interp.charAt(interp.length() - 1)); // "}"
-                    }
+    private void printExpListLiteral(ListLiteral node) throws ConverterException {
+        int openCharPos = getStartPosition(node);
+        int closeCharPos = getEndPositionInclusive(node);
+        assertNodeContent(src.charAt(openCharPos) == '[', node,
+                "Expected '['", null);
+        assertNodeContent(src.charAt(closeCharPos) == ']', node,
+                "Expected ']'", null);
+
+        int paramCnt = node.getParameterCount();
+        if (paramCnt == 0) {
+            print('[');
+            printWithConvertedExpComments(src.substring(openCharPos + 1, closeCharPos));
+            print(']');
+        } else {
+            printWithParamsLeadingSkippedTokens("[", node);
+            Expression prevItem = null;
+            for (int paramIdx = 0; paramIdx < paramCnt; paramIdx++) {
+                Expression item = getParam(node, paramIdx, ParameterRole.ITEM_VALUE, Expression.class);
+
+                if (prevItem != null) {
+                    printParameterSeparatorSource(prevItem, item);
                 }
+                printExp(item);
+
+                prevItem = item;
             }
+            printWithParamsTrailingSkippedTokens("]", node, node.getParameterCount() - 1);
+        }
+    }
 
-            print(quote);
-        } else if (node instanceof AddConcatExpression) {
-            assertParamCount(node, 2);
-            TemplateObject lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, TemplateObject.class);
-            TemplateObject rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, TemplateObject.class);
-            printNode(lho);
-            printParameterSeparatorSource(lho, rho);
-            printNode(rho);
-        } else if (node instanceof ArithmeticExpression) {
-            assertParamCount(node, 3);
-            assertParamRole(node, 2, ParameterRole.AST_NODE_SUBTYPE);
-            TemplateObject lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, TemplateObject.class);
-            TemplateObject rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, TemplateObject.class);
-            printNode(lho);
-            printParameterSeparatorSource(lho, rho);
-            printNode(rho);
-        } else if (node instanceof UnaryPlusMinusExpression) {
-            assertParamCount(node, 2);
-            assertParamRole(node, 1, ParameterRole.AST_NODE_SUBTYPE);
-            printWithParamsLeadingSkippedTokens(node.getNodeTypeSymbol().substring(0, 1),
node);
-            printNode(getParam(node, 0, ParameterRole.RIGHT_HAND_OPERAND, TemplateObject.class));
-        } else if (node instanceof ParentheticalExpression) {
-            printWithParamsLeadingSkippedTokens("(", node);
-            printNode(getOnlyParam(node, ParameterRole.ENCLOSED_OPERAND, TemplateObject.class));
-            printWithParamsTrailingSkippedTokens(")", node, 0);
-        } else if (node instanceof MethodCall) {
-            TemplateObject callee = getParam(node, 0, ParameterRole.CALLEE, TemplateObject.class);
-            printExpressionNode(callee);
-
-            TemplateObject prevParam = callee;
-            int argCnt = node.getParameterCount() - 1;
-            for (int argIdx = 0; argIdx < argCnt; argIdx++) {
-                TemplateObject argExp = getParam(node, argIdx + 1, ParameterRole.ARGUMENT_VALUE,
TemplateObject.class);
-                printParameterSeparatorSource(prevParam, argExp);
-                printExpressionNode(argExp);
-                prevParam = argExp;
+    private void printExpParenthetical(ParentheticalExpression node) throws ConverterException
{
+        printWithParamsLeadingSkippedTokens("(", node);
+        printNode(getOnlyParam(node, ParameterRole.ENCLOSED_OPERAND, Expression.class));
+        printWithParamsTrailingSkippedTokens(")", node, 0);
+    }
+
+    private void printExpUnaryPlusMinus(UnaryPlusMinusExpression node) throws ConverterException
{
+        assertParamCount(node, 2);
+        assertParamRole(node, 1, ParameterRole.AST_NODE_SUBTYPE);
+        printWithParamsLeadingSkippedTokens(node.getNodeTypeSymbol().substring(0, 1), node);
+        printNode(getParam(node, 0, ParameterRole.RIGHT_HAND_OPERAND, Expression.class));
+    }
+
+    private void printExpArithmetic(ArithmeticExpression node) throws ConverterException
{
+        assertParamCount(node, 3);
+        assertParamRole(node, 2, ParameterRole.AST_NODE_SUBTYPE);
+        Expression lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class);
+        Expression rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, Expression.class);
+        printNode(lho);
+        printParameterSeparatorSource(lho, rho);
+        printNode(rho);
+    }
+
+    private void printExpAddConcat(AddConcatExpression node) throws ConverterException {
+        assertParamCount(node, 2);
+        Expression lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class);
+        Expression rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, Expression.class);
+        printNode(lho);
+        printParameterSeparatorSource(lho, rho);
+        printNode(rho);
+    }
+
+    private void printExpBooleanLiteral(BooleanLiteral node) {
+        print(node.getCanonicalForm());
+    }
+
+    private void printExpNumericalLiteral(NumberLiteral node) {
+        print(getSrcSectionInclEnd(
+                node.getBeginColumn(), node.getBeginLine(),
+                node.getEndColumn(), node.getEndLine()));
+    }
+
+    private void printExpIdentifier(Identifier node) {
+        print(FTLUtil.escapeIdentifier(node.getName()));
+    }
+
+    private void printExpMethodCall(MethodCall node) throws ConverterException {
+        Expression callee = getParam(node, 0, ParameterRole.CALLEE, Expression.class);
+        printExp(callee);
+
+        Expression prevParam = callee;
+        int argCnt = node.getParameterCount() - 1;
+        for (int argIdx = 0; argIdx < argCnt; argIdx++) {
+            Expression argExp = getParam(node, argIdx + 1, ParameterRole.ARGUMENT_VALUE,
Expression.class);
+            printParameterSeparatorSource(prevParam, argExp);
+            printExp(argExp);
+            prevParam = argExp;
+        }
+        printWithParamsTrailingSkippedTokens(")", node, argCnt);
+    }
+
+    private void printExpBuiltIn(BuiltIn node) throws ConverterException {
+        assertParamCount(node, 2);
+        Expression lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, Expression.class);
+        String rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, String.class);
+
+        printExp(lho); // [lho]?biName
+
+        int postLHOPos = getEndPositionExclusive(lho);
+        int endPos = getEndPositionInclusive(node);
+        boolean foundQuestionMark = false;
+        int pos = postLHOPos;
+        scanForRHO: while (pos < endPos) {
+            char c = src.charAt(pos);
+            if (c == '?') {
+                foundQuestionMark = true;
+                pos++;
+            } else if (Character.isWhitespace(c)) {
+                pos++;
+            } else if (isCoreNameChar(c)) {
+                break scanForRHO;
+            } else {
+                throw new UnexpectedNodeContentException(node,
+                        "Unexpected character when scanning for for built-in key: '{}'",
c);
             }
-            printWithParamsTrailingSkippedTokens(")", node, argCnt);
-        } else if (node instanceof BuiltIn) {
-            assertParamCount(node, 2);
-            TemplateObject lho = getParam(node, 0, ParameterRole.LEFT_HAND_OPERAND, TemplateObject.class);
-            String rho = getParam(node, 1, ParameterRole.RIGHT_HAND_OPERAND, String.class);
+        }
+        if (pos == endPos || !foundQuestionMark) {
+            throw new UnexpectedNodeContentException(node, "Couldn't find built-in key in
source", null);
+        }
+        print(src.substring(postLHOPos, pos)); // lho[?]biName
 
-            printExpressionNode(lho); // [lho]?biName
+        print(convertBuiltInName(rho));
+    }
 
-            int postLHOPos = getEndPositionExclusive(lho);
-            int endPos = getEndPositionInclusive(node);
-            boolean foundQuestionMark = false;
-            int pos = postLHOPos;
-            scanForRHO: while (pos < endPos) {
-                char c = src.charAt(pos);
-                if (c == '?') {
-                    foundQuestionMark = true;
-                    pos++;
-                } else if (Character.isWhitespace(c)) {
-                    pos++;
-                } else if (isCoreNameChar(c)) {
-                    break scanForRHO;
-                } else {
-                    throw new UnexpectedNodeContentException(node,
-                            "Unexpected character when scanning for for built-in key: '{}'",
c);
+    private void printExpStringLiteral(StringLiteral node) throws ConverterException {
+        boolean rawString = false;
+        char quote;
+        {
+            int pos = getStartPosition(node);
+            quote = src.charAt(pos);
+            while ((quote == '\\' || quote == '{' /* 2.3.26 bug workaround */ || quote ==
'r')
+                    && pos < src.length()) {
+                pos++;
+                if (quote == 'r') {
+                    rawString = true;
                 }
+                quote = src.charAt(pos);
             }
-            if (pos == endPos || !foundQuestionMark) {
-                throw new UnexpectedNodeContentException(node, "Couldn't find built-in key
in source", null);
+            if (quote != '\'' && quote != '"') {
+                throw new UnexpectedNodeContentException(node, "Unexpected string quote character:
{}", quote);
             }
-            print(src.substring(postLHOPos, pos)); // lho[?]biName
+        }
+        if (rawString) {
+            print('r');
+        }
+        print(quote);
 
-            print(convertBuiltInName(rho));
+        int parameterCount = node.getParameterCount();
+        if (parameterCount == 0) {
+            if (!rawString) {
+                print(FTLUtil.escapeStringLiteralPart(node.getAsString(), quote));
+            } else {
+                print(node.getAsString());
+            }
         } else {
-            throw new ConverterException("Unhandled AST node class: " + node.getClass().getName());
+            // Not really a literal; contains interpolations
+            for (int paramIdx = 0; paramIdx < parameterCount; paramIdx++) {
+                Object param = getParam(node, paramIdx, ParameterRole.VALUE_PART, Object.class);
+                if (param instanceof String) {
+                    print(FTLUtil.escapeStringLiteralPart((String) param));
+                } else {
+                    assertNodeContent(param instanceof Interpolation, node,
+                            "Unexpected parameter type: {}", param.getClass().getName());
+
+                    // We print the interpolation, the cut it out from the output, then put
it back escaped:
+                    int interpStartPos = out.length();
+                    printNode((TemplateElement) param);
+                    int interpEndPos = out.length();
+                    String interp = out.substring(interpStartPos, interpEndPos);
+                    out.setLength(interpStartPos + 2); // +2 to keep the "${"
+                    String inerpInside = interp.substring(2, interp.length() - 1);
+                    print(FTLUtil.escapeStringLiteralPart(inerpInside, quote)); // For now
we escape as FTL2
+                    print(interp.charAt(interp.length() - 1)); // "}"
+                }
+            }
         }
+
+        print(quote);
     }
 
     private String convertBuiltInName(String name) throws ConverterException {
@@ -415,7 +596,7 @@ public class FM2ASTToFM3SourceConverter {
         return converted;
     }
 
-    private void printParameterSeparatorSource(TemplateObject lho, TemplateObject rho) {
+    private void printParameterSeparatorSource(Expression lho, Expression rho) {
         print(getSrcSectionExclEnd(
                 lho.getEndColumn() + 1, lho.getEndLine(),
                 rho.getBeginColumn(), rho.getBeginLine()));
@@ -439,8 +620,8 @@ public class FM2ASTToFM3SourceConverter {
         if (node.getParameterCount() == 0) {
             return;
         }
-        TemplateObject param = getParam(node, 0, null, TemplateObject.class);
-        print(getSrcSectionExclEnd(
+        Expression param = getParam(node, 0, null, Expression.class);
+        printWithConvertedExpComments(getSrcSectionExclEnd(
                 node.getBeginColumn() + beforeParamsLength, node.getBeginLine(),
                 param.getBeginColumn(), param.getBeginLine()));
     }
@@ -452,7 +633,7 @@ public class FM2ASTToFM3SourceConverter {
         String skippedTokens = getSrcSectionExclEnd(
                 node.getBeginColumn() + beforeSkippedTokens.length(), node.getBeginLine(),
                 node.getEndColumn() - afterSkippedTokens.length() + 1, node.getEndLine());
-        print(skippedTokens);
+        printWithConvertedExpComments(skippedTokens);
         print(afterSkippedTokens);
     }
 
@@ -462,11 +643,11 @@ public class FM2ASTToFM3SourceConverter {
         int parameterCount = node.getParameterCount();
         assertNodeContent(lastVisualParamIdx < parameterCount, node,
                 "Parameter count too low: {}", parameterCount);
-        TemplateObject param = getParam(node, lastVisualParamIdx, null, TemplateObject.class);
+        Expression param = getParam(node, lastVisualParamIdx, null, Expression.class);
         String skippedTokens = getSrcSectionExclEnd(
                 param.getEndColumn() + 1, param.getEndLine(),
                 node.getEndColumn() - afterParams.length() + 1, node.getEndLine());
-        print(skippedTokens);
+        printWithConvertedExpComments(skippedTokens);
         print(afterParams);
     }
 
@@ -477,7 +658,7 @@ public class FM2ASTToFM3SourceConverter {
      * @return The position of the last character of the start tag. Note that the printed
string never includes this
      *         character.
      */
-    private int printStartTagSkippedTokens(TemplateElement node, TemplateObject lastParam,
boolean trimSlash)
+    private int printStartTagSkippedTokens(TemplateElement node, Expression lastParam, boolean
trimSlash)
             throws ConverterException {
         int pos;
         if (lastParam == null) {
@@ -507,7 +688,7 @@ public class FM2ASTToFM3SourceConverter {
     }
 
     /**
-     * Similar to {@link #printStartTagSkippedTokens(TemplateElement, TemplateObject, boolean)},
but with explicitly
+     * Similar to {@link #printStartTagSkippedTokens(TemplateElement, Expression, boolean)},
but with explicitly
      * specified scan start position.
      *
      * @param pos The position where the first skipped character can occur (or the tag end
character).
@@ -524,10 +705,10 @@ public class FM2ASTToFM3SourceConverter {
 
         char c = src.charAt(pos);
         if (c == '/' && pos + 1 < src.length() && src.charAt(pos + 1)
== tagEndChar) {
-            print(src.substring(startPos, trimSlash ? pos : pos + 1));
+            printWithConvertedExpComments(src.substring(startPos, trimSlash ? pos : pos +
1));
             return pos + 1;
         } else if (c == tagEndChar) {
-            print(src.substring(startPos, pos));
+            printWithConvertedExpComments(src.substring(startPos, pos));
             return pos;
         } else {
             throw new UnexpectedNodeContentException(node,
@@ -551,7 +732,12 @@ public class FM2ASTToFM3SourceConverter {
         assertNodeContent(pos > 0 && isCoreNameChar(src.charAt(pos)), node,
                 "Can't find end tag name", null);
 
-        print(src.substring(pos + 1, tagEndPos));
+        printWithConvertedExpComments(src.substring(pos + 1, tagEndPos));
+    }
+
+    private void printWithConvertedExpComments(String s) {
+        // Later we might want to convert comment syntax here
+        print(s);
     }
 
     private void print(String s) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/50b4a93e/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
index 31367b2..a00e54d 100644
--- a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
+++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
@@ -65,6 +65,24 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
 
         assertConvertedSame("${true}");
         assertConvertedSame("${false}");
+
+        assertConvertedSame("${f([])}");
+        assertConvertedSame("${f([ <#-- C --> ])}");
+        assertConvertedSame("${f([1])}");
+        assertConvertedSame("${f([1, [x,y], 3])}");
+        assertConvertedSame("${f([<#-- C1 --> 1, <#-- C2 --> 2, <#-- C3 -->
3 <#-- C4 -->])}");
+
+        assertConvertedSame("${f({})}");
+        assertConvertedSame("${f({k: v})}");
+        assertConvertedSame("${f({k1: v1, k2: v2, 'k3': 33})}");
+        assertConvertedSame("${f({ <#-- C1 --> k1 <#-- C1 --> : <#-- C1 -->
v1 <#-- C1 -->,k2:v2 <#-- C1 -->})}");
+
+        assertConvertedSame("${f(1 .. 9)}");
+        assertConvertedSame("${f(1 ..* 9)}");
+        assertConvertedSame("${f(1 ..! 9)}");
+        assertConvertedSame("${f(1 ..< 9)}");
+        assertConvertedSame("${f(1 ..)}");
+        assertConvertedSame("${f(1<#-- C1 -->..\t<#-- C2 -->9)}");
     }
 
     @Test
@@ -76,6 +94,13 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
         assertConvertedSame("${f(1)}");
         assertConvertedSame("${f(1, 2)}");
         assertConvertedSame("${f<#-- C1 -->(<#-- C2 --> 1, 2 ,<#-- C3 -->
3,<#-- C4 -->4 <#-- C5 -->)}");
+
+        assertConvertedSame("${m[key]}");
+        assertConvertedSame("${m['key']}");
+        assertConvertedSame("${m <#-- C1 --> [ <#-- C2 --> key <#-- C3 -->
]}");
+
+        assertConvertedSame("${m.key}");
+        assertConvertedSame("${m <#-- C1 --> . <#-- C3 --> key}");
     }
 
     @Test



Mime
View raw message