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&"); + } + + @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&"); + } + + @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&"); + } + + @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&"); + } + +}