Repository: freemarker
Updated Branches:
  refs/heads/3 056635cd6 -> e8e58ffa5


http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultTemplateLanguage.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultTemplateLanguage.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultTemplateLanguage.java
index 644ab25..df61532 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultTemplateLanguage.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/DefaultTemplateLanguage.java
@@ -41,29 +41,37 @@ public final class DefaultTemplateLanguage extends 
TemplateLanguage {
      * from the {@link Configuration} (or {@link TemplateConfiguration}). 
Avoid it, as it's problematic for tooling.
      */
     public static final DefaultTemplateLanguage F3AC = new 
DefaultTemplateLanguage("f3ac", true,
+            DefaultDialect.INSTANCE,
             null, null,
             TagSyntax.ANGLE_BRACKET, InterpolationSyntax.DOLLAR);
     public static final DefaultTemplateLanguage F3SC = new 
DefaultTemplateLanguage("f3sc", true,
+            DefaultDialect.INSTANCE,
             null, null,
             TagSyntax.SQUARE_BRACKET, InterpolationSyntax.SQUARE_BRACKET);
 
     public static final DefaultTemplateLanguage F3AH = new 
DefaultTemplateLanguage("f3ah", true,
+            DefaultDialect.INSTANCE,
             HTMLOutputFormat.INSTANCE, AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.ANGLE_BRACKET, InterpolationSyntax.DOLLAR);
     public static final DefaultTemplateLanguage F3AX = new 
DefaultTemplateLanguage("f3ax", true,
+            DefaultDialect.INSTANCE,
             XMLOutputFormat.INSTANCE, AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.ANGLE_BRACKET, InterpolationSyntax.DOLLAR);
     public static final DefaultTemplateLanguage F3AU = new 
DefaultTemplateLanguage("f3au", true,
+            DefaultDialect.INSTANCE,
             UndefinedOutputFormat.INSTANCE, 
AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.ANGLE_BRACKET, InterpolationSyntax.DOLLAR);
 
     public static final DefaultTemplateLanguage F3SH = new 
DefaultTemplateLanguage("f3sh", true,
+            DefaultDialect.INSTANCE,
             HTMLOutputFormat.INSTANCE, AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.SQUARE_BRACKET, InterpolationSyntax.SQUARE_BRACKET);
     public static final DefaultTemplateLanguage F3SX = new 
DefaultTemplateLanguage("f3sx", true,
+            DefaultDialect.INSTANCE,
             XMLOutputFormat.INSTANCE, AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.SQUARE_BRACKET, InterpolationSyntax.SQUARE_BRACKET);
     public static final DefaultTemplateLanguage F3SU = new 
DefaultTemplateLanguage("f3su", true,
+            DefaultDialect.INSTANCE,
             UndefinedOutputFormat.INSTANCE, 
AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.SQUARE_BRACKET, InterpolationSyntax.SQUARE_BRACKET);
 
@@ -81,11 +89,11 @@ public final class DefaultTemplateLanguage extends 
TemplateLanguage {
      * call this yourself; use constants like {@link #F3AH} instead when 
possible.
      * 
      * @param fileExtension
-     *            See in {@link TemplateLanguage#TemplateLanguage(String, 
OutputFormat, AutoEscapingPolicy)}
+     *            See in {@link TemplateLanguage#TemplateLanguage(String, 
Dialect, OutputFormat, AutoEscapingPolicy)}
      * @param outputFormat
-     *            See in {@link TemplateLanguage#TemplateLanguage(String, 
OutputFormat, AutoEscapingPolicy)}
+     *            See in {@link TemplateLanguage#TemplateLanguage(String, 
Dialect, OutputFormat, AutoEscapingPolicy)}
      * @param autoEscapingPolicy
-     *            See in {@link TemplateLanguage#TemplateLanguage(String, 
OutputFormat, AutoEscapingPolicy)}
+     *            See in {@link TemplateLanguage#TemplateLanguage(String, 
Dialect, OutputFormat, AutoEscapingPolicy)}
      * @param tagSyntax
      *            The tag syntax used; not {@code null}.
      * @param interpolationSyntax
@@ -93,20 +101,21 @@ public final class DefaultTemplateLanguage extends 
TemplateLanguage {
      */
     public DefaultTemplateLanguage(
             String fileExtension,
+            Dialect dialect,
             OutputFormat outputFormat, AutoEscapingPolicy autoEscapingPolicy,
             TagSyntax tagSyntax, InterpolationSyntax interpolationSyntax) {
-        this(fileExtension, false, outputFormat, autoEscapingPolicy, 
tagSyntax, interpolationSyntax);
+        this(fileExtension, false, dialect, outputFormat, autoEscapingPolicy, 
tagSyntax, interpolationSyntax);
     }
 
     /**
-     * Used internally to allow extensions starting with "f" 
+     * Used internally to allow extensions starting with "f" (as those are 
reserved for FreeMarker) 
      */
     DefaultTemplateLanguage(
-            String fileExtension,
-            boolean allowExtensionStartingWithF,
+            String fileExtension, boolean allowExtensionStartingWithF,
+            Dialect dialect,
             OutputFormat outputFormat, AutoEscapingPolicy autoEscapingPolicy,
             TagSyntax tagSyntax, InterpolationSyntax interpolationSyntax) {
-        super(fileExtension, allowExtensionStartingWithF, outputFormat, 
autoEscapingPolicy);
+        super(fileExtension, allowExtensionStartingWithF, dialect, 
outputFormat, autoEscapingPolicy);
         _NullArgumentException.check("tagSyntax", tagSyntax);
         _NullArgumentException.check("interpolationSyntax", 
interpolationSyntax);
         this.tagSyntax = tagSyntax;

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/Dialect.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Dialect.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Dialect.java
new file mode 100644
index 0000000..056bce5
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Dialect.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+/**
+ * A {@linkplain TemplateLanguage template language} dialect; specifies what 
predefined callables (like directives,
+ * built-ins) are available in the template language, but it doesn't specify 
the syntax. Unlike callables exposed to
+ * templates otherwise, callables that are part of the dialect use the same 
syntax as the callables traditionally
+ * predefined ones (like {@code <#if ...>}, {@code ?upperCase}", etc.), and 
they are linked statically (see
+ * {@link StaticallyLinkedNamespaceEntry} for the advantages).
+ * 
+ * <p>
+ * A {@link Dialect} object is usually a static singleton. If you need 
internal state that's bound to a
+ * {@link Configuration} instance, you can do that in the {@link 
ConfiguredDialect} created by
+ * {@link Dialect#createConfiguredDialect(Configuration)} object.
+ * 
+ * @see StaticallyLinkedNamespaceEntry
+ */
+//TODO [FM3] will be public. Also, then move it into core.dialect?
+abstract class Dialect {
+    
+    private final String name;
+    private final Version version;
+    
+    public Dialect(String name, Version version) {
+        this.name = name;
+        this.version = version;
+    }
+
+    /**
+     * The informal name of this dialect (maybe used in error messages)
+     */
+    public String getName() {
+        return name;
+    }
+    
+    /**
+     * The version number of this dialect (maybe used in error messages).
+     */
+    public Version getVersion() {
+        return version;
+    }
+
+    /**
+     * Creates a {@link ConfiguredDialect} object that's bound to the 
parameter {@link Configuration} object. FreeMarker
+     * calls this method of each {@link Dialect} that will be used with the 
{@link Configuration} when the
+     * {@link Configuration} object is constructed.
+     * 
+     * @param cfg
+     *            The fully initialized {@link Configuration}, except that 
some {@link Dialect}-s in it may not have an
+     *            associated {@link ConfiguredDialect} yet. This {@link 
Configuration} is not yet "safely published"
+     *            (see in the Java Memory Model), so it must not be exposed to 
other threads by this method.
+     * 
+     * @throws ConfigurationException
+     *             If any problem occurs, it should be wrapped into this.
+     */
+    public abstract ConfiguredDialect createConfiguredDialect(Configuration 
cfg) throws ConfigurationException;
+    
+    /**
+     * A {@link Dialect} that is bound to a {@link Configuration}. While a 
dialect is in principle a static singleton,
+     * and so is independent of any particular {@link Configuration} object, 
some dialects may have internal state
+     * initialized depending on some {@link Configuration} settings.
+     * 
+     * <p>
+     * Instances should be created by {@link 
Dialect#createConfiguredDialect(Configuration)}. The concrete
+     * {@link ConfiguredDialect} is usually private and is a nested class in 
the concrete {@link Dialect}, which
+     * overrides {@link Dialect#createConfiguredDialect(Configuration)} to 
return the concrete instance
+     * 
+     * <p>
+     * The main function of a {@link ConfiguredDialect} is to expose the 
namespace of the dialect. The dialect namespace
+     * contains the entries that are statically linked, and can usually can be 
accessed without any namespace prefix (
+     * usually, as the exact details depend on the template language). For 
example, for the default dialect the
+     * namespace contains entries like "if", "list", "upperCase", and so on.
+     */
+    //TODO [FM3] will be public. Also, then move it into core.dialect?
+    abstract class ConfiguredDialect {
+        
+        /**
+         * Returns a namespace entry of this dialect. or {@code null} if 
there's no match. 
+         */
+        public abstract StaticallyLinkedNamespaceEntry 
getNamespaceEntry(String name);
+        
+        /**
+         * To iterate through the namespace entries of this dialect.
+         */
+        public abstract Iterable<StaticallyLinkedNamespaceEntry> 
getNamespaceEntries();
+        
+        /**
+         * Returns the {@link Dialect} whose {@link 
Dialect#createConfiguredDialect(Configuration)} method has created this
+         * instance.
+         */
+        public final Dialect getDialect() {
+            return Dialect.this;
+        }
+        
+    }
+
+    // Final implementation, as {@link Dialect}-s maybe used as hash keys.
+    @Override
+    public final int hashCode() {
+        return super.hashCode();
+    }
+
+    // Final implementation, as {@link Dialect}-s maybe used as hash keys.
+    @Override
+    public final boolean equals(Object obj) {
+        return super.equals(obj);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/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 38a4f17..637901e 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
@@ -63,6 +63,8 @@ import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.SimpleHash;
+import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
+import org.apache.freemarker.core.outputformat.OutputFormat;
 import 
org.apache.freemarker.core.templateresolver.MalformedTemplateNameException;
 import org.apache.freemarker.core.templateresolver.TemplateResolver;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
@@ -70,6 +72,7 @@ import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util._DateUtils;
 import org.apache.freemarker.core.util._DateUtils.DateToISO8601CalendarFactory;
+import org.apache.freemarker.core.util._NullArgumentException;
 import org.apache.freemarker.core.util._NullWriter;
 import org.apache.freemarker.core.util._StringUtils;
 import org.apache.freemarker.core.valueformat.TemplateDateFormat;
@@ -280,7 +283,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
             clearCachedValues();
             try {
                 doAutoImportsAndIncludes(this);
-                visit(getMainTemplate().getRootASTNode());
+                executeElement(getMainTemplate().getRootASTNode());
                 // It's here as we must not flush if there was an exception.
                 if (getAutoFlush()) {
                     out.flush();
@@ -366,17 +369,18 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
     /**
      * "Visit" the template element.
      */
-    void visit(ASTElement element) throws IOException, TemplateException {
+    // TODO [FM3] will be public
+    void executeElement(ASTElement element) throws IOException, 
TemplateException {
         // ATTENTION: This method body is manually "inlined" into 
visit(ASTElement[]); keep them in sync!
         pushElement(element);
         try {
-            ASTElement[] templateElementsToVisit = element.accept(this);
+            ASTElement[] templateElementsToVisit = element.execute(this);
             if (templateElementsToVisit != null) {
                 for (ASTElement el : templateElementsToVisit) {
                     if (el == null) {
                         break;  // Skip unused trailing buffer capacity 
                     }
-                    visit(el);
+                    executeElement(el);
                 }
             }
         } catch (TemplateException te) {
@@ -386,12 +390,20 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         }
         // ATTENTION: This method body above is manually "inlined" into 
visit(ASTElement[]); keep them in sync!
     }
+
+    // TODO [FM3] will be public
+    final void executeNestedContent(ASTDirective parentCall) throws 
IOException, TemplateException {
+        executeElements(parentCall.getChildBuffer());
+    }
     
     /**
+     * Executes the elements passed in (which is usually the return value of 
{@link ASTElement#getChildBuffer()}).
+     * 
      * @param elementBuffer
      *            The elements to visit; might contains trailing {@code 
null}-s. Can be {@code null}.
      */
-    final void visit(ASTElement[] elementBuffer) throws IOException, 
TemplateException {
+    // TODO [FM3] will be public
+    final void executeElements(ASTElement[] elementBuffer) throws IOException, 
TemplateException {
         if (elementBuffer == null) {
             return;
         }
@@ -404,13 +416,13 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
             // We don't just let Hotspot to do it, as we want a hard guarantee 
regarding maximum stack usage. 
             pushElement(element);
             try {
-                ASTElement[] templateElementsToVisit = element.accept(this);
+                ASTElement[] templateElementsToVisit = element.execute(this);
                 if (templateElementsToVisit != null) {
                     for (ASTElement el : templateElementsToVisit) {
                         if (el == null) {
                             break;  // Skip unused trailing buffer capacity 
                         }
-                        visit(el);
+                        executeElement(el);
                     }
                 }
             } catch (TemplateException te) {
@@ -429,13 +441,27 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
 
     private static final TemplateModel[] NO_OUT_ARGS = new TemplateModel[0];
 
-    void visit(
-            ASTElement[] childBuffer,
-            final StringToIndexMap nestedContentParamNames, final 
TemplateModel[] nestedContentParamValues,
-            Writer out)
-        throws IOException, TemplateException {
+    /**
+     * Executes the nested content of a {@link ASTDirective}.
+     * 
+     * @param directiveCall
+     *            This is the directive call AST node whose nested content we 
will execute.
+     * @param nestedContentParamNames
+     *            The names of the nested content parameters as they were 
declared by the directive call. For example
+     *            {@code code <#list m as k, v>...</#list>}, a call of the 
{@code list} directive, declares "k" at index
+     *            0, and "v" at index 1.
+     * @param nestedContentParamValues
+     *            The actual values of the nested content parameters, which 
will be visible for the nested content as
+     *            variables. The caller of this method <em>must</em> ensure 
that {@code nestedContentParamNames} doesn't
+     *            return an index that's out of bounds in {@code 
nestedContentParamValues}!
+     */
+    // TODO [FM3] will be public
+    void executeNestedContent(
+            ASTDirective directiveCall,
+            final StringToIndexMap nestedContentParamNames, final 
TemplateModel[] nestedContentParamValues) throws IOException, TemplateException 
{
+        ASTElement[] childBuffer = directiveCall.getChildBuffer();
         if (nestedContentParamNames == null) { // This is by far the most 
frequent case
-            visit(childBuffer, out);
+            executeElements(childBuffer);
         } else {
             pushLocalContext(new LocalContext() {
                 @Override
@@ -450,24 +476,13 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
                 }
             });
             try {
-                visit(childBuffer, out);
+                executeElements(childBuffer);
             } 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;
-        }
-    }
-
     /**
      * Visit a block using buffering/recovery
      */
@@ -482,7 +497,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         boolean lastInAttemptBlock = inAttemptBlock;
         try {
             inAttemptBlock = true;
-            visit(attemptedSection);
+            executeElement(attemptedSection);
         } catch (TemplateException te) {
             thrownException = te;
         } finally {
@@ -493,7 +508,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         if (thrownException != null) {
             try {
                 recoveredErrorStack.add(thrownException);
-                visit(recoverySection);
+                executeElement(recoverySection);
             } finally {
                 recoveredErrorStack.remove(recoveredErrorStack.size() - 1);
             }
@@ -988,11 +1003,11 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         return _EvalUtils.compare(leftValue, 
_EvalUtils.CMP_OP_GREATER_THAN_EQUALS, rightValue, this);
     }
 
-    public void setOut(Writer out) {
+    public final void setOut(Writer out) {
         this.out = out;
     }
 
-    public Writer getOut() {
+    public final Writer getOut() {
         return out;
     }
 
@@ -1049,6 +1064,67 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         return _EvalUtils.coerceModelToPlainText(tm, null, null, this);
     }
     
+    /**
+     * Evaluates the expression and prints its value as if it was printed with 
an interpolation (like
+     * <code>${exp}</code>).
+     * 
+     * @param outputFormat
+     *            Acts like the output format in a template where an 
interpolation is called. Not {@code null}.
+     * @param autoEscapingPolicy
+     *            Acts like the auto escaping policy in a template where an 
interpolation is called. Not {@code null}.
+     */
+    // TODO [FM3] will be public
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    void interpolate(
+            ASTExpression exp,
+            OutputFormat outputFormat, AutoEscapingPolicy autoEscapingPolicy,
+            Environment env) throws IOException, TemplateException {
+        interpolate(exp.eval(env), exp, outputFormat, autoEscapingPolicy, env);
+    }
+    
+    /**
+     * Same as {@link #interpolate(ASTExpression, OutputFormat, 
AutoEscapingPolicy, Environment)}, but is used in the
+     * rare case where you have already evaluated the expression.
+     * 
+     * @param value
+     *            The value resulting from evaluating {@code exp}
+     * @param exp
+     *            The expression whose evaluation was resulted in {@code 
value}. It won't be evaluated again, but is
+     *            still used for error messages. {@code null} is tolerated, 
but should be avoided due to the resulting
+     *            poorer quality error messages.
+     */
+    void interpolate(
+            TemplateModel value, ASTExpression exp,
+            OutputFormat outputFormat, AutoEscapingPolicy autoEscapingPolicy,
+            Environment env) throws IOException, TemplateException {
+        _NullArgumentException.check("outputFormat", outputFormat);
+        _NullArgumentException.check("autoEscapingPolicy", autoEscapingPolicy);
+        
+        final Writer out = env.getOut();
+        
+        final Object moOrStr = 
_EvalUtils.coerceModelToPlainTextOrMarkup(value, exp, null, env);
+        if (moOrStr instanceof String) {
+            final String s = (String) moOrStr;
+            
+            MarkupOutputFormat markupOutputFormat;
+            if (outputFormat instanceof MarkupOutputFormat) {
+                markupOutputFormat = (MarkupOutputFormat) outputFormat;
+                if (autoEscapingPolicy == 
AutoEscapingPolicy.ENABLE_IF_SUPPORTED
+                        || autoEscapingPolicy == 
AutoEscapingPolicy.ENABLE_IF_DEFAULT
+                                && 
markupOutputFormat.isAutoEscapedByDefault()) {
+                    markupOutputFormat.output(s, out);
+                } else {
+                    out.write(s);
+                }
+            } else {
+                out.write(s);
+            }
+        } else {
+            final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) 
moOrStr;
+            _EvalUtils.printTemplateMarkupOutputModel(mo, outputFormat, out, 
exp);
+        }
+    }
+    
     String formatBoolean(boolean value, boolean fallbackToTrueFalse) throws 
TemplateException {
         TemplateBooleanFormat templateBooleanFormat = 
getTemplateBooleanFormat();
         if (value) {
@@ -2135,7 +2211,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
     }
 
     static void appendInstructionStackItem(ASTElement stackEl, StringBuilder 
sb) {
-        sb.append(MessageUtils.shorten(stackEl.getDescription(), 40));
+        sb.append(MessageUtils.shorten(stackEl.getLabelWithParameters(), 40));
 
         sb.append("  [");
         ASTDirMacroOrFunction enclosingMacro = getEnclosingMacro(stackEl);
@@ -2286,7 +2362,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         };
     }
 
-    private void pushElement(ASTElement element) {
+    void pushElement(ASTElement element) {
         final int newSize = ++instructionStackSize;
         ASTElement[] instructionStack = this.instructionStack;
         if (newSize > instructionStack.length) {
@@ -2300,7 +2376,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         instructionStack[newSize - 1] = element;
     }
 
-    private void popElement() {
+    void popElement() {
         instructionStackSize--;
     }
 
@@ -2481,7 +2557,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         final Template prevTemplate;
 
         importMacros(includedTemplate);
-        visit(includedTemplate.getRootASTNode());
+        executeElement(includedTemplate.getRootASTNode());
     }
 
     /**
@@ -2976,7 +3052,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
                     : callableDefinition.getName();
         }
 
-        protected void genericExecute(TemplateModel[] args, CallPlace 
callPlace, Writer out, Environment env)
+        protected void genericExecute(TemplateModel[] args, CallPlace 
callPlace, Environment env)
                 throws TemplateException, IOException {
             pushElement(callableDefinition);
             try {
@@ -2996,7 +3072,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
                     // Note: Default expressions are evaluated here, so 
namespace, stack, etc. must be already set
                     setLocalsFromArguments(macroCtx, args);
 
-                    visit(callableDefinition.getChildBuffer(), out);
+                    executeElements(callableDefinition.getChildBuffer());
                 } catch (ASTDirReturn.Return re) {
                     // Not an error, just a <#return>
                 } catch (TemplateException te) {
@@ -3079,7 +3155,14 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
             if (getCallableDefinition() == ASTDirMacroOrFunction.PASS_MACRO) {
                 return;
             }
-            genericExecute(args, callPlace, out, env);
+            
+            Writer prevOut = env.getOut();
+            try {
+                env.setOut(out);
+                genericExecute(args, callPlace, env);
+            } finally {
+                env.setOut(prevOut);
+            }
         }
 
         @Override
@@ -3115,11 +3198,15 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         public TemplateModel execute(TemplateModel[] args, CallPlace 
callPlace, Environment env)
                 throws TemplateException {
             env.setLastReturnValue(null);
+            Writer prevOut = env.getOut();
             try {
-                genericExecute(args, callPlace, _NullWriter.INSTANCE, env);
+                env.setOut(_NullWriter.INSTANCE);
+                genericExecute(args, callPlace, env);
             } catch (IOException e) {
                 // Should not occur
                 throw new TemplateException("Unexpected exception during 
function execution", e, env);
+            } finally {
+                env.setOut(prevOut);
             }
             return env.getLastReturnValue();
         }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
index e79bed2..a139a0a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
@@ -31,6 +31,7 @@ import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._StringUtils;
 import org.apache.freemarker.core.valueformat.TemplateDateFormat;
@@ -146,6 +147,9 @@ class MessageUtils {
      * The truncation is always signaled with a a {@code "..."} at the end of 
the result string.
      */
     static String shorten(String s, int maxLength) {
+        if (s == null) {
+            return null;
+        }
         if (maxLength < 5) maxLength = 5;
 
         boolean isTruncated = false;
@@ -359,4 +363,19 @@ class MessageUtils {
         return errorDescBuilder;
     }
 
+    static _ErrorDescriptionBuilder 
newBadNumberOfNestedContentParameterPassedMessage(
+            StringToIndexMap paramNamesOnCallPlace,
+            int numberOfParamsAtCallee) {
+        int numberOfParamsAtCallPlace = paramNamesOnCallPlace != null ? 
paramNamesOnCallPlace.size() : 0;
+        return new _ErrorDescriptionBuilder(
+            "The invocation declares ", (numberOfParamsAtCallPlace != 0 ? 
numberOfParamsAtCallPlace : "no"),
+            " nested content parameter(s)",
+            (numberOfParamsAtCallPlace != 0
+                    ? new Object[] { " (", new 
_DelayedJQuotedListing(paramNamesOnCallPlace.getKeys()), ")", }
+                    : ""),
+            ", but the called object intends to pass ",
+            numberOfParamsAtCallee, " parameters. You need to declare ", 
numberOfParamsAtCallee,
+            " nested content parameters.");
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
index 983c05d..f17b778 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
@@ -50,7 +50,10 @@ public interface ParsingConfiguration {
      * {@link TemplateLanguage#getAutoEscapingPolicy() autoEscapingPolicy}, 
that overrides the value of
      * the {@linkplain #getOutputFormat() outputFormat} and {@link 
#getAutoEscapingPolicy() autoEscapingPolicy}
      * settings that are coming from {@link 
Configuration#getTemplateConfigurations templateConfigurations}, from the
-     * {@link Configuration}, or from any other {@link ParsingConfiguration}.
+     * {@link Configuration}, or from any other {@link ParsingConfiguration}. 
Most {@link TemplateLanguage}-s should
+     * have non-{@code null} for those settings, to prevent confusion on the 
template author side. There can be
+     * exceptions from this though, like {@link DefaultTemplateLanguage#F3AC} 
(where the "C" at the end stands for
+     * "configurable") has {@code null} for these settings.
      * 
      * @see ParsingConfiguration#getRecognizeStandardFileExtensions()
      */

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/StaticLinkingCheckException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/StaticLinkingCheckException.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/StaticLinkingCheckException.java
new file mode 100644
index 0000000..50587b8
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/StaticLinkingCheckException.java
@@ -0,0 +1,74 @@
+/*
+ * 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 org.apache.freemarker.core.util._NullArgumentException;
+
+/**
+ * Exception thrown when a {@link Dialect} detects a problem during parsing 
(such a required parameter to a directive is
+ * missing). The exception will be converted to a {@link ParseException} by 
the parser, where the error message and
+ * cause exception will be the same as of the {@link 
StaticLinkingCheckException}. 
+ */
+//TODO [FM3][DIALECTS] will be public
+@SuppressWarnings("serial")
+final class StaticLinkingCheckException extends Exception {
+    
+    private final _ErrorDescriptionBuilder messageBuilder;
+    private String builtMessage; 
+    private final String simpleMessage; 
+
+    public StaticLinkingCheckException(_ErrorDescriptionBuilder 
messageBuilder) {
+        this(messageBuilder, null);
+    }
+    
+    public StaticLinkingCheckException(_ErrorDescriptionBuilder 
messageBuilder, Throwable cause) {
+        _NullArgumentException.check("messageBuilder", messageBuilder);
+        this.messageBuilder = messageBuilder;
+        simpleMessage = null;
+    }
+
+    public StaticLinkingCheckException(String message) {
+        this(message, null);
+    }
+    
+    public StaticLinkingCheckException(String message, Throwable cause) {
+        _NullArgumentException.check("message", message);
+        this.messageBuilder = null;
+        simpleMessage = message;
+    }
+
+    @Override
+    public String getMessage() {
+        if (simpleMessage != null) {
+            return simpleMessage;
+        }
+        
+        String builtDescription = this.builtMessage;
+        if (builtDescription != null) {
+            return builtDescription;
+        }
+        synchronized (this) {
+            builtDescription = messageBuilder.toString();
+            this.builtMessage = builtDescription;
+        }
+        return builtDescription;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/StaticallyLinkedNamespaceEntry.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/StaticallyLinkedNamespaceEntry.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/StaticallyLinkedNamespaceEntry.java
new file mode 100644
index 0000000..87ba7f7
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/StaticallyLinkedNamespaceEntry.java
@@ -0,0 +1,117 @@
+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 org.apache.freemarker.core.model.TemplateCallableModel;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.CommonSupplier;
+import org.apache.freemarker.core.util._NullArgumentException;
+
+/**
+ * A template language namespace entry that's linked statically to the 
referring template (TODO [FM3] for planned
+ * Dialects feature, currently unused).
+ * Static linking allows detecting mistakes during template parsing (as 
opposed to during the later template
+ * processing), can make accessing the value faster (by avoiding runtime 
lookup), and allows callables to have
+ * parse-time effects. Currently used in {@link Dialect}. 
+ */
+//TODO [FM3][FREEMARKER-99][DIALECTS] will be public. Also move it into 
core.dialect?
+final class StaticallyLinkedNamespaceEntry {
+    
+    private final String name;
+    private final TemplateModel value;
+    private final CommonSupplier<ASTDirective> directiveCallNodeFactory;
+    private final CommonSupplier<ASTExpFunctionCall> functionCallNodeFactory;
+    
+    /**
+     * @param name
+     *            Variable name in the namespace of the dialect
+     * @param value
+     *            The runtime value of the entry. Not {@code null}.
+     *            If this entry is a {@link TemplateCallableModel} that's not 
callable dynamically (as it has
+     *            parse-time effects or such dependencies), then the 
corresponding {@code execute} method should to
+     *            throw exception explaining that, but the {@link 
TemplateCallableModel} will be still used by the
+     *            parser to retrieve meta-data such as
+     *            {@link 
TemplateDirectiveModel#getDirectiveArgumentArrayLayout()},
+     *            {@link TemplateDirectiveModel#isNestedContentSupported()},
+     *            {@link 
TemplateFunctionModel#getFunctionArgumentArrayLayout()}.
+     * @param directiveCallNodeFactory
+     *            If this entry is usable as directive, the factory for the 
corresponding {@linkplain ASTNode AST node}.
+     *            At least one of {@code directiveCallNodeFactory} and {@code 
functionCallNodeFactory} must be
+     *            non-{@code null}. It must be ensured that the behavior of 
this node is consistent with argument array
+     *            layout and other meta-data present in the entry {@code 
value}.
+     * @param functionCallNodeFactory
+     *            If this entry is usable as function (or like an FM2 
"built-in"), the factory for the corresponding
+     *            {@linkplain ASTNode AST node}. Not {@code null}. It must be 
ensured that the behavior of this node is consistent with argument array
+     *            layout and other meta-data present in the entry {@code 
value}.
+     */
+    public StaticallyLinkedNamespaceEntry(
+            String name, TemplateModel value,
+            CommonSupplier<ASTDirective> directiveCallNodeFactory,
+            CommonSupplier<ASTExpFunctionCall> functionCallNodeFactory) {
+        _NullArgumentException.check("name", name);
+        this.name = name;
+        if (directiveCallNodeFactory == null && functionCallNodeFactory == 
null) {
+            throw new IllegalArgumentException(
+                    "At least one of \"directiveCallNodeFactory\" and 
\"functionCallNodeFactory\" must be non-null.");
+        }
+        this.directiveCallNodeFactory = directiveCallNodeFactory;
+        this.functionCallNodeFactory = functionCallNodeFactory;
+        this.value = value;
+    }
+
+    /**
+     * See similarly named
+     * {@linkplain #StaticallyLinkedNamespaceEntry(String, TemplateModel, 
CommonSupplier, CommonSupplier) constructor}
+     * parameter.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * See similarly named
+     * {@linkplain #StaticallyLinkedNamespaceEntry(String, TemplateModel, 
CommonSupplier, CommonSupplier) constructor}
+     * parameter.
+     */
+    public CommonSupplier<ASTDirective> getDirectiveCallNodeFactory() {
+        return directiveCallNodeFactory;
+    }
+
+    /**
+     * See similarly named
+     * {@linkplain #StaticallyLinkedNamespaceEntry(String, TemplateModel, 
CommonSupplier, CommonSupplier) constructor}
+     * parameter.
+     */
+    public CommonSupplier<ASTExpFunctionCall> getFunctionCallNodeFactory() {
+        return functionCallNodeFactory;
+    }
+
+    /**
+     * See similarly named
+     * {@linkplain #StaticallyLinkedNamespaceEntry(String, TemplateModel, 
CommonSupplier, CommonSupplier) constructor}
+     * parameter.
+     */
+    public TemplateModel getValue() {
+        return value;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
index 42e2686..c184281 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
@@ -122,7 +122,7 @@ public class Template implements ProcessingConfiguration, 
CustomStateScope {
     private AutoEscapingPolicy autoEscapingPolicy;
     // Values from template content that are detected automatically:
     private Charset actualSourceEncoding;
-    TagSyntax actualTagSyntax; // TODO [FM3][CF] Should be private
+    TagSyntax actualTagSyntax; // TODO [FM3][FREEMARKER-99] Should be private
     private InterpolationSyntax interpolationSyntax;
 
     // Custom state:

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
index 8627059..1b1b9a9 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
@@ -36,10 +36,11 @@ import 
org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory;
 /**
  * A partial set of configuration settings used for customizing the {@link 
Configuration}-level settings for individual
  * {@link Template}-s (or rather, for a group of templates). That it's partial 
means that you should call the
- * corresponding {@code isXxxSet()} before getting a settings, or else you may 
cause
- * {@link CoreSettingValueNotSetException}. (There's no fallback to the {@link 
Configuration}-level settings to keep the
- * dependency graph of configuration related beans non-cyclic. As user code 
seldom reads settings directly from
- * {@link TemplateConfiguration}-s anyway, this compromise was chosen.)
+ * corresponding {@code isXxxSet()} before getting a setting, or else you may 
cause
+ * {@link CoreSettingValueNotSetException}. (There's no fallback to the {@link 
Configuration}-level settings implemented
+ * in this class, to keep the dependency graph of configuration related beans 
non-cyclic. Instead, the caller of this
+ * API must implement the fallback itself. As user code hardly ever reads 
directly from {@link TemplateConfiguration}-s,
+ * this compromise was chosen.)
  * <p>
  * Note on the {@code locale} setting: When used with the standard template 
loading/caching mechanism ({@link
  * Configuration#getTemplate(String)} and its overloads), localized template 
lookup happens before the {@code locale}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementArrayBuilder.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementArrayBuilder.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementArrayBuilder.java
index 4bc9a4f..734622e 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementArrayBuilder.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementArrayBuilder.java
@@ -83,18 +83,5 @@ class TemplateElements {
             }
         }
     }
-    
-    /**
-     * Used for some backward compatibility hacks.
-     */
-    ASTImplicitParent asMixedContent() {
-        ASTImplicitParent mixedContent = new ASTImplicitParent();
-        if (count != 0) {
-            ASTElement first = buffer[0];
-            mixedContent.setChildren(this);
-            mixedContent.setLocation(first.getTemplate(), first, getLast());
-        }
-        return mixedContent;
-    }
 
 }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementsToVisit.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementsToVisit.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementsToVisit.java
index 145916c..defcf5d 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementsToVisit.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateElementsToVisit.java
@@ -22,9 +22,9 @@ import java.util.Collection;
 import java.util.Collections;
 
 /**
- * Used as the return value of {@link ASTElement#accept(Environment)} when the 
invoked element has nested elements
+ * Used as the return value of {@link ASTElement#execute(Environment)} when 
the invoked element has nested elements
  * to invoke. It would be more natural to invoke child elements before 
returning from
- * {@link ASTElement#accept(Environment)}, however, if there's nothing to do 
after the child elements were invoked,
+ * {@link ASTElement#execute(Environment)}, however, if there's nothing to do 
after the child elements were invoked,
  * that would mean wasting stack space.
  */
 class TemplateElementsToVisit {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
index 3b1a812..98002c9 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateLanguage.java
@@ -29,9 +29,8 @@ import org.apache.freemarker.core.util._NullArgumentException;
 import org.apache.freemarker.core.util._StringUtils;
 
 /**
- * Represents a template language; a template language specifies the syntax, 
and usually also the {@link OutputFormat}
- * and {@link AutoEscapingPolicy} of the template. In the future (TODO [FM3]) 
custom template languages may also
- * specify the dialect (which is the set of core directives and functions).  
+ * Represents a template language; a template language specifies the syntax, 
the {@link Dialect}, and usually also the
+ * {@link OutputFormat} and {@link AutoEscapingPolicy}.  
  * 
  * <p><em>Currently this class is not mature, so it can't be implemented 
outside FreeMarker,
  * also its methods shouldn't be called from outside FreeMarker.</em>
@@ -48,6 +47,7 @@ public abstract class TemplateLanguage {
     private final String fileExtension;
     private final OutputFormat outputFormat;
     private final AutoEscapingPolicy autoEscapingPolicy;
+    private final Dialect dialect;
     
     // Package visibility to prevent user implementations until this API is 
mature.
 
@@ -70,14 +70,14 @@ public abstract class TemplateLanguage {
      *             If the {@code #fileExtension} argument contains upper case 
letter or dot, or if it starts with "f"
      *             and the language isn't defined by the FreeMarker project.
      */
-    public TemplateLanguage(String fileExtension, OutputFormat outputFormat, 
AutoEscapingPolicy autoEscapingPolicy) {
-        this(fileExtension, false, outputFormat, autoEscapingPolicy);
+    public TemplateLanguage(String fileExtension, Dialect dialect, 
OutputFormat outputFormat, AutoEscapingPolicy autoEscapingPolicy) {
+        this(fileExtension, false, dialect, outputFormat, autoEscapingPolicy);
     }
     
     /**
-     * Non-public constructor used for languages defined by the FreeMarker 
project.
+     * Used internally to allow extensions starting with "f" (as those are 
reserved for FreeMarker) 
      */
-    TemplateLanguage(String fileExtension, boolean allowExtensionStartingWithF,
+    TemplateLanguage(String fileExtension, boolean 
allowExtensionStartingWithF, Dialect dialect,
             OutputFormat outputFormat, AutoEscapingPolicy autoEscapingPolicy) {
         _NullArgumentException.check("fileExtension", fileExtension);
         for (int i = 0; i < fileExtension.length(); i++) {
@@ -94,6 +94,10 @@ public abstract class TemplateLanguage {
                     "The \"fileExtension\" argument can't start with 'f' for 
an user-defined language.");
         }
         this.fileExtension = fileExtension;
+        
+        _NullArgumentException.check("dialect", dialect);
+        this.dialect = dialect;
+        
         this.outputFormat = outputFormat;
         this.autoEscapingPolicy = autoEscapingPolicy;
     }
@@ -123,13 +127,15 @@ public abstract class TemplateLanguage {
     public String getFileExtension() {
         return fileExtension;
     }
-
-    @Override
-    public final String toString() {
-        return "TemplateLanguage(" + _StringUtils.jQuote(fileExtension) + ")";
-    }
     
     /**
+     * Returns the {@link Dialect} used by this template language; not {@code 
null}.
+     */
+    public Dialect getDialect() {
+        return dialect;
+    }
+
+    /**
      * The {@link OutputFormat} that this language enforces, or else {@code 
null} (and it comes from the
      * {@link ParsingConfiguration}).
      * 
@@ -151,4 +157,9 @@ public abstract class TemplateLanguage {
         return autoEscapingPolicy;
     }
 
+    @Override
+    public final String toString() {
+        return "TemplateLanguage(" + _StringUtils.jQuote(fileExtension) + ")";
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
index be7877c..874a896 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
@@ -56,7 +56,7 @@ class ThreadInterruptionSupportTemplatePostProcessor extends 
TemplatePostProcess
         
         final int childCount = te.getChildCount();
         for (int i = 0; i < childCount; i++) {
-            addInterruptionChecks(te.getChild(i));
+            addInterruptionChecks(te.fastGetChild(i));
         }
         
         if (te.isNestedBlockRepeater()) {
@@ -79,7 +79,7 @@ class ThreadInterruptionSupportTemplatePostProcessor extends 
TemplatePostProcess
         }
 
         @Override
-        ASTElement[] accept(Environment env) throws TemplateException, 
IOException {
+        ASTElement[] execute(Environment env) throws TemplateException, 
IOException {
             // As the API doesn't allow throwing InterruptedException here 
(nor anywhere else, most importantly,
             // Template.process can't throw it), we must not clear the 
"interrupted" flag of the thread.
             if (Thread.currentThread().isInterrupted()) {
@@ -89,12 +89,12 @@ class ThreadInterruptionSupportTemplatePostProcessor 
extends TemplatePostProcess
         }
 
         @Override
-        protected String dump(boolean canonical) {
-            return canonical ? "" : "<#--" + getASTNodeDescriptor() + "--#>";
+        String dump(boolean canonical) {
+            return canonical ? "" : "<#--" + getLabelWithoutParameters() + 
"--#>";
         }
 
         @Override
-        String getASTNodeDescriptor() {
+        public String getLabelWithoutParameters() {
             return "##threadInterruptionCheck";
         }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/UnparsedTemplateLanguage.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/UnparsedTemplateLanguage.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/UnparsedTemplateLanguage.java
index 2119ee3..6fe58f5 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/UnparsedTemplateLanguage.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/UnparsedTemplateLanguage.java
@@ -45,7 +45,8 @@ public final class UnparsedTemplateLanguage extends 
TemplateLanguage {
     
     private UnparsedTemplateLanguage(String fileExtension, boolean 
allowExtensionStartingWithF,
             OutputFormat outputFormat) {
-        super(fileExtension, allowExtensionStartingWithF, outputFormat, 
AutoEscapingPolicy.ENABLE_IF_DEFAULT);
+        super(fileExtension, allowExtensionStartingWithF, 
DefaultDialect.INSTANCE,
+                outputFormat, AutoEscapingPolicy.ENABLE_IF_DEFAULT);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/UnsupportedFM2TemplateLanguage.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/UnsupportedFM2TemplateLanguage.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/UnsupportedFM2TemplateLanguage.java
index 158a35a..e67091d 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/UnsupportedFM2TemplateLanguage.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/UnsupportedFM2TemplateLanguage.java
@@ -41,7 +41,7 @@ class UnsupportedFM2TemplateLanguage extends TemplateLanguage 
{
     
     private UnsupportedFM2TemplateLanguage(String fileExtension, OutputFormat 
outputFormat,
             AutoEscapingPolicy autoEscapingPolicy) {
-        super(fileExtension, true, outputFormat, autoEscapingPolicy);
+        super(fileExtension, true, DefaultDialect.INSTANCE, outputFormat, 
autoEscapingPolicy);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
index 542a2f2..a4132c0 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
@@ -57,7 +57,7 @@ public class _CallableUtils {
             throws TemplateException {
         if (namedArgs != null) {
             throw new TemplateException(env,
-                    getNamedArgumentsNotSupportedMessage(callable, 
namedArgs[0], calledAsFunction));
+                    getNamedArgumentsNotSupportedMessage(callable, 
namedArgs[0].name, calledAsFunction));
         }
 
         TemplateModel[] execArgs;
@@ -77,8 +77,8 @@ public class _CallableUtils {
             ASTExpression[] positionalArgs, NamedArgument[] namedArgs, 
ArgumentArrayLayout argsLayout,
             TemplateCallableModel callable, boolean calledAsFunction,
             Environment env) throws TemplateException {
-        int predefPosArgCnt = 
argsLayout.getPredefinedPositionalArgumentCount();
-        int posVarargsArgIdx = argsLayout.getPositionalVarargsArgumentIndex();
+        final int predefPosArgCnt = 
argsLayout.getPredefinedPositionalArgumentCount();
+        final int posVarargsArgIdx = 
argsLayout.getPositionalVarargsArgumentIndex();
 
         TemplateModel[] execArgs = new 
TemplateModel[argsLayout.getTotalLength()];
 
@@ -104,44 +104,14 @@ public class _CallableUtils {
             }
             execArgs[posVarargsArgIdx] = varargsSeq;
         } else if (positionalArgs != null && positionalArgs.length > 
predefPosArgCnt) {
-            checkSupportsAnyParameters(callable, argsLayout, calledAsFunction);
-            List<String> validPredefNames = 
argsLayout.getPredefinedNamedArgumentsMap().getKeys();
-            _ErrorDescriptionBuilder errorDesc = new _ErrorDescriptionBuilder(
-                    getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
-                    "This ", getCallableTypeName(callable, calledAsFunction),
-                    " ",
-                    (predefPosArgCnt != 0
-                            ? new Object[]{ "can only have ", predefPosArgCnt }
-                            : "can't have"
-                    ),
-                    " arguments passed by position, but the invocation has ",
-                    positionalArgs.length, " such arguments.",
-                    (!argsLayout.isPositionalParametersSupported() && 
argsLayout.isNamedParametersSupported() ?
-                            new Object[] {
-                                    " Try to pass arguments by name (as in ",
-                                    (callable instanceof TemplateDirectiveModel
-                                            ? "<@example x=1 y=2 />"
-                                            : "example(x=1, y=2)"),
-                                    ")",
-                                    (!validPredefNames.isEmpty()
-                                            ? new Object[] { " The supported 
parameter names are: ",
-                                            new 
_DelayedJQuotedListing(validPredefNames)}
-                                            : 
_CollectionUtils.EMPTY_OBJECT_ARRAY)}
-                            : "")
-            );
-            if (callable instanceof Environment.TemplateLanguageDirective
-                    && !argsLayout.isPositionalParametersSupported() && 
argsLayout.isNamedParametersSupported()) {
-                errorDesc.tip("You can pass a parameter by position (i.e., 
without specifying its name, as you"
-                        + " have tried now) when the macro has defined that 
parameter to be a positional parameter. "
-                        + "See in the documentation how, and when that's a 
good practice.");
-            }
-            throw new TemplateException(env, errorDesc);
+            throw new TemplateException(env,
+                    newPositionalArgDoesNotFitArgLayoutErrorDesc(argsLayout, 
callable, calledAsFunction));
         }
 
         int namedVarargsArgumentIndex = 
argsLayout.getNamedVarargsArgumentIndex();
         NativeHashEx namedVarargsHash = null;
         if (namedArgs != null) {
-            StringToIndexMap predefNamedArgsMap = 
argsLayout.getPredefinedNamedArgumentsMap();
+            final StringToIndexMap predefNamedArgsMap = 
argsLayout.getPredefinedNamedArgumentsMap();
             for (NamedArgument namedArg : namedArgs) {
                 int argIdx = predefNamedArgsMap.get(namedArg.name);
                 if (argIdx != -1) {
@@ -149,20 +119,9 @@ public class _CallableUtils {
                 } else {
                     if (namedVarargsHash == null) {
                         if (namedVarargsArgumentIndex == -1) {
-                            checkSupportsAnyParameters(callable, argsLayout, 
calledAsFunction);
-                            Collection<String> validNames = 
predefNamedArgsMap.getKeys();
                             throw new TemplateException(env,
-                                    validNames == null || validNames.isEmpty()
-                                            ? 
getNamedArgumentsNotSupportedMessage(
-                                                    callable, namedArg, 
calledAsFunction)
-                                            : new Object[] {
-                                                    
getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
-                                                    "This ", 
getCallableTypeName(callable, calledAsFunction),
-                                                    " has no parameter that's 
passed by name and is called ",
-                                                    new 
_DelayedJQuote(namedArg.name),
-                                                    ". The supported parameter 
names are:\n",
-                                                    new 
_DelayedJQuotedListing(validNames)
-                                            });
+                                    newNamedArgumentDoesNotArgLayoutErrorDesc(
+                                            argsLayout, namedArg.name, 
callable, calledAsFunction));
                         }
 
                         namedVarargsHash = new NativeHashEx();
@@ -177,13 +136,88 @@ public class _CallableUtils {
         return execArgs;
     }
 
+    static _ErrorDescriptionBuilder 
newPositionalArgDoesNotFitArgLayoutErrorDesc(
+            ArgumentArrayLayout argsLayout, TemplateCallableModel callable, 
boolean calledAsFunction) {
+        _ErrorDescriptionBuilder noParamsED = 
checkSupportsAnyParameters(callable, argsLayout, calledAsFunction);
+        if (noParamsED != null) {
+            return noParamsED;
+        }
+        
+        List<String> validPredefNames;
+        _ErrorDescriptionBuilder errorDesc = new _ErrorDescriptionBuilder(
+                getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
+                "This ", getCallableTypeName(callable, calledAsFunction),
+                " ",
+                (argsLayout.getPredefinedPositionalArgumentCount() != 0
+                        ? new Object[]{ "can only have ", 
argsLayout.getPredefinedPositionalArgumentCount() }
+                        : "can't have"
+                ),
+                " arguments passed by position, but the invocation tries to 
pass in more.",
+                (!argsLayout.isPositionalParametersSupported() && 
argsLayout.isNamedParametersSupported() ?
+                        new Object[] {
+                                " Try to pass arguments by name (as in ",
+                                (callable instanceof TemplateDirectiveModel
+                                        ? "<@example x=1 y=2 />"
+                                        : "example(x=1, y=2)"),
+                                ")",
+                                (!(validPredefNames = 
argsLayout.getPredefinedNamedArgumentsMap().getKeys())
+                                        .isEmpty()
+                                        ? new Object[] {
+                                                " The supported parameter 
names are: ",
+                                                new 
_DelayedJQuotedListing(validPredefNames)
+                                        }
+                                        : _CollectionUtils.EMPTY_OBJECT_ARRAY)}
+                        : "")
+        );
+        if (callable instanceof Environment.TemplateLanguageDirective
+                && !argsLayout.isPositionalParametersSupported() && 
argsLayout.isNamedParametersSupported()) {
+            errorDesc.tip("You can pass a parameter by position (i.e., without 
specifying its name, as you"
+                    + " have tried now) when the directuve (the macro) has 
defined that parameter to be a "
+                    + "positional parameter. See in the documentation how, and 
when that's a good practice.");
+        }
+        return errorDesc;
+    }
+    
+    static _ErrorDescriptionBuilder 
newNamedArgumentDoesNotArgLayoutErrorDesc(ArgumentArrayLayout argsLayout,
+            String argName, TemplateCallableModel callable, boolean 
calledAsFunction) {
+        _ErrorDescriptionBuilder noParamsED = 
checkSupportsAnyParameters(callable, argsLayout, calledAsFunction);
+        if (noParamsED != null) {
+            return noParamsED;
+        }
+        
+        Collection<String> validNames = 
argsLayout.getPredefinedNamedArgumentsMap().getKeys();
+        _ErrorDescriptionBuilder errorDesc = new _ErrorDescriptionBuilder(
+                validNames == null || validNames.isEmpty()
+                ? getNamedArgumentsNotSupportedMessage(
+                        callable, argName, calledAsFunction)
+                : new Object[] {
+                        getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
+                        "This ", getCallableTypeName(callable, 
calledAsFunction),
+                        " has no parameter that's passed by name and is called 
",
+                        new _DelayedJQuote(argName),
+                        ". The supported parameter names are:\n",
+                        new _DelayedJQuotedListing(validNames)
+                });
+        return errorDesc;
+    }
+    
+    static private _ErrorDescriptionBuilder checkSupportsAnyParameters(
+            TemplateCallableModel callable, ArgumentArrayLayout argsLayout, 
boolean calledAsFunction)  {
+        return argsLayout.getTotalLength() == 0
+                ? new _ErrorDescriptionBuilder(
+                            getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
+                            "This ", getCallableTypeName(callable, 
calledAsFunction),
+                            " doesn't support any parameters.")
+                : null;
+    }
+
     private static Object[] 
getNamedArgumentsNotSupportedMessage(TemplateCallableModel callable,
-            _CallableUtils.NamedArgument namedArg, boolean calledAsFunction) {
+            String argName, boolean calledAsFunction) {
         return new Object[] {
                 getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
                 "This ", getCallableTypeName(callable, calledAsFunction),
                 " can't have arguments that are passed by name (like ",
-                new _DelayedJQuote(namedArg.name), "). Try to pass arguments 
by position "
+                new _DelayedJQuote(argName), "). Try to pass arguments by 
position "
                 + "(i.e, without name, as in ",
                 (callable instanceof TemplateDirectiveModel
                         ? "<@example arg1, arg2, arg3 />"
@@ -192,17 +226,6 @@ public class _CallableUtils {
         };
     }
 
-    static private void checkSupportsAnyParameters(
-            TemplateCallableModel callable, ArgumentArrayLayout argsLayout, 
boolean calledAsFunction)
-            throws TemplateException {
-        if (argsLayout.getTotalLength() == 0) {
-            throw new TemplateException(
-                    getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
-                    "This ", getCallableTypeName(callable, calledAsFunction),
-                    " doesn't support any parameters.");
-        }
-    }
-
     /**
      * Something like {@code "When calling function \"lib.f3ah:foo\": " or 
"When calling ?leftPad: "}
      */

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java
index e15374b..c94e369 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java
@@ -19,7 +19,6 @@
 package org.apache.freemarker.core;
 
 import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.List;
 
 /**
@@ -42,7 +41,7 @@ public final class _Debug {
         // TODO: Ensure there always is a parent by making sure
         // that the root element in the template is always a ASTImplicitParent
         // Also make sure it doesn't conflict with anyone's code.
-        parent.setChildAt(parent.getIndex(te), db);
+        parent.setChildAt(indexOfChild(parent, te), db);
     }
 
     public static void removeDebugBreak(Template t, int line) {
@@ -62,7 +61,16 @@ public final class _Debug {
             return;
         }
         ASTElement parent = db.getParent();
-        parent.setChildAt(parent.getIndex(db), db.getChild(0));
+        parent.setChildAt(indexOfChild(parent, db), db.fastGetChild(0));
+    }
+    
+    private static final int indexOfChild(ASTElement parent, ASTElement node) {
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            if (parent.getChild(i) == node) {
+                return i;
+            }
+        }
+        return -1;
     }
 
     private static ASTElement findTemplateElement(ASTElement te, int line) {
@@ -70,9 +78,8 @@ public final class _Debug {
             return null;
         }
         // Find the narrowest match
-        List childMatches = new ArrayList();
-        for (Enumeration children = te.children(); children.hasMoreElements(); 
) {
-            ASTElement child = (ASTElement) children.nextElement();
+        List<ASTElement> childMatches = new ArrayList<>();
+        for (ASTElement child : te.getChildren()) {
             ASTElement childmatch = findTemplateElement(child, line);
             if (childmatch != null) {
                 childMatches.add(childmatch);
@@ -81,7 +88,7 @@ public final class _Debug {
         //find a match that exactly matches the begin/end line
         ASTElement bestMatch = null;
         for (int i = 0; i < childMatches.size(); i++) {
-            ASTElement e = (ASTElement) childMatches.get(i);
+            ASTElement e = childMatches.get(i);
 
             if ( bestMatch == null ) {
                 bestMatch = e;
@@ -110,9 +117,9 @@ public final class _Debug {
     private static void removeDebugBreaks(ASTElement te) {
         int count = te.getChildCount();
         for (int i = 0; i < count; ++i) {
-            ASTElement child = te.getChild(i);
+            ASTElement child = te.fastGetChild(i);
             while (child instanceof ASTDebugBreak) {
-                ASTElement dbchild = child.getChild(0);
+                ASTElement dbchild = child.fastGetChild(0);
                 te.setChildAt(i, dbchild);
                 child = dbchild;
             }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
index 7d96e3d..f00da1a 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
@@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
  * Note that this class isn't serializable, thus the containing exception 
should render the message before it's
  * serialized.
  */
+//TODO [FM3] will be public
 public class _ErrorDescriptionBuilder {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(_ErrorDescriptionBuilder.class);
@@ -77,7 +78,7 @@ public class _ErrorDescriptionBuilder {
                 Blaming blaming = findBlaming(parentElement, blamed, 0);
                 if (blaming != null) {
                     sb.append("For ");
-                    String nss = blaming.blamer.getASTNodeDescriptor();
+                    String nss = blaming.blamer.getLabelWithoutParameters();
                     char q = nss.indexOf('"') == -1 ? '\"' : '`';
                     sb.append(q).append(nss).append(q);
                     sb.append(" ").append(blaming.roleOfblamed).append(": ");
@@ -117,7 +118,8 @@ public class _ErrorDescriptionBuilder {
             }
             
             sb.append("  [");
-            sb.append(blamed.getStartLocation());
+            sb.append(MessageUtils.formatLocationForEvaluationError(
+                    blamed.getTemplate(), blamed.getBeginLine(), 
blamed.getEndLine()));
             sb.append(']');
             
             

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
index 525ed82..150bb6e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
@@ -21,6 +21,8 @@ package org.apache.freemarker.core;
 
 import static org.apache.freemarker.core.MessageUtils.*;
 
+import java.io.IOException;
+import java.io.Writer;
 import java.util.Date;
 
 import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
@@ -33,6 +35,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
+import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util._ClassUtils;
 import org.apache.freemarker.core.valueformat.TemplateDateFormat;
@@ -338,6 +341,9 @@ public class _EvalUtils {
      * 
      * @param seqTip
      *            Tip to display if the value type is not coercable, but it's 
iterable.
+     * @param exp
+     *            The expression that was evaluated to {@code tm}. This can be 
{@code null}, however that may results
+     *            in poor quality error messages.
      * 
      * @return Never {@code null}
      */
@@ -540,5 +546,29 @@ public class _EvalUtils {
                 ? env.getArithmeticEngine()
                 : 
tObj.getTemplate().getParsingConfiguration().getArithmeticEngine();
     }
+
+    public static void printTemplateMarkupOutputModel(final 
TemplateMarkupOutputModel mo, OutputFormat outputFormat,
+            final Writer out, ASTExpression exp) throws TemplateException, 
IOException {
+        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(exp,
+                        "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);
+        }
+    }
     
 }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayAdapterList.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayAdapterList.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayAdapterList.java
index b653f7f..95c06bc 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayAdapterList.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayAdapterList.java
@@ -51,7 +51,7 @@ public class _ArrayAdapterList<E> extends AbstractList<E> {
 
     @Override
     public Iterator<E> iterator() {
-        return new _ArrayIterator(array);
+        return new _ArrayIterator<E>(array);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayEnumeration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayEnumeration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayEnumeration.java
deleted file mode 100644
index 1c82658..0000000
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayEnumeration.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.util;
-
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
-
-/** Don't use this; used internally by FreeMarker, might changes without 
notice. */
-public class _ArrayEnumeration implements Enumeration {
-
-    private final Object[] array;
-    private final int size;
-    private int nextIndex;
-
-    public _ArrayEnumeration(Object[] array, int size) {
-        this.array = array;
-        this.size = size;
-        nextIndex = 0;
-    }
-
-    @Override
-    public boolean hasMoreElements() {
-        return nextIndex < size;
-    }
-
-    @Override
-    public Object nextElement() {
-        if (nextIndex >= size) {
-            throw new NoSuchElementException();
-        }
-        return array[nextIndex++];
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayIterator.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayIterator.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayIterator.java
index 7e02449..0ff9241 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayIterator.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_ArrayIterator.java
@@ -23,12 +23,12 @@ import java.util.Iterator;
 import java.util.NoSuchElementException;
 
 /** Don't use this; used internally by FreeMarker, might changes without 
notice. */
-public class _ArrayIterator implements Iterator {
+public class _ArrayIterator<T> implements Iterator<T> {
 
-    private final Object[] array;
+    private final T[] array;
     private int nextIndex;
 
-    public _ArrayIterator(Object[] array) {
+    public _ArrayIterator(T[] array) {
         this.array = array;
         nextIndex = 0;
     }
@@ -39,7 +39,7 @@ public class _ArrayIterator implements Iterator {
     }
 
     @Override
-    public Object next() {
+    public T next() {
         if (nextIndex >= array.length) {
             throw new NoSuchElementException();
         }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/util/_NullArgumentException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_NullArgumentException.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_NullArgumentException.java
index 4e7f777..d9cdad9 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_NullArgumentException.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_NullArgumentException.java
@@ -22,6 +22,7 @@ package org.apache.freemarker.core.util;
 /**
  * Indicates that an argument that must be non-{@code null} was {@code null}. 
  */
+@SuppressWarnings("serial")
 public class _NullArgumentException extends IllegalArgumentException {
 
     public _NullArgumentException() {
@@ -46,6 +47,7 @@ public class _NullArgumentException extends 
IllegalArgumentException {
     }
 
     /**
+     * Convenience method to protect against a {@code null} argument.
      */
     public static void check(Object argumentValue) {
         if (argumentValue == null) {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/javacc/FTL.jj 
b/freemarker-core/src/main/javacc/FTL.jj
index aff3750..11e7ed2 100644
--- a/freemarker-core/src/main/javacc/FTL.jj
+++ b/freemarker-core/src/main/javacc/FTL.jj
@@ -30,6 +30,7 @@ PARSER_BEGIN(FMParser)
 package org.apache.freemarker.core;
 
 import org.apache.freemarker.core.*;
+import org.apache.freemarker.core.Dialect.ConfiguredDialect;
 import org.apache.freemarker.core.outputformat.*;
 import org.apache.freemarker.core.outputformat.impl.*;
 import org.apache.freemarker.core.model.*;
@@ -84,6 +85,7 @@ public class FMParser {
     private boolean autoEscaping;
     private ParsingConfiguration pCfg;
     DefaultTemplateLanguage templateLanguage;
+    ConfiguredDialect configuredDialect;
     private InputStream streamToUnmarkWhenEncEstabd;
 
     /** Keeps track of #list nesting. */
@@ -113,6 +115,7 @@ public class FMParser {
     private StringToIndexMap.Entry[] topNestedContentParamNamesBuffer;
     private int topNestedContentParamNamesLength;
 
+
     FMParser(Template template, Reader reader,
             ParsingConfiguration pCfg,
             InputStream streamToUnmarkWhenEncEstabd) {
@@ -143,6 +146,7 @@ public class FMParser {
         token_source.incompatibleImprovements = incompatibleImprovements;
         this.incompatibleImprovements = incompatibleImprovements;
 
+        // We know that it's a DefaultTemplateLanguage, as this is the parser 
of the DefaultTemplateLanguage. 
         templateLanguage = (DefaultTemplateLanguage) 
pCfg.getTemplateLanguage();
 
         outputFormat = pCfg.getOutputFormat();
@@ -165,6 +169,8 @@ public class FMParser {
         token_source.interpolationSyntax = 
templateLanguage.getInterpolationSyntax();
 
         this.stripWhitespace = pCfg.getWhitespaceStripping();
+
+        configuredDialect = 
template.getConfiguration().getConfiguredDialect(templateLanguage.getDialect());
     }
     
     void setupStringLiteralMode(FMParserTokenManager parentTokenSource, 
OutputFormat outputFormat) {
@@ -3315,7 +3321,7 @@ ASTElement DynamicTopLevelCall() :
         )
         |
         (
-            // This could be part of the positional paramter choice, but we 
can give better error messages this way.
+            // This could be part of the positional parameter choice, but we 
can give better error messages this way.
             t = <COMMA>
             {
                 if (prevChoiceComma != null) {

Reply via email to