http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/EvalUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/EvalUtil.java 
b/src/main/java/org/apache/freemarker/core/EvalUtil.java
deleted file mode 100644
index a0cb119..0000000
--- a/src/main/java/org/apache/freemarker/core/EvalUtil.java
+++ /dev/null
@@ -1,539 +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;
-
-import java.util.Date;
-
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateDateModel;
-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.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
-import org.apache.freemarker.core.util.BugException;
-
-/**
- * Internally used static utilities for evaluation expressions.
- */
-class EvalUtil {
-    static final int CMP_OP_EQUALS = 1;
-    static final int CMP_OP_NOT_EQUALS = 2;
-    static final int CMP_OP_LESS_THAN = 3;
-    static final int CMP_OP_GREATER_THAN = 4;
-    static final int CMP_OP_LESS_THAN_EQUALS = 5;
-    static final int CMP_OP_GREATER_THAN_EQUALS = 6;
-    // If you add a new operator here, update the "compare" and 
"cmpOpToString" methods!
-    
-    // Prevents instantination.
-    private EvalUtil() { }
-    
-    /**
-     * @param expr {@code null} is allowed, but may results in less helpful 
error messages
-     * @param env {@code null} is allowed
-     */
-    static String modelToString(TemplateScalarModel model, ASTExpression expr, 
Environment env)
-    throws TemplateModelException {
-        String value = model.getAsString();
-        if (value == null) {
-            throw newModelHasStoredNullException(String.class, model, expr);
-        }
-        return value;
-    }
-    
-    /**
-     * @param expr {@code null} is allowed, but may results in less helpful 
error messages
-     */
-    static Number modelToNumber(TemplateNumberModel model, ASTExpression expr)
-        throws TemplateModelException {
-        Number value = model.getAsNumber();
-        if (value == null) throw newModelHasStoredNullException(Number.class, 
model, expr);
-        return value;
-    }
-
-    /**
-     * @param expr {@code null} is allowed, but may results in less helpful 
error messages
-     */
-    static Date modelToDate(TemplateDateModel model, ASTExpression expr)
-        throws TemplateModelException {
-        Date value = model.getAsDate();
-        if (value == null) throw newModelHasStoredNullException(Date.class, 
model, expr);
-        return value;
-    }
-    
-    /** Signals the buggy case where we have a non-null model, but it wraps a 
null. */
-    static TemplateModelException newModelHasStoredNullException(
-            Class expected, TemplateModel model, ASTExpression expr) {
-        return new _TemplateModelException(expr,
-                
_TemplateModelException.modelHasStoredNullDescription(expected, model));
-    }
-
-    /**
-     * Compares two expressions according the rules of the FTL comparator 
operators.
-     * 
-     * @param leftExp not {@code null}
-     * @param operator one of the {@code COMP_OP_...} constants, like {@link 
#CMP_OP_EQUALS}.
-     * @param operatorString can be null {@code null}; the actual operator 
used, used for more accurate error message.
-     * @param rightExp not {@code null}
-     * @param env {@code null} is tolerated, but should be avoided
-     */
-    static boolean compare(
-            ASTExpression leftExp,
-            int operator, String  operatorString,
-            ASTExpression rightExp,
-            ASTExpression defaultBlamed,
-            Environment env) throws TemplateException {
-        TemplateModel ltm = leftExp.eval(env);
-        TemplateModel rtm = rightExp.eval(env);
-        return compare(
-                ltm, leftExp,
-                operator, operatorString,
-                rtm, rightExp,
-                defaultBlamed, false,
-                false, false, false,
-                env);
-    }
-    
-    /**
-     * Compares values according the rules of the FTL comparator operators; if 
the {@link ASTExpression}-s are
-     * accessible, use {@link #compare(ASTExpression, int, String, 
ASTExpression, ASTExpression, Environment)} instead,
-     * as that gives better error messages.
-     * 
-     * @param leftValue maybe {@code null}, which will usually cause the 
appropriate {@link TemplateException}. 
-     * @param operator one of the {@code COMP_OP_...} constants, like {@link 
#CMP_OP_EQUALS}.
-     * @param rightValue maybe {@code null}, which will usually cause the 
appropriate {@link TemplateException}.
-     * @param env {@code null} is tolerated, but should be avoided
-     */
-    static boolean compare(
-            TemplateModel leftValue, int operator, TemplateModel rightValue,
-            Environment env) throws TemplateException {
-        return compare(
-                leftValue, null,
-                operator, null,
-                rightValue, null,
-                null, false,
-                false, false, false,
-                env);
-    }
-
-    /**
-     * Same as {@link #compare(TemplateModel, int, TemplateModel, 
Environment)}, but if the two types are incompatible,
-     *     they are treated as non-equal instead of throwing an exception. 
Comparing dates of different types will
-     *     still throw an exception, however.
-     */
-    static boolean compareLenient(
-            TemplateModel leftValue, int operator, TemplateModel rightValue,
-            Environment env) throws TemplateException {
-        return compare(
-                leftValue, null,
-                operator, null,
-                rightValue, null,
-                null, false,
-                true, false, false,
-                env);
-    }
-    
-    private static final String VALUE_OF_THE_COMPARISON_IS_UNKNOWN_DATE_LIKE
-            = "value of the comparison is a date-like value where "
-              + "it's not known if it's a date (no time part), time, or 
date-time, "
-              + "and thus can't be used in a comparison.";
-    
-    /**
-     * @param leftExp {@code null} is allowed, but may results in less helpful 
error messages
-     * @param operator one of the {@code COMP_OP_...} constants, like {@link 
#CMP_OP_EQUALS}.
-     * @param operatorString can be null {@code null}; the actual operator 
used, used for more accurate error message.
-     * @param rightExp {@code null} is allowed, but may results in less 
helpful error messages
-     * @param defaultBlamed {@code null} allowed; the expression to which the 
error will point to if something goes
-     *        wrong that is not specific to the left or right side expression, 
or if that expression is {@code null}.
-     * @param typeMismatchMeansNotEqual If the two types are incompatible, 
they are treated as non-equal instead
-     *     of throwing an exception. Comparing dates of different types will 
still throw an exception, however. 
-     * @param leftNullReturnsFalse if {@code true}, a {@code null} left value 
will not cause exception, but make the
-     *     expression {@code false}.  
-     * @param rightNullReturnsFalse if {@code true}, a {@code null} right 
value will not cause exception, but make the
-     *     expression {@code false}.  
-     */
-    static boolean compare(
-            TemplateModel leftValue, ASTExpression leftExp,
-            int operator, String operatorString,
-            TemplateModel rightValue, ASTExpression rightExp,
-            ASTExpression defaultBlamed, boolean quoteOperandsInErrors,
-            boolean typeMismatchMeansNotEqual,
-            boolean leftNullReturnsFalse, boolean rightNullReturnsFalse,
-            Environment env) throws TemplateException {
-        if (leftValue == null) {
-            if (leftNullReturnsFalse) { 
-                return false;
-            } else {
-                if (leftExp != null) {
-                    throw InvalidReferenceException.getInstance(leftExp, env);
-                } else {
-                    throw new _MiscTemplateException(defaultBlamed, env, 
-                                "The left operand of the comparison was 
undefined or null.");
-                }
-            }
-        }
-
-        if (rightValue == null) {
-            if (rightNullReturnsFalse) { 
-                return false;
-            } else {
-                if (rightExp != null) {
-                    throw InvalidReferenceException.getInstance(rightExp, env);
-                } else {
-                    throw new _MiscTemplateException(defaultBlamed, env,
-                                "The right operand of the comparison was 
undefined or null.");
-                }
-            }
-        }
-
-        final int cmpResult;
-        if (leftValue instanceof TemplateNumberModel && rightValue instanceof 
TemplateNumberModel) {
-            Number leftNum = EvalUtil.modelToNumber((TemplateNumberModel) 
leftValue, leftExp);
-            Number rightNum = EvalUtil.modelToNumber((TemplateNumberModel) 
rightValue, rightExp);
-            ArithmeticEngine ae =
-                    env != null
-                        ? env.getArithmeticEngine()
-                        : (leftExp != null
-                            ? leftExp.getTemplate().getArithmeticEngine()
-                            : ArithmeticEngine.BIGDECIMAL_ENGINE);
-            try {
-                cmpResult = ae.compareNumbers(leftNum, rightNum);
-            } catch (RuntimeException e) {
-                throw new _MiscTemplateException(defaultBlamed, e, env,
-                        "Unexpected error while comparing two numbers: ", e);
-            }
-        } else if (leftValue instanceof TemplateDateModel && rightValue 
instanceof TemplateDateModel) {
-            TemplateDateModel leftDateModel = (TemplateDateModel) leftValue;
-            TemplateDateModel rightDateModel = (TemplateDateModel) rightValue;
-            
-            int leftDateType = leftDateModel.getDateType();
-            int rightDateType = rightDateModel.getDateType();
-            
-            if (leftDateType == TemplateDateModel.UNKNOWN || rightDateType == 
TemplateDateModel.UNKNOWN) {
-                String sideName;
-                ASTExpression sideExp;
-                if (leftDateType == TemplateDateModel.UNKNOWN) {
-                    sideName = "left";
-                    sideExp = leftExp;
-                } else {
-                    sideName = "right";
-                    sideExp = rightExp;
-                }
-                
-                throw new _MiscTemplateException(sideExp != null ? sideExp : 
defaultBlamed, env,
-                        "The ", sideName, " ", 
VALUE_OF_THE_COMPARISON_IS_UNKNOWN_DATE_LIKE);
-            }
-            
-            if (leftDateType != rightDateType) {
-                throw new _MiscTemplateException(defaultBlamed, env,
-                        "Can't compare dates of different types. Left date 
type is ",
-                        TemplateDateModel.TYPE_NAMES.get(leftDateType), ", 
right date type is ",
-                        TemplateDateModel.TYPE_NAMES.get(rightDateType), ".");
-            }
-
-            Date leftDate = EvalUtil.modelToDate(leftDateModel, leftExp);
-            Date rightDate = EvalUtil.modelToDate(rightDateModel, rightExp);
-            cmpResult = leftDate.compareTo(rightDate);
-        } else if (leftValue instanceof TemplateScalarModel && rightValue 
instanceof TemplateScalarModel) {
-            if (operator != CMP_OP_EQUALS && operator != CMP_OP_NOT_EQUALS) {
-                throw new _MiscTemplateException(defaultBlamed, env,
-                        "Can't use operator \"", cmpOpToString(operator, 
operatorString), "\" on string values.");
-            }
-            String leftString = EvalUtil.modelToString((TemplateScalarModel) 
leftValue, leftExp, env);
-            String rightString = EvalUtil.modelToString((TemplateScalarModel) 
rightValue, rightExp, env);
-            // FIXME NBC: Don't use the Collator here. That's locale-specific, 
but ==/!= should not be.
-            cmpResult = env.getCollator().compare(leftString, rightString);
-        } else if (leftValue instanceof TemplateBooleanModel && rightValue 
instanceof TemplateBooleanModel) {
-            if (operator != CMP_OP_EQUALS && operator != CMP_OP_NOT_EQUALS) {
-                throw new _MiscTemplateException(defaultBlamed, env,
-                        "Can't use operator \"", cmpOpToString(operator, 
operatorString), "\" on boolean values.");
-            }
-            boolean leftBool = ((TemplateBooleanModel) 
leftValue).getAsBoolean();
-            boolean rightBool = ((TemplateBooleanModel) 
rightValue).getAsBoolean();
-            cmpResult = (leftBool ? 1 : 0) - (rightBool ? 1 : 0);
-        } else {
-            if (typeMismatchMeansNotEqual) {
-                if (operator == CMP_OP_EQUALS) {
-                    return false;
-                } else if (operator == CMP_OP_NOT_EQUALS) {
-                    return true;
-                }
-                // Falls through
-            }
-            throw new _MiscTemplateException(defaultBlamed, env,
-                    "Can't compare values of these types. ",
-                    "Allowed comparisons are between two numbers, two strings, 
two dates, or two booleans.\n",
-                    "Left hand operand ",
-                    (quoteOperandsInErrors && leftExp != null
-                            ? new Object[] { "(", new 
_DelayedGetCanonicalForm(leftExp), ") value " }
-                            : ""),
-                    "is ", new _DelayedAOrAn(new 
_DelayedFTLTypeDescription(leftValue)), ".\n",
-                    "Right hand operand ",
-                    (quoteOperandsInErrors && rightExp != null
-                            ? new Object[] { "(", new 
_DelayedGetCanonicalForm(rightExp), ") value " }
-                            : ""),
-                    "is ", new _DelayedAOrAn(new 
_DelayedFTLTypeDescription(rightValue)),
-                    ".");
-        }
-
-        switch (operator) {
-            case CMP_OP_EQUALS: return cmpResult == 0;
-            case CMP_OP_NOT_EQUALS: return cmpResult != 0;
-            case CMP_OP_LESS_THAN: return cmpResult < 0;
-            case CMP_OP_GREATER_THAN: return cmpResult > 0;
-            case CMP_OP_LESS_THAN_EQUALS: return cmpResult <= 0;
-            case CMP_OP_GREATER_THAN_EQUALS: return cmpResult >= 0;
-            default: throw new BugException("Unsupported comparator operator 
code: " + operator);
-        }
-    }
-
-    private static String cmpOpToString(int operator, String operatorString) {
-        if (operatorString != null) {
-            return operatorString;
-        } else {
-            switch (operator) {
-                case CMP_OP_EQUALS: return "equals";
-                case CMP_OP_NOT_EQUALS: return "not-equals";
-                case CMP_OP_LESS_THAN: return "less-than";
-                case CMP_OP_GREATER_THAN: return "greater-than";
-                case CMP_OP_LESS_THAN_EQUALS: return "less-than-equals";
-                case CMP_OP_GREATER_THAN_EQUALS: return "greater-than-equals";
-                default: return "???";
-            }
-        }
-    }
-
-    /**
-     * Converts a value to plain text {@link String}, or a {@link 
TemplateMarkupOutputModel} if that's what the
-     * {@link TemplateValueFormat} involved produces.
-     * 
-     * @param seqTip
-     *            Tip to display if the value type is not coercable, but it's 
sequence or collection.
-     * 
-     * @return Never {@code null}
-     */
-    static Object coerceModelToStringOrMarkup(TemplateModel tm, ASTExpression 
exp, String seqTip, Environment env)
-            throws TemplateException {
-        return coerceModelToStringOrMarkup(tm, exp, false, seqTip, env);
-    }
-    
-    /**
-     * @return {@code null} if the {@code returnNullOnNonCoercableType} 
parameter is {@code true}, and the coercion is
-     *         not possible, because of the type is not right for it.
-     * 
-     * @see #coerceModelToStringOrMarkup(TemplateModel, ASTExpression, String, 
Environment)
-     */
-    static Object coerceModelToStringOrMarkup(
-            TemplateModel tm, ASTExpression exp, boolean 
returnNullOnNonCoercableType, String seqTip, Environment env)
-            throws TemplateException {
-        if (tm instanceof TemplateNumberModel) {
-            TemplateNumberModel tnm = (TemplateNumberModel) tm; 
-            TemplateNumberFormat format = env.getTemplateNumberFormat(exp, 
false);
-            try {
-                return assertFormatResultNotNull(format.format(tnm));
-            } catch (TemplateValueFormatException e) {
-                throw MessageUtil.newCantFormatNumberException(format, exp, e, 
false);
-            }
-        } else if (tm instanceof TemplateDateModel) {
-            TemplateDateModel tdm = (TemplateDateModel) tm;
-            TemplateDateFormat format = env.getTemplateDateFormat(tdm, exp, 
false);
-            try {
-                return assertFormatResultNotNull(format.format(tdm));
-            } catch (TemplateValueFormatException e) {
-                throw MessageUtil.newCantFormatDateException(format, exp, e, 
false);
-            }
-        } else if (tm instanceof TemplateMarkupOutputModel) {
-            return tm;
-        } else { 
-            return coerceModelToTextualCommon(tm, exp, seqTip, true, 
returnNullOnNonCoercableType, env);
-        }
-    }
-
-    /**
-     * Like {@link #coerceModelToStringOrMarkup(TemplateModel, ASTExpression, 
String, Environment)}, but gives error
-     * if the result is markup. This is what you normally use where markup 
results can't be used.
-     *
-     * @param seqTip
-     *            Tip to display if the value type is not coercable, but it's 
sequence or collection.
-     * 
-     * @return Never {@code null}
-     */
-    static String coerceModelToStringOrUnsupportedMarkup(
-            TemplateModel tm, ASTExpression exp, String seqTip, Environment 
env)
-            throws TemplateException {
-        if (tm instanceof TemplateNumberModel) {
-            TemplateNumberModel tnm = (TemplateNumberModel) tm; 
-            TemplateNumberFormat format = env.getTemplateNumberFormat(exp, 
false);
-            try {
-                return ensureFormatResultString(format.format(tnm), exp, env);
-            } catch (TemplateValueFormatException e) {
-                throw MessageUtil.newCantFormatNumberException(format, exp, e, 
false);
-            }
-        } else if (tm instanceof TemplateDateModel) {
-            TemplateDateModel tdm = (TemplateDateModel) tm;
-            TemplateDateFormat format = env.getTemplateDateFormat(tdm, exp, 
false);
-            try {
-                return ensureFormatResultString(format.format(tdm), exp, env);
-            } catch (TemplateValueFormatException e) {
-                throw MessageUtil.newCantFormatDateException(format, exp, e, 
false);
-            }
-        } else { 
-            return coerceModelToTextualCommon(tm, exp, seqTip, false, false, 
env);
-        }
-    }
-
-    /**
-     * Converts a value to plain text {@link String}, even if the {@link 
TemplateValueFormat} involved normally produces
-     * markup. This should be used rarely, where the user clearly intend to 
use the plain text variant of the format.
-     * 
-     * @param seqTip
-     *            Tip to display if the value type is not coercable, but it's 
sequence or collection.
-     * 
-     * @return Never {@code null}
-     */
-    static String coerceModelToPlainText(TemplateModel tm, ASTExpression exp, 
String seqTip,
-            Environment env) throws TemplateException {
-        if (tm instanceof TemplateNumberModel) {
-            return 
assertFormatResultNotNull(env.formatNumberToPlainText((TemplateNumberModel) tm, 
exp, false));
-        } else if (tm instanceof TemplateDateModel) {
-            return 
assertFormatResultNotNull(env.formatDateToPlainText((TemplateDateModel) tm, 
exp, false));
-        } else {
-            return coerceModelToTextualCommon(tm, exp, seqTip, false, false, 
env);
-        }
-    }
-
-    /**
-     * @param tm
-     *            If {@code null} that's an exception
-     * 
-     * @param supportsTOM
-     *            Whether the caller {@code coerceModelTo...} method could 
handle a {@link TemplateMarkupOutputModel}.
-     *            
-     * @return Never {@code null}
-     */
-    private static String coerceModelToTextualCommon(
-            TemplateModel tm, ASTExpression exp, String seqHint, boolean 
supportsTOM, boolean returnNullOnNonCoercableType,
-            Environment env)
-            throws TemplateException {
-        if (tm instanceof TemplateScalarModel) {
-            return modelToString((TemplateScalarModel) tm, exp, env);
-        } else if (tm == null) {
-            if (exp != null) {
-                throw InvalidReferenceException.getInstance(exp, env);
-            } else {
-                throw new InvalidReferenceException(
-                        "Null/missing value (no more informatoin avilable)",
-                        env);
-            }
-        } else if (tm instanceof TemplateBooleanModel) {
-            // [FM3] This should be before TemplateScalarModel, but automatic 
boolean-to-string is only non-error since
-            // 2.3.20, so to keep backward compatibility we couldn't insert 
this before TemplateScalarModel.
-            boolean booleanValue = ((TemplateBooleanModel) tm).getAsBoolean();
-            return env.formatBoolean(booleanValue, false);
-        } else {
-            if (returnNullOnNonCoercableType) {
-                return null;
-            }
-            if (seqHint != null && (tm instanceof TemplateSequenceModel || tm 
instanceof TemplateCollectionModel)) {
-                if (supportsTOM) {
-                    throw new NonStringOrTemplateOutputException(exp, tm, 
seqHint, env);
-                } else {
-                    throw new NonStringException(exp, tm, seqHint, env);
-                }
-            } else {
-                if (supportsTOM) {
-                    throw new NonStringOrTemplateOutputException(exp, tm, env);
-                } else {
-                    throw new NonStringException(exp, tm, env);
-                }
-            }
-        }
-    }
-
-    private static String ensureFormatResultString(Object formatResult, 
ASTExpression exp, Environment env)
-            throws NonStringException {
-        if (formatResult instanceof String) { 
-            return (String) formatResult;
-        }
-        
-        assertFormatResultNotNull(formatResult);
-        
-        TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) 
formatResult;
-        _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
-                "Value was formatted to convert it to string, but the result 
was markup of ouput format ",
-                new _DelayedJQuote(mo.getOutputFormat()), ".")
-                .tip("Use value?string to force formatting to plain text.")
-                .blame(exp);
-        throw new NonStringException(null, desc);
-    }
-
-    static String assertFormatResultNotNull(String r) {
-        if (r != null) {
-            return r;
-        }
-        throw new NullPointerException("TemplateValueFormatter result can't be 
null");
-    }
-
-    static Object assertFormatResultNotNull(Object r) {
-        if (r != null) {
-            return r;
-        }
-        throw new NullPointerException("TemplateValueFormatter result can't be 
null");
-    }
-
-    static TemplateMarkupOutputModel concatMarkupOutputs(ASTNode parent, 
TemplateMarkupOutputModel leftMO,
-            TemplateMarkupOutputModel rightMO) throws TemplateException {
-        MarkupOutputFormat leftOF = leftMO.getOutputFormat();
-        MarkupOutputFormat rightOF = rightMO.getOutputFormat();
-        if (rightOF != leftOF) {
-            String rightPT;
-            String leftPT;
-            if ((rightPT = rightOF.getSourcePlainText(rightMO)) != null) {
-                return leftOF.concat(leftMO, 
leftOF.fromPlainTextByEscaping(rightPT));
-            } else if ((leftPT = leftOF.getSourcePlainText(leftMO)) != null) {
-                return rightOF.concat(rightOF.fromPlainTextByEscaping(leftPT), 
rightMO);
-            } else {
-                Object[] message = { "Concatenation left hand operand is in ", 
new _DelayedToString(leftOF),
-                        " format, while the right hand operand is in ", new 
_DelayedToString(rightOF),
-                        ". Conversion to common format wasn't possible." };
-                if (parent instanceof ASTExpression) {
-                    throw new _MiscTemplateException((ASTExpression) parent, 
message);
-                } else {
-                    throw new _MiscTemplateException(message);
-                }
-            }
-        } else {
-            return leftOF.concat(leftMO, rightMO);
-        }
-    }
-
-    /**
-     * Returns an {@link ArithmeticEngine} even if {@code env} is {@code 
null}, because we are in parsing phase.
-     */
-    static ArithmeticEngine getArithmeticEngine(Environment env, ASTNode tObj) 
{
-        return env != null
-                ? env.getArithmeticEngine()
-                : 
tObj.getTemplate().getParserConfiguration().getArithmeticEngine();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/ExtendedDecimalFormatParser.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/ExtendedDecimalFormatParser.java 
b/src/main/java/org/apache/freemarker/core/ExtendedDecimalFormatParser.java
deleted file mode 100644
index 62651b6..0000000
--- a/src/main/java/org/apache/freemarker/core/ExtendedDecimalFormatParser.java
+++ /dev/null
@@ -1,525 +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;
-
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.ParseException;
-import java.util.Arrays;
-import java.util.Currency;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Set;
-
-import org.apache.freemarker.core.util._StringUtil;
-
-class ExtendedDecimalFormatParser {
-    
-    private static final String PARAM_ROUNDING_MODE = "roundingMode";
-    private static final String PARAM_MULTIPIER = "multipier";
-    private static final String PARAM_DECIMAL_SEPARATOR = "decimalSeparator";
-    private static final String PARAM_MONETARY_DECIMAL_SEPARATOR = 
"monetaryDecimalSeparator";
-    private static final String PARAM_GROUP_SEPARATOR = "groupingSeparator";
-    private static final String PARAM_EXPONENT_SEPARATOR = "exponentSeparator";
-    private static final String PARAM_MINUS_SIGN = "minusSign";
-    private static final String PARAM_INFINITY = "infinity";
-    private static final String PARAM_NAN = "nan";
-    private static final String PARAM_PERCENT = "percent";
-    private static final String PARAM_PER_MILL = "perMill";
-    private static final String PARAM_ZERO_DIGIT = "zeroDigit";
-    private static final String PARAM_CURRENCY_CODE = "currencyCode";
-    private static final String PARAM_CURRENCY_SYMBOL = "currencySymbol";
-
-    private static final String PARAM_VALUE_RND_UP = "up";
-    private static final String PARAM_VALUE_RND_DOWN = "down";
-    private static final String PARAM_VALUE_RND_CEILING = "ceiling";
-    private static final String PARAM_VALUE_RND_FLOOR = "floor";
-    private static final String PARAM_VALUE_RND_HALF_DOWN = "halfDown";
-    private static final String PARAM_VALUE_RND_HALF_EVEN = "halfEven";
-    private static final String PARAM_VALUE_RND_HALF_UP = "halfUp";
-    private static final String PARAM_VALUE_RND_UNNECESSARY = "unnecessary";
-    
-    private static final HashMap<String, ? extends ParameterHandler> 
PARAM_HANDLERS;
-    static {
-        HashMap<String, ParameterHandler> m = new HashMap<>();
-        m.put(PARAM_ROUNDING_MODE, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                RoundingMode parsedValue;
-                if (value.equals(PARAM_VALUE_RND_UP)) {
-                    parsedValue = RoundingMode.UP;
-                } else if (value.equals(PARAM_VALUE_RND_DOWN)) {
-                    parsedValue = RoundingMode.DOWN;
-                } else if (value.equals(PARAM_VALUE_RND_CEILING)) {
-                    parsedValue = RoundingMode.CEILING;
-                } else if (value.equals(PARAM_VALUE_RND_FLOOR)) {
-                    parsedValue = RoundingMode.FLOOR;
-                } else if (value.equals(PARAM_VALUE_RND_HALF_DOWN)) {
-                    parsedValue = RoundingMode.HALF_DOWN;
-                } else if (value.equals(PARAM_VALUE_RND_HALF_EVEN)) {
-                    parsedValue = RoundingMode.HALF_EVEN;
-                } else if (value.equals(PARAM_VALUE_RND_HALF_UP)) {
-                    parsedValue = RoundingMode.HALF_UP;
-                } else if (value.equals(PARAM_VALUE_RND_UNNECESSARY)) {
-                    parsedValue = RoundingMode.UNNECESSARY;
-                } else {
-                    throw new InvalidParameterValueException("Should be one 
of: u, d, c, f, hd, he, hu, un");
-                }
-
-                parser.roundingMode = parsedValue;
-            }
-        });
-        m.put(PARAM_MULTIPIER, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                try {
-                    parser.multipier = Integer.valueOf(value);
-                } catch (NumberFormatException e) {
-                    throw new InvalidParameterValueException("Malformed 
integer.");
-                }
-            }
-        });
-        m.put(PARAM_DECIMAL_SEPARATOR, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setDecimalSeparator(value.charAt(0));
-            }
-        });
-        m.put(PARAM_MONETARY_DECIMAL_SEPARATOR, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setMonetaryDecimalSeparator(value.charAt(0));
-            }
-        });
-        m.put(PARAM_GROUP_SEPARATOR, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setGroupingSeparator(value.charAt(0));
-            }
-        });
-        m.put(PARAM_EXPONENT_SEPARATOR, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                parser.symbols.setExponentSeparator(value);
-            }
-        });
-        m.put(PARAM_MINUS_SIGN, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setMinusSign(value.charAt(0));
-            }
-        });
-        m.put(PARAM_INFINITY, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                parser.symbols.setInfinity(value);
-            }
-        });
-        m.put(PARAM_NAN, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                parser.symbols.setNaN(value);
-            }
-        });
-        m.put(PARAM_PERCENT, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setPercent(value.charAt(0));
-            }
-        });
-        m.put(PARAM_PER_MILL, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setPerMill(value.charAt(0));
-            }
-        });
-        m.put(PARAM_ZERO_DIGIT, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                if (value.length() != 1) {
-                    throw new InvalidParameterValueException("Must contain 
exactly 1 character.");
-                }
-                parser.symbols.setZeroDigit(value.charAt(0));
-            }
-        });
-        m.put(PARAM_CURRENCY_CODE, new ParameterHandler() {
-            @Override
-            public void handle(ExtendedDecimalFormatParser parser, String 
value)
-                    throws InvalidParameterValueException {
-                Currency currency;
-                try {
-                    currency = Currency.getInstance(value);
-                } catch (IllegalArgumentException e) {
-                    throw new InvalidParameterValueException("Not a known ISO 
4217 code.");
-                }
-                parser.symbols.setCurrency(currency);
-            }
-        });
-        PARAM_HANDLERS = m;
-    }
-
-    private static final String SNIP_MARK = "[...]";
-    private static final int MAX_QUOTATION_LENGTH = 10; // Must be more than 
SNIP_MARK.length!
-
-    private final String src;
-    private int pos = 0;
-
-    private final DecimalFormatSymbols symbols;
-    private RoundingMode roundingMode;
-    private Integer multipier;
-
-    static DecimalFormat parse(String formatString, Locale locale) throws 
ParseException {
-        return new ExtendedDecimalFormatParser(formatString, locale).parse();
-    }
-
-    private DecimalFormat parse() throws ParseException {
-        String stdPattern = fetchStandardPattern();
-        skipWS();
-        parseFormatStringExtension();
-
-        DecimalFormat decimalFormat;
-        try {
-            decimalFormat = new DecimalFormat(stdPattern, symbols);
-        } catch (IllegalArgumentException e) {
-            ParseException pe = new ParseException(e.getMessage(), 0);
-            if (e.getCause() != null) {
-                try {
-                    e.initCause(e.getCause());
-                } catch (Exception e2) {
-                    // Supress
-                }
-            }
-            throw pe;
-        }
-
-        if (roundingMode != null) {
-            decimalFormat.setRoundingMode(roundingMode);
-        }
-
-        if (multipier != null) {
-            decimalFormat.setMultiplier(multipier.intValue());
-        }
-
-        return decimalFormat;
-    }
-
-    private void parseFormatStringExtension() throws ParseException {
-        int ln = src.length();
-
-        if (pos == ln) {
-            return;
-        }
-
-        String currencySymbol = null;  // Exceptional, as must be applied 
after "currency code"
-        fetchParamters: do {
-            int namePos = pos;
-            String name = fetchName();
-            if (name == null) {
-                throw newExpectedSgParseException("name");
-            }
-
-            skipWS();
-
-            if (!fetchChar('=')) {
-                throw newExpectedSgParseException("\"=\"");
-            }
-
-            skipWS();
-
-            int valuePos = pos;
-            String value = fetchValue();
-            if (value == null) {
-                throw newExpectedSgParseException("value");
-            }
-            int paramEndPos = pos;
-
-            ParameterHandler handler = PARAM_HANDLERS.get(name);
-            if (handler == null) {
-                if (name.equals(PARAM_CURRENCY_SYMBOL)) {
-                    currencySymbol = value;
-                } else {
-                    throw newUnknownParameterException(name, namePos);
-                }
-            } else {
-                try {
-                    handler.handle(this, value);
-                } catch (InvalidParameterValueException e) {
-                    throw newInvalidParameterValueException(name, value, 
valuePos, e);
-                }
-            }
-
-            skipWS();
-
-            // Optional comma
-            if (fetchChar(',')) {
-                skipWS();
-            } else {
-                if (pos == ln) {
-                    break fetchParamters;
-                }
-                if (pos == paramEndPos) {
-                    throw newExpectedSgParseException("parameter separator 
whitespace or comma");
-                }
-            }
-        } while (true);
-        
-        // This is brought out to here to ensure that it's applied after 
"currency code":
-        if (currencySymbol != null) {
-            symbols.setCurrencySymbol(currencySymbol);
-        }
-    }
-
-    private ParseException newInvalidParameterValueException(String name, 
String value, int valuePos,
-            InvalidParameterValueException e) {
-        return new java.text.ParseException(
-                _StringUtil.jQuote(value) + " is an invalid value for the \"" 
+ name + "\" parameter: "
-                + e.message,
-                valuePos);
-    }
-
-    private ParseException newUnknownParameterException(String name, int 
namePos) throws ParseException {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Unsupported parameter name, 
").append(_StringUtil.jQuote(name));
-        sb.append(". The supported names are: ");
-        Set<String> legalNames = PARAM_HANDLERS.keySet();
-        String[] legalNameArr = legalNames.toArray(new 
String[legalNames.size()]);
-        Arrays.sort(legalNameArr);
-        for (int i = 0; i < legalNameArr.length; i++) {
-            if (i != 0) {
-                sb.append(", ");
-            }
-            sb.append(legalNameArr[i]);
-        }
-        return new java.text.ParseException(sb.toString(), namePos);
-    }
-
-    private void skipWS() {
-        int ln = src.length();
-        while (pos < ln && isWS(src.charAt(pos))) {
-            pos++;
-        }
-    }
-
-    private boolean fetchChar(char fetchedChar) {
-        if (pos < src.length() && src.charAt(pos) == fetchedChar) {
-            pos++;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private boolean isWS(char c) {
-        return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 
'\u00A0';
-    }
-
-    private String fetchName() throws ParseException {
-        int ln = src.length();
-        int startPos = pos;
-        boolean firstChar = true;
-        scanUntilEnd: while (pos < ln) {
-            char c = src.charAt(pos);
-            if (firstChar) {
-                if (!Character.isJavaIdentifierStart(c)) {
-                    break scanUntilEnd;
-                }
-                firstChar = false;
-            } else if (!Character.isJavaIdentifierPart(c)) {
-                break scanUntilEnd;
-            }
-            pos++;
-        }
-        return !firstChar ? src.substring(startPos, pos) : null;
-    }
-
-    private String fetchValue() throws ParseException {
-        int ln = src.length();
-        int startPos = pos;
-        char openedQuot = 0;
-        boolean needsUnescaping = false;
-        scanUntilEnd: while (pos < ln) {
-            char c = src.charAt(pos);
-            if (c == '\'' || c == '"') {
-                if (openedQuot == 0) {
-                    if (startPos != pos) {
-                        throw new java.text.ParseException(
-                                "The " + c + " character can only be used for 
quoting values, "
-                                        + "but it was in the middle of an 
non-quoted value.",
-                                pos);
-                    }
-                    openedQuot = c;
-                } else if (c == openedQuot) {
-                    if (pos + 1 < ln && src.charAt(pos + 1) == openedQuot) {
-                        pos++; // skip doubled quote (escaping)
-                        needsUnescaping = true;
-                    } else {
-                        String str = src.substring(startPos + 1, pos);
-                        pos++;
-                        return needsUnescaping ? unescape(str, openedQuot) : 
str;
-                    }
-                }
-            } else {
-                if (openedQuot == 0 && !Character.isJavaIdentifierPart(c)) {
-                    break scanUntilEnd;
-                }
-            }
-            pos++;
-        } // while
-        if (openedQuot != 0) {
-            throw new java.text.ParseException(
-                    "The " + openedQuot 
-                    + " quotation wasn't closed when the end of the source was 
reached.",
-                    pos);
-        }
-        return startPos == pos ? null : src.substring(startPos, pos);
-    }
-
-    private String unescape(String s, char openedQuot) {
-        return openedQuot == '\'' ? _StringUtil.replace(s, "\'\'", "\'") : 
_StringUtil.replace(s, "\"\"", "\"");
-    }
-
-    private String fetchStandardPattern() {
-        int pos = this.pos;
-        int ln = src.length();
-        int semicolonCnt = 0;
-        boolean quotedMode = false;
-        findStdPartEnd: while (pos < ln) {
-            char c = src.charAt(pos);
-            if (c == ';' && !quotedMode) {
-                semicolonCnt++;
-                if (semicolonCnt == 2) {
-                    break findStdPartEnd;
-                }
-            } else if (c == '\'') {
-                if (quotedMode) {
-                    if (pos + 1 < ln && src.charAt(pos + 1) == '\'') {
-                        // Skips "''" used for escaping "'"
-                        pos++;
-                    } else {
-                        quotedMode = false;
-                    }
-                } else {
-                    quotedMode = true;
-                }
-            }
-            pos++;
-        }
-
-        String stdFormatStr;
-        if (semicolonCnt < 2) { // We have a standard DecimalFormat string
-            // Note that "0.0;" and "0.0" gives the same result with 
DecimalFormat, so we leave a ';' there
-            stdFormatStr = src;
-        } else { // `pos` points to the 2nd ';'
-            int stdEndPos = pos;
-            if (src.charAt(pos - 1) == ';') { // we have a ";;"
-                // Note that ";;" is illegal in DecimalFormat, so this is 
backward compatible.
-                stdEndPos--;
-            }
-            stdFormatStr = src.substring(0, stdEndPos);
-        }
-
-        if (pos < ln) {
-            pos++; // Skips closing ';'
-        }
-        this.pos = pos;
-
-        return stdFormatStr;
-    }
-
-    private ExtendedDecimalFormatParser(String formatString, Locale locale) {
-        src = formatString;
-        symbols = new DecimalFormatSymbols(locale);
-    }
-
-    private ParseException newExpectedSgParseException(String expectedThing) {
-        String quotation;
-
-        // Ignore trailing WS when calculating the length:
-        int i = src.length() - 1;
-        while (i >= 0 && Character.isWhitespace(src.charAt(i))) {
-            i--;
-        }
-        int ln = i + 1;
-
-        if (pos < ln) {
-            int qEndPos = pos + MAX_QUOTATION_LENGTH;
-            if (qEndPos >= ln) {
-                quotation = src.substring(pos, ln);
-            } else {
-                quotation = src.substring(pos, qEndPos - SNIP_MARK.length()) + 
SNIP_MARK;
-            }
-        } else {
-            quotation = null;
-        }
-
-        return new ParseException(
-                "Expected a(n) " + expectedThing + " at position " + pos + " 
(0-based), but "
-                        + (quotation == null ? "reached the end of the input." 
: "found: " + quotation),
-                pos);
-    }
-
-    private interface ParameterHandler {
-
-        void handle(ExtendedDecimalFormatParser parser, String value)
-                throws InvalidParameterValueException;
-
-    }
-
-    private static class InvalidParameterValueException extends Exception {
-
-        private final String message;
-
-        public InvalidParameterValueException(String message) {
-            this.message = message;
-        }
-
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormat.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormat.java 
b/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormat.java
deleted file mode 100644
index bc48a98..0000000
--- a/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormat.java
+++ /dev/null
@@ -1,264 +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;
-
-import java.util.Date;
-import java.util.TimeZone;
-
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util._DateUtil;
-import org.apache.freemarker.core.util._DateUtil.CalendarFieldsToDateConverter;
-import org.apache.freemarker.core.util._DateUtil.DateParseException;
-import org.apache.freemarker.core.util._DateUtil.DateToISO8601CalendarFactory;
-import org.apache.freemarker.core.util._StringUtil;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-abstract class ISOLikeTemplateDateFormat  extends TemplateDateFormat {
-    
-    private static final String XS_LESS_THAN_SECONDS_ACCURACY_ERROR_MESSAGE
-            = "Less than seconds accuracy isn't allowed by the XML Schema 
format";
-    private final ISOLikeTemplateDateFormatFactory factory;
-    private final Environment env;
-    protected final int dateType;
-    protected final boolean zonelessInput;
-    protected final TimeZone timeZone;
-    protected final Boolean forceUTC;
-    protected final Boolean showZoneOffset;
-    protected final int accuracy;
-
-    /**
-     * @param formatString The value of the ..._format setting, like "iso nz".
-     * @param parsingStart The index of the char in the {@code settingValue} 
that directly after the prefix that has
-     *     indicated the exact formatter class (like "iso" or "xs") 
-     */
-    public ISOLikeTemplateDateFormat(
-            final String formatString, int parsingStart,
-            int dateType, boolean zonelessInput,
-            TimeZone timeZone,
-            ISOLikeTemplateDateFormatFactory factory, Environment env)
-            throws InvalidFormatParametersException, 
UnknownDateTypeFormattingUnsupportedException {
-        this.factory = factory;
-        this.env = env;
-        if (dateType == TemplateDateModel.UNKNOWN) {
-            throw new UnknownDateTypeFormattingUnsupportedException();
-        }
-        
-        this.dateType = dateType;
-        this.zonelessInput = zonelessInput;
-        
-        final int ln = formatString.length();
-        boolean afterSeparator = false;
-        int i = parsingStart;
-        int accuracy = _DateUtil.ACCURACY_MILLISECONDS;
-        Boolean showZoneOffset = null;
-        Boolean forceUTC = Boolean.FALSE;
-        while (i < ln) {
-            final char c = formatString.charAt(i++);
-            if (c == '_' || c == ' ') {
-                afterSeparator = true;
-            } else {
-                if (!afterSeparator) {
-                    throw new InvalidFormatParametersException(
-                            "Missing space or \"_\" before \"" + c + "\" (at 
char pos. " + i + ").");
-                }
-                
-                switch (c) {
-                case 'h':
-                case 'm':
-                case 's':
-                    if (accuracy != _DateUtil.ACCURACY_MILLISECONDS) {
-                        throw new InvalidFormatParametersException(
-                                "Character \"" + c + "\" is unexpected as 
accuracy was already specified earlier "
-                                + "(at char pos. " + i + ").");
-                    }
-                    switch (c) {
-                    case 'h':
-                        if (isXSMode()) {
-                            throw new InvalidFormatParametersException(
-                                    
XS_LESS_THAN_SECONDS_ACCURACY_ERROR_MESSAGE);
-                        }
-                        accuracy = _DateUtil.ACCURACY_HOURS;
-                        break;
-                    case 'm':
-                        if (i < ln && formatString.charAt(i) == 's') {
-                            i++;
-                            accuracy = _DateUtil.ACCURACY_MILLISECONDS_FORCED;
-                        } else {
-                            if (isXSMode()) {
-                                throw new InvalidFormatParametersException(
-                                        
XS_LESS_THAN_SECONDS_ACCURACY_ERROR_MESSAGE);
-                            }
-                            accuracy = _DateUtil.ACCURACY_MINUTES;
-                        }
-                        break;
-                    case 's':
-                        accuracy = _DateUtil.ACCURACY_SECONDS;
-                        break;
-                    }
-                    break;
-                case 'f':
-                    if (i < ln && formatString.charAt(i) == 'u') {
-                        checkForceUTCNotSet(forceUTC);
-                        i++;
-                        forceUTC = Boolean.TRUE;
-                        break;
-                    }
-                    // Falls through
-                case 'n':
-                    if (showZoneOffset != null) {
-                        throw new InvalidFormatParametersException(
-                                "Character \"" + c + "\" is unexpected as zone 
offset visibility was already "
-                                + "specified earlier. (at char pos. " + i + 
").");
-                    }
-                    switch (c) {
-                    case 'n':
-                        if (i < ln && formatString.charAt(i) == 'z') {
-                            i++;
-                            showZoneOffset = Boolean.FALSE;
-                        } else {
-                            throw new InvalidFormatParametersException(
-                                    "\"n\" must be followed by \"z\" (at char 
pos. " + i + ").");
-                        }
-                        break;
-                    case 'f':
-                        if (i < ln && formatString.charAt(i) == 'z') {
-                            i++;
-                            showZoneOffset = Boolean.TRUE;
-                        } else {
-                            throw new InvalidFormatParametersException(
-                                    "\"f\" must be followed by \"z\" (at char 
pos. " + i + ").");
-                        }
-                        break;
-                    }
-                    break;
-                case 'u':
-                    checkForceUTCNotSet(forceUTC);
-                    forceUTC = null;  // means UTC will be used except for 
zonelessInput
-                    break;
-                default:
-                    throw new InvalidFormatParametersException(
-                            "Unexpected character, " + 
_StringUtil.jQuote(String.valueOf(c))
-                            + ". Expected the beginning of one of: h, m, s, 
ms, nz, fz, u"
-                            + " (at char pos. " + i + ").");
-                } // switch
-                afterSeparator = false;
-            } // else
-        } // while
-        
-        this.accuracy = accuracy;
-        this.showZoneOffset = showZoneOffset;
-        this.forceUTC = forceUTC;
-        this.timeZone = timeZone;
-    }
-
-    private void checkForceUTCNotSet(Boolean fourceUTC) throws 
InvalidFormatParametersException {
-        if (fourceUTC != Boolean.FALSE) {
-            throw new InvalidFormatParametersException(
-                    "The UTC usage option was already set earlier.");
-        }
-    }
-    
-    @Override
-    public final String formatToPlainText(TemplateDateModel dateModel) throws 
TemplateModelException {
-        final Date date = TemplateFormatUtil.getNonNullDate(dateModel);
-        return format(
-                date,
-                dateType != TemplateDateModel.TIME,
-                dateType != TemplateDateModel.DATE,
-                showZoneOffset == null
-                        ? !zonelessInput
-                        : showZoneOffset.booleanValue(),
-                accuracy,
-                (forceUTC == null ? !zonelessInput : forceUTC.booleanValue()) 
? _DateUtil.UTC : timeZone,
-                factory.getISOBuiltInCalendar(env));
-    }
-    
-    protected abstract String format(Date date,
-            boolean datePart, boolean timePart, boolean offsetPart,
-            int accuracy,
-            TimeZone timeZone,
-            DateToISO8601CalendarFactory calendarFactory);
-
-    @Override
-    @SuppressFBWarnings(value = "RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN",
-            justification = "Known to use the singleton Boolean-s only")
-    public final Date parse(String s, int dateType) throws 
UnparsableValueException {
-        CalendarFieldsToDateConverter calToDateConverter = 
factory.getCalendarFieldsToDateCalculator(env);
-        TimeZone tz = forceUTC != Boolean.FALSE ? _DateUtil.UTC : timeZone;
-        try {
-            if (dateType == TemplateDateModel.DATE) {
-                return parseDate(s, tz, calToDateConverter);
-            } else if (dateType == TemplateDateModel.TIME) {
-                return parseTime(s, tz, calToDateConverter);
-            } else if (dateType == TemplateDateModel.DATETIME) {
-                return parseDateTime(s, tz, calToDateConverter);
-            } else {
-                throw new BugException("Unexpected date type: " + dateType);
-            }
-        } catch (DateParseException e) {
-            throw new UnparsableValueException(e.getMessage(), e);
-        }
-    }
-    
-    protected abstract Date parseDate(
-            String s, TimeZone tz,
-            CalendarFieldsToDateConverter calToDateConverter) 
-            throws DateParseException;
-    
-    protected abstract Date parseTime(
-            String s, TimeZone tz,
-            CalendarFieldsToDateConverter calToDateConverter) 
-            throws DateParseException;
-    
-    protected abstract Date parseDateTime(
-            String s, TimeZone tz,
-            CalendarFieldsToDateConverter calToDateConverter) 
-            throws DateParseException;
-
-    @Override
-    public final String getDescription() {
-        switch (dateType) {
-            case TemplateDateModel.DATE: return getDateDescription();
-            case TemplateDateModel.TIME: return getTimeDescription();
-            case TemplateDateModel.DATETIME: return getDateTimeDescription();
-            default: return "<error: wrong format dateType>";
-        }
-    }
-    
-    protected abstract String getDateDescription();
-    protected abstract String getTimeDescription();
-    protected abstract String getDateTimeDescription();
-    
-    @Override
-    public final boolean isLocaleBound() {
-        return false;
-    }
-    
-    @Override
-    public boolean isTimeZoneBound() {
-        return true;
-    }
-
-    protected abstract boolean isXSMode();
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormatFactory.java
 
b/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormatFactory.java
deleted file mode 100644
index 9a03a16..0000000
--- 
a/src/main/java/org/apache/freemarker/core/ISOLikeTemplateDateFormatFactory.java
+++ /dev/null
@@ -1,52 +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;
-
-import org.apache.freemarker.core.util._DateUtil.CalendarFieldsToDateConverter;
-import org.apache.freemarker.core.util._DateUtil.DateToISO8601CalendarFactory;
-import 
org.apache.freemarker.core.util._DateUtil.TrivialCalendarFieldsToDateConverter;
-import 
org.apache.freemarker.core.util._DateUtil.TrivialDateToISO8601CalendarFactory;
-
-abstract class ISOLikeTemplateDateFormatFactory extends 
TemplateDateFormatFactory {
-    
-    private static final Object DATE_TO_CAL_CONVERTER_KEY = new Object();
-    private static final Object CAL_TO_DATE_CONVERTER_KEY = new Object();
-    
-    protected ISOLikeTemplateDateFormatFactory() { }
-
-    public DateToISO8601CalendarFactory getISOBuiltInCalendar(Environment env) 
{
-        DateToISO8601CalendarFactory r = (DateToISO8601CalendarFactory) 
env.getCustomState(DATE_TO_CAL_CONVERTER_KEY);
-        if (r == null) {
-            r = new TrivialDateToISO8601CalendarFactory();
-            env.setCustomState(DATE_TO_CAL_CONVERTER_KEY, r);
-        }
-        return r;
-    }
-
-    public CalendarFieldsToDateConverter 
getCalendarFieldsToDateCalculator(Environment env) {
-        CalendarFieldsToDateConverter r = (CalendarFieldsToDateConverter) 
env.getCustomState(CAL_TO_DATE_CONVERTER_KEY);
-        if (r == null) {
-            r = new TrivialCalendarFieldsToDateConverter();
-            env.setCustomState(CAL_TO_DATE_CONVERTER_KEY, r);
-        }
-        return r;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormat.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormat.java 
b/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormat.java
deleted file mode 100644
index f973628..0000000
--- a/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormat.java
+++ /dev/null
@@ -1,87 +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;
-
-import java.util.Date;
-import java.util.TimeZone;
-
-import org.apache.freemarker.core.util._DateUtil;
-import org.apache.freemarker.core.util._DateUtil.CalendarFieldsToDateConverter;
-import org.apache.freemarker.core.util._DateUtil.DateParseException;
-import org.apache.freemarker.core.util._DateUtil.DateToISO8601CalendarFactory;
-
-final class ISOTemplateDateFormat extends ISOLikeTemplateDateFormat {
-
-    ISOTemplateDateFormat(
-            String settingValue, int parsingStart,
-            int dateType, boolean zonelessInput,
-            TimeZone timeZone,
-            ISOLikeTemplateDateFormatFactory factory,
-            Environment env)
-            throws InvalidFormatParametersException, 
UnknownDateTypeFormattingUnsupportedException {
-        super(settingValue, parsingStart, dateType, zonelessInput, timeZone, 
factory, env);
-    }
-
-    @Override
-    protected String format(Date date, boolean datePart, boolean timePart, 
boolean offsetPart, int accuracy,
-            TimeZone timeZone, DateToISO8601CalendarFactory calendarFactory) {
-        return _DateUtil.dateToISO8601String(
-                date, datePart, timePart, timePart && offsetPart, accuracy, 
timeZone, calendarFactory);
-    }
-
-    @Override
-    protected Date parseDate(String s, TimeZone tz, 
CalendarFieldsToDateConverter calToDateConverter)
-            throws DateParseException {
-        return _DateUtil.parseISO8601Date(s, tz, calToDateConverter);
-    }
-
-    @Override
-    protected Date parseTime(String s, TimeZone tz, 
CalendarFieldsToDateConverter calToDateConverter)
-            throws DateParseException {
-        return _DateUtil.parseISO8601Time(s, tz, calToDateConverter);
-    }
-
-    @Override
-    protected Date parseDateTime(String s, TimeZone tz,
-            CalendarFieldsToDateConverter calToDateConverter) throws 
DateParseException {
-        return _DateUtil.parseISO8601DateTime(s, tz, calToDateConverter);
-    }
-    
-    @Override
-    protected String getDateDescription() {
-        return "ISO 8601 (subset) date";
-    }
-
-    @Override
-    protected String getTimeDescription() {
-        return "ISO 8601 (subset) time";
-    }
-
-    @Override
-    protected String getDateTimeDescription() {
-        return "ISO 8601 (subset) date-time";
-    }
-
-    @Override
-    protected boolean isXSMode() {
-        return false;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormatFactory.java 
b/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormatFactory.java
deleted file mode 100644
index 068e555..0000000
--- a/src/main/java/org/apache/freemarker/core/ISOTemplateDateFormatFactory.java
+++ /dev/null
@@ -1,43 +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;
-
-import java.util.Locale;
-import java.util.TimeZone;
-
-class ISOTemplateDateFormatFactory extends ISOLikeTemplateDateFormatFactory {
-    
-    static final ISOTemplateDateFormatFactory INSTANCE = new 
ISOTemplateDateFormatFactory();
-
-    private ISOTemplateDateFormatFactory() {
-        // Not meant to be instantiated
-    }
-
-    @Override
-    public TemplateDateFormat get(String params, int dateType, Locale locale, 
TimeZone timeZone, boolean zonelessInput,
-            Environment env) throws 
UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException 
{
-        // We don't cache these as creating them is cheap (only 10% speedup of 
${d?string.xs} with caching)
-        return new ISOTemplateDateFormat(
-                params, 3,
-                dateType, zonelessInput,
-                timeZone, this, env);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/InvalidFormatParametersException.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/InvalidFormatParametersException.java
 
b/src/main/java/org/apache/freemarker/core/InvalidFormatParametersException.java
deleted file mode 100644
index 59e6458..0000000
--- 
a/src/main/java/org/apache/freemarker/core/InvalidFormatParametersException.java
+++ /dev/null
@@ -1,37 +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;
-
-/**
- * Used when creating {@link TemplateDateFormat}-s and {@link 
TemplateNumberFormat}-s to indicate that the parameters
- * part of the format string (like some kind of pattern) is malformed.
- * 
- * @since 2.3.24
- */
-public final class InvalidFormatParametersException extends 
InvalidFormatStringException {
-    
-    public InvalidFormatParametersException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public InvalidFormatParametersException(String message) {
-        this(message, null);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/InvalidFormatStringException.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/InvalidFormatStringException.java 
b/src/main/java/org/apache/freemarker/core/InvalidFormatStringException.java
deleted file mode 100644
index e7cb59b..0000000
--- a/src/main/java/org/apache/freemarker/core/InvalidFormatStringException.java
+++ /dev/null
@@ -1,37 +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;
-
-/**
- * Used when creating {@link TemplateDateFormat}-s and {@link 
TemplateNumberFormat}-s to indicate that the format
- * string (like the value of the {@code dateFormat} setting) is malformed.
- * 
- * @since 2.3.24
- */
-public abstract class InvalidFormatStringException extends 
TemplateValueFormatException {
-    
-    public InvalidFormatStringException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public InvalidFormatStringException(String message) {
-        this(message, null);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java 
b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java
deleted file mode 100644
index 949cc9e..0000000
--- a/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java
+++ /dev/null
@@ -1,72 +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;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * Java {@link DateFormat}-based format.
- */
-class JavaTemplateDateFormat extends TemplateDateFormat {
-    
-    private final DateFormat javaDateFormat;
-
-    public JavaTemplateDateFormat(DateFormat javaDateFormat) {
-        this.javaDateFormat = javaDateFormat;
-    }
-    
-    @Override
-    public String formatToPlainText(TemplateDateModel dateModel) throws 
TemplateModelException {
-        return 
javaDateFormat.format(TemplateFormatUtil.getNonNullDate(dateModel));
-    }
-
-    @Override
-    public Date parse(String s, int dateType) throws UnparsableValueException {
-        try {
-            return javaDateFormat.parse(s);
-        } catch (ParseException e) {
-            throw new UnparsableValueException(e.getMessage(), e);
-        }
-    }
-
-    @Override
-    public String getDescription() {
-        return javaDateFormat instanceof SimpleDateFormat
-                ? ((SimpleDateFormat) javaDateFormat).toPattern()
-                : javaDateFormat.toString();
-    }
-
-    @Override
-    public boolean isLocaleBound() {
-        return true;
-    }
-
-    @Override
-    public boolean isTimeZoneBound() {
-        return true;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java 
b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java
deleted file mode 100644
index 331a0ca..0000000
--- 
a/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java
+++ /dev/null
@@ -1,173 +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;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.slf4j.Logger;
-
-class JavaTemplateDateFormatFactory extends TemplateDateFormatFactory {
-    
-    static final JavaTemplateDateFormatFactory INSTANCE = new 
JavaTemplateDateFormatFactory(); 
-    
-    private static final Logger LOG = _CoreLogs.RUNTIME;
-
-    private static final ConcurrentHashMap<CacheKey, DateFormat> 
GLOBAL_FORMAT_CACHE
-            = new ConcurrentHashMap<>();
-    private static final int LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE = 1024;
-    
-    private JavaTemplateDateFormatFactory() {
-        // Can't be instantiated
-    }
-    
-    /**
-     * @param zonelessInput
-     *            Has no effect in this implementation.
-     */
-    @Override
-    public TemplateDateFormat get(String params, int dateType, Locale locale, 
TimeZone timeZone, boolean zonelessInput,
-            Environment env) throws 
UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException 
{
-        return new JavaTemplateDateFormat(getJavaDateFormat(dateType, params, 
locale, timeZone));
-    }
-
-    /**
-     * Returns a "private" copy (not in the global cache) for the given 
format.  
-     */
-    private DateFormat getJavaDateFormat(int dateType, String nameOrPattern, 
Locale locale, TimeZone timeZone)
-            throws UnknownDateTypeFormattingUnsupportedException, 
InvalidFormatParametersException {
-
-        // Get DateFormat from global cache:
-        CacheKey cacheKey = new CacheKey(dateType, nameOrPattern, locale, 
timeZone);
-        DateFormat jFormat;
-        
-        jFormat = GLOBAL_FORMAT_CACHE.get(cacheKey);
-        if (jFormat == null) {
-            // Add format to global format cache.
-            StringTokenizer tok = new StringTokenizer(nameOrPattern, "_");
-            int tok1Style = tok.hasMoreTokens() ? 
parseDateStyleToken(tok.nextToken()) : DateFormat.DEFAULT;
-            if (tok1Style != -1) {
-                switch (dateType) {
-                    case TemplateDateModel.UNKNOWN: {
-                        throw new 
UnknownDateTypeFormattingUnsupportedException();
-                    }
-                    case TemplateDateModel.TIME: {
-                        jFormat = DateFormat.getTimeInstance(tok1Style, 
cacheKey.locale);
-                        break;
-                    }
-                    case TemplateDateModel.DATE: {
-                        jFormat = DateFormat.getDateInstance(tok1Style, 
cacheKey.locale);
-                        break;
-                    }
-                    case TemplateDateModel.DATETIME: {
-                        int tok2Style = tok.hasMoreTokens() ? 
parseDateStyleToken(tok.nextToken()) : tok1Style;
-                        if (tok2Style != -1) {
-                            jFormat = 
DateFormat.getDateTimeInstance(tok1Style, tok2Style, cacheKey.locale);
-                        }
-                        break;
-                    }
-                }
-            }
-            if (jFormat == null) {
-                try {
-                    jFormat = new SimpleDateFormat(nameOrPattern, 
cacheKey.locale);
-                } catch (IllegalArgumentException e) {
-                    final String msg = e.getMessage();
-                    throw new InvalidFormatParametersException(
-                            msg != null ? msg : "Invalid SimpleDateFormat 
pattern", e);
-                }
-            }
-            jFormat.setTimeZone(cacheKey.timeZone);
-            
-            if (GLOBAL_FORMAT_CACHE.size() >= 
LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE) {
-                boolean triggered = false;
-                synchronized (JavaTemplateNumberFormatFactory.class) {
-                    if (GLOBAL_FORMAT_CACHE.size() >= 
LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE) {
-                        triggered = true;
-                        GLOBAL_FORMAT_CACHE.clear();
-                    }
-                }
-                if (triggered) {
-                    LOG.warn("Global Java DateFormat cache has exceeded {} 
entries => cache flushed. "
-                            + "Typical cause: Some template generates high 
variety of format pattern strings.",
-                            LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE);
-                }
-            }
-            
-            DateFormat prevJFormat = GLOBAL_FORMAT_CACHE.putIfAbsent(cacheKey, 
jFormat);
-            if (prevJFormat != null) {
-                jFormat = prevJFormat;
-            }
-        }  // if cache miss
-        
-        return (DateFormat) jFormat.clone();  // For thread safety
-    }
-
-    private static final class CacheKey {
-        private final int dateType;
-        private final String pattern;
-        private final Locale locale;
-        private final TimeZone timeZone;
-
-        CacheKey(int dateType, String pattern, Locale locale, TimeZone 
timeZone) {
-            this.dateType = dateType;
-            this.pattern = pattern;
-            this.locale = locale;
-            this.timeZone = timeZone;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof CacheKey) {
-                CacheKey fk = (CacheKey) o;
-                return dateType == fk.dateType && fk.pattern.equals(pattern) 
&& fk.locale.equals(locale)
-                        && fk.timeZone.equals(timeZone);
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return dateType ^ pattern.hashCode() ^ locale.hashCode() ^ 
timeZone.hashCode();
-        }
-    }
-
-    private int parseDateStyleToken(String token) {
-        if ("short".equals(token)) {
-            return DateFormat.SHORT;
-        }
-        if ("medium".equals(token)) {
-            return DateFormat.MEDIUM;
-        }
-        if ("long".equals(token)) {
-            return DateFormat.LONG;
-        }
-        if ("full".equals(token)) {
-            return DateFormat.FULL;
-        }
-        return -1;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef968757/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java 
b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java
deleted file mode 100644
index 9e37c6f..0000000
--- a/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java
+++ /dev/null
@@ -1,66 +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;
-
-import java.text.NumberFormat;
-
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-
-final class JavaTemplateNumberFormat extends 
BackwardCompatibleTemplateNumberFormat {
-    
-    private final String formatString;
-    private final NumberFormat javaNumberFormat;
-
-    public JavaTemplateNumberFormat(NumberFormat javaNumberFormat, String 
formatString) {
-        this.formatString = formatString;
-        this.javaNumberFormat = javaNumberFormat;
-    }
-
-    @Override
-    public String formatToPlainText(TemplateNumberModel numberModel) throws 
UnformattableValueException, TemplateModelException {
-        Number number = TemplateFormatUtil.getNonNullNumber(numberModel);
-        return format(number);
-    }
-
-    @Override
-    public boolean isLocaleBound() {
-        return true;
-    }
-
-    @Override
-    String format(Number number) throws UnformattableValueException {
-        try {
-            return javaNumberFormat.format(number);
-        } catch (ArithmeticException e) {
-            throw new UnformattableValueException(
-                    "This format can't format the " + number + " number. 
Reason: " + e.getMessage(), e);
-        }
-    }
-
-    public NumberFormat getJavaNumberFormat() {
-        return javaNumberFormat;
-    }
-
-    @Override
-    public String getDescription() {
-        return formatString;
-    }
-
-}

Reply via email to