http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..7766012
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTStaticText.java
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.IOException;
+
+import org.apache.freemarker.core.util._CollectionUtil;
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * AST node representing static text.
+ */
+final class ASTStaticText extends ASTElement {
+    
+    // We're using char[] instead of String for storing the text block because
+    // Writer.write(String) involves copying the String contents to a char[] 
+    // using String.getChars(), and then calling Writer.write(char[]). By
+    // using Writer.write(char[]) directly, we avoid array copying on each 
+    // write. 
+    private char[] text;
+    private final boolean unparsed;
+
+    public ASTStaticText(String text) {
+        this(text, false);
+    }
+
+    public ASTStaticText(String text, boolean unparsed) {
+        this(text.toCharArray(), unparsed);
+    }
+
+    ASTStaticText(char[] text, boolean unparsed) {
+        this.text = text;
+        this.unparsed = unparsed;
+    }
+    
+    void replaceText(String text) {
+        this.text = text.toCharArray();
+    }
+
+    /**
+     * 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 {
+        env.getOut().write(text);
+        return null;
+    }
+
+    @Override
+    protected String dump(boolean canonical) {
+        if (canonical) {
+            String text = new String(this.text);
+            if (unparsed) {
+                return "<#noparse>" + text + "</#noparse>";
+            }
+            return text;
+        } else {
+            return "text " + _StringUtil.jQuote(new String(text));
+        }
+    }
+    
+    @Override
+    String getNodeTypeSymbol() {
+        return "#text";
+    }
+    
+    @Override
+    int getParameterCount() {
+        return 1;
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        if (idx != 0) throw new IndexOutOfBoundsException();
+        return new String(text);
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        if (idx != 0) throw new IndexOutOfBoundsException();
+        return ParameterRole.CONTENT;
+    }
+
+    @Override
+    ASTElement postParseCleanup(boolean stripWhitespace) {
+        if (text.length == 0) return this;
+        int openingCharsToStrip = 0, trailingCharsToStrip = 0;
+        boolean deliberateLeftTrim = deliberateLeftTrim();
+        boolean deliberateRightTrim = deliberateRightTrim();
+        if (!stripWhitespace || text.length == 0 ) {
+            return this;
+        }
+        ASTElement parentElement = getParent();
+        if (isTopLevelTextIfParentIs(parentElement) && previousSibling() == 
null) {
+            return this;
+        }
+        if (!deliberateLeftTrim) {
+            trailingCharsToStrip = trailingCharsToStrip();
+        }
+        if (!deliberateRightTrim) {
+            openingCharsToStrip = openingCharsToStrip();
+        }
+        if (openingCharsToStrip == 0 && trailingCharsToStrip == 0) {
+            return this;
+        }
+        text = substring(text, openingCharsToStrip, text.length - 
trailingCharsToStrip);
+        if (openingCharsToStrip > 0) {
+            beginLine++;
+            beginColumn = 1;
+        }
+        if (trailingCharsToStrip > 0) {
+            endColumn = 0;
+        }
+        return this;
+    }
+    
+    /**
+     * Scans forward the nodes on the same line to see whether there is a 
+     * deliberate left trim in effect. Returns true if the left trim was 
present.
+     */
+    private boolean deliberateLeftTrim() {
+        boolean result = false;
+        for (ASTElement elem = nextTerminalNode();
+             elem != null && elem.beginLine == endLine;
+             elem = elem.nextTerminalNode()) {
+            if (elem instanceof ASTDirTOrTrOrTl) {
+                ASTDirTOrTrOrTl ti = (ASTDirTOrTrOrTl) elem;
+                if (!ti.left && !ti.right) {
+                    result = true;
+                }
+                if (ti.left) {
+                    result = true;
+                    int lastNewLineIndex = lastNewLineIndex();
+                    if (lastNewLineIndex >= 0  || beginColumn == 1) {
+                        char[] firstPart = substring(text, 0, lastNewLineIndex 
+ 1);
+                        char[] lastLine = substring(text, 1 + 
lastNewLineIndex); 
+                        if (_StringUtil.isTrimmableToEmpty(lastLine)) {
+                            text = firstPart;
+                            endColumn = 0;
+                        } else {
+                            int i = 0;
+                            while (Character.isWhitespace(lastLine[i])) {
+                                i++;
+                            }
+                            char[] printablePart = substring(lastLine, i);
+                            text = concat(firstPart, printablePart);
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Checks for the presence of a t or rt directive on the 
+     * same line. Returns true if the right trim directive was present.
+     */
+    private boolean deliberateRightTrim() {
+        boolean result = false;
+        for (ASTElement elem = prevTerminalNode();
+             elem != null && elem.endLine == beginLine;
+             elem = elem.prevTerminalNode()) {
+            if (elem instanceof ASTDirTOrTrOrTl) {
+                ASTDirTOrTrOrTl ti = (ASTDirTOrTrOrTl) elem;
+                if (!ti.left && !ti.right) {
+                    result = true;
+                }
+                if (ti.right) {
+                    result = true;
+                    int firstLineIndex = firstNewLineIndex() + 1;
+                    if (firstLineIndex == 0) {
+                        return false;
+                    }
+                    if (text.length > firstLineIndex 
+                        && text[firstLineIndex - 1] == '\r' 
+                        && text[firstLineIndex] == '\n') {
+                        firstLineIndex++;
+                    }
+                    char[] trailingPart = substring(text, firstLineIndex);
+                    char[] openingPart = substring(text, 0, firstLineIndex);
+                    if (_StringUtil.isTrimmableToEmpty(openingPart)) {
+                        text = trailingPart;
+                        beginLine++;
+                        beginColumn = 1;
+                    } else {
+                        int lastNonWS = openingPart.length - 1;
+                        while (Character.isWhitespace(text[lastNonWS])) {
+                            lastNonWS--;
+                        }
+                        char[] printablePart = substring(text, 0, lastNonWS + 
1);
+                        if (_StringUtil.isTrimmableToEmpty(trailingPart)) {
+                        // THIS BLOCK IS HEINOUS! THERE MUST BE A BETTER WAY! 
REVISIT (JR)
+                            boolean trimTrailingPart = true;
+                            for (ASTElement te = nextTerminalNode();
+                                 te != null && te.beginLine == endLine;
+                                 te = te.nextTerminalNode()) {
+                                if (te.heedsOpeningWhitespace()) {
+                                    trimTrailingPart = false;
+                                }
+                                if (te instanceof ASTDirTOrTrOrTl && 
((ASTDirTOrTrOrTl) te).left) {
+                                    trimTrailingPart = true;
+                                    break;
+                                }
+                            }
+                            if (trimTrailingPart) trailingPart = 
_CollectionUtil.EMPTY_CHAR_ARRAY;
+                        }
+                        text = concat(printablePart, trailingPart);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+    
+    private int firstNewLineIndex() {
+        char[] text = this.text;
+        for (int i = 0; i < text.length; i++) {
+            char c = text[i];
+            if (c == '\r' || c == '\n' ) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private int lastNewLineIndex() {
+        char[] text = this.text;
+        for (int i = text.length - 1; i >= 0; i--) {
+            char c = text[i];
+            if (c == '\r' || c == '\n' ) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * figures out how many opening whitespace characters to strip
+     * in the post-parse cleanup phase.
+     */
+    private int openingCharsToStrip() {
+        int newlineIndex = firstNewLineIndex();
+        if (newlineIndex == -1 && beginColumn != 1) {
+            return 0;
+        }
+        ++newlineIndex;
+        if (text.length > newlineIndex) {
+            if (newlineIndex > 0 && text[newlineIndex - 1] == '\r' && 
text[newlineIndex] == '\n') {
+                ++newlineIndex;
+            }
+        }
+        if (!_StringUtil.isTrimmableToEmpty(text, 0, newlineIndex)) {
+            return 0;
+        }
+        // We look at the preceding elements on the line to see if we should
+        // strip the opening newline and any whitespace preceding it.
+        for (ASTElement elem = prevTerminalNode();
+             elem != null && elem.endLine == beginLine;
+             elem = elem.prevTerminalNode()) {
+            if (elem.heedsOpeningWhitespace()) {
+                return 0;
+            }
+        }
+        return newlineIndex;
+    }
+
+    /**
+     * figures out how many trailing whitespace characters to strip
+     * in the post-parse cleanup phase.
+     */
+    private int trailingCharsToStrip() {
+        int lastNewlineIndex = lastNewLineIndex();
+        if (lastNewlineIndex == -1 && beginColumn != 1) {
+            return 0;
+        }
+        if (!_StringUtil.isTrimmableToEmpty(text, lastNewlineIndex + 1)) {
+            return 0;
+        }
+        // We look at the elements afterward on the same line to see if we 
should
+        // strip any whitespace after the last newline
+        for (ASTElement elem = nextTerminalNode();
+             elem != null && elem.beginLine == endLine;
+             elem = elem.nextTerminalNode()) {
+            if (elem.heedsTrailingWhitespace()) {
+                return 0;
+            }
+        }
+        return text.length - (lastNewlineIndex + 1);
+    }
+
+    @Override
+    boolean heedsTrailingWhitespace() {
+        if (isIgnorable(true)) {
+            return false;
+        }
+        for (char c : text) {
+            if (c == '\n' || c == '\r') {
+                return false;
+            }
+            if (!Character.isWhitespace(c)) {
+                return true;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    boolean heedsOpeningWhitespace() {
+        if (isIgnorable(true)) {
+            return false;
+        }
+        for (int i = text.length - 1; i >= 0; i--) {
+            char c = text[i];
+            if (c == '\n' || c == '\r') {
+                return false;
+            }
+            if (!Character.isWhitespace(c)) {
+                return true;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    boolean isIgnorable(boolean stripWhitespace) {
+        if (text == null || text.length == 0) {
+            return true;
+        }
+        if (stripWhitespace) {
+            if (!_StringUtil.isTrimmableToEmpty(text)) {
+                return false;
+            }
+            ASTElement parentElement = getParent();
+            boolean atTopLevel = isTopLevelTextIfParentIs(parentElement);
+            ASTElement prevSibling = previousSibling();
+            ASTElement nextSibling = nextSibling();
+            return ((prevSibling == null && atTopLevel) || 
nonOutputtingType(prevSibling))
+                    && ((nextSibling == null && atTopLevel) || 
nonOutputtingType(nextSibling));
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isTopLevelTextIfParentIs(ASTElement parentElement) {
+        return parentElement == null
+                || parentElement.getParent() == null && parentElement 
instanceof ASTImplicitParent;
+    }
+    
+
+    private boolean nonOutputtingType(ASTElement element) {
+        return (element instanceof ASTDirMacro ||
+                element instanceof ASTDirAssignment || 
+                element instanceof ASTDirAssignmentsContainer ||
+                element instanceof ASTDirSetting ||
+                element instanceof ASTDirImport ||
+                element instanceof ASTComment);
+    }
+
+    private static char[] substring(char[] c, int from, int to) {
+        char[] c2 = new char[to - from];
+        System.arraycopy(c, from, c2, 0, c2.length);
+        return c2;
+    }
+    
+    private static char[] substring(char[] c, int from) {
+        return substring(c, from, c.length);
+    }
+    
+    private static char[] concat(char[] c1, char[] c2) {
+        char[] c = new char[c1.length + c2.length];
+        System.arraycopy(c1, 0, c, 0, c1.length);
+        System.arraycopy(c2, 0, c, c1.length, c2.length);
+        return c;
+    }
+    
+    @Override
+    boolean isOutputCacheable() {
+        return true;
+    }
+
+    @Override
+    boolean isNestedBlockRepeater() {
+        return false;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ArithmeticExpression.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ArithmeticExpression.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ArithmeticExpression.java
new file mode 100644
index 0000000..764ec8a
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ArithmeticExpression.java
@@ -0,0 +1,129 @@
+/*
+ * 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.arithmetic.ArithmeticEngine;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+
+/**
+ * An operator for arithmetic operations. Note that the + operator is in 
{@link ASTExpAddOrConcat}, because its
+ * overloaded (does string concatenation and more).
+ */
+final class ArithmeticExpression extends ASTExpression {
+
+    static final int TYPE_SUBSTRACTION = 0;
+    static final int TYPE_MULTIPLICATION = 1;
+    static final int TYPE_DIVISION = 2;
+    static final int TYPE_MODULO = 3;
+
+    private static final char[] OPERATOR_IMAGES = new char[] { '-', '*', '/', 
'%' };
+
+    private final ASTExpression lho;
+    private final ASTExpression rho;
+    private final int operator;
+
+    ArithmeticExpression(ASTExpression lho, ASTExpression rho, int operator) {
+        this.lho = lho;
+        this.rho = rho;
+        this.operator = operator;
+    }
+
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        return _eval(env, this, lho.evalToNumber(env), operator, 
rho.evalToNumber(env));
+    }
+
+    static TemplateModel _eval(Environment env, ASTNode parent, Number 
lhoNumber, int operator, Number rhoNumber)
+            throws TemplateException {
+        ArithmeticEngine ae = _EvalUtil.getArithmeticEngine(env, parent);
+        switch (operator) {
+            case TYPE_SUBSTRACTION : 
+                return new SimpleNumber(ae.subtract(lhoNumber, rhoNumber));
+            case TYPE_MULTIPLICATION :
+                return new SimpleNumber(ae.multiply(lhoNumber, rhoNumber));
+            case TYPE_DIVISION :
+                return new SimpleNumber(ae.divide(lhoNumber, rhoNumber));
+            case TYPE_MODULO :
+                return new SimpleNumber(ae.modulus(lhoNumber, rhoNumber));
+            default:
+                if (parent instanceof ASTExpression) {
+                    throw new _MiscTemplateException((ASTExpression) parent,
+                            "Unknown operation: ", Integer.valueOf(operator));
+                } else {
+                    throw new _MiscTemplateException("Unknown operation: ", 
Integer.valueOf(operator));
+                }
+        }
+    }
+
+    @Override
+    public String getCanonicalForm() {
+        return lho.getCanonicalForm() + ' ' + getOperatorSymbol(operator) + ' 
' + rho.getCanonicalForm();
+    }
+    
+    @Override
+    String getNodeTypeSymbol() {
+        return String.valueOf(getOperatorSymbol(operator));
+    }
+
+    static char getOperatorSymbol(int operator) {
+        return OPERATOR_IMAGES[operator];
+    }
+    
+    @Override
+    boolean isLiteral() {
+        return constantValue != null || (lho.isLiteral() && rho.isLiteral());
+    }
+
+    @Override
+    protected ASTExpression deepCloneWithIdentifierReplaced_inner(
+            String replacedIdentifier, ASTExpression replacement, 
ReplacemenetState replacementState) {
+       return new ArithmeticExpression(
+               lho.deepCloneWithIdentifierReplaced(replacedIdentifier, 
replacement, replacementState),
+               rho.deepCloneWithIdentifierReplaced(replacedIdentifier, 
replacement, replacementState),
+               operator);
+    }
+    
+    @Override
+    int getParameterCount() {
+        return 3;
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        switch (idx) {
+        case 0: return lho;
+        case 1: return rho;
+        case 2: return Integer.valueOf(operator);
+        default: throw new IndexOutOfBoundsException();
+        }
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        switch (idx) {
+        case 0: return ParameterRole.LEFT_HAND_OPERAND;
+        case 1: return ParameterRole.RIGHT_HAND_OPERAND;
+        case 2: return ParameterRole.AST_NODE_SUBTYPE;
+        default: throw new IndexOutOfBoundsException();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
new file mode 100644
index 0000000..05efa98
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BoundedRangeModel.java
@@ -0,0 +1,70 @@
+/*
+ * 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 range between two integers (maybe 0 long).
+ */
+final class BoundedRangeModel extends RangeModel {
+
+    private final int step, size;
+    private final boolean rightAdaptive;
+    private final boolean affectedByStringSlicingBug;
+    
+    /**
+     * @param inclusiveEnd Tells if the {@code end} index is part of the 
range. 
+     * @param rightAdaptive Tells if the right end of the range adapts to the 
size of the sliced value, if otherwise
+     *     it would be bigger than that. 
+     */
+    BoundedRangeModel(int begin, int end, boolean inclusiveEnd, boolean 
rightAdaptive) {
+        super(begin);
+        step = begin <= end ? 1 : -1;
+        size = Math.abs(end - begin) + (inclusiveEnd ? 1 : 0);
+        this.rightAdaptive = rightAdaptive;
+        affectedByStringSlicingBug = inclusiveEnd;
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+    
+    @Override
+    int getStep() {
+        return step;
+    }
+
+    @Override
+    boolean isRightUnbounded() {
+        return false;
+    }
+
+    @Override
+    boolean isRightAdaptive() {
+        return rightAdaptive;
+    }
+
+    @Override
+    boolean isAffactedByStringSlicingBug() {
+        return affectedByStringSlicingBug;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenAutoEscaping.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenAutoEscaping.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenAutoEscaping.java
new file mode 100644
index 0000000..642b939
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenAutoEscaping.java
@@ -0,0 +1,27 @@
+/*
+ * 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 string built-in whose usage is banned when auto-escaping with a 
markup-output format is active.
+ * This is just a marker; the actual checking is in {@code FTL.jj}.
+ */
+abstract class BuiltInBannedWhenAutoEscaping extends SpecialBuiltIn {
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForDate.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForDate.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForDate.java
new file mode 100644
index 0000000..33971f5
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForDate.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Date;
+
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+abstract class BuiltInForDate extends ASTExpBuiltIn {
+    @Override
+    TemplateModel _eval(Environment env)
+            throws TemplateException {
+        TemplateModel model = target.eval(env);
+        if (model instanceof TemplateDateModel) {
+            TemplateDateModel tdm = (TemplateDateModel) model;
+            return calculateResult(_EvalUtil.modelToDate(tdm, target), 
tdm.getDateType(), env);
+        } else {
+            throw newNonDateException(env, model, target);
+        }
+    }
+
+    /** Override this to implement the built-in. */
+    protected abstract TemplateModel calculateResult(
+            Date date, int dateType, Environment env)
+    throws TemplateException;
+    
+    static TemplateException newNonDateException(Environment env, 
TemplateModel model, ASTExpression target)
+            throws InvalidReferenceException {
+        TemplateException e;
+        if (model == null) {
+            e = InvalidReferenceException.getInstance(target, env);
+        } else {
+            e = new NonDateException(target, model, "date", env);
+        }
+        return e;
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForHashEx.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForHashEx.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForHashEx.java
new file mode 100644
index 0000000..ec21061
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForHashEx.java
@@ -0,0 +1,55 @@
+/*
+ * 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.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+abstract class BuiltInForHashEx extends ASTExpBuiltIn {
+
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        TemplateModel model = target.eval(env);
+        if (model instanceof TemplateHashModelEx) {
+            return calculateResult((TemplateHashModelEx) model, env);
+        }
+        throw new NonExtendedHashException(target, model, env);
+    }
+    
+    abstract TemplateModel calculateResult(TemplateHashModelEx hashExModel, 
Environment env)
+            throws TemplateModelException, InvalidReferenceException;
+    
+    protected InvalidReferenceException newNullPropertyException(
+            String propertyName, TemplateModel tm, Environment env) {
+        if (env.getFastInvalidReferenceExceptions()) {
+            return InvalidReferenceException.FAST_INSTANCE;
+        } else {
+            return new InvalidReferenceException(
+                    new _ErrorDescriptionBuilder(
+                        "The exteneded hash (of class ", 
tm.getClass().getName(), ") has returned null for its \"",
+                        propertyName,
+                        "\" property. This is maybe a bug. The extended hash 
was returned by this expression:")
+                    .blame(target),
+                    env, this);
+        }
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLegacyEscaping.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLegacyEscaping.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLegacyEscaping.java
new file mode 100644
index 0000000..8cdcbf6
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLegacyEscaping.java
@@ -0,0 +1,48 @@
+/*
+ * 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.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * A string built-in whose usage is banned when auto-escaping with a 
markup-output format is active.
+ * This is just a marker; the actual checking is in {@code FTL.jj}.
+ */
+abstract class BuiltInForLegacyEscaping extends BuiltInBannedWhenAutoEscaping {
+    
+    @Override
+    TemplateModel _eval(Environment env)
+    throws TemplateException {
+        TemplateModel tm = target.eval(env);
+        Object moOrStr = _EvalUtil.coerceModelToStringOrMarkup(tm, target, 
null, env);
+        if (moOrStr instanceof String) {
+            return calculateResult((String) moOrStr, env);
+        } else {
+            TemplateMarkupOutputModel<?> mo = (TemplateMarkupOutputModel<?>) 
moOrStr;
+            if (mo.getOutputFormat().isLegacyBuiltInBypassed(key)) {
+                return mo;
+            }
+            throw new NonStringException(target, tm, env);
+        }
+    }
+    
+    abstract TemplateModel calculateResult(String s, Environment env) throws 
TemplateException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
new file mode 100644
index 0000000..5b7d9c3
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLoopVariable.java
@@ -0,0 +1,48 @@
+/*
+ * 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.ASTDirList.IterationContext;
+import org.apache.freemarker.core.model.TemplateModel;
+
+abstract class BuiltInForLoopVariable extends SpecialBuiltIn {
+    
+    private String loopVarName;
+    
+    void bindToLoopVariable(String loopVarName) {
+        this.loopVarName = loopVarName;
+    }
+    
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        IterationContext iterCtx = 
ASTDirList.findEnclosingIterationContext(env, loopVarName);
+        if (iterCtx == null) {
+            // The parser should prevent this situation
+            throw new _MiscTemplateException(
+                    this, env,
+                    "There's no iteration in context that uses loop variable 
", new _DelayedJQuote(loopVarName), ".");
+        }
+        
+        return calculateResult(iterCtx, env);
+    }
+
+    abstract TemplateModel calculateResult(IterationContext iterCtx, 
Environment env) throws TemplateException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForMarkupOutput.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForMarkupOutput.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForMarkupOutput.java
new file mode 100644
index 0000000..fa617c2
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForMarkupOutput.java
@@ -0,0 +1,40 @@
+/*
+ * 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.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+abstract class BuiltInForMarkupOutput extends ASTExpBuiltIn {
+    
+    @Override
+    TemplateModel _eval(Environment env)
+            throws TemplateException {
+        TemplateModel model = target.eval(env);
+        if (!(model instanceof TemplateMarkupOutputModel)) {
+            throw new NonMarkupOutputException(target, model, env);
+        }
+        return calculateResult((TemplateMarkupOutputModel) model);
+    }
+    
+    protected abstract TemplateModel calculateResult(TemplateMarkupOutputModel 
model) throws TemplateModelException;
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNode.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNode.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNode.java
new file mode 100644
index 0000000..ca0cd61
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNode.java
@@ -0,0 +1,39 @@
+/*
+ * 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.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateNodeModel;
+
+abstract class BuiltInForNode extends ASTExpBuiltIn {
+    @Override
+    TemplateModel _eval(Environment env)
+            throws TemplateException {
+        TemplateModel model = target.eval(env);
+        if (model instanceof TemplateNodeModel) {
+            return calculateResult((TemplateNodeModel) model, env);
+        } else {
+            throw new NonNodeException(target, model, env);
+        }
+    }
+    abstract TemplateModel calculateResult(TemplateNodeModel nodeModel, 
Environment env)
+            throws TemplateModelException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNodeEx.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNodeEx.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNodeEx.java
new file mode 100644
index 0000000..8360cbd
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNodeEx.java
@@ -0,0 +1,37 @@
+/*
+ * 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.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateNodeModelEx;
+
+abstract class BuiltInForNodeEx extends ASTExpBuiltIn {
+    @Override
+    TemplateModel _eval(Environment env) throws TemplateException {
+        TemplateModel model = target.eval(env);
+        if (model instanceof TemplateNodeModelEx) {
+            return calculateResult((TemplateNodeModelEx) model, env);
+        } else {
+            throw new NonExtendedNodeException(target, model, env);
+        }
+    }
+    abstract TemplateModel calculateResult(TemplateNodeModelEx nodeModel, 
Environment env)
+            throws TemplateModelException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNumber.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNumber.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNumber.java
new file mode 100644
index 0000000..02954f0
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNumber.java
@@ -0,0 +1,35 @@
+/*
+ * 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.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+abstract class BuiltInForNumber extends ASTExpBuiltIn {
+    @Override
+    TemplateModel _eval(Environment env)
+            throws TemplateException {
+        TemplateModel model = target.eval(env);
+        return calculateResult(target.modelToNumber(model, env), model);
+    }
+    
+    abstract TemplateModel calculateResult(Number num, TemplateModel model)
+    throws TemplateModelException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
new file mode 100644
index 0000000..8c36823
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
@@ -0,0 +1,38 @@
+/*
+ * 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.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+abstract class BuiltInForSequence extends ASTExpBuiltIn {
+    @Override
+    TemplateModel _eval(Environment env)
+            throws TemplateException {
+        TemplateModel model = target.eval(env);
+        if (!(model instanceof TemplateSequenceModel)) {
+            throw new NonSequenceException(target, model, env);
+        }
+        return calculateResult((TemplateSequenceModel) model);
+    }
+    abstract TemplateModel calculateResult(TemplateSequenceModel tsm)
+    throws TemplateModelException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForString.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForString.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForString.java
new file mode 100644
index 0000000..1102169
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForString.java
@@ -0,0 +1,36 @@
+/*
+ * 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.model.TemplateModel;
+
+abstract class BuiltInForString extends ASTExpBuiltIn {
+    @Override
+    TemplateModel _eval(Environment env)
+    throws TemplateException {
+        return calculateResult(getTargetString(target, env), env);
+    }
+    abstract TemplateModel calculateResult(String s, Environment env) throws 
TemplateException;
+    
+    static String getTargetString(ASTExpression target, Environment env) 
throws TemplateException {
+        return target.evalAndCoerceToStringOrUnsupportedMarkup(env);
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..d2fa8be
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInWithParseTimeParameters.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+import java.util.List;
+
+
+abstract class BuiltInWithParseTimeParameters extends SpecialBuiltIn {
+
+    abstract void bindToParameters(List/*<ASTExpression>*/ parameters, Token 
openParen, Token closeParen)
+            throws ParseException;
+
+    @Override
+    public String getCanonicalForm() {
+        StringBuilder buf = new StringBuilder();
+        
+        buf.append(super.getCanonicalForm());
+        
+        buf.append("(");
+        List/*<ASTExpression>*/args = getArgumentsAsList();
+        int size = args.size();
+        for (int i = 0; i < size; i++) {
+            if (i != 0) {
+                buf.append(", ");
+            }
+            ASTExpression arg = (ASTExpression) args.get(i);
+            buf.append(arg.getCanonicalForm());
+        }
+        buf.append(")");
+        
+        return buf.toString();
+    }
+    
+    @Override
+    String getNodeTypeSymbol() {
+        return super.getNodeTypeSymbol() + "(...)";
+    }        
+    
+    @Override
+    int getParameterCount() {
+        return super.getParameterCount() + getArgumentsCount();
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        final int superParamCnt = super.getParameterCount();
+        if (idx < superParamCnt) {
+            return super.getParameterValue(idx); 
+        }
+        
+        final int argIdx = idx - superParamCnt;
+        return getArgumentParameterValue(argIdx);
+    }
+    
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        final int superParamCnt = super.getParameterCount();
+        if (idx < superParamCnt) {
+            return super.getParameterRole(idx); 
+        }
+        
+        if (idx - superParamCnt < getArgumentsCount()) {
+            return ParameterRole.ARGUMENT_VALUE;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    protected ParseException newArgumentCountException(String ordinalityDesc, 
Token openParen, Token closeParen) {
+        return new ParseException(
+                "?" + key + "(...) " + ordinalityDesc + " parameters", 
getTemplate(),
+                openParen.beginLine, openParen.beginColumn,
+                closeParen.endLine, closeParen.endColumn);
+    }
+
+    @Override
+    protected ASTExpression deepCloneWithIdentifierReplaced_inner(
+            String replacedIdentifier, ASTExpression replacement, 
ReplacemenetState replacementState) {
+        final ASTExpression clone = 
super.deepCloneWithIdentifierReplaced_inner(replacedIdentifier, replacement, 
replacementState);
+        cloneArguments(clone, replacedIdentifier, replacement, 
replacementState);
+        return clone;
+    }
+
+    protected abstract List getArgumentsAsList();
+    
+    protected abstract int getArgumentsCount();
+
+    protected abstract ASTExpression getArgumentParameterValue(int argIdx);
+    
+    protected abstract void cloneArguments(ASTExpression clone, String 
replacedIdentifier,
+            ASTExpression replacement, ReplacemenetState replacementState);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
new file mode 100644
index 0000000..ad11b37
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.freemarker.core.model.AdapterTemplateModel;
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.impl.SimpleDate;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.util.UnrecognizedTimeZoneException;
+import org.apache.freemarker.core.util._DateUtil;
+
+/**
+ * A holder for built-ins that operate exclusively on date left-hand values.
+ */
+class BuiltInsForDates {
+    
+    static class dateType_if_unknownBI extends ASTExpBuiltIn {
+        
+        private final int dateType;
+
+        dateType_if_unknownBI(int dateType) {
+            this.dateType = dateType;
+        }
+
+        @Override
+        TemplateModel _eval(Environment env)
+                throws TemplateException {
+            TemplateModel model = target.eval(env);
+            if (model instanceof TemplateDateModel) {
+                TemplateDateModel tdm = (TemplateDateModel) model;
+                int tdmDateType = tdm.getDateType();
+                if (tdmDateType != TemplateDateModel.UNKNOWN) {
+                    return tdm;
+                }
+                return new SimpleDate(_EvalUtil.modelToDate(tdm, target), 
dateType);
+            } else {
+                throw BuiltInForDate.newNonDateException(env, model, target);
+            }
+        }
+
+        protected TemplateModel calculateResult(Date date, int dateType, 
Environment env) throws TemplateException {
+            // TODO Auto-generated method stub
+            return null;
+        }
+        
+    }
+    
+    /**
+     * Implements {@code ?iso(timeZone)}.
+     */
+    static class iso_BI extends AbstractISOBI {
+        
+        class Result implements TemplateMethodModelEx {
+            private final Date date;
+            private final int dateType;
+            private final Environment env;
+            
+            Result(Date date, int dateType, Environment env) {
+                this.date = date;
+                this.dateType = dateType;
+                this.env = env;
+            }
+
+            @Override
+            public Object exec(List args) throws TemplateModelException {
+                checkMethodArgCount(args, 1);
+                
+                TemplateModel tzArgTM = (TemplateModel) args.get(0);
+                TimeZone tzArg; 
+                Object adaptedObj;
+                if (tzArgTM instanceof AdapterTemplateModel
+                        && (adaptedObj =
+                                ((AdapterTemplateModel) tzArgTM)
+                                .getAdaptedObject(TimeZone.class))
+                            instanceof TimeZone) {
+                    tzArg = (TimeZone) adaptedObj;                    
+                } else if (tzArgTM instanceof TemplateScalarModel) {
+                    String tzName = 
_EvalUtil.modelToString((TemplateScalarModel) tzArgTM, null, null);
+                    try {
+                        tzArg = _DateUtil.getTimeZone(tzName);
+                    } catch (UnrecognizedTimeZoneException e) {
+                        throw new _TemplateModelException(
+                                "The time zone string specified for ?", key,
+                                "(...) is not recognized as a valid time zone 
name: ",
+                                new _DelayedJQuote(tzName));
+                    }
+                } else {
+                    throw MessageUtil.newMethodArgUnexpectedTypeException(
+                            "?" + key, 0, "string or java.util.TimeZone", 
tzArgTM);
+                }
+                
+                return new SimpleScalar(_DateUtil.dateToISO8601String(
+                        date,
+                        dateType != TemplateDateModel.TIME,
+                        dateType != TemplateDateModel.DATE,
+                        shouldShowOffset(date, dateType, env),
+                        accuracy,
+                        tzArg, 
+                        env.getISOBuiltInCalendarFactory()));
+            }
+            
+        }
+
+        iso_BI(Boolean showOffset, int accuracy) {
+            super(showOffset, accuracy);
+        }
+        
+        @Override
+        protected TemplateModel calculateResult(
+                Date date, int dateType, Environment env)
+        throws TemplateException {
+            checkDateTypeNotUnknown(dateType);
+            return new Result(date, dateType, env);
+        }
+        
+    }
+
+    /**
+     * Implements {@code ?iso_utc} and {@code ?iso_local} variants, but not
+     * {@code ?iso(timeZone)}.
+     */
+    static class iso_utc_or_local_BI extends AbstractISOBI {
+        
+        private final boolean useUTC;
+        
+        iso_utc_or_local_BI(Boolean showOffset, int accuracy, boolean useUTC) {
+            super(showOffset, accuracy);
+            this.useUTC = useUTC;
+        }
+
+        @Override
+        protected TemplateModel calculateResult(
+                Date date, int dateType, Environment env)
+        throws TemplateException {
+            checkDateTypeNotUnknown(dateType);
+            return new SimpleScalar(_DateUtil.dateToISO8601String(
+                    date,
+                    dateType != TemplateDateModel.TIME,
+                    dateType != TemplateDateModel.DATE,
+                    shouldShowOffset(date, dateType, env),
+                    accuracy,
+                    useUTC
+                            ? _DateUtil.UTC
+                            : env.shouldUseSQLDTTZ(date.getClass())
+                                    ? env.getSQLDateAndTimeTimeZone()
+                                    : env.getTimeZone(),
+                    env.getISOBuiltInCalendarFactory()));
+        }
+
+    }
+    
+    // Can't be instantiated
+    private BuiltInsForDates() { }
+
+    static abstract class AbstractISOBI extends BuiltInForDate {
+        protected final Boolean showOffset;
+        protected final int accuracy;
+    
+        protected AbstractISOBI(Boolean showOffset, int accuracy) {
+            this.showOffset = showOffset;
+            this.accuracy = accuracy;
+        }
+        
+        protected void checkDateTypeNotUnknown(int dateType)
+        throws TemplateException {
+            if (dateType == TemplateDateModel.UNKNOWN) {
+                throw new _MiscTemplateException(new _ErrorDescriptionBuilder(
+                            "The value of the following has unknown date type, 
but ?", key,
+                            " needs a value where it's known if it's a date 
(no time part), time, or date-time value:"                        
+                        
).blame(target).tip(MessageUtil.UNKNOWN_DATE_TYPE_ERROR_TIP));
+            }
+        }
+    
+        protected boolean shouldShowOffset(Date date, int dateType, 
Environment env) {
+            if (dateType == TemplateDateModel.DATE) {
+                return false;  // ISO 8061 doesn't allow zone for date-only 
values
+            } else if (showOffset != null) {
+                return showOffset.booleanValue();
+            } else {
+                // java.sql.Time values meant to carry calendar field values 
only, so we don't show offset for them.
+                return !(date instanceof java.sql.Time);
+            }
+        }
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
new file mode 100644
index 0000000..6e7cce0
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * A holder for builtins that deal with null left-hand values.
+ */
+class BuiltInsForExistenceHandling {
+
+    // Can't be instantiated
+    private BuiltInsForExistenceHandling() { }
+
+    private static abstract class ExistenceBuiltIn extends ASTExpBuiltIn {
+    
+        protected TemplateModel evalMaybeNonexistentTarget(Environment env) 
throws TemplateException {
+            TemplateModel tm;
+            if (target instanceof ASTExpParenthesis) {
+                boolean lastFIRE = env.setFastInvalidReferenceExceptions(true);
+                try {
+                    tm = target.eval(env);
+                } catch (InvalidReferenceException ire) {
+                    tm = null;
+                } finally {
+                    env.setFastInvalidReferenceExceptions(lastFIRE);
+                }
+            } else {
+                tm = target.eval(env);
+            }
+            return tm;
+        }
+        
+    }
+    
+    static class defaultBI extends 
BuiltInsForExistenceHandling.ExistenceBuiltIn {
+        
+        @Override
+        TemplateModel _eval(final Environment env) throws TemplateException {
+            TemplateModel model = evalMaybeNonexistentTarget(env);
+            return model == null ? FIRST_NON_NULL_METHOD : new 
ConstantMethod(model);
+        }
+
+        private static class ConstantMethod implements TemplateMethodModelEx {
+            private final TemplateModel constant;
+
+            ConstantMethod(TemplateModel constant) {
+                this.constant = constant;
+            }
+
+            @Override
+            public Object exec(List args) {
+                return constant;
+            }
+        }
+
+        /**
+         * A method that goes through the arguments one by one and returns
+         * the first one that is non-null. If all args are null, returns null.
+         */
+        private static final TemplateMethodModelEx FIRST_NON_NULL_METHOD =
+            new TemplateMethodModelEx() {
+                @Override
+                public Object exec(List args) throws TemplateModelException {
+                    int argCnt = args.size();
+                    if (argCnt == 0) throw 
MessageUtil.newArgCntError("?default", argCnt, 1, Integer.MAX_VALUE);
+                    for (int i = 0; i < argCnt; i++ ) {
+                        TemplateModel result = (TemplateModel) args.get(i);
+                        if (result != null) return result;
+                    }
+                    return null;
+                }
+            };
+    }
+    
+    static class existsBI extends 
BuiltInsForExistenceHandling.ExistenceBuiltIn {
+        @Override
+        TemplateModel _eval(Environment env) throws TemplateException {
+            return evalMaybeNonexistentTarget(env) == null ? 
TemplateBooleanModel.FALSE : TemplateBooleanModel.TRUE;
+        }
+    
+        @Override
+        boolean evalToBoolean(Environment env) throws TemplateException {
+            return _eval(env) == TemplateBooleanModel.TRUE;
+        }
+    }
+
+    static class has_contentBI extends 
BuiltInsForExistenceHandling.ExistenceBuiltIn {
+        @Override
+        TemplateModel _eval(Environment env) throws TemplateException {
+            return ASTExpression.isEmpty(evalMaybeNonexistentTarget(env))
+                    ? TemplateBooleanModel.FALSE
+                    : TemplateBooleanModel.TRUE;
+        }
+    
+        @Override
+        boolean evalToBoolean(Environment env) throws TemplateException {
+            return _eval(env) == TemplateBooleanModel.TRUE;
+        }
+    }
+
+    static class if_existsBI extends 
BuiltInsForExistenceHandling.ExistenceBuiltIn {
+        @Override
+        TemplateModel _eval(Environment env)
+                throws TemplateException {
+            TemplateModel model = evalMaybeNonexistentTarget(env);
+            return model == null ? TemplateModel.NOTHING : model;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
new file mode 100644
index 0000000..8baa9cc
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForHashes.java
@@ -0,0 +1,59 @@
+/*
+ * 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.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.CollectionAndSequence;
+
+/**
+ * A holder for builtins that operate exclusively on hash left-hand value.
+ */
+class BuiltInsForHashes {
+
+    static class keysBI extends BuiltInForHashEx {
+
+        @Override
+        TemplateModel calculateResult(TemplateHashModelEx hashExModel, 
Environment env)
+                throws TemplateModelException, InvalidReferenceException {
+            TemplateCollectionModel keys = hashExModel.keys();
+            if (keys == null) throw newNullPropertyException("keys", 
hashExModel, env);
+            return keys instanceof TemplateSequenceModel ? keys : new 
CollectionAndSequence(keys);
+        }
+        
+    }
+    
+    static class valuesBI extends BuiltInForHashEx {
+        @Override
+        TemplateModel calculateResult(TemplateHashModelEx hashExModel, 
Environment env)
+                throws TemplateModelException, InvalidReferenceException {
+            TemplateCollectionModel values = hashExModel.values();
+            if (values == null) throw newNullPropertyException("values", 
hashExModel, env);
+            return values instanceof TemplateSequenceModel ? values : new 
CollectionAndSequence(values);
+        }
+    }
+
+    // Can't be instantiated
+    private BuiltInsForHashes() { }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
new file mode 100644
index 0000000..8d55e0e
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForLoopVariables.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core;
+
+import java.util.List;
+
+import org.apache.freemarker.core.ASTDirList.IterationContext;
+import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+
+
+class BuiltInsForLoopVariables {
+    
+    static class indexBI extends BuiltInForLoopVariable {
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return new SimpleNumber(iterCtx.getIndex());
+        }
+        
+    }
+    
+    static class counterBI extends BuiltInForLoopVariable {
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return new SimpleNumber(iterCtx.getIndex() + 1);
+        }
+        
+    }
+
+    static abstract class BooleanBuiltInForLoopVariable extends 
BuiltInForLoopVariable {
+
+        @Override
+        final TemplateModel calculateResult(IterationContext iterCtx, 
Environment env) throws TemplateException {
+            return calculateBooleanResult(iterCtx, env) ? 
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
+        }
+
+        protected abstract boolean calculateBooleanResult(IterationContext 
iterCtx, Environment env);
+        
+    }
+    
+    static class has_nextBI extends BooleanBuiltInForLoopVariable {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.hasNext();
+        }
+
+    }
+
+    static class is_lastBI extends BooleanBuiltInForLoopVariable {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return !iterCtx.hasNext();
+        }
+        
+    }
+
+    static class is_firstBI extends BooleanBuiltInForLoopVariable {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.getIndex() == 0;
+        }
+        
+    }
+
+    static class is_odd_itemBI extends BooleanBuiltInForLoopVariable {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.getIndex() % 2 == 0;
+        }
+        
+    }
+
+    static class is_even_itemBI extends BooleanBuiltInForLoopVariable {
+
+        @Override
+        protected boolean calculateBooleanResult(IterationContext iterCtx, 
Environment env) {
+            return iterCtx.getIndex() % 2 != 0;
+        }
+        
+    }
+    
+    static class item_parityBI extends BuiltInForLoopVariable {
+        
+        private static final SimpleScalar ODD = new SimpleScalar("odd");
+        private static final SimpleScalar EVEN = new SimpleScalar("even");
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return iterCtx.getIndex() % 2 == 0 ? ODD: EVEN;
+        }
+        
+    }
+
+    static class item_parity_capBI extends BuiltInForLoopVariable {
+        
+        private static final SimpleScalar ODD = new SimpleScalar("Odd");
+        private static final SimpleScalar EVEN = new SimpleScalar("Even");
+
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return iterCtx.getIndex() % 2 == 0 ? ODD: EVEN;
+        }
+        
+    }
+
+    static class item_cycleBI extends BuiltInForLoopVariable {
+
+        private class BIMethod implements TemplateMethodModelEx {
+            
+            private final IterationContext iterCtx;
+    
+            private BIMethod(IterationContext iterCtx) {
+                this.iterCtx = iterCtx;
+            }
+    
+            @Override
+            public Object exec(List args) throws TemplateModelException {
+                checkMethodArgCount(args, 1, Integer.MAX_VALUE);
+                return args.get(iterCtx.getIndex() % args.size());
+            }
+        }
+        
+        @Override
+        TemplateModel calculateResult(IterationContext iterCtx, Environment 
env) throws TemplateException {
+            return new BIMethod(iterCtx);
+        }
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMarkupOutputs.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMarkupOutputs.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMarkupOutputs.java
new file mode 100644
index 0000000..f895526
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMarkupOutputs.java
@@ -0,0 +1,41 @@
+/*
+ * 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.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+
+/**
+ * A holder for builtins that operate exclusively on markup output left-hand 
value.
+ */
+class BuiltInsForMarkupOutputs {
+    
+    static class markup_stringBI extends BuiltInForMarkupOutput {
+
+        @Override
+        protected TemplateModel calculateResult(TemplateMarkupOutputModel 
model) throws TemplateModelException {
+            return new 
SimpleScalar(model.getOutputFormat().getMarkupString(model));
+        }
+        
+    }
+    
+}

Reply via email to