Simplified (and faster) capturing assignment implementation (backported from 
FM3 branch)


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

Branch: refs/heads/2.3
Commit: 16ff17463762a18e9316814e53b1ad0e5eb09559
Parents: 77e877c
Author: ddekany <ddek...@apache.org>
Authored: Thu Jul 27 18:54:01 2017 +0200
Committer: ddekany <ddek...@apache.org>
Committed: Thu Jul 27 19:14:57 2017 +0200

----------------------------------------------------------------------
 .../java/freemarker/core/BlockAssignment.java   | 84 +++++---------------
 src/main/java/freemarker/core/Environment.java  | 15 ++++
 .../core/CapturingAssignmentTest.java           | 55 +++++++++++++
 3 files changed, 89 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/16ff1746/src/main/java/freemarker/core/BlockAssignment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BlockAssignment.java 
b/src/main/java/freemarker/core/BlockAssignment.java
index 07ff070..8c9e403 100644
--- a/src/main/java/freemarker/core/BlockAssignment.java
+++ b/src/main/java/freemarker/core/BlockAssignment.java
@@ -21,14 +21,11 @@ package freemarker.core;
 
 import java.io.IOException;
 import java.io.StringWriter;
-import java.io.Writer;
-import java.util.Map;
 
 import freemarker.template.SimpleScalar;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateModel;
 import freemarker.template.TemplateModelException;
-import freemarker.template.TemplateTransformModel;
 
 /**
  * Like [#local x]...[/#local].
@@ -51,21 +48,28 @@ final class BlockAssignment extends TemplateElement {
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, 
IOException {
         TemplateElement[] children = getChildBuffer();
+        
+        TemplateModel value;
         if (children != null) {
-            env.visitAndTransform(children, new CaptureOutput(env), null);
+            StringWriter out = new StringWriter();
+            env.visit(children, out);
+            value = capturedStringToModel(out.toString());
+        } else {
+            value = capturedStringToModel("");
+        }
+        
+        if (namespaceExp != null) {
+            ((Environment.Namespace) namespaceExp.eval(env)).put(varName, 
value);
+        } else if (scope == Assignment.NAMESPACE) {
+            env.setVariable(varName, value);
+        } else if (scope == Assignment.GLOBAL) {
+            env.setGlobalVariable(varName, value);
+        } else if (scope == Assignment.LOCAL) {
+            env.setLocalVariable(varName, value);
         } else {
-            TemplateModel value = capturedStringToModel("");
-            if (namespaceExp != null) {
-                Environment.Namespace ns = (Environment.Namespace) 
namespaceExp.eval(env);
-                ns.put(varName, value);
-            } else if (scope == Assignment.NAMESPACE) {
-                env.setVariable(varName, value);
-            } else if (scope == Assignment.GLOBAL) {
-                env.setGlobalVariable(varName, value);
-            } else if (scope == Assignment.LOCAL) {
-                env.setLocalVariable(varName, value);
-            }
+            throw new BugException("Unhandled scope");
         }
+        
         return null;
     }
 
@@ -73,56 +77,6 @@ final class BlockAssignment extends TemplateElement {
         return markupOutputFormat == null ? new SimpleScalar(s) : 
markupOutputFormat.fromMarkup(s);
     }
 
-    private class CaptureOutput implements TemplateTransformModel {
-        private final Environment env;
-        private final Environment.Namespace fnsModel;
-        
-        CaptureOutput(Environment env) throws TemplateException {
-            this.env = env;
-            TemplateModel nsModel = null;
-            if (namespaceExp != null) {
-                nsModel = namespaceExp.eval(env);
-                if (!(nsModel instanceof Environment.Namespace)) {
-                    throw new NonNamespaceException(namespaceExp, nsModel, 
env);
-                }
-            }
-            fnsModel = (Environment.Namespace ) nsModel; 
-        }
-        
-        public Writer getWriter(Writer out, Map args) {
-            return new StringWriter() {
-                @Override
-                public void close() throws IOException {
-                    TemplateModel result;
-                    try {
-                        result = capturedStringToModel(toString());
-                    } catch (TemplateModelException e) {
-                        // [Java 1.6] e to cause
-                        throw new IOException("Failed to create FTL value from 
captured string: " + e);
-                    }
-                    switch(scope) {
-                        case Assignment.NAMESPACE: {
-                            if (fnsModel != null) {
-                                fnsModel.put(varName, result);
-                            } else {
-                                env.setVariable(varName, result);
-                            }
-                            break;
-                        }
-                        case Assignment.LOCAL: {
-                            env.setLocalVariable(varName, result);
-                            break;
-                        }
-                        case Assignment.GLOBAL: {
-                            env.setGlobalVariable(varName, result);
-                            break;
-                        }
-                    }
-                }
-            };
-        }
-    }
-    
     @Override
     protected String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/16ff1746/src/main/java/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Environment.java 
b/src/main/java/freemarker/core/Environment.java
index 1fa59a0..c826381 100644
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@ -377,6 +377,21 @@ public final class Environment extends Configurable {
         }
     }
 
+    /**
+     * Visits the elements while temporarily using the parameter output {@link 
Writer}.
+     * 
+     * @since 2.3.27
+     */
+    final void visit(TemplateElement[] elementBuffer, Writer out) throws 
IOException, TemplateException {
+        Writer prevOut = this.out;
+        this.out = out;
+        try {
+            visit(elementBuffer);
+        } finally {
+            this.out = prevOut;
+        }
+    }
+    
     @SuppressFBWarnings(value = "RANGE_ARRAY_INDEX", justification = "Not 
called when stack is empty")
     private TemplateElement replaceTopElement(TemplateElement element) {
         return instructionStack[instructionStackSize - 1] = element;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/16ff1746/src/test/java/freemarker/core/CapturingAssignmentTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/CapturingAssignmentTest.java 
b/src/test/java/freemarker/core/CapturingAssignmentTest.java
new file mode 100644
index 0000000..2664dff
--- /dev/null
+++ b/src/test/java/freemarker/core/CapturingAssignmentTest.java
@@ -0,0 +1,55 @@
+package freemarker.core;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import freemarker.cache.StringTemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.TemplateException;
+import freemarker.test.TemplateTest;
+
+public class CapturingAssignmentTest extends TemplateTest {
+
+    @Override
+    protected Configuration createConfiguration() throws Exception {
+        Configuration cfg = super.createConfiguration();
+        cfg.setTemplateLoader(new StringTemplateLoader());
+        return cfg;
+    }
+
+    @Test
+    public void testAssign() throws IOException, TemplateException {
+        assertOutput("<#assign x></#assign>[${x}]", "[]");
+        assertOutput("<#assign x><p>${1 + 1}</#assign>${x + '&'}", "<p>2&");
+        assertOutput("<#ftl outputFormat='HTML'><#assign x><p>${1 + 
1}</#assign>${x + '&'}", "<p>2&amp;");
+    }
+
+    @Test
+    public void testAssignNs() throws IOException, TemplateException {
+        addTemplate("lib.ftl", "");
+        assertOutput("<#import 'lib.ftl' as lib>"
+                + "<#assign x in lib></#assign>[${lib.x}]", "[]");
+        assertOutput("<#import 'lib.ftl' as lib>"
+                + "<#assign x in lib><p>${1 + 1}</#assign>${lib.x + '&'}", 
"<p>2&");
+        assertOutput("<#ftl outputFormat='HTML'>"
+                + "<#import 'lib.ftl' as lib>"
+                + "<#assign x in lib><p>${1 + 1}</#assign>${lib.x + '&'}", 
"<p>2&amp;");
+    }
+    
+    @Test
+    public void testGlobal() throws IOException, TemplateException {
+        assertOutput("<#global x></#global>[${.globals.x}]", "[]");
+        assertOutput("<#global x><p>${1 + 1}</#global>${.globals.x + '&'}", 
"<p>2&");
+        assertOutput("<#ftl outputFormat='HTML'><#global x><p>${1 + 
1}</#global>${.globals.x + '&'}", "<p>2&amp;");
+    }
+
+    @Test
+    public void testLocal() throws IOException, TemplateException {
+        assertOutput("<#macro m><#local x></#local>[${x}]</#macro><@m/>${x!}", 
"[]");
+        assertOutput("<#macro m><#local x><p>${1 + 1}</#local>${x + 
'&'}</#macro><@m/>${x!}", "<p>2&");
+        assertOutput("<#ftl outputFormat='HTML'>"
+                + "<#macro m><#local x><p>${1 + 1}</#local>${x + 
'&'}</#macro><@m/>${x!}", "<p>2&amp;");
+    }
+    
+}

Reply via email to