http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java index 9482f9c..1624732 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java @@ -32,12 +32,12 @@ class ASTDirSep extends ASTDirective { } @Override - ASTElement[] accept(Environment env) throws TemplateException, IOException { + ASTElement[] execute(Environment env) throws TemplateException, IOException { final IterationContext iterCtx = ASTDirList.findEnclosingIterationContext(env, null); if (iterCtx == null) { // The parser should prevent this situation throw new TemplateException(env, - getASTNodeDescriptor(), " without iteration in context"); + getLabelWithoutParameters(), " without iteration in context"); } if (iterCtx.hasNext()) { @@ -52,22 +52,22 @@ class ASTDirSep extends ASTDirective { } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder sb = new StringBuilder(); if (canonical) sb.append('<'); - sb.append(getASTNodeDescriptor()); + sb.append(getLabelWithoutParameters()); if (canonical) { sb.append('>'); sb.append(getChildrenCanonicalForm()); sb.append("</"); - sb.append(getASTNodeDescriptor()); + sb.append(getLabelWithoutParameters()); sb.append('>'); } return sb.toString(); } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#sep"; }
http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSetting.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSetting.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSetting.java index 953fa7f..9d76aef 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSetting.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSetting.java @@ -104,7 +104,7 @@ final class ASTDirSetting extends ASTDirective { } @Override - ASTElement[] accept(Environment env) throws TemplateException { + ASTElement[] execute(Environment env) throws TemplateException { TemplateModel mval = value.eval(env); String strval; if (mval instanceof TemplateStringModel) { @@ -125,10 +125,10 @@ final class ASTDirSetting extends ASTDirective { } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder sb = new StringBuilder(); if (canonical) sb.append('<'); - sb.append(getASTNodeDescriptor()); + sb.append(getLabelWithoutParameters()); sb.append(' '); sb.append(_StringUtils.toFTLTopLevelTragetIdentifier(key)); sb.append('='); @@ -138,7 +138,7 @@ final class ASTDirSetting extends ASTDirective { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#setting"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirStop.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirStop.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirStop.java index bef9654..dcee928 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirStop.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirStop.java @@ -31,7 +31,7 @@ final class ASTDirStop extends ASTDirective { } @Override - ASTElement[] accept(Environment env) throws TemplateException { + ASTElement[] execute(Environment env) throws TemplateException { if (exp == null) { throw new StopException(env); } @@ -39,10 +39,10 @@ final class ASTDirStop extends ASTDirective { } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder sb = new StringBuilder(); if (canonical) sb.append('<'); - sb.append(getASTNodeDescriptor()); + sb.append(getLabelWithoutParameters()); if (exp != null) { sb.append(' '); sb.append(exp.getCanonicalForm()); @@ -52,7 +52,7 @@ final class ASTDirStop extends ASTDirective { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#stop"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSwitch.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSwitch.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSwitch.java index 296dd48..909e88d 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSwitch.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSwitch.java @@ -39,7 +39,7 @@ final class ASTDirSwitch extends ASTDirective { int ignoredCnt = ignoredSectionBeforeFirstCase != null ? ignoredSectionBeforeFirstCase.getChildCount() : 0; setChildBufferCapacity(ignoredCnt + 4); for (int i = 0; i < ignoredCnt; i++) { - addChild(ignoredSectionBeforeFirstCase.getChild(i)); + addChild(ignoredSectionBeforeFirstCase.fastGetChild(i)); } firstCaseIndex = ignoredCnt; // Note that normally postParseCleanup will overwrite this } @@ -52,13 +52,13 @@ final class ASTDirSwitch extends ASTDirective { } @Override - ASTElement[] accept(Environment env) + ASTElement[] execute(Environment env) throws TemplateException, IOException { boolean processedCase = false; int ln = getChildCount(); try { for (int i = firstCaseIndex; i < ln; i++) { - ASTDirCase cas = (ASTDirCase) getChild(i); + ASTDirCase cas = (ASTDirCase) fastGetChild(i); boolean processCase = false; // Fall through if a previous case tested true. @@ -71,7 +71,7 @@ final class ASTDirSwitch extends ASTDirective { _EvalUtils.CMP_OP_EQUALS, "case==", cas.condition, cas.condition, env); } if (processCase) { - env.visit(cas); + env.executeElement(cas); processedCase = true; } } @@ -79,7 +79,7 @@ final class ASTDirSwitch extends ASTDirective { // If we didn't process any nestedElements, and we have a default, // process it. if (!processedCase && defaultCase != null) { - env.visit(defaultCase); + env.executeElement(defaultCase); } } catch (BreakOrContinueException br) { // #break was called @@ -88,25 +88,25 @@ final class ASTDirSwitch extends ASTDirective { } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder buf = new StringBuilder(); if (canonical) buf.append('<'); - buf.append(getASTNodeDescriptor()); + buf.append(getLabelWithoutParameters()); buf.append(' '); buf.append(searched.getCanonicalForm()); if (canonical) { buf.append('>'); int ln = getChildCount(); for (int i = 0; i < ln; i++) { - buf.append(getChild(i).getCanonicalForm()); + buf.append(fastGetChild(i).getCanonicalForm()); } - buf.append("</").append(getASTNodeDescriptor()).append('>'); + buf.append("</").append(getLabelWithoutParameters()).append('>'); } return buf.toString(); } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#switch"; } @@ -139,7 +139,7 @@ final class ASTDirSwitch extends ASTDirective { // The first #case might have shifted in the child array, so we have to find it again: int ln = getChildCount(); int i = 0; - while (i < ln && !(getChild(i) instanceof ASTDirCase)) { + while (i < ln && !(fastGetChild(i) instanceof ASTDirCase)) { i++; } firstCaseIndex = i; http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirTOrRtOrLtOrNt.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirTOrRtOrLtOrNt.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirTOrRtOrLtOrNt.java index 5b79e30..102d1c9 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirTOrRtOrLtOrNt.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirTOrRtOrLtOrNt.java @@ -37,22 +37,22 @@ final class ASTDirTOrRtOrLtOrNt extends ASTDirective { } @Override - ASTElement[] accept(Environment env) { + ASTElement[] execute(Environment env) { // This instruction does nothing at render-time, only parse-time. return null; } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder sb = new StringBuilder(); if (canonical) sb.append('<'); - sb.append(getASTNodeDescriptor()); + sb.append(getLabelWithoutParameters()); if (canonical) sb.append("/>"); return sb.toString(); } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { if (left && right) { return "#t"; } else if (left) { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java index d2a6e4d..181bb00 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java @@ -40,7 +40,7 @@ final class ASTDirVisit extends ASTDirective { } @Override - ASTElement[] accept(Environment env) throws IOException, TemplateException { + ASTElement[] execute(Environment env) throws IOException, TemplateException { TemplateModel node = targetNode.eval(env); if (!(node instanceof TemplateNodeModel)) { throw MessageUtils.newUnexpectedOperandTypeException(targetNode, node, TemplateNodeModel.class, env); @@ -72,10 +72,10 @@ final class ASTDirVisit extends ASTDirective { } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder sb = new StringBuilder(); if (canonical) sb.append('<'); - sb.append(getASTNodeDescriptor()); + sb.append(getLabelWithoutParameters()); sb.append(' '); sb.append(targetNode.getCanonicalForm()); if (namespaces != null) { @@ -87,7 +87,7 @@ final class ASTDirVisit extends ASTDirective { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#visit"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirective.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirective.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirective.java index 6015376..40cdc90 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirective.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirective.java @@ -22,9 +22,13 @@ import java.util.Collections; import java.util.Set; import java.util.TreeSet; +import org.apache.freemarker.core.util.StringToIndexMap; + /** * AST directive node superclass. + * Concrete instances are normally created using {@link StaticallyLinkedNamespaceEntry#getDirectiveCallNodeFactory()}/ */ +// TODO [FM3] will be public abstract class ASTDirective extends ASTElement { static final Set<String> BUILT_IN_DIRECTIVE_NAMES; @@ -75,5 +79,64 @@ abstract class ASTDirective extends ASTElement { BUILT_IN_DIRECTIVE_NAMES = Collections.unmodifiableSet(names); } + + /** + * Called by the parser to when it has parsed a parameter value expression for a positional parameter. + * + * <p> + * It's guaranteed that either {@link #setPositionalArgument(int, ASTExpression)} or + * {@link #setNamedArgument(String, ASTExpression)} is called for each parameter in the source code exactly once, in + * the order as the corresponding parameters occur in the source code. (While {@link DefaultTemplateLanguage} + * guarantees that positional parameters are before the named parameters, other {@link TemplateLanguage}-s may don't + * have such restriction.) + * + * @param position + * The 0-based position of the parameter among the positional parameters. + */ + public void setPositionalArgument(int position, ASTExpression valueExp) + throws StaticLinkingCheckException { + // TODO [FM3][FREEMARKER-99] Will be abstract + } + + /** + * Called by the parser when it has parsed a parameter expression. + * + * <p>See guarantees regarding the call order and the number of calls in the description of + * {@link #setPositionalArgument(int, ASTExpression)}. + */ + public void setNamedArgument(String name, ASTExpression valueExp) + throws StaticLinkingCheckException { + // TODO [FM3][FREEMARKER-99] Will be abstract + } + + /** + * Called by the parser when it has already passed in all arguments (via + * {@link #setPositionalArgument(int, ASTExpression)} and such). This allows the directive to check if all required + * arguments were provided, and some more. It also sets the nested content parameter names (like {@code "i", "j"} in + * {@code <#list m as i, j>}). (These two operations are packed into this method together as optimization, though + * admittedly the gain is very small.) + * + * @param nestedContentParamNames + * Will be {@code null} exactly if there are 0 nested content parameters. + */ + public void checkArgumentsAndSetNestedContentParameters(StringToIndexMap nestedContentParamNames) + throws StaticLinkingCheckException { + // TODO [FM3][FREEMARKER-99] Will be abstract + } + + /** + * Tells if this directive can have nested content; the parser may need this information. + */ + public boolean isNestedContentSupported() { + // TODO [FM3][FREEMARKER-99] Will be abstract + return true; + } + + /** + * @return {@code null} if there was no nested content parameter + */ + public StringToIndexMap getNestedContentParamNames() { + return null; + } } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java index dd8394f..a84460f 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java @@ -83,7 +83,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace { } @Override - ASTElement[] accept(Environment env) throws TemplateException, IOException { + ASTElement[] execute(Environment env) throws TemplateException, IOException { TemplateCallableModel callableValue; TemplateDirectiveModel directive; TemplateFunctionModel function; @@ -157,7 +157,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace { } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { StringBuilder sb = new StringBuilder(); if (canonical) sb.append('<'); sb.append('@'); @@ -211,7 +211,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "@"; } @@ -286,7 +286,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace { @Override public boolean hasNestedContent() { int childCount = getChildCount(); - return childCount != 0 && (childCount > 1 || !(getChild(0) instanceof ASTThreadInterruptionCheck)); + return childCount != 0 && (childCount > 1 || !(fastGetChild(0) instanceof ASTThreadInterruptionCheck)); } @Override @@ -301,16 +301,16 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace { int nestedContentParamValuesSize = nestedContentArgs != null ? nestedContentArgs.length : 0; if (nestedContentParamValuesSize != nestedContentParamNamesSize) { throw new TemplateException(env, - "The invocation declares ", (nestedContentParamNamesSize != 0 ? nestedContentParamNamesSize : "no"), - " nested content parameter(s)", - (nestedContentParamNamesSize != 0 - ? new Object[] { " (", new _DelayedJQuotedListing(nestedContentParamNames.getKeys()), ")", } - : ""), - ", but the called object intends to pass ", - nestedContentParamValuesSize, " parameters. You need to declare ", nestedContentParamValuesSize, - " nested content parameters."); + MessageUtils.newBadNumberOfNestedContentParameterPassedMessage( + nestedContentParamNames, nestedContentParamValuesSize)); + } + Writer prevOut = env.getOut(); + try { + env.setOut(out); + env.executeNestedContent(this, nestedContentParamNames, nestedContentArgs); + } finally { + env.setOut(prevOut); } - env.visit(getChildBuffer(), nestedContentParamNames, nestedContentArgs, out); } @Override http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTElement.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTElement.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTElement.java index 5bc68e0..5309177 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTElement.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTElement.java @@ -21,14 +21,14 @@ package org.apache.freemarker.core; import java.io.IOException; import java.util.Collections; -import java.util.Enumeration; - -import org.apache.freemarker.core.util._ArrayEnumeration; +import java.util.Iterator; +import java.util.NoSuchElementException; /** * AST non-expression node superclass: Superclass of directive calls, interpolations, static text, top-level comments, * or other such non-expression node in the parsed template. */ +//TODO [FM3] will be public abstract class ASTElement extends ASTNode { private static final int INITIAL_CHILD_BUFFER_CAPACITY = 6; @@ -51,10 +51,13 @@ abstract class ASTElement extends ASTNode { * The index of the element in the parent's {@link #childBuffer} array. */ private int index; + + // Package visible constructor to prevent instantiating outside FreeMarker + ASTElement() { } /** * Executes this {@link ASTElement}. Usually should not be called directly, but through - * {@link Environment#visit(ASTElement)} or a similar {@link Environment} method. + * {@link Environment#executeElement(ASTElement)} or a similar {@link Environment} method. * * @param env * The runtime environment @@ -64,42 +67,32 @@ abstract class ASTElement extends ASTNode { * executing them inside this method is a trick used for decreasing stack usage when there's nothing to do * after the children was processed anyway. */ - abstract ASTElement[] accept(Environment env) throws TemplateException, IOException; + abstract ASTElement[] execute(Environment env) throws TemplateException, IOException; /** - * One-line description of the element, that contain all the information that is used in {@link #getCanonicalForm()} - * , except the nested content (elements) of the element. The expressions inside the element (the parameters) has to - * be shown. Meant to be used for stack traces, also for tree views that don't go down to the expression-level. - * There are no backward-compatibility guarantees regarding the format used ATM, but it must be regular enough to be - * machine-parseable, and it must contain all information necessary for restoring an AST equivalent to the original. - * - * This final implementation calls {@link #dump(boolean) dump(false)}. + * Single line description of the element, which contain information about what kind of element it is, what are its + * parameters, but doesn't contain the child nodes. Meant to be used for stack traces, also for tree views (that + * don't want to show the parameters as spearate nodes). There are no backward-compatibility guarantees regarding + * the format used at the moment. * + * @see #getLabelWithoutParameters() * @see #getCanonicalForm() - * @see #getASTNodeDescriptor() */ - public final String getDescription() { + public final String getLabelWithParameters() { return dump(false); } - /** - * This final implementation calls {@link #dump(boolean) dump(false)}. - */ @Override public final String getCanonicalForm() { return dump(true); } final String getChildrenCanonicalForm() { - return getChildrenCanonicalForm(childBuffer); - } - - static String getChildrenCanonicalForm(ASTElement[] children) { - if (children == null) { + if (childBuffer == null) { return ""; } StringBuilder sb = new StringBuilder(); - for (ASTElement child : children) { + for (ASTElement child : childBuffer) { if (child == null) { break; } @@ -107,7 +100,7 @@ abstract class ASTElement extends ASTNode { } return sb.toString(); } - + /** * Tells if the element should show up in error stack traces. Note that this will be ignored for the top (current) * element of a stack trace, as that's always shown. @@ -125,53 +118,51 @@ abstract class ASTElement extends ASTNode { abstract boolean isNestedBlockRepeater(); /** - * Brings the implementation of {@link #getCanonicalForm()} and {@link #getDescription()} to a single place. Don't + * Brings the implementation of {@link #getCanonicalForm()} and {@link #getLabelWithParameters()} to a single place. Don't * call those methods in method on {@code this}, because that will result in infinite recursion! * * @param canonical * if {@code true}, it calculates the return value of {@link #getCanonicalForm()}, otherwise of - * {@link #getDescription()}. + * {@link #getLabelWithParameters()}. */ - abstract protected String dump(boolean canonical); - - // Methods to implement TemplateNodeModel - - public String getNodeName() { - String className = getClass().getName(); - int shortNameOffset = className.lastIndexOf('.') + 1; - return className.substring(shortNameOffset); - } - - // Methods so that we can implement the Swing TreeNode API. - - public boolean isLeaf() { - return childCount == 0; - } + abstract String dump(boolean canonical); - public int getIndex(ASTElement node) { - for (int i = 0; i < childCount; i++) { - if (childBuffer[i].equals(node)) { - return i; + /** + * Note: For element with {@code #nested}, this will hide the {@code #nested} when that's an + * {@link ASTImplicitParent}. + */ + public final Iterable<ASTElement> getChildren() { + return childBuffer != null ? new Iterable<ASTElement>() { + @Override + public Iterator<ASTElement> iterator() { + return new _ChildIterator(); } - } - return -1; + } : Collections.<ASTElement>emptyList(); } - public int getChildCount() { + public final int getChildCount() { return childCount; } /** - * Note: For element with {@code #nestedBlock}, this will hide the {@code #nestedBlock} when that's a - * {@link ASTImplicitParent}. + * Return the child node at the given index. + * + * @throws IndexOutOfBoundsException + * if the index is out of range, such as not less than {@link #getChildCount()}. */ - public Enumeration children() { - return childBuffer != null - ? new _ArrayEnumeration(childBuffer, childCount) - : Collections.enumeration(Collections.EMPTY_LIST); + public final ASTElement getChild(int index) { + if (index >= childCount) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds. There are " + childCount + + " child node(s)."); + } + return fastGetChild(index); + } + + final ASTElement fastGetChild(int index) { + return childBuffer[index]; } - public void setChildAt(int index, ASTElement element) { + final void setChildAt(int index, ASTElement element) { if (index < childCount && index >= 0) { childBuffer[index] = element; element.index = index; @@ -230,11 +221,7 @@ abstract class ASTElement extends ASTNode { lChildBuffer[index] = nestedElement; childCount = lChildCount + 1; } - - final ASTElement getChild(int index) { - return childBuffer[index]; - } - + /** * @return Array containing 1 or more nested elements with optional trailing {@code null}-s, or is {@code null} * exactly if there are no nested elements. @@ -386,7 +373,8 @@ abstract class ASTElement extends ASTNode { private ASTElement getFirstLeaf() { ASTElement te = this; - while (!te.isLeaf() && !(te instanceof ASTDirMacroOrFunction) && !(te instanceof ASTDirCapturingAssignment)) { + while (te.getChildCount() != 0 && !(te instanceof ASTDirMacroOrFunction) + && !(te instanceof ASTDirCapturingAssignment)) { // A macro or macro invocation is treated as a leaf here for special reasons te = te.getFirstChild(); } @@ -395,7 +383,8 @@ abstract class ASTElement extends ASTNode { private ASTElement getLastLeaf() { ASTElement te = this; - while (!te.isLeaf() && !(te instanceof ASTDirMacroOrFunction) && !(te instanceof ASTDirCapturingAssignment)) { + while (te.getChildCount() != 0 && !(te instanceof ASTDirMacroOrFunction) + && !(te instanceof ASTDirCapturingAssignment)) { // A macro or macro invocation is treated as a leaf here for special reasons te = te.getLastChild(); } @@ -435,4 +424,29 @@ abstract class ASTElement extends ASTNode { boolean heedsTrailingWhitespace() { return false; } + + private class _ChildIterator implements Iterator<ASTElement> { + + private int nextIndex; + + @Override + public boolean hasNext() { + return nextIndex < childCount; + } + + @Override + public ASTElement next() { + if (nextIndex >= childCount) { + throw new NoSuchElementException(); + } + return childBuffer[nextIndex++]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java index 38ed36d..040235b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java @@ -152,7 +152,7 @@ final class ASTExpAddOrConcat extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpAddOrConcat( left.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), @@ -165,7 +165,7 @@ final class ASTExpAddOrConcat extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "+"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAnd.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAnd.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAnd.java index ec60b26..e6958cf 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAnd.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAnd.java @@ -45,7 +45,7 @@ final class ASTExpAnd extends ASTExpBoolean { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "&&"; } @@ -55,7 +55,7 @@ final class ASTExpAnd extends ASTExpBoolean { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpAnd( lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpArithmetic.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpArithmetic.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpArithmetic.java index 632baca..579a96b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpArithmetic.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpArithmetic.java @@ -85,7 +85,7 @@ final class ASTExpArithmetic extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return String.valueOf(getOperatorSymbol(operator)); } @@ -99,7 +99,7 @@ final class ASTExpArithmetic extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpArithmetic( lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBooleanLiteral.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBooleanLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBooleanLiteral.java index 0cb9c50..bb2bada 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBooleanLiteral.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBooleanLiteral.java @@ -48,16 +48,11 @@ final class ASTExpBooleanLiteral extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return getCanonicalForm(); } @Override - public String toString() { - return val ? TemplateBooleanFormat.C_TRUE : TemplateBooleanFormat.C_FALSE; - } - - @Override TemplateModel _eval(Environment env) { return val ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; } @@ -68,7 +63,7 @@ final class ASTExpBooleanLiteral extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpBooleanLiteral(val); } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java index 3ff5c0a..6324cf4 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltIn.java @@ -384,7 +384,7 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "?" + key; } @@ -394,7 +394,7 @@ abstract class ASTExpBuiltIn extends ASTExpression implements Cloneable { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { try { ASTExpBuiltIn clone = (ASTExpBuiltIn) clone(); http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java index 08b9218..42fe3f1 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpBuiltInVariable.java @@ -220,17 +220,12 @@ final class ASTExpBuiltInVariable extends ASTExpression { } @Override - public String toString() { - return "." + name; - } - - @Override public String getCanonicalForm() { return "." + name; } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return getCanonicalForm(); } @@ -240,7 +235,7 @@ final class ASTExpBuiltInVariable extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return this; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpComparison.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpComparison.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpComparison.java index c7d92f0..9b6beac 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpComparison.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpComparison.java @@ -68,7 +68,7 @@ final class ASTExpComparison extends ASTExpBoolean { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return opString; } @@ -78,7 +78,7 @@ final class ASTExpComparison extends ASTExpBoolean { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpComparison( left.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java index 04c1afc..0aac874 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDefault.java @@ -169,7 +169,7 @@ class ASTExpDefault extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner(String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { + ASTExpression deepCloneWithIdentifierReplaced_inner(String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpDefault( lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), rho != null @@ -186,7 +186,7 @@ class ASTExpDefault extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "...!..."; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java index 1e67fad..a041909 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java @@ -46,11 +46,11 @@ final class ASTExpDot extends ASTExpression { @Override public String getCanonicalForm() { - return target.getCanonicalForm() + getASTNodeDescriptor() + _StringUtils.toFTLIdentifierReferenceAfterDot(key); + return target.getCanonicalForm() + getLabelWithoutParameters() + _StringUtils.toFTLIdentifierReferenceAfterDot(key); } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "."; } @@ -60,7 +60,7 @@ final class ASTExpDot extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpDot( target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java index e14e009..29e902a 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java @@ -237,7 +237,7 @@ final class ASTExpDynamicKeyName extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "...[...]"; } @@ -262,7 +262,7 @@ final class ASTExpDynamicKeyName extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpDynamicKeyName( target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpExists.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpExists.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpExists.java index 24f6e19..ce96532 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpExists.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpExists.java @@ -58,18 +58,18 @@ class ASTExpExists extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner(String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { + ASTExpression deepCloneWithIdentifierReplaced_inner(String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpExists( exp.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState)); } @Override public String getCanonicalForm() { - return exp.getCanonicalForm() + getASTNodeDescriptor(); + return exp.getCanonicalForm() + getLabelWithoutParameters(); } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "??"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java index 07985c8..8621085 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java @@ -36,7 +36,7 @@ import org.apache.freemarker.core.util._StringUtils; /** * AST expression node: {@code exp(args)}. */ -final class ASTExpFunctionCall extends ASTExpression implements CallPlace { +public final class ASTExpFunctionCall extends ASTExpression implements CallPlace { private final ASTExpression functionExp; private final ASTExpression[] positionalArgs; @@ -110,7 +110,7 @@ final class ASTExpFunctionCall extends ASTExpression implements CallPlace { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "...(...)"; } @@ -124,7 +124,7 @@ final class ASTExpFunctionCall extends ASTExpression implements CallPlace { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { ASTExpression[] positionalArgsClone; if (positionalArgs != null) { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java index 6c337dd..e2df12f 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java @@ -68,7 +68,7 @@ final class ASTExpHashLiteral extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "{...}"; } @@ -89,7 +89,7 @@ final class ASTExpHashLiteral extends ASTExpression { @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { ArrayList clonedKeys = (ArrayList) keys.clone(); for (ListIterator iter = clonedKeys.listIterator(); iter.hasNext(); ) { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java index 1d3f8d9..42ef089 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpListLiteral.java @@ -70,7 +70,7 @@ final class ASTExpListLiteral extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "[...]"; } @@ -113,7 +113,7 @@ final class ASTExpListLiteral extends ASTExpression { @SuppressWarnings("unchecked") @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { ArrayList<ASTExpression> clonedValues = (ArrayList<ASTExpression>) items.clone(); for (ListIterator<ASTExpression> iter = clonedValues.listIterator(); iter.hasNext(); ) { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java index 820e2e7..f0a0bf4 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java @@ -67,7 +67,7 @@ final class ASTExpNegateOrPlus extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return isMinus ? "-..." : "+..."; } @@ -77,7 +77,7 @@ final class ASTExpNegateOrPlus extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpNegateOrPlus( target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNot.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNot.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNot.java index 934c58e..6344624 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNot.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNot.java @@ -41,7 +41,7 @@ final class ASTExpNot extends ASTExpBoolean { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "!"; } @@ -51,7 +51,7 @@ final class ASTExpNot extends ASTExpBoolean { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpNot( target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState)); http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNumberLiteral.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNumberLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNumberLiteral.java index a61629d..a98fc78 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNumberLiteral.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNumberLiteral.java @@ -59,7 +59,7 @@ final class ASTExpNumberLiteral extends ASTExpression implements TemplateNumberM } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return getCanonicalForm(); } @@ -69,7 +69,7 @@ final class ASTExpNumberLiteral extends ASTExpression implements TemplateNumberM } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpNumberLiteral(value); } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpOr.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpOr.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpOr.java index e9d64a0..5222b50 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpOr.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpOr.java @@ -45,7 +45,7 @@ final class ASTExpOr extends ASTExpBoolean { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "||"; } @@ -55,7 +55,7 @@ final class ASTExpOr extends ASTExpBoolean { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpOr( lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpParenthesis.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpParenthesis.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpParenthesis.java index 9f64b3d..b7d82b7 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpParenthesis.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpParenthesis.java @@ -43,7 +43,7 @@ final class ASTExpParenthesis extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "(...)"; } @@ -62,7 +62,7 @@ final class ASTExpParenthesis extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpParenthesis( nested.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState)); http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java index edd80a5..2037fbc 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java @@ -62,11 +62,11 @@ final class ASTExpRange extends ASTExpression { @Override public String getCanonicalForm() { String rhs = rho != null ? rho.getCanonicalForm() : ""; - return lho.getCanonicalForm() + getASTNodeDescriptor() + rhs; + return lho.getCanonicalForm() + getLabelWithoutParameters() + rhs; } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { switch (endType) { case END_EXCLUSIVE: return "..<"; case END_INCLUSIVE: return ".."; @@ -83,7 +83,7 @@ final class ASTExpRange extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { return new ASTExpRange( lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java index c20de82..6c8d222 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java @@ -171,7 +171,7 @@ final class ASTExpStringLiteral extends ASTExpression implements TemplateStringM } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return dynamicValue == null ? getCanonicalForm() : "dynamic \"...\""; } @@ -181,7 +181,7 @@ final class ASTExpStringLiteral extends ASTExpression implements TemplateStringM } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { ASTExpStringLiteral cloned = new ASTExpStringLiteral(value); // FIXME: replacedIdentifier should be searched inside interpolatedOutput too: http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpVariable.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpVariable.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpVariable.java index 1044403..b261c26 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpVariable.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpVariable.java @@ -61,7 +61,7 @@ final class ASTExpVariable extends ASTExpression { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return getCanonicalForm(); } @@ -86,7 +86,7 @@ final class ASTExpVariable extends ASTExpression { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { if (name.equals(replacedIdentifier)) { if (replacementState.replacementAlreadyInUse) { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java index 31c855e..b15d1d0 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java @@ -34,6 +34,7 @@ import org.apache.freemarker.core.model.impl.BeanModel; /** * AST expression node superclass */ +//TODO [FM3] will be public abstract class ASTExpression extends ASTNode { /** @@ -51,6 +52,9 @@ abstract class ASTExpression extends ASTNode { // Hook in here to set the constant value if possible. + // Package visible constructor to prevent extending this class outside FreeMarker + ASTExpression () { } + @Override void setLocation(Template template, int beginColumn, int beginLine, int endColumn, int endLine) { super.setLocation(template, beginColumn, beginLine, endColumn, endLine); @@ -63,11 +67,10 @@ abstract class ASTExpression extends ASTNode { } } - final TemplateModel getAsTemplateModel(Environment env) throws TemplateException { - return eval(env); - } - - final TemplateModel eval(Environment env) throws TemplateException { + /** + * Evaluates the expression, returning its current value. + */ + public final TemplateModel eval(Environment env) throws TemplateException { try { return constantValue != null ? constantValue : _eval(env); } catch (FlowControlException | TemplateException e) { @@ -179,7 +182,7 @@ abstract class ASTExpression extends ASTNode { * This should return an equivalent new expression object (or an identifier replacement expression). * The position need not be filled, unless it will be different from the position of what we were cloning. */ - protected abstract ASTExpression deepCloneWithIdentifierReplaced_inner( + abstract ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState); static boolean isEmpty(TemplateModel model) throws TemplateException { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTImplicitParent.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTImplicitParent.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTImplicitParent.java index 97991a7..f87da42 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTImplicitParent.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTImplicitParent.java @@ -25,15 +25,17 @@ import java.io.IOException; * AST directive-like node, used where there's no other parent for a list of {@link ASTElement}-s. Most often occurs as * the root node of the AST. */ +//TODO [FM3] will be public final class ASTImplicitParent extends ASTElement { + // Package visible constructor to prevent instantiating outside FreeMarker ASTImplicitParent() { } @Override ASTElement postParseCleanup(boolean stripWhitespace) throws ParseException { super.postParseCleanup(stripWhitespace); - return getChildCount() == 1 ? getChild(0) : this; + return getChildCount() == 1 ? fastGetChild(0) : this; } /** @@ -41,20 +43,19 @@ final class ASTImplicitParent extends ASTElement { * and outputs the resulting text. */ @Override - ASTElement[] accept(Environment env) - throws TemplateException, IOException { + ASTElement[] execute(Environment env) throws TemplateException, IOException { return getChildBuffer(); } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { if (canonical) { return getChildrenCanonicalForm(); } else { if (getParent() == null) { return "root"; } - return getASTNodeDescriptor(); // ASTImplicitParent is uninteresting in a stack trace. + return getLabelWithoutParameters(); // ASTImplicitParent is uninteresting in a stack trace. } } @@ -62,7 +63,7 @@ final class ASTImplicitParent extends ASTElement { protected boolean isOutputCacheable() { int ln = getChildCount(); for (int i = 0; i < ln; i++) { - if (!getChild(i).isOutputCacheable()) { + if (!fastGetChild(i).isOutputCacheable()) { return false; } } @@ -70,7 +71,7 @@ final class ASTImplicitParent extends ASTElement { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#mixedContent"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java index 6ce0b7e..56805d2 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java @@ -31,6 +31,7 @@ import org.apache.freemarker.core.util.TemplateLanguageUtils; /** * AST interpolation node: <tt>${exp}</tt> */ +//TODO [FM3] will be public final class ASTInterpolation extends ASTElement { private final ASTExpression expression; @@ -42,7 +43,7 @@ final class ASTInterpolation extends ASTElement { private final OutputFormat outputFormat; private final MarkupOutputFormat markupOutputFormat; private final boolean autoEscape; - + ASTInterpolation( ASTExpression expression, ASTExpression escapedExpression, OutputFormat outputFormat, boolean autoEscape) { @@ -58,9 +59,21 @@ final class ASTInterpolation extends ASTElement { * Outputs the string value of the enclosed expression. */ @Override - ASTElement[] accept(Environment env) throws TemplateException, IOException { - final Object moOrStr = calculateInterpolatedStringOrMarkup(env); + ASTElement[] execute(Environment env) throws TemplateException, IOException { + printStringOrTemplateOutputModel( + escapedExpression, outputFormat, markupOutputFormat, autoEscape, env); + return null; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + static void printStringOrTemplateOutputModel( + ASTExpression exp, + OutputFormat outputFormat, + MarkupOutputFormat markupOutputFormat, boolean autoEscape, + Environment env) throws IOException, TemplateException { final Writer out = env.getOut(); + + final Object moOrStr = _EvalUtils.coerceModelToPlainTextOrMarkup(exp.eval(env), exp, null, env); if (moOrStr instanceof String) { final String s = (String) moOrStr; if (autoEscape) { @@ -70,28 +83,8 @@ final class ASTInterpolation extends ASTElement { } } else { final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) moOrStr; - final MarkupOutputFormat moOF = mo.getOutputFormat(); - // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic! - if (moOF != outputFormat && !outputFormat.isOutputFormatMixingAllowed()) { - final String srcPlainText; - // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic! - srcPlainText = moOF.getSourcePlainText(mo); - if (srcPlainText == null) { - throw new TemplateException(escapedExpression, - "The value to print is in ", new _DelayedToString(moOF), - " format, which differs from the current output format, ", - new _DelayedToString(outputFormat), ". Format conversion wasn't possible."); - } - if (outputFormat instanceof MarkupOutputFormat) { - ((MarkupOutputFormat) outputFormat).output(srcPlainText, out); - } else { - out.write(srcPlainText); - } - } else { - moOF.output(mo, out); - } + _EvalUtils.printTemplateMarkupOutputModel(mo, outputFormat, out, exp); } - return null; } /** @@ -104,7 +97,7 @@ final class ASTInterpolation extends ASTElement { } @Override - protected final String dump(boolean canonical) { + final String dump(boolean canonical) { return dump(canonical, false); } @@ -137,7 +130,7 @@ final class ASTInterpolation extends ASTElement { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "${...}"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTNode.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTNode.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTNode.java index 4ca9b7f..bcce5de 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTNode.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTNode.java @@ -20,8 +20,21 @@ package org.apache.freemarker.core; /** - * AST node: The superclass of all AST nodes + * Superclass of all AST (Abstract Syntax Tree) nodes. + * <p> + * The AST is a tree data structure that represent the complete content of a template (static content, directive calls, + * interpolations and the expressions inside them, possibly comments as well), without the complexities of syntactical + * details. The AST is generated from the source code (which is text) by the parser of the {@link TemplateLanguage}, + * and focuses on the meaning of the template. Thus, if the same template is rewritten in different template languages + * (like F3AH is converted to F3SH), the resulting AST-s will remain practically identical. + * <p> + * When a {@link Template} is processed, FreeMarker executes the AST directly. (In theory the AST could be translated + * further to byte code, FreeMarker doesn't try to do that, at least currently.) + * <p> + * The AST can also be used to analyze the content of templates, such as to discover its dependencies (on data-model + * variables, on other templates). */ +//TODO [FM3] will be public abstract class ASTNode { private Template template; @@ -67,51 +80,46 @@ abstract class ASTNode { this.endLine = endLine; } + // Package visible constructor to prevent extending this class outside FreeMarker + ASTNode() { } + + /** + * The template that contains this node. + */ + public Template getTemplate() { + return template; + } + + /** + * 1-based column number of the last character of this node in the source code. 0 if not available. + */ public final int getBeginColumn() { return beginColumn; } + /** + * 1-based line number of the first character of this node in the source code. 0 if not available. + */ + // TODO [FM3] No negative number hack in ?eval and such. public final int getBeginLine() { return beginLine; } - public final int getEndColumn() { - return endColumn; - } - - public final int getEndLine() { - return endLine; - } - /** - * Returns a string that indicates - * where in the template source, this object is. + * 1-based column number of the first character of this node in the source code. 0 if not available. */ - public String getStartLocation() { - return MessageUtils.formatLocationForEvaluationError(template, beginLine, beginColumn); + public final int getEndColumn() { + return endColumn; } /** - * As of 2.3.20. the same as {@link #getStartLocation}. Meant to be used where there's a risk of XSS - * when viewing error messages. + * 1-based line number of the last character of this node in the source code. 0 if not available. */ - public String getStartLocationQuoted() { - return getStartLocation(); - } - - public String getEndLocation() { - return MessageUtils.formatLocationForEvaluationError(template, endLine, endColumn); + public final int getEndLine() { + return endLine; } - /** - * As of 2.3.20. the same as {@link #getEndLocation}. Meant to be used where there's a risk of XSS - * when viewing error messages. - */ - public String getEndLocationQuoted() { - return getEndLocation(); - } - - public final String getSource() { + final String getSource() { String s; if (template != null) { s = template.getSource(beginColumn, beginLine, endColumn, endLine); @@ -124,14 +132,9 @@ abstract class ASTNode { } @Override - public String toString() { + public final String toString() { String s; - try { - s = getSource(); - } catch (Exception e) { // REVISIT: A bit of a hack? (JR) - s = null; - } - return s != null ? s : getCanonicalForm(); + return (s = getSource()) != null ? s : getCanonicalForm(); } /** @@ -155,10 +158,6 @@ abstract class ASTNode { return true; } - public Template getTemplate() { - return template; - } - ASTNode copyLocationFrom(ASTNode from) { template = from.template; beginColumn = from.beginColumn; @@ -169,12 +168,14 @@ abstract class ASTNode { } /** - * FTL generated from the AST of the node, which must be parseable to an AST that does the same as the original - * source, assuming we turn off automatic white-space removal when parsing the canonical form. + * Template source code generated from the AST of this node. + * When parsed, it should result in a practically identical AST that does the same as the original + * source, assuming that you turn off automatic white-space removal when parsing the canonical form. * - * @see ASTElement#getDescription() - * @see #getASTNodeDescriptor() + * @see ASTElement#getLabelWithParameters() + * @see #getLabelWithoutParameters() */ + // TODO [FM3] The whitespace problem isn't OK; do pretty-formatting, outside core if too big. abstract public String getCanonicalForm(); /** @@ -185,9 +186,9 @@ abstract class ASTNode { * leaf nodes the symbols should be the canonical form of value. * * @see #getCanonicalForm() - * @see ASTElement#getDescription() + * @see ASTElement#getLabelWithParameters() */ - abstract String getASTNodeDescriptor(); + public abstract String getLabelWithoutParameters(); /** * Returns highest valid parameter index + 1. So one should scan indexes with {@link #getParameterValue(int)} http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTStaticText.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTStaticText.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTStaticText.java index 76f1019..4e8ec95 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTStaticText.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTStaticText.java @@ -27,6 +27,7 @@ import org.apache.freemarker.core.util._StringUtils; /** * AST node representing static text. */ +//TODO [FM3] will be public final class ASTStaticText extends ASTElement { // We're using char[] instead of String for storing the text block because @@ -37,11 +38,11 @@ final class ASTStaticText extends ASTElement { private char[] text; private final boolean unparsed; - public ASTStaticText(String text) { + ASTStaticText(String text) { this(text, false); } - public ASTStaticText(String text, boolean unparsed) { + ASTStaticText(String text, boolean unparsed) { this(text.toCharArray(), unparsed); } @@ -56,19 +57,15 @@ final class ASTStaticText extends ASTElement { /** * Simply outputs the text. - * - * @deprecated This is an internal API; don't call or override it. */ - @Deprecated @Override - public ASTElement[] accept(Environment env) - throws IOException { + ASTElement[] execute(Environment env) throws IOException { env.getOut().write(text); return null; } @Override - protected String dump(boolean canonical) { + String dump(boolean canonical) { if (canonical) { String text = new String(this.text); if (unparsed) { @@ -81,7 +78,7 @@ final class ASTStaticText extends ASTElement { } @Override - String getASTNodeDescriptor() { + public String getLabelWithoutParameters() { return "#text"; } http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInWithParseTimeParameters.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInWithParseTimeParameters.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInWithParseTimeParameters.java index 47a37bd..31aeb00 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInWithParseTimeParameters.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInWithParseTimeParameters.java @@ -48,8 +48,8 @@ abstract class BuiltInWithParseTimeParameters extends SpecialBuiltIn { } @Override - String getASTNodeDescriptor() { - return super.getASTNodeDescriptor() + "(...)"; + public String getLabelWithoutParameters() { + return super.getLabelWithoutParameters() + "(...)"; } @Override @@ -90,7 +90,7 @@ abstract class BuiltInWithParseTimeParameters extends SpecialBuiltIn { } @Override - protected ASTExpression deepCloneWithIdentifierReplaced_inner( + ASTExpression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, ASTExpression replacement, ReplacemenetState replacementState) { final ASTExpression clone = super.deepCloneWithIdentifierReplaced_inner(replacedIdentifier, replacement, replacementState); cloneArguments(clone, replacedIdentifier, replacement, replacementState); http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/CallPlace.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/CallPlace.java b/freemarker-core/src/main/java/org/apache/freemarker/core/CallPlace.java index 6b9ba34..6135d3a 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/CallPlace.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/CallPlace.java @@ -65,6 +65,8 @@ public interface CallPlace { * that many nested content parameters as the length of this array. If you want to allow the caller to not * declare some of the nested content parameters, then you have to make this array shorter according to * {@link #getNestedContentParameterCount()}. + * @param out + * The {@link Writer} to print. */ void executeNestedContent(TemplateModel[] nestedContentArgs, Writer out, Environment env) throws TemplateException, IOException; http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java index be9f802..9a9d13b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java @@ -43,6 +43,7 @@ import java.util.TimeZone; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import org.apache.freemarker.core.Dialect.ConfiguredDialect; import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.arithmetic.impl.BigDecimalArithmeticEngine; import org.apache.freemarker.core.model.ObjectWrapper; @@ -175,6 +176,7 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc private final Long templateUpdateDelayMilliseconds; private final Boolean localizedTemplateLookup; private final List<OutputFormat> registeredCustomOutputFormats; + private final Map<Dialect, ConfiguredDialect> configuredDialects; private final Map<String, OutputFormat> registeredCustomOutputFormatsByName; private final Map<String, Object> sharedVariables; private final Map<String, TemplateModel> wrappedSharedVariables; @@ -441,6 +443,12 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc this.templateConfigurations = templateConfigurations; templateResolver.setDependencies(new TemplateResolverDependenciesImpl(this, templateResolver)); + + // ATTENTION! Creating the configuredDialects must be the last thing, as here the user code can already access + // this Configuration! + configuredDialects = new HashMap<>(); + // TODO [FM3] This is hard-code for now, but later we need to add the "dialects" configuration setting + configuredDialects.put(DefaultDialect.INSTANCE, DefaultDialect.INSTANCE.createConfiguredDialect(this)); } private void checkSettingIsNullForThisTemplateResolver( @@ -671,8 +679,8 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc throw new IllegalArgumentException("Missing opening '{' in: " + name); } - MarkupOutputFormat outerOF = getMarkupOutputFormatForCombined(name.substring(0, openBrcIdx)); - MarkupOutputFormat innerOF = getMarkupOutputFormatForCombined( + MarkupOutputFormat<?> outerOF = getMarkupOutputFormatForCombined(name.substring(0, openBrcIdx)); + MarkupOutputFormat<?> innerOF = getMarkupOutputFormatForCombined( name.substring(openBrcIdx + 1, name.length() - 1)); return new CombinedMarkupOutputFormat(name, outerOF, innerOF); @@ -708,6 +716,21 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc return stdOF; } } + + /** + * Same as {@link #getOutputFormat(String)}, but also throws {@link UnregisteredOutputFormatException} of the + * output format doesn't extend {@link MarkupOutputFormat}. + */ + public MarkupOutputFormat<?> getMarkupOutputFormat(String name) throws UnregisteredOutputFormatException { + OutputFormat outputFormat = getOutputFormat(name); + if (!(outputFormat instanceof MarkupOutputFormat<?>)) { + // TODO [FM3] It's kind of silly, but so is introducing a subclass exception just for this... + throw new UnregisteredOutputFormatException( + "The " + _StringUtils.jQuote(name) + " output format (class " + outputFormat.getClass().getName() + + " is registered, but doesn't implement " + MarkupOutputFormat.class.getName() + "."); + } + return (MarkupOutputFormat<?>) outputFormat; + } /** * Returns the argument {@link OutputFormat} as is, unless a {@link #getRegisteredCustomOutputFormats() @@ -815,7 +838,7 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc public TemplateLanguage getTemplateLanguage() { return templateLanguage; } - + /** * Always {@code true} in {@link Configuration}-s; even if this setting wasn't set in the builder, it gets a default * value in the {@link Configuration}. @@ -824,6 +847,30 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc public boolean isTemplateLanguageSet() { return true; } + + /** + * Returns the {@link ConfiguredDialect} for a {@link Dialect} that's known by this {@link Configuration}. + * + * @param dialect + * Not {@code null}. + * + * @return Never {@code null}. When invoked on the same {@link Configuration} with the same {@link Dialect} + * instance, it always returns the same {@link ConfiguredDialect} instance. + * + * @throws IllegalStateException + * If the {@link Dialect} is not know by this {@link Configuration}. (TODO [FM3]: For now it only knows + * {@link DefaultDialect#INSTANCE}, but that will change later when custom {@link TemplateLanguage}-s + * can be added to the {@link Configuration}.) + */ + public ConfiguredDialect getConfiguredDialect(Dialect dialect) { + _NullArgumentException.check("dialect", dialect); + ConfiguredDialect configuredDialect = configuredDialects.get(dialect); + if (configuredDialect == null) { + throw new IllegalStateException("No such " + Dialect.class.getName() + " was added to this Configuration: " + + dialect); + } + return configuredDialect; + } @Override public int getTabSize() { http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultDialect.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultDialect.java b/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultDialect.java new file mode 100644 index 0000000..b04351a --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultDialect.java @@ -0,0 +1,82 @@ +package org.apache.freemarker.core; + +/* + * 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. + */ + +import java.util.HashMap; +import java.util.Map; + +import org.apache.freemarker.core.outputformat.MarkupOutputFormat; +import org.apache.freemarker.core.outputformat.UnregisteredOutputFormatException; +import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat; + +/** + * The default {@link Dialect} for FreeMarker. Most applications are expected to use this. + */ +//TODO [FM3][DIALECTS] will be public. Also, then move it into core.dialect? +final class DefaultDialect extends Dialect { + + public static final DefaultDialect INSTANCE = new DefaultDialect(); + + private DefaultDialect() { + super("Default FreeMarker 3 Dialect", Configuration.getVersion()); + } + + @Override + public ConfiguredDialect createConfiguredDialect(Configuration cfg) { + return new ConfiguredDefaultDialect(cfg); + } + + private class ConfiguredDefaultDialect extends ConfiguredDialect { + private final Map<String, StaticallyLinkedNamespaceEntry> namespaceEntriesByName; + + ConfiguredDefaultDialect(Configuration cfg) { + MarkupOutputFormat<?> htmlOutputFormat; + try { + htmlOutputFormat = cfg.getMarkupOutputFormat(HTMLOutputFormat.INSTANCE.getName()); + } catch (UnregisteredOutputFormatException e) { + throw new ConfigurationException("Couldn't get HTML output format.", e); + } + namespaceEntriesByName = new HashMap<>(16, 0.5f); // The speed of get(key) is important + //!!T Test entries until FREEMARKER-99 is finished: + addNamespaceEntry( + new StaticallyLinkedNamespaceEntry("adhocTest1", ASTDirAdhocTest1.VALUE, + new ASTDirAdhocTest1.Factory(htmlOutputFormat), null)); + addNamespaceEntry( + new StaticallyLinkedNamespaceEntry("adhocTest2", ASTDirAdhocTest2.VALUE, + ASTDirAdhocTest2.FACTORY, null)); + } + + @Override + public StaticallyLinkedNamespaceEntry getNamespaceEntry(String name) { + return namespaceEntriesByName.get(name); + } + + @Override + public Iterable<StaticallyLinkedNamespaceEntry> getNamespaceEntries() { + return namespaceEntriesByName.values(); + } + + private void addNamespaceEntry(StaticallyLinkedNamespaceEntry entry) { + namespaceEntriesByName.put(entry.getName(), entry); + } + + } + +}
