You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/07/03 00:07:44 UTC

incubator-freemarker git commit: Continued work on the FM2 to FM3 converter

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 1f086e3f2 -> 70e47bef7


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/70e47bef
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/70e47bef
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/70e47bef

Branch: refs/heads/3
Commit: 70e47bef7b4ae7128ee725acf98e1df87fa3d019
Parents: 1f086e3
Author: ddekany <dd...@apache.org>
Authored: Mon Jul 3 02:07:25 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Mon Jul 3 02:07:25 2017 +0200

----------------------------------------------------------------------
 .../core/FM2ASTToFM3SourceConverter.java        | 151 ++++++++++++++++---
 .../freemarker/converter/FM2ToFM3Converter.java |  13 +-
 .../converter/FM2ToFM3ConverterTest.java        |   7 +
 3 files changed, 143 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/70e47bef/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 b1f221c..8322f95 100644
--- a/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
+++ b/freemarker-converter/src/main/java/freemarker/core/FM2ASTToFM3SourceConverter.java
@@ -28,10 +28,7 @@ import java.util.Set;
 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;
+import org.apache.freemarker.core.util.*;
 
 import freemarker.template.Configuration;
 import freemarker.template.Template;
@@ -67,6 +64,7 @@ import freemarker.template.utility.StringUtil;
 @SuppressWarnings("deprecation")
 public class FM2ASTToFM3SourceConverter {
 
+    private final Template template;
     private final String src;
     private final StringBuilder out;
     private List<Integer> rowStartPositions;
@@ -78,17 +76,34 @@ public class FM2ASTToFM3SourceConverter {
             .build();
     private final Set<String> fm3BuiltInNames = fm3Config.getSupportedBuiltInNames();
 
+    private boolean printNextCustomDirAsFtlDir;
+
     /**
-     * @param template Must have been parsed with {@link Configuration#getWhitespaceStripping()} {@code false}.
+     * @param fm2Cfg The {@link Configuration} used for parsing; {@link Configuration#getWhitespaceStripping()} must
+     *               return {@code false}.
      */
-    public static String convert(Template template, String src) throws ConverterException {
-        FM2ASTToFM3SourceConverter instance = new FM2ASTToFM3SourceConverter(template, src);
-        instance.printNode(template.getRootTreeNode());
-        return instance.getOutput();
+    public static Result convert(String templateName, String src, Configuration fm2Cfg) throws ConverterException {
+        return new FM2ASTToFM3SourceConverter(templateName, src, fm2Cfg).convert();
     }
 
-    private FM2ASTToFM3SourceConverter(Template template, String src) {
-        _NullArgumentException.check("template", template);
+    private Result convert() throws ConverterException {
+        printDirFtl();
+        printNode(template.getRootTreeNode());
+
+        String outAsString = out.toString();
+        try {
+            //!!T new org.apache.freemarker.core.Template(null, s, fm3Config);
+        } catch (Exception e) {
+            throw new ConverterException(
+                    "The result of the conversion wasn't valid FreeMarker 3 template; see cause exception", e);
+        }
+
+        return new Result(template, outAsString);
+    }
+
+    private FM2ASTToFM3SourceConverter(String templateName, String src, Configuration fm2Cfg)
+            throws ConverterException {
+        template = createTemplate(templateName, src, fm2Cfg);
         if (template.getParserConfiguration().getWhitespaceStripping()) {
             throw new IllegalArgumentException("The Template must have been parsed with whitespaceStripping false.");
         }
@@ -106,15 +121,80 @@ public class FM2ASTToFM3SourceConverter {
         }
     }
 
-    private String getOutput() throws ConverterException {
-        String s = out.toString();
+    /**
+     * Converts a {@link String} to a {@link Template}.
+     */
+    private static Template createTemplate(String templateName, String src, Configuration fm2Cfg) throws
+            ConverterException {
+        if (fm2Cfg.getWhitespaceStripping()) {
+            throw new IllegalArgumentException("Configuration.whitespaceStripping must be false");
+        }
         try {
-            //!!T new org.apache.freemarker.core.Template(null, s, fm3Config);
+            return new Template(templateName, src, fm2Cfg);
         } catch (Exception e) {
-            throw new ConverterException(
-                    "The result of the conversion wasn't valid FreeMarker 3 template; see cause exception", e);
+            throw new ConverterException("Failed to load FreeMarker 2.3.x template", e);
         }
-        return s;
+    }
+
+    // The FTL tag is not part of the AST tree, so we have to treat it differently
+    private void printDirFtl() throws ConverterException {
+        int pos = getPositionAfterWSAndExpComments(0);
+        if (src.length() > pos + 1 && src.charAt(pos) == tagBeginChar && src.startsWith("#ftl", pos + 1)) {
+            printWithConvertedExpComments(src.substring(0, pos));
+
+            pos += 5; // "<#ftl".length()
+
+            int tagEnd;
+            String postFtlTagSkippedWS;
+            {
+                int firstNodePos;
+
+                TemplateElement rootNode = template.getRootTreeNode();
+                if (rootNode.getBeginLine() != 0) {
+                    firstNodePos = getStartPosition(rootNode);
+                    assertNodeContent(firstNodePos > pos, rootNode,
+                            "Root node position should be after #ftl header");
+                } else {
+                    // Extreme case where the template only contains the #ftl header.
+                    assertNodeContent(rootNode.getEndLine() == 0, rootNode,
+                            "Expected 0 end line for root node");
+                    firstNodePos = src.length();
+                }
+
+                tagEnd = firstNodePos - 1;
+                while (tagEnd >= 0 && src.charAt(tagEnd) != tagEndChar) {
+                    if (!Character.isWhitespace(src.charAt(tagEnd))) {
+                        throw new ConverterException("Non-WS character while backtracking to #ftl tag end character.");
+                    }
+                    tagEnd--;
+                }
+                if (tagEnd < 0) {
+                    throw new ConverterException("Couldn't backtrack to #ftl tag end character.");
+                }
+
+                postFtlTagSkippedWS = src.substring(tagEnd + 1, firstNodePos);
+            }
+
+            boolean hasSlash = src.charAt(tagEnd - 1) == '/';
+
+            // We need the Expression-s parsed, but they aren't part of the AST. So, we parse a template that contains
+            // a similar custom "ftl" directive, and the converter to print it as an #ftl directive.
+            FM2ASTToFM3SourceConverter customFtlDirSrcConverter = new FM2ASTToFM3SourceConverter(
+                    template.getName(),
+                    tagBeginChar + "@ftl" + src.substring(pos, tagEnd) + (hasSlash ? "" : "/") + tagEndChar,
+                    template.getConfiguration());
+            customFtlDirSrcConverter.printNextCustomDirAsFtlDir = true;
+            String fm3Content = customFtlDirSrcConverter.convert().fm3Content;
+            print(hasSlash
+                    ? fm3Content
+                    : fm3Content.substring(0, fm3Content.length() - 2) + tagEndChar);
+
+            print(postFtlTagSkippedWS);
+        }
+    }
+
+    private String convertFtlHeaderParamName(String name) throws ConverterException {
+        return name.indexOf('_') == -1 ? name : ConverterUtils.snakeCaseToCamelCase(name);
     }
 
     private void printNode(TemplateObject node) throws ConverterException {
@@ -222,6 +302,8 @@ public class FM2ASTToFM3SourceConverter {
             printDirAssignmentMultiple((AssignmentInstruction) node);
         } else if (node instanceof AttemptBlock) {
             printDirAttemptRecover((AttemptBlock) node);
+        } else if (node instanceof AttemptBlock) {
+            printDirAttemptRecover((AttemptBlock) node);
         } else {
             throw new ConverterException("Unhandled AST TemplateElement class: " + node.getClass().getName());
         }
@@ -443,8 +525,11 @@ public class FM2ASTToFM3SourceConverter {
     }
 
     private void printDirCustom(UnifiedCall node) throws ConverterException {
+        boolean ftlDirMode = printNextCustomDirAsFtlDir;
+        printNextCustomDirAsFtlDir = false;
+
         print(tagBeginChar);
-        print('@');
+        print(ftlDirMode ? '#' : '@');
 
         Expression callee = getParam(node, 0, ParameterRole.CALLEE, Expression.class);
         printExp(callee);
@@ -467,9 +552,19 @@ public class FM2ASTToFM3SourceConverter {
         // Print named arguments:
         while (paramIdx < paramCount
                 && node.getParameterRole(paramIdx) == ParameterRole.ARGUMENT_NAME) {
+            String paramName = getParam(node, paramIdx, ParameterRole.ARGUMENT_NAME, String.class);
             Expression argValue = getParam(node, paramIdx + 1, ParameterRole.ARGUMENT_VALUE, Expression.class);
 
-            printParameterSeparatorSource(lastPrintedExp, argValue); // Prints something like " someArgName="
+            int pos = getEndPositionExclusive(lastPrintedExp);
+            pos = printWSAndExpComments(pos, ",", true);
+            int paramNameStartPos = pos;
+            pos = getPositionAfterIdentifier(pos);
+            assertNodeContent(pos > paramNameStartPos, node, "Parameter name in src was empty");
+            if (ftlDirMode) {
+                paramName = convertFtlHeaderParamName(paramName);
+            }
+            print(FTLUtil.escapeIdentifier(paramName));
+            printWSAndExpComments(pos, "=", false);
             printExp(argValue);
 
             lastPrintedExp = argValue;
@@ -1385,4 +1480,22 @@ public class FM2ASTToFM3SourceConverter {
 
     }
 
+    public static class Result {
+        private final Template fm2Template;
+        private final String fm3Content;
+
+        public Result(Template fm2Template, String fm3Content) {
+            this.fm2Template = fm2Template;
+            this.fm3Content = fm3Content;
+        }
+
+        public Template getFM2Template() {
+            return fm2Template;
+        }
+
+        public String getFM3Content() {
+            return fm3Content;
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/70e47bef/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java
----------------------------------------------------------------------
diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java
index e4c3fa5..a70c08e 100644
--- a/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java
+++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java
@@ -93,16 +93,11 @@ public class FM2ToFM3Converter extends Converter {
     @Override
     protected void convertFile(FileConversionContext fileTransCtx) throws ConverterException, IOException {
         String src = IOUtils.toString(fileTransCtx.getSourceStream(), StandardCharsets.UTF_8);
-        Template template;
-        try {
-            template = new Template(fileTransCtx.getSourceFile().getName(), src, fm2Cfg);
-        } catch (Exception e) {
-            throw new ConverterException("Failed to load FreeMarker 2.3.x template", e);
-        }
-
-        fileTransCtx.setDestinationFileName(getDestinationFileName(template));
+        FM2ASTToFM3SourceConverter.Result result = FM2ASTToFM3SourceConverter.convert(fileTransCtx.getSourceFile()
+                .getName(), src, fm2Cfg);
+        fileTransCtx.setDestinationFileName(getDestinationFileName(result.getFM2Template()));
         fileTransCtx.getDestinationStream().write(
-                FM2ASTToFM3SourceConverter.convert(template, src).getBytes(getTemplateEncoding(template)));
+                result.getFM3Content().getBytes(getTemplateEncoding(result.getFM2Template())));
     }
 
     private String getTemplateEncoding(Template template) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/70e47bef/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 0f74125..675a3c6 100644
--- a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
+++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterTest.java
@@ -203,6 +203,13 @@ public class FM2ToFM3ConverterTest extends ConverterTest {
         assertConvertedSame("<#attempt >a<#recover  >r</#attempt   >");
         assertConverted("<#attempt>a<#recover>r</#attempt>", "<#attempt>a<#recover>r</#recover>");
         assertConverted("<#attempt >a<#recover  >r</#attempt   >", "<#attempt >a<#recover  >r</#recover   >");
+
+        assertConvertedSame("<#ftl>");
+        assertConvertedSame("<#ftl>x");
+        assertConvertedSame("<#ftl>x${x}");
+        assertConvertedSame("<#ftl>\nx${x}");
+        assertConvertedSame("\n\n  <#ftl>\n\nx");
+        assertConverted("<#ftl outputFormat='HTML'>x", "<#ftl output_format='HTML'>x");
     }
 
     @Test