FREEMARKER-63: Very early state. Simplified capturing assignment implementation
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/146c4257 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/146c4257 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/146c4257 Branch: refs/heads/3 Commit: 146c42572c440dd4ef911d437239d3e54a7fe95b Parents: fbbfadb Author: ddekany <[email protected]> Authored: Thu Jul 27 18:43:05 2017 +0200 Committer: ddekany <[email protected]> Committed: Thu Jul 27 19:13:54 2017 +0200 ---------------------------------------------------------------------- .../core/CapturingAssignmentTest.java | 63 ++++++++++++++ .../core/ASTDirCapturingAssignment.java | 87 +++++--------------- .../org/apache/freemarker/core/Environment.java | 50 +++++------ .../apache/freemarker/test/TemplateTest.java | 2 +- 4 files changed, 112 insertions(+), 90 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/146c4257/freemarker-core-test/src/test/java/org/apache/freemarker/core/CapturingAssignmentTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/CapturingAssignmentTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CapturingAssignmentTest.java new file mode 100644 index 0000000..44cf134 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/CapturingAssignmentTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import java.io.IOException; + +import org.apache.freemarker.test.TemplateTest; +import org.junit.Test; + +public class CapturingAssignmentTest extends TemplateTest { + + @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&"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/146c4257/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java index 28edbf2..77fc8fa 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java @@ -21,14 +21,12 @@ package org.apache.freemarker.core; import java.io.IOException; import java.io.StringWriter; -import java.io.Writer; -import java.util.Map; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.impl.SimpleScalar; import org.apache.freemarker.core.outputformat.MarkupOutputFormat; +import org.apache.freemarker.core.util.BugException; /** * AST directive node: Like {@code <#local x>...</#local>}. @@ -51,21 +49,29 @@ final class ASTDirCapturingAssignment extends ASTDirective { @Override ASTElement[] accept(Environment env) throws TemplateException, IOException { ASTElement[] children = getChildBuffer(); + + TemplateModel capturedValue; if (children != null) { - env.visitAndTransform(children, new CaptureOutput(env), null); + StringWriter out = new StringWriter(); + env.visit(children, out); + capturedValue = capturedStringToModel(out.toString()); + } else { + capturedValue = capturedStringToModel(""); + } + + if (namespaceExp != null) { + Environment.Namespace ns = (Environment.Namespace) namespaceExp.eval(env); + ns.put(varName, capturedValue); + } else if (scope == ASTDirAssignment.NAMESPACE) { + env.setVariable(varName, capturedValue); + } else if (scope == ASTDirAssignment.GLOBAL) { + env.setGlobalVariable(varName, capturedValue); + } else if (scope == ASTDirAssignment.LOCAL) { + env.setLocalVariable(varName, capturedValue); } else { - TemplateModel value = capturedStringToModel(""); - if (namespaceExp != null) { - Environment.Namespace ns = (Environment.Namespace) namespaceExp.eval(env); - ns.put(varName, value); - } else if (scope == ASTDirAssignment.NAMESPACE) { - env.setVariable(varName, value); - } else if (scope == ASTDirAssignment.GLOBAL) { - env.setGlobalVariable(varName, value); - } else if (scope == ASTDirAssignment.LOCAL) { - env.setLocalVariable(varName, value); - } + throw new BugException("Unhandled scope"); } + return null; } @@ -73,57 +79,6 @@ final class ASTDirCapturingAssignment extends ASTDirective { 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; - } - - @Override - 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 invoke FTL value from captured string: " + e); - } - switch(scope) { - case ASTDirAssignment.NAMESPACE: { - if (fnsModel != null) { - fnsModel.put(varName, result); - } else { - env.setVariable(varName, result); - } - break; - } - case ASTDirAssignment.LOCAL: { - env.setLocalVariable(varName, result); - break; - } - case ASTDirAssignment.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/146c4257/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java index 875366f..3be9fb5 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java @@ -499,32 +499,36 @@ public final class Environment extends MutableProcessingConfiguration<Environmen ASTElement[] childBuffer, final StringToIndexMap loopVarNames, final TemplateModel[] loopVarValues, Writer out) - throws IOException, TemplateException { - // TODO [FM][CF] The plan is that `out` will be the root read only sink, so then this won't be here. - Writer prevOut = this.out; - this.out = out; - try { - if (loopVarNames == null) { - visit(childBuffer); - } else { - pushLocalContext(new LocalContext() { - @Override - public TemplateModel getLocalVariable(String name) throws TemplateModelException { - int index = loopVarNames.get(name); - return index != -1 ? loopVarValues[index] : null; - } + throws IOException, TemplateException { + if (loopVarNames == null) { // This is by far the most frequent case + visit(childBuffer, out); + } else { + pushLocalContext(new LocalContext() { + @Override + public TemplateModel getLocalVariable(String name) throws TemplateModelException { + int index = loopVarNames.get(name); + return index != -1 ? loopVarValues[index] : null; + } - @Override - public Collection<String> getLocalVariableNames() throws TemplateModelException { - return loopVarNames.getKeys(); - } - }); - try { - visit(childBuffer); - } finally { - popLocalContext(); + @Override + public Collection<String> getLocalVariableNames() throws TemplateModelException { + return loopVarNames.getKeys(); } + }); + try { + visit(childBuffer, out); + } finally { + popLocalContext(); } + } + } + + void visit(ASTElement[] childBuffer, Writer out) throws IOException, TemplateException { + // TODO [FM][CF] The plan is that `out` will be the root read only sink, so then it will work differently + Writer prevOut = this.out; + this.out = out; + try { + visit(childBuffer); } finally { this.out = prevOut; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/146c4257/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java ---------------------------------------------------------------------- diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java index 3ee5fb2..9e588d4 100644 --- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java +++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java @@ -58,7 +58,7 @@ public abstract class TemplateTest { private Map<String, String> addedTemplates = new HashMap<>(); /** - * Gets the {@link Configuration} used, automaticlly creating and setting if it wasn't yet. + * Gets the {@link Configuration} used, automatically creating and setting if it wasn't yet. */ protected final Configuration getConfiguration() { if (configuration == null) {
