You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/08/16 23:24:48 UTC

[1/3] incubator-freemarker git commit: FREEMARKER-65: Utilities for argument validation: Work in progress. Changes in this commit:

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 e941aedc1 -> f231e64fe


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
index 0f11a18..d1019e1 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CallableUtils.java
@@ -19,487 +19,27 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core.util.TemplateLanguageUtils.*;
+import static org.apache.freemarker.core.util.TemplateLanguageUtils.getCallableTypeName;
 
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelWithOriginName;
-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.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
-import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._CollectionUtils;
 
 /**
  * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ * The published part of this API is in {@link CallableUtils}.
  */
-// TODO [FM3] Most functionality here should be made public on some way. Also BuiltIn-s has some duplicates utiltity
-// methods for this functionality (checking arguments). Need to clean this up.
-public final class _CallableUtils {
-
-    public static final TemplateModel[] EMPTY_TEMPLATE_MODEL_ARRAY = new TemplateModel[0];
-
-    private _CallableUtils() {
-        //
-    }
-
-    /** Convenience method for calling {@link #newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
-    public static TemplateException newGenericExecuteException(
-            TemplateFunctionModel callable, String errorDescription) {
-        return newGenericExecuteException(callable, true, errorDescription);
-    }
-
-    /** Convenience method for calling {@link #newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
-    public static TemplateException newGenericExecuteException(
-            TemplateDirectiveModel callable, String errorDescription) {
-        return newGenericExecuteException(callable, false, errorDescription);
-    }
-
-    /**
-     * @param errorDescription Complete sentence describing the problem. This will be after
-     *      {@code "When calling xxx: "}.
-     */
-    public static TemplateException newGenericExecuteException(
-            TemplateCallableModel callable, boolean calledAsFunction, String errorDescription) {
-        return new TemplateException(
-                getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
-                errorDescription);
-    }
-
-    public static TemplateException newArgumentValueException(
-            int argIdx, String problemDescription,
-            TemplateDirectiveModel callable) {
-        return newArgumentValueException(
-                argIdx, problemDescription, callable, false);
-    }
-
-    public static TemplateException newArgumentValueException(
-            int argIdx, String problemDescription,
-            TemplateFunctionModel callable) {
-        return newArgumentValueException(
-                argIdx, problemDescription, callable, true);
-    }
-
-    /**
-     * @param problemDescription The continuation of a sentence like {@code "When calling xxx: The 1st argument "}, for
-     *                           example {@code "must be a positive number."}.
-     */
-    public static TemplateException newArgumentValueException(
-            int argIdx, String problemDescription,
-            TemplateCallableModel callable, boolean calledAsFunction) {
-        return new TemplateException(
-                getMessageArgumentProblem(
-                        callable, argIdx,
-                        problemDescription,
-                        calledAsFunction));
-    }
-
-    /**
-     * Convenience method to call
-     * {@link #newArgumentValueTypeException(TemplateModel, int, Class, TemplateCallableModel, boolean)}.
-     */
-    public static TemplateException newArgumentValueTypeException(
-            TemplateModel argValue, int argIdx, Class<? extends TemplateModel> expectedType,
-            TemplateDirectiveModel callable) {
-        return newArgumentValueTypeException(
-                argValue, argIdx, expectedType,
-                callable, false);
-    }
-
-    /**
-     * Convenience method to call
-     * {@link #newArgumentValueTypeException(TemplateModel, int, Class, TemplateCallableModel, boolean)}.
-     */
-    public static TemplateException newArgumentValueTypeException(
-            TemplateModel argValue, int argIdx, Class<? extends TemplateModel> expectedType,
-            TemplateFunctionModel callable) {
-        return newArgumentValueTypeException(
-                argValue, argIdx, expectedType,
-                callable, true);
-    }
-
-    public static TemplateException newArgumentValueTypeException(
-            TemplateModel argValue, int argIdx, Class<? extends TemplateModel> expectedType,
-            TemplateCallableModel callable, boolean calledAsFunction) {
-        return new TemplateException(
-                getMessageBadArgumentType(argValue, argIdx,
-                        new Class[] { expectedType },
-                        TemplateLanguageUtils.getTypeName(expectedType),
-                        callable, calledAsFunction));
-    }
-
-    /**
-     * Convenience method for calling
-     * {@link #newArgumentValueTypeException(TemplateModel, int, Class[], String, TemplateCallableModel, boolean)}.
-     */
-    public static TemplateException newArgumentValueTypeException(
-            TemplateModel argValue, int argIdx, Class[] expectedTypes, String expectedTypeDescription,
-            TemplateDirectiveModel callable) {
-        return newArgumentValueTypeException(
-                argValue, argIdx, expectedTypes, expectedTypeDescription,
-                callable, false);
-    }
-
-    /**
-     * Convenience method for calling
-     * {@link #newArgumentValueTypeException(TemplateModel, int, Class[], String, TemplateCallableModel, boolean)}.
-     */
-    public static TemplateException newArgumentValueTypeException(
-            TemplateModel argValue, int argIdx, Class[] expectedTypes, String expectedTypeDescription,
-            TemplateFunctionModel callable) {
-        return newArgumentValueTypeException(
-                argValue, argIdx, expectedTypes, expectedTypeDescription,
-                callable, true);
-    }
-
-    /**
-     * @param expectedTypeDescription Something like "string or number".
-     */
-    public static TemplateException newArgumentValueTypeException(
-            TemplateModel argValue, int argIdx, Class[] expectedTypes, String expectedTypeDescription,
-            TemplateCallableModel callable, boolean calledAsFunction) {
-        return new TemplateException(
-                getMessageBadArgumentType(argValue, argIdx,
-                        expectedTypes,
-                        expectedTypeDescription,
-                        callable, calledAsFunction));
-    }
-
-    public static TemplateException newNullOrOmittedArgumentException(int argIdx, TemplateFunctionModel callable) {
-        return newNullOrOmittedArgumentException(argIdx, callable, true);
-    }
-
-    public static TemplateException newNullOrOmittedArgumentException(int argIdx, TemplateDirectiveModel callable) {
-        return newNullOrOmittedArgumentException(argIdx, callable, false);
-    }
-
-    public static TemplateException newNullOrOmittedArgumentException(int argIdx, TemplateCallableModel callable,
-            boolean calledAsFunction) {
-        return newArgumentValueException(argIdx, "can't be omitted or null.", callable, calledAsFunction);
-    }
-
-    /**
-     * Something like {@code "When calling function \"lib.ftl:foo\": " or "When calling ?leftPad: "}
-     */
-    private static Object getMessagePartWhenCallingSomethingColon(
-            TemplateCallableModel callable, boolean calledAsFunction) {
-        return callable instanceof ASTExpBuiltIn.BuiltInCallable
-                ? new Object[] { "When calling ?", ((ASTExpBuiltIn.BuiltInCallable) callable).getBuiltInName() + ": " }
-                : new Object[] {
-                        "When calling ",
-                        getCallableTypeName(callable, calledAsFunction),
-                        " ",
-                        callable instanceof TemplateModelWithOriginName
-                                ? new _DelayedJQuote(((TemplateModelWithOriginName) callable).getOriginName())
-                                : new _DelayedShortClassName(callable.getClass()),
-                        ": "
-                };
-    }
-
-    private static Object getMessagePartsTheSomethingArgument(ArgumentArrayLayout argsLayout, int argsArrayIndex) {
-        if (argsArrayIndex < 0) {
-            throw new IllegalArgumentException("argsArrayIndex can't be negative");
-        }
-        if (argsLayout == null || argsArrayIndex < argsLayout.getPredefinedPositionalArgumentCount()) {
-            return new Object[] { "The ", new _DelayedOrdinal(argsArrayIndex + 1), " argument " };
-        } else if (argsLayout.getPositionalVarargsArgumentIndex() == argsArrayIndex) {
-            return argsLayout.getNamedVarargsArgumentIndex() != -1 ? "The positional varargs argument "
-                    : "The varargs argument ";
-        } else if (argsLayout.getNamedVarargsArgumentIndex() == argsArrayIndex) {
-            return "The named varargs argument ";
-        } else {
-            String argName = argsLayout.getPredefinedNamedArgumentsMap().getKeyOfValue(argsArrayIndex);
-                return argName != null
-                        ? new Object[] { "The ", new _DelayedJQuote(argName), " argument " }
-                        : "The argument "; // Shouldn't occur...
-        }
-    }
-
-    static Object[] getMessageArgumentProblem(TemplateCallableModel callable, int argIndex, Object
-            problemDescription, boolean calledAsFunction) {
-        return new Object[] {
-                getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
-                getMessagePartsTheSomethingArgument(
-                        calledAsFunction ? ((TemplateFunctionModel) callable).getFunctionArgumentArrayLayout()
-                                : ((TemplateDirectiveModel) callable).getDirectiveArgumentArrayLayout(),
-                        argIndex),
-                problemDescription
-        };
-    }
-
-    private static _ErrorDescriptionBuilder getMessageBadArgumentType(
-            TemplateModel argValue, int argIdx, Class<? extends TemplateModel>[] expectedTypes,
-            String expectedTypesDesc, TemplateCallableModel callable,
-            boolean calledAsFunction) {
-        _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
-                getMessageArgumentProblem(
-                        callable, argIdx,
-                        new Object[]{ " should be ", new _DelayedAOrAn(expectedTypesDesc), ", but was ",
-                                new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(argValue)),
-                                "." },
-                        calledAsFunction));
-        if (argValue instanceof _UnexpectedTypeErrorExplainerTemplateModel) {
-            Object[] tip = ((_UnexpectedTypeErrorExplainerTemplateModel) argValue).explainTypeError(expectedTypes);
-            if (tip != null) {
-                desc.tip(tip);
-            }
-        }
-        return desc;
-    }
-
-    public static void executeWith0Arguments(
-            TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env)
-            throws IOException, TemplateException {
-        directive.execute(
-                getArgumentArrayWithNoArguments(directive.getDirectiveArgumentArrayLayout()), callPlace, out, env);
-    }
-
-    public static TemplateModel executeWith0Arguments(
-            TemplateFunctionModel function, CallPlace callPlace, Environment env)
-            throws TemplateException {
-        return function.execute(
-                getArgumentArrayWithNoArguments(function.getFunctionArgumentArrayLayout()), callPlace, env);
-    }
-
-    private static TemplateModel[] getArgumentArrayWithNoArguments(ArgumentArrayLayout argsLayout) {
-        int totalLength = argsLayout != null ? argsLayout.getTotalLength() : 0;
-        if (totalLength == 0) {
-            return EMPTY_TEMPLATE_MODEL_ARRAY;
-        } else {
-            TemplateModel[] args = new TemplateModel[totalLength];
-
-            int positionalVarargsArgumentIndex = argsLayout.getPositionalVarargsArgumentIndex();
-            if (positionalVarargsArgumentIndex != -1) {
-                args[positionalVarargsArgumentIndex] = TemplateSequenceModel.EMPTY_SEQUENCE;
-            }
-
-            int namedVarargsArgumentIndex = argsLayout.getNamedVarargsArgumentIndex();
-            if (namedVarargsArgumentIndex != -1) {
-                args[namedVarargsArgumentIndex] = TemplateSequenceModel.EMPTY_SEQUENCE;
-            }
-            
-            return args;
-        }
-    }
-
-    // String arg:
-
-    /**
-     * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, true, false)}.
-     */
-    public static String getStringArgument(
-            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
-            throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, true, false);
-    }
-
-    /**
-     * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, false, false)}.
-     */
-    public static String getStringArgument(
-            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
-            throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, false, false);
-    }
-
-    /**
-     * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, true, true)}.
-     */
-    public static String getOptionalStringArgument(
-            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
-            throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, true, true);
-    }
-
-    /**
-     * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, false, true)}.
-     */
-    public static String getOptionalStringArgument(
-            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
-            throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, false, true);
-    }
-
-    /**
-     * Checks if the argument value is a string; it does NOT check if {@code args} is big enough.
-     *
-     * @param calledAsFunction
-     *         If {@code callable} was called as function (as opposed to called as a directive)
-     * @param optional
-     *         If we allow a {@code null} return value
-     *
-     * @return Null {@code null} if the argument was omitted or {@code null}
-     *
-     * @throws TemplateException
-     *         If the argument is not of the proper type or is non-optional yet {@code null}. The error message
-     *         describes the problem in detail.
-     */
-    public static String castArgumentValueToString(
-            TemplateModel argValue, int argIdx, TemplateCallableModel callable,
-            boolean calledAsFunction, boolean optional)
-            throws TemplateException {
-        if (argValue instanceof TemplateScalarModel) {
-            return _EvalUtils.modelToString((TemplateScalarModel) argValue, null);
-        }
-        if (argValue == null) {
-            if (optional) {
-                return null;
-            }
-            throw newNullOrOmittedArgumentException(argIdx, callable, calledAsFunction);
-        }
-        throw newArgumentValueTypeException(argValue, argIdx, TemplateScalarModel.class, callable, calledAsFunction);
-    }
-
-    // Number arg:
-    
-    public static Number getNumberArgument(
-            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
-            throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, true, false);
-    }
-
-    public static Number getNumberArgument(
-            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
-            throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, false, false);
-    }
-
-    public static Number getOptionalNumberArgument(
-            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
-            throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, true, true);
-    }
-
-    public static Number getOptionalNumberArgument(
-            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
-            throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, false, true);
-    }
-
-    public static Number castArgumentValueToNumber(
-            TemplateModel argValue, int argIdx, TemplateCallableModel callable,
-            boolean calledAsFunction, boolean optional)
-            throws TemplateException {
-        if (argValue instanceof TemplateNumberModel) {
-            return _EvalUtils.modelToNumber((TemplateNumberModel) argValue, null);
-        }
-        if (argValue == null) {
-            if (optional) {
-                return null;
-            }
-            throw newNullOrOmittedArgumentException(argIdx, callable, calledAsFunction);
-        }
-        throw newArgumentValueTypeException(
-                argValue, argIdx, TemplateNumberModel.class, callable,
-                calledAsFunction);
-    }
-
-    // TODO boolean, etc.
-
-    // Argument count
-
-    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
-    public static void checkArgumentCount(int argCnt, int expectedCnt, TemplateFunctionModel callable)
-            throws TemplateException {
-        checkArgumentCount(argCnt, expectedCnt, callable, true);
-    }
-
-    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
-    public static void checkArgumentCount(int argCnt, int expectedCnt, TemplateDirectiveModel callable)
-            throws TemplateException {
-        checkArgumentCount(argCnt, expectedCnt, callable, false);
-    }
-
-    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
-    public static void checkArgumentCount(int argCnt, int expectedCnt,
-            TemplateCallableModel callable, boolean calledAsFunction) throws TemplateException {
-        checkArgumentCount(argCnt, expectedCnt, expectedCnt, callable, calledAsFunction);
-    }
-
-    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
-    public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt, TemplateFunctionModel callable)
-            throws TemplateException {
-        checkArgumentCount(argCnt, minCnt, maxCnt, callable, true);
-    }
-
-    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
-    public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt, TemplateDirectiveModel callable)
-            throws TemplateException {
-        checkArgumentCount(argCnt, minCnt, maxCnt, callable, false);
-    }
-
-    /**
-     * Useful when the {@link ArgumentArrayLayout} is {@code null} and so the argument array length is not fixed,
-     * to check if the number of arguments is in the given range.
-     */
-    public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt,
-        TemplateCallableModel callable, boolean calledAsFunction) throws TemplateException {
-        if (argCnt < minCnt || argCnt > maxCnt) {
-            throw new TemplateException(
-                    getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
-                    getMessagePartExpectedNArgumentButHadM(argCnt, minCnt, maxCnt));
-        }
-    }
-
-    private static Object[] getMessagePartExpectedNArgumentButHadM(int argCnt, int minCnt, int maxCnt) {
-        ArrayList<Object> desc = new ArrayList<>(20);
-
-        desc.add("Expected ");
-
-        if (minCnt == maxCnt) {
-            if (maxCnt == 0) {
-                desc.add("no");
-            } else {
-                desc.add(maxCnt);
-            }
-        } else if (maxCnt - minCnt == 1) {
-            desc.add(minCnt);
-            desc.add(" or ");
-            desc.add(maxCnt);
-        } else {
-            desc.add(minCnt);
-            if (maxCnt != Integer.MAX_VALUE) {
-                desc.add(" to ");
-                desc.add(maxCnt);
-            } else {
-                desc.add(" or more (unlimited)");
-            }
-        }
-        desc.add(" argument");
-        if (maxCnt > 1) desc.add("s");
-
-        desc.add(" but has received ");
-        if (argCnt == 0) {
-            desc.add("none");
-        } else {
-            desc.add(argCnt);
-        }
-        desc.add(".");
-
-        return desc.toArray();
-    }
-
-    //
-
+public class _CallableUtils {
     static TemplateModel[] getExecuteArgs(
             ASTExpression[] positionalArgs, NamedArgument[] namedArgs, ArgumentArrayLayout argsLayout,
             TemplateCallableModel callable, boolean calledAsFunction,
@@ -528,7 +68,7 @@ public final class _CallableUtils {
                 execArgs[i] = positionalArg.eval(env);
             }
         } else {
-            execArgs = EMPTY_TEMPLATE_MODEL_ARRAY;
+            execArgs = CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
         return execArgs;
     }
@@ -638,7 +178,7 @@ public final class _CallableUtils {
     }
 
     private static Object[] getNamedArgumentsNotSupportedMessage(TemplateCallableModel callable,
-            NamedArgument namedArg, boolean calledAsFunction) {
+            _CallableUtils.NamedArgument namedArg, boolean calledAsFunction) {
         return new Object[] {
                 getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
                 "This ", getCallableTypeName(callable, calledAsFunction),
@@ -663,6 +203,24 @@ public final class _CallableUtils {
         }
     }
 
+    /**
+     * Something like {@code "When calling function \"lib.ftl:foo\": " or "When calling ?leftPad: "}
+     */
+    public static Object getMessagePartWhenCallingSomethingColon(
+            TemplateCallableModel callable, boolean calledAsFunction) {
+        return callable instanceof ASTExpBuiltIn.BuiltInCallable
+                ? new Object[] { "When calling ?", ((ASTExpBuiltIn.BuiltInCallable) callable).getBuiltInName() + ": " }
+                : new Object[] {
+                        "When calling ",
+                        getCallableTypeName(callable, calledAsFunction),
+                        " ",
+                        callable instanceof TemplateModelWithOriginName
+                                ? new _DelayedJQuote(((TemplateModelWithOriginName) callable).getOriginName())
+                                : new _DelayedShortClassName(callable.getClass()),
+                        ": "
+                };
+    }
+
     static final class NamedArgument {
         private final String name;
         private final ASTExpression value;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java
index c904afa..df7042b 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java
@@ -69,6 +69,10 @@ public final class _CoreAPI {
         return Environment.TemplateLanguageFunction.class.isAssignableFrom(cl);
     }
 
+    public static boolean isTemplateLanguageCallable(Class<? extends TemplateModel> cl) {
+        return Environment.TemplateLanguageCallable.class.isAssignableFrom(cl);
+    }
+
     public static void checkVersionNotNullAndSupported(Version incompatibleImprovements) {
         _NullArgumentException.check("incompatibleImprovements", incompatibleImprovements);
         int iciV = incompatibleImprovements.intValue();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
index e9a7967..17f6459 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java
@@ -19,6 +19,8 @@
 
 package org.apache.freemarker.core;
 
+import static org.apache.freemarker.core.MessageUtils.*;
+
 import java.util.Date;
 
 import org.apache.freemarker.core.arithmetic.ArithmeticEngine;
@@ -57,7 +59,7 @@ public class _EvalUtils {
     /**
      * @param expr {@code null} is allowed, but may results in less helpful error messages
      */
-    static String modelToString(TemplateScalarModel model, ASTExpression expr)
+    public static String modelToString(TemplateScalarModel model, ASTExpression expr)
     throws TemplateModelException {
         String value = model.getAsString();
         if (value == null) {
@@ -69,7 +71,7 @@ public class _EvalUtils {
     /**
      * @param expr {@code null} is allowed, but may results in less helpful error messages
      */
-    static Number modelToNumber(TemplateNumberModel model, ASTExpression expr)
+    public static Number modelToNumber(TemplateNumberModel model, ASTExpression expr)
         throws TemplateModelException {
         Number value = model.getAsNumber();
         if (value == null) throw newModelHasStoredNullException(Number.class, model, expr);
@@ -79,7 +81,7 @@ public class _EvalUtils {
     /**
      * @param expr {@code null} is allowed, but may results in less helpful error messages
      */
-    static Date modelToDate(TemplateDateModel model, ASTExpression expr)
+    public static Date modelToDate(TemplateDateModel model, ASTExpression expr)
         throws TemplateModelException {
         Date value = model.getAsDate();
         if (value == null) throw newModelHasStoredNullException(Date.class, model, expr);
@@ -447,7 +449,7 @@ public class _EvalUtils {
                 throw InvalidReferenceException.getInstance(exp, env);
             } else {
                 throw new InvalidReferenceException(
-                        "Null/missing value (no more informatoin avilable)",
+                        "Null/missing value (no more information available)",
                         env);
             }
         } else if (tm instanceof TemplateBooleanModel) {
@@ -459,37 +461,31 @@ public class _EvalUtils {
             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);
-                }
-            }
+
+            throw newUnexpectedOperandTypeException(
+                    exp, tm,
+                    supportsTOM ? STRING_COERCABLE_TYPES_OR_TOM_DESC : STRING_COERCABLE_TYPES_DESC,
+                    supportsTOM ? EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM : EXPECTED_TYPES_STRING_COERCABLE,
+                    seqHint != null && (tm instanceof TemplateSequenceModel || tm instanceof TemplateCollectionModel)
+                            ? new Object[] { seqHint }
+                            : null,
+                    env);
         }
     }
 
     private static String ensureFormatResultString(Object formatResult, ASTExpression exp, Environment env)
-            throws NonStringException {
+            throws TemplateException {
         if (formatResult instanceof String) { 
             return (String) formatResult;
         }
         
         assertFormatResultNotNull(formatResult);
         
-        TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) formatResult;
-        _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
+        throw new TemplateException(env, new _ErrorDescriptionBuilder(
                 "Value was formatted to convert it to string, but the result was markup of ouput format ",
-                new _DelayedJQuote(mo.getOutputFormat()), ".")
+                new _DelayedJQuote(((TemplateMarkupOutputModel) formatResult).getOutputFormat()), ".")
                 .tip("Use value?string to force formatting to plain text.")
-                .blame(exp);
-        throw new NonStringException(null, desc);
+                .blame(exp));
     }
 
     static String assertFormatResultNotNull(String r) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
index 285021c..ecd4ebb 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java
@@ -34,7 +34,11 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem
         TemplateFunctionModel {
 
     public static final TemplateModel INSTANCE = new GeneralPurposeNothing();
-      
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+            0, true,
+            null, true);
+
     private GeneralPurposeNothing() {
     }
 
@@ -75,9 +79,7 @@ implements TemplateBooleanModel, TemplateScalarModel, TemplateSequenceModel, Tem
 
     @Override
     public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
-        return ArgumentArrayLayout.create(
-                0, true,
-                null, true);
+        return ARGS_LAYOUT;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
index 2f1a506..7f57c68 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
@@ -18,7 +18,7 @@
  */
 package org.apache.freemarker.core.model.impl;
 
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -47,7 +47,7 @@ class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
     throws TemplateModelException {
         if (tmArgs == null) {
             // null is treated as empty args
-            tmArgs = _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
+            tmArgs = CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
         final int argCount = tmArgs.length;
         final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
index 8b325ed..fd5c5ee 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
@@ -20,7 +20,7 @@ package org.apache.freemarker.core.model.impl;
 
 import java.lang.reflect.Array;
 
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -139,7 +139,7 @@ class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
     throws TemplateModelException {
         if (tmArgs == null) {
             // null is treated as empty args
-            tmArgs = _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
+            tmArgs = CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
         final int argsLen = tmArgs.length;
         final Class<?>[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
index f794083..b5fb41c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
@@ -30,7 +30,7 @@ import java.util.Set;
 import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core._DelayedJQuote;
 import org.apache.freemarker.core._TemplateModelException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
@@ -112,7 +112,7 @@ public class ResourceBundleModel extends BeanModel implements TemplateFunctionMo
         if (args.length < 1)
             throw new TemplateException("No message key was specified", env);
         // Read it
-        String key = _CallableUtils.getStringArgument(args, 0, this);
+        String key = CallableUtils.getStringArgument(args, 0, this);
         try {
             if (args.length == 1) {
                 return wrap(((ResourceBundle) object).getObject(key));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
index c1786af..78c624c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
@@ -21,7 +21,7 @@ package org.apache.freemarker.core.model.impl;
 import java.lang.reflect.Array;
 import java.lang.reflect.Member;
 
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core._DelayedTemplateLanguageTypeDescription;
 import org.apache.freemarker.core._DelayedOrdinal;
 import org.apache.freemarker.core._ErrorDescriptionBuilder;
@@ -52,7 +52,7 @@ class SimpleMethod {
     
     Object[] unwrapArguments(TemplateModel[] args, DefaultObjectWrapper wrapper) throws TemplateModelException {
         if (args == null) {
-            args = _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
+            args = CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY;
         }
         boolean isVarArg = _MethodUtils.isVarargs(member);
         int typesLen = argTypes.length;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
new file mode 100644
index 0000000..a208612
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
@@ -0,0 +1,496 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+
+import org.apache.freemarker.core.CallPlace;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core._DelayedAOrAn;
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._DelayedOrdinal;
+import org.apache.freemarker.core._DelayedTemplateLanguageTypeDescription;
+import org.apache.freemarker.core._ErrorDescriptionBuilder;
+import org.apache.freemarker.core._EvalUtils;
+import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateCallableModel;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ */
+public final class CallableUtils {
+
+    public static final TemplateModel[] EMPTY_TEMPLATE_MODEL_ARRAY = new TemplateModel[0];
+
+    private CallableUtils() {
+        //
+    }
+
+    /** Convenience method for calling {@link #newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
+    public static TemplateException newGenericExecuteException(
+            TemplateFunctionModel callable, String errorDescription) {
+        return newGenericExecuteException(callable, true, errorDescription);
+    }
+
+    /** Convenience method for calling {@link #newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
+    public static TemplateException newGenericExecuteException(
+            TemplateDirectiveModel callable, String errorDescription) {
+        return newGenericExecuteException(callable, false, errorDescription);
+    }
+
+    /**
+     * @param errorDescription Complete sentence describing the problem. This will be after
+     *      {@code "When calling xxx: "}.
+     */
+    public static TemplateException newGenericExecuteException(
+            TemplateCallableModel callable, boolean calledAsFunction, String errorDescription) {
+        return new TemplateException(
+                _CallableUtils.getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
+                errorDescription);
+    }
+
+    public static TemplateException newArgumentValueException(
+            int argIdx, String problemDescription,
+            TemplateDirectiveModel callable) {
+        return newArgumentValueException(
+                argIdx, problemDescription, callable, false);
+    }
+
+    public static TemplateException newArgumentValueException(
+            int argIdx, String problemDescription,
+            TemplateFunctionModel callable) {
+        return newArgumentValueException(
+                argIdx, problemDescription, callable, true);
+    }
+
+    /**
+     * @param problemDescription The continuation of a sentence like {@code "When calling xxx: The 1st argument "}, for
+     *                           example {@code "must be a positive number."}.
+     */
+    public static TemplateException newArgumentValueException(
+            int argIdx, String problemDescription,
+            TemplateCallableModel callable, boolean calledAsFunction) {
+        return _newArgumentValueException(argIdx, problemDescription, callable, calledAsFunction, null);
+    }
+
+    // TODO [FM3] How to expose tips API?
+    public static TemplateException _newArgumentValueException(
+            int argIdx, String problemDescription,
+            TemplateCallableModel callable, boolean calledAsFunction,
+            Object[] tips) {
+        return new TemplateException(
+                new _ErrorDescriptionBuilder(
+                        getMessageArgumentProblem(callable, argIdx, problemDescription, calledAsFunction)
+                        ).tips(tips));
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #newArgumentValueTypeException(TemplateModel, int, Class, TemplateCallableModel, boolean)}.
+     */
+    public static TemplateException newArgumentValueTypeException(
+            TemplateModel argValue, int argIdx, Class<? extends TemplateModel> expectedType,
+            TemplateDirectiveModel callable) {
+        return newArgumentValueTypeException(
+                argValue, argIdx, expectedType,
+                callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #newArgumentValueTypeException(TemplateModel, int, Class, TemplateCallableModel, boolean)}.
+     */
+    public static TemplateException newArgumentValueTypeException(
+            TemplateModel argValue, int argIdx, Class<? extends TemplateModel> expectedType,
+            TemplateFunctionModel callable) {
+        return newArgumentValueTypeException(
+                argValue, argIdx, expectedType,
+                callable, true);
+    }
+
+    public static TemplateException newArgumentValueTypeException(
+            TemplateModel argValue, int argIdx, Class<? extends TemplateModel> expectedType,
+            TemplateCallableModel callable, boolean calledAsFunction) {
+        return new TemplateException(
+                getMessageBadArgumentType(argValue, argIdx,
+                        new Class[] { expectedType },
+                        TemplateLanguageUtils.getTypeName(expectedType),
+                        callable, calledAsFunction));
+    }
+
+    /**
+     * Convenience method for calling
+     * {@link #newArgumentValueTypeException(TemplateModel, int, Class[], String, TemplateCallableModel, boolean)}.
+     */
+    public static TemplateException newArgumentValueTypeException(
+            TemplateModel argValue, int argIdx, Class[] expectedTypes, String expectedTypeDescription,
+            TemplateDirectiveModel callable) {
+        return newArgumentValueTypeException(
+                argValue, argIdx, expectedTypes, expectedTypeDescription,
+                callable, false);
+    }
+
+    /**
+     * Convenience method for calling
+     * {@link #newArgumentValueTypeException(TemplateModel, int, Class[], String, TemplateCallableModel, boolean)}.
+     */
+    public static TemplateException newArgumentValueTypeException(
+            TemplateModel argValue, int argIdx, Class[] expectedTypes, String expectedTypeDescription,
+            TemplateFunctionModel callable) {
+        return newArgumentValueTypeException(
+                argValue, argIdx, expectedTypes, expectedTypeDescription,
+                callable, true);
+    }
+
+    /**
+     * @param expectedTypeDescription Something like "string or number".
+     */
+    public static TemplateException newArgumentValueTypeException(
+            TemplateModel argValue, int argIdx, Class[] expectedTypes, String expectedTypeDescription,
+            TemplateCallableModel callable, boolean calledAsFunction) {
+        return new TemplateException(
+                getMessageBadArgumentType(argValue, argIdx,
+                        expectedTypes,
+                        expectedTypeDescription,
+                        callable, calledAsFunction));
+    }
+
+    public static TemplateException newNullOrOmittedArgumentException(int argIdx, TemplateFunctionModel callable) {
+        return newNullOrOmittedArgumentException(argIdx, callable, true);
+    }
+
+    public static TemplateException newNullOrOmittedArgumentException(int argIdx, TemplateDirectiveModel callable) {
+        return newNullOrOmittedArgumentException(argIdx, callable, false);
+    }
+
+    public static TemplateException newNullOrOmittedArgumentException(int argIdx, TemplateCallableModel callable,
+            boolean calledAsFunction) {
+        return _newNullOrOmittedArgumentException(argIdx, callable, calledAsFunction, null);
+    }
+
+    // TODO [FM3] How to expose tips API?
+    public static TemplateException _newNullOrOmittedArgumentException(int argIdx, TemplateCallableModel callable,
+            boolean calledAsFunction, Object[] tips) {
+        return _newArgumentValueException(argIdx, "can't be omitted or null.", callable, calledAsFunction, tips);
+    }
+
+    private static Object getMessagePartsTheSomethingArgument(ArgumentArrayLayout argsLayout, int argsArrayIndex) {
+        if (argsArrayIndex < 0) {
+            throw new IllegalArgumentException("argsArrayIndex can't be negative");
+        }
+        if (argsLayout == null || argsArrayIndex < argsLayout.getPredefinedPositionalArgumentCount()) {
+            return new Object[] { "The ", new _DelayedOrdinal(argsArrayIndex + 1), " argument " };
+        } else if (argsLayout.getPositionalVarargsArgumentIndex() == argsArrayIndex) {
+            return argsLayout.getNamedVarargsArgumentIndex() != -1 ? "The positional varargs argument "
+                    : "The varargs argument ";
+        } else if (argsLayout.getNamedVarargsArgumentIndex() == argsArrayIndex) {
+            return "The named varargs argument ";
+        } else {
+            String argName = argsLayout.getPredefinedNamedArgumentsMap().getKeyOfValue(argsArrayIndex);
+                return argName != null
+                        ? new Object[] { "The ", new _DelayedJQuote(argName), " argument " }
+                        : "The argument "; // Shouldn't occur...
+        }
+    }
+
+    static Object[] getMessageArgumentProblem(TemplateCallableModel callable, int argIndex, Object
+            problemDescription, boolean calledAsFunction) {
+        return new Object[] {
+                _CallableUtils.getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
+                getMessagePartsTheSomethingArgument(
+                        calledAsFunction ? ((TemplateFunctionModel) callable).getFunctionArgumentArrayLayout()
+                                : ((TemplateDirectiveModel) callable).getDirectiveArgumentArrayLayout(),
+                        argIndex),
+                problemDescription
+        };
+    }
+
+    private static _ErrorDescriptionBuilder getMessageBadArgumentType(
+            TemplateModel argValue, int argIdx, Class<? extends TemplateModel>[] expectedTypes,
+            String expectedTypesDesc, TemplateCallableModel callable,
+            boolean calledAsFunction) {
+        _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
+                getMessageArgumentProblem(
+                        callable, argIdx,
+                        new Object[]{ " should be ", new _DelayedAOrAn(expectedTypesDesc), ", but was ",
+                                new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(argValue)),
+                                "." },
+                        calledAsFunction));
+        if (argValue instanceof _UnexpectedTypeErrorExplainerTemplateModel) {
+            Object[] tip = ((_UnexpectedTypeErrorExplainerTemplateModel) argValue).explainTypeError(expectedTypes);
+            if (tip != null) {
+                desc.tip(tip);
+            }
+        }
+        return desc;
+    }
+
+    public static void executeWith0Arguments(
+            TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env)
+            throws IOException, TemplateException {
+        directive.execute(
+                getArgumentArrayWithNoArguments(directive.getDirectiveArgumentArrayLayout()), callPlace, out, env);
+    }
+
+    public static TemplateModel executeWith0Arguments(
+            TemplateFunctionModel function, CallPlace callPlace, Environment env)
+            throws TemplateException {
+        return function.execute(
+                getArgumentArrayWithNoArguments(function.getFunctionArgumentArrayLayout()), callPlace, env);
+    }
+
+    private static TemplateModel[] getArgumentArrayWithNoArguments(ArgumentArrayLayout argsLayout) {
+        int totalLength = argsLayout != null ? argsLayout.getTotalLength() : 0;
+        if (totalLength == 0) {
+            return EMPTY_TEMPLATE_MODEL_ARRAY;
+        } else {
+            TemplateModel[] args = new TemplateModel[totalLength];
+
+            int positionalVarargsArgumentIndex = argsLayout.getPositionalVarargsArgumentIndex();
+            if (positionalVarargsArgumentIndex != -1) {
+                args[positionalVarargsArgumentIndex] = TemplateSequenceModel.EMPTY_SEQUENCE;
+            }
+
+            int namedVarargsArgumentIndex = argsLayout.getNamedVarargsArgumentIndex();
+            if (namedVarargsArgumentIndex != -1) {
+                args[namedVarargsArgumentIndex] = TemplateSequenceModel.EMPTY_SEQUENCE;
+            }
+            
+            return args;
+        }
+    }
+
+    // String arg:
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, callable, true, false)}.
+     */
+    public static String getStringArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToString(args[argIndex], argIndex, callable, true, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, callable, false, false)}.
+     */
+    public static String getStringArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToString(args[argIndex], argIndex, callable, false, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, callable, true, true)}.
+     */
+    public static String getOptionalStringArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToString(args[argIndex], argIndex, callable, true, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToString(TemplateModel, int, TemplateCallableModel, boolean, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, callable, false, true)}.
+     */
+    public static String getOptionalStringArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToString(args[argIndex], argIndex, callable, false, true);
+    }
+
+    /**
+     * Checks if the argument value is a string; it does NOT check if {@code args} is big enough.
+     *
+     * @param calledAsFunction
+     *         If {@code callable} was called as function (as opposed to called as a directive)
+     * @param optional
+     *         If we allow a {@code null} return value
+     *
+     * @return Null {@code null} if the argument was omitted or {@code null}
+     *
+     * @throws TemplateException
+     *         If the argument is not of the proper type or is non-optional yet {@code null}. The error message
+     *         describes the problem in detail.
+     */
+    public static String castArgumentValueToString(
+            TemplateModel argValue, int argIdx, TemplateCallableModel callable,
+            boolean calledAsFunction, boolean optional)
+            throws TemplateException {
+        if (argValue instanceof TemplateScalarModel) {
+            return _EvalUtils.modelToString((TemplateScalarModel) argValue, null);
+        }
+        if (argValue == null) {
+            if (optional) {
+                return null;
+            }
+            throw newNullOrOmittedArgumentException(argIdx, callable, calledAsFunction);
+        }
+        throw newArgumentValueTypeException(argValue, argIdx, TemplateScalarModel.class, callable, calledAsFunction);
+    }
+
+    // Number arg:
+    
+    public static Number getNumberArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToNumber(args[argIndex], argIndex, callable, true, false);
+    }
+
+    public static Number getNumberArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToNumber(args[argIndex], argIndex, callable, false, false);
+    }
+
+    public static Number getOptionalNumberArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToNumber(args[argIndex], argIndex, callable, true, true);
+    }
+
+    public static Number getOptionalNumberArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToNumber(args[argIndex], argIndex, callable, false, true);
+    }
+
+    public static Number castArgumentValueToNumber(
+            TemplateModel argValue, int argIdx, TemplateCallableModel callable,
+            boolean calledAsFunction, boolean optional)
+            throws TemplateException {
+        if (argValue instanceof TemplateNumberModel) {
+            return _EvalUtils.modelToNumber((TemplateNumberModel) argValue, null);
+        }
+        if (argValue == null) {
+            if (optional) {
+                return null;
+            }
+            throw newNullOrOmittedArgumentException(argIdx, callable, calledAsFunction);
+        }
+        throw newArgumentValueTypeException(
+                argValue, argIdx, TemplateNumberModel.class, callable,
+                calledAsFunction);
+    }
+
+    // TODO boolean, etc.
+
+    // Argument count
+
+    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
+    public static void checkArgumentCount(int argCnt, int expectedCnt, TemplateFunctionModel callable)
+            throws TemplateException {
+        checkArgumentCount(argCnt, expectedCnt, callable, true);
+    }
+
+    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
+    public static void checkArgumentCount(int argCnt, int expectedCnt, TemplateDirectiveModel callable)
+            throws TemplateException {
+        checkArgumentCount(argCnt, expectedCnt, callable, false);
+    }
+
+    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
+    public static void checkArgumentCount(int argCnt, int expectedCnt,
+            TemplateCallableModel callable, boolean calledAsFunction) throws TemplateException {
+        checkArgumentCount(argCnt, expectedCnt, expectedCnt, callable, calledAsFunction);
+    }
+
+    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
+    public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt, TemplateFunctionModel callable)
+            throws TemplateException {
+        checkArgumentCount(argCnt, minCnt, maxCnt, callable, true);
+    }
+
+    /** Convenience method for calling {@link #checkArgumentCount(int, int, int, TemplateCallableModel, boolean)}. */
+    public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt, TemplateDirectiveModel callable)
+            throws TemplateException {
+        checkArgumentCount(argCnt, minCnt, maxCnt, callable, false);
+    }
+
+    /**
+     * Useful when the {@link ArgumentArrayLayout} is {@code null} and so the argument array length is not fixed,
+     * to check if the number of arguments is in the given range.
+     */
+    public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt,
+        TemplateCallableModel callable, boolean calledAsFunction) throws TemplateException {
+        if (argCnt < minCnt || argCnt > maxCnt) {
+            throw new TemplateException(
+                    _CallableUtils.getMessagePartWhenCallingSomethingColon(callable, calledAsFunction),
+                    getMessagePartExpectedNArgumentButHadM(argCnt, minCnt, maxCnt));
+        }
+    }
+
+    private static Object[] getMessagePartExpectedNArgumentButHadM(int argCnt, int minCnt, int maxCnt) {
+        ArrayList<Object> desc = new ArrayList<>(20);
+
+        desc.add("Expected ");
+
+        if (minCnt == maxCnt) {
+            if (maxCnt == 0) {
+                desc.add("no");
+            } else {
+                desc.add(maxCnt);
+            }
+        } else if (maxCnt - minCnt == 1) {
+            desc.add(minCnt);
+            desc.add(" or ");
+            desc.add(maxCnt);
+        } else {
+            desc.add(minCnt);
+            if (maxCnt != Integer.MAX_VALUE) {
+                desc.add(" to ");
+                desc.add(maxCnt);
+            } else {
+                desc.add(" or more (unlimited)");
+            }
+        }
+        desc.add(" argument");
+        if (maxCnt > 1) desc.add("s");
+
+        desc.add(" but has received ");
+        if (argCnt == 0) {
+            desc.add("none");
+        } else {
+            desc.add(argCnt);
+        }
+        desc.add(".");
+
+        return desc.toArray();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
index 749ccd5..9b980d9 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/TemplateLanguageUtils.java
@@ -840,12 +840,18 @@ public final class TemplateLanguageUtils {
         }
 
         if (TemplateCallableModel.class.isAssignableFrom(cl)) {
+            boolean recognized = false;
             if (TemplateDirectiveModel.class.isAssignableFrom(cl)) {
                 appendTypeName(sb, typeNamesAppended, _CoreAPI.isMacro(cl) ? "macro" : "directive");
+                recognized = true;
             }
             if (TemplateFunctionModel.class.isAssignableFrom(cl)) {
                 appendTypeName(sb, typeNamesAppended,
                         JavaMethodModel.class.isAssignableFrom(cl) ? "method" : "function");
+                recognized = true;
+            }
+            if (!recognized && _CoreAPI.isTemplateLanguageCallable(cl)) {
+                appendTypeName(sb, typeNamesAppended, "macro or function defined with template language");
             }
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/javacc/FTL.jj b/freemarker-core/src/main/javacc/FTL.jj
index 613b252..4a3295f 100644
--- a/freemarker-core/src/main/javacc/FTL.jj
+++ b/freemarker-core/src/main/javacc/FTL.jj
@@ -2248,8 +2248,8 @@ ASTDollarInterpolation ASTDollarInterpolation() :
     begin = <DOLLAR_INTERPOLATION_OPENING>
     exp = ASTExpression()
     {
-        notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
-        notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
+        notHashLiteral(exp, MessageUtils.STRING_COERCABLE_TYPES_DESC);
+        notListLiteral(exp, MessageUtils.STRING_COERCABLE_TYPES_DESC);
     }
     end = <CLOSING_CURLY_BRACKET>
     {


[2/3] incubator-freemarker git commit: FREEMARKER-65: Utilities for argument validation: Work in progress. Changes in this commit:

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
index d31c885..119a1b8 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.*;
+import static org.apache.freemarker.core.util.CallableUtils.*;
 
 import java.util.ArrayList;
 import java.util.StringTokenizer;
@@ -34,6 +34,7 @@ import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util._StringUtils;
 
 class BuiltInsForStringsBasic {
@@ -71,7 +72,7 @@ class BuiltInsForStringsBasic {
 
     static class containsBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateFunctionModel {
+        private class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             
             private final String s;
     
@@ -82,7 +83,7 @@ class BuiltInsForStringsBasic {
             @Override
             public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
                     throws TemplateException {
-                return s.contains(_CallableUtils.getStringArgument(args, 0, this))
+                return s.contains(CallableUtils.getStringArgument(args, 0, this))
                         ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
             }
 
@@ -257,6 +258,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_afterBI extends BuiltInForString {
+
         class KeepAfterMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             private String s;
 
@@ -367,6 +369,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class keep_beforeBI extends BuiltInForString {
+
         class KeepUntilMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             private String s;
 
@@ -414,8 +417,8 @@ class BuiltInsForStringsBasic {
         
     }
     
-    // TODO
     static class keep_before_lastBI extends BuiltInForString {
+
         class KeepUntilMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             private String s;
 
@@ -597,6 +600,7 @@ class BuiltInsForStringsBasic {
     }
     
     static class split_BI extends BuiltInForString {
+
         class SplitMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             private String s;
 
@@ -639,7 +643,7 @@ class BuiltInsForStringsBasic {
     
     static class starts_withBI extends BuiltInForString {
     
-        private class BIMethod implements TemplateFunctionModel {
+        private class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             private String s;
     
             private BIMethod(String s) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
index 7a3fe12..8c6a6df 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsEncoding.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.getStringArgument;
+import static org.apache.freemarker.core.util.CallableUtils.getStringArgument;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
index ab1dbb3..d2e5f48 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
@@ -159,10 +159,10 @@ class BuiltInsForStringsMisc {
             } else if (model instanceof TemplateScalarModel) {
                 sourceExpr = target;
             } else {
-                throw new UnexpectedTypeException(
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, model,
                         "sequence or string", new Class[] { TemplateSequenceModel.class, TemplateScalarModel.class },
-                        env);
+                        null, env);
             }
             String templateSource = sourceExpr.evalAndCoerceToPlainText(env);
             Template parentTemplate = env.getCurrentTemplate();
@@ -236,7 +236,10 @@ class BuiltInsForStringsMisc {
             try {
                 return new SimpleNumber(env.getArithmeticEngine().toNumber(s));
             } catch (NumberFormatException nfe) {
-                throw NonNumericalException.newMalformedNumberException(this, s, env);
+                throw new TemplateException(
+                        new _ErrorDescriptionBuilder(
+                                "Can't convert this string to number: ", new _DelayedJQuote(s))
+                        .blame(this));
             }
         }
     }
@@ -253,7 +256,7 @@ class BuiltInsForStringsMisc {
             return new ConstructorFunction(target.evalAndCoerceToPlainText(env), env, target.getTemplate());
         }
 
-        class ConstructorFunction implements TemplateFunctionModel {
+        class ConstructorFunction extends BuiltInCallableImpl implements TemplateFunctionModel {
 
             private final Class<?> cl;
             private final Environment env;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
index 6879477..15ea886 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.*;
+import static org.apache.freemarker.core.util.CallableUtils.*;
 
 import java.util.ArrayList;
 import java.util.regex.Matcher;
@@ -54,9 +54,11 @@ class BuiltInsForStringsRegexp {
                 return new NativeStringArraySequence(((RegexMatchModel.MatchWithGroups) targetModel).groups);
 
             } else {
-                throw new UnexpectedTypeException(target, targetModel,
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        target, targetModel,
                         "regular expression matcher",
                         new Class[] { RegexMatchModel.class, RegexMatchModel.MatchWithGroups.class },
+                        null,
                         env);
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index fcf9471..7e7703d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -19,6 +19,8 @@
 
 package org.apache.freemarker.core;
 
+import static org.apache.freemarker.core.util.CallableUtils.*;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.Serializable;
@@ -64,6 +66,7 @@ import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException;
 import org.apache.freemarker.core.templateresolver.TemplateResolver;
 import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util._DateUtils;
 import org.apache.freemarker.core.util._DateUtils.DateToISO8601CalendarFactory;
@@ -582,7 +585,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
         try {
             TemplateDirectiveModel nodeProcessor = getNodeProcessor(node);
             if (nodeProcessor != null) {
-                _CallableUtils.executeWith0Arguments(
+                CallableUtils.executeWith0Arguments(
                         nodeProcessor, NonTemplateCallPlace.INSTANCE, out, this);
             } else if (nodeProcessor == null) {
                 String nodeType = node.getNodeType();
@@ -641,7 +644,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     void fallback() throws TemplateException, IOException {
         TemplateDirectiveModel nodeProcessor = getNodeProcessor(currentNodeName, currentNodeNS, nodeNamespaceIndex);
         if (nodeProcessor != null) {
-            _CallableUtils.executeWith0Arguments(
+            CallableUtils.executeWith0Arguments(
                     nodeProcessor, NonTemplateCallPlace.INSTANCE, out, this);
         }
     }
@@ -2999,22 +3002,23 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
                     } else {
                         // TODO [FM3] Had to give different messages depending on if the argument was omitted, or if
                         // it was null, but this will be fixed with the null related refactoring.
-                        throw new TemplateException(Environment.this,
-                                new _ErrorDescriptionBuilder(
-                                        _CallableUtils.getMessageArgumentProblem(
-                                                this, argIdx,
-                                                " can't be null or omitted.",
-                                                isFunction())
-                                )
-                                .tip("If the parameter value expression on the caller side is known to "
+                        throw _newNullOrOmittedArgumentException(
+                                argIdx, this, isFunction(),
+                                new Object[] {
+                                        "If the parameter value expression on the caller side is known to "
                                         + "be legally null/missing, you may want to specify a default "
                                         + "value for it on the caller side with the \"!\" operator, like "
-                                        + "paramValue!defaultValue.")
-                                .tip("If the parameter was omitted on the caller side, and the omission was "
-                                        + "deliberate, you may consider making the parameter optional in the macro "
-                                        + "by specifying a default value for it, like <#macro macroName "
-                                        + "paramName=defaultExpr>."
-                                )
+                                        + "paramValue!defaultValue.",
+                                        new Object[]{
+                                                "If the parameter was omitted on the caller side, and the omission "
+                                                + "was deliberate, you may consider making the parameter optional in "
+                                                + "the macro by specifying a default value for it, like ",
+                                                (isFunction()
+                                                        ? "<#function functionName(paramName=defaultExpr)>"
+                                                        : "<#macro macroName paramName=defaultExpr>"),
+                                                "."
+                                        }
+                                }
                         );
                     }
                 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
index 0edbee6..d1db6e6 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java
@@ -19,7 +19,17 @@
 
 package org.apache.freemarker.core;
 
+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.util.BugException;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 import org.apache.freemarker.core.util._StringUtils;
 import org.apache.freemarker.core.valueformat.TemplateDateFormat;
 import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
@@ -52,6 +62,29 @@ class MessageUtils {
     static final String EMBEDDED_MESSAGE_END = "\n---end-message---";
 
     static final String ERROR_MESSAGE_HR = "----";
+    
+    static final String STRING_COERCABLE_TYPES_DESC
+            = "string or something automatically convertible to string (number, date or boolean)";
+    static final Class[] EXPECTED_TYPES_STRING_COERCABLE = new Class[] {
+        TemplateScalarModel.class, TemplateNumberModel.class, TemplateDateModel.class, TemplateBooleanModel.class
+    };
+
+    static final String STRING_COERCABLE_TYPES_OR_TOM_DESC
+            = STRING_COERCABLE_TYPES_DESC + ", or \"template output\"";
+    static final Class[] EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM;
+    static {
+        EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM = new Class[EXPECTED_TYPES_STRING_COERCABLE.length + 1];
+        int i;
+        for (i = 0; i < EXPECTED_TYPES_STRING_COERCABLE.length; i++) {
+            EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM[i] = EXPECTED_TYPES_STRING_COERCABLE[i];
+        }
+        EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM[i] = TemplateMarkupOutputModel.class;
+    }
+
+    static final String SEQUENCE_OR_COLLECTION = "sequence or collection";
+    static final Class[] EXPECTED_TYPES_SEQUENCE_OR_COLLECTION = new Class[] {
+            TemplateSequenceModel.class, TemplateCollectionModel.class
+    };
 
     // Can't be instantiated
     private MessageUtils() { }
@@ -178,9 +211,9 @@ class MessageUtils {
     static TemplateModelException newCantFormatUnknownTypeDateException(
             ASTExpression dateSourceExpr, UnknownDateTypeFormattingUnsupportedException cause) {
         return new _TemplateModelException(cause, null, new _ErrorDescriptionBuilder(
-                MessageUtils.UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE)
+                UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE)
                 .blame(dateSourceExpr)
-                .tips(MessageUtils.UNKNOWN_DATE_TO_STRING_TIPS));
+                .tips(UNKNOWN_DATE_TO_STRING_TIPS));
     }
 
     static TemplateException newCantFormatDateException(TemplateDateFormat format, ASTExpression dataSrcExp,
@@ -235,5 +268,83 @@ class MessageUtils {
             }
         }
     }
-    
+
+    static TemplateException newUnexpectedOperandTypeException(
+            ASTExpression blamed, TemplateModel model, Class<? extends TemplateModel> expectedType, Environment env)
+            throws InvalidReferenceException {
+        return newUnexpectedOperandTypeException(blamed, model, null, new Class[] { expectedType }, null, env);
+    }
+
+    static TemplateException newUnexpectedOperandTypeException(
+            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Object[] tips,
+            Environment env)
+            throws InvalidReferenceException {
+        return new TemplateException(null, env, blamed, newUnexpectedOperandTypeDescriptionBuilder(
+                blamed,
+                null,
+                model, expectedTypesDesc, expectedTypes, env)
+                .tips(tips));
+    }
+
+    static TemplateException newUnexpectedAssignmentTargetTypeException(
+            String blamedAssignmentTargetVarName, TemplateModel model, Class<? extends TemplateModel> expectedType,
+            Environment env)
+            throws InvalidReferenceException {
+        return newUnexpectedAssignmentTargetTypeException(
+                blamedAssignmentTargetVarName, model, null, new Class[] { expectedType }, null, env);
+    }
+
+    /**
+     * Used for assignments that use {@code +=} and such.
+     */
+    static TemplateException newUnexpectedAssignmentTargetTypeException(
+            String blamedAssignmentTargetVarName, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes,
+            Object[] tips,
+            Environment env)
+            throws InvalidReferenceException {
+        return new TemplateException(null, env, null, newUnexpectedOperandTypeDescriptionBuilder(
+                null,
+                blamedAssignmentTargetVarName,
+                model, expectedTypesDesc, expectedTypes, env).tips(tips));
+    }
+
+    private static _ErrorDescriptionBuilder newUnexpectedOperandTypeDescriptionBuilder(
+            ASTExpression blamed, String blamedAssignmentTargetVarName,
+            TemplateModel model, String expectedTypesDesc, Class<? extends TemplateModel>[] expectedTypes, Environment env)
+            throws InvalidReferenceException {
+        if (model == null) {
+            throw InvalidReferenceException.getInstance(blamed, env);
+        }
+
+        if (expectedTypesDesc == null) {
+            if (expectedTypes.length != 1) {
+                throw new BugException("Can't generate expectedTypesDesc");
+            }
+            expectedTypesDesc = TemplateLanguageUtils.getTypeName(expectedTypes[0]);
+        }
+        _ErrorDescriptionBuilder errorDescBuilder = new _ErrorDescriptionBuilder(
+                "Expected ", new _DelayedAOrAn(expectedTypesDesc), ", but ",
+                (
+                        blamedAssignmentTargetVarName != null
+                                ? new Object[] {
+                                "assignment target variable ",
+                                new _DelayedJQuote(blamedAssignmentTargetVarName) }
+                                : blamed != null
+                                        ? "this"
+                                        : "the expression"
+                ),
+                " has evaluated to ",
+                new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(model)),
+                (blamed != null ? ":" : ".")
+        )
+        .blame(blamed).showBlamer(true);
+        if (model instanceof _UnexpectedTypeErrorExplainerTemplateModel) {
+            Object[] tip = ((_UnexpectedTypeErrorExplainerTemplateModel) model).explainTypeError(expectedTypes);
+            if (tip != null) {
+                errorDescBuilder.tip(tip);
+            }
+        }
+        return errorDescBuilder;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonBooleanException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonBooleanException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonBooleanException.java
deleted file mode 100644
index 3844fd0..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonBooleanException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateBooleanModel} value was expected, but the value had a different type.
- */
-public class NonBooleanException extends UnexpectedTypeException {
-    
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateBooleanModel.class }; 
-
-    public NonBooleanException(Environment env) {
-        super(env, "Expecting boolean value here");
-    }
-
-    public NonBooleanException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonBooleanException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonBooleanException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "boolean", EXPECTED_TYPES, env);
-    }
-
-    NonBooleanException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "boolean", EXPECTED_TYPES, tip, env);
-    }
-
-    NonBooleanException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "boolean", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonDateException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonDateException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonDateException.java
deleted file mode 100644
index 2e63e48..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonDateException.java
+++ /dev/null
@@ -1,58 +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.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateDateModel} value was expected, but the value had a different type.
- */
-public class NonDateException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateDateModel.class };
-
-    public NonDateException(Environment env) {
-        super(env, "Expecting date/time value here");
-    }
-
-    public NonDateException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonDateException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "date/time", EXPECTED_TYPES, env);
-    }
-
-    NonDateException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "date/time", EXPECTED_TYPES, tip, env);
-    }
-
-    NonDateException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "date/time", EXPECTED_TYPES, tips, env);
-    }    
-        
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonDirectiveException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonDirectiveException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonDirectiveException.java
deleted file mode 100644
index 2053361..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonDirectiveException.java
+++ /dev/null
@@ -1,63 +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.model.TemplateDirectiveModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateDirectiveModel} was expected, but the value had a different type.
- */
-class NonDirectiveException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] {
-        TemplateDirectiveModel.class, ASTDirMacroOrFunction.class };
-    
-    public NonDirectiveException(Environment env) {
-        super(env, "Expecting user-defined directive, transform or macro value here");
-    }
-
-    public NonDirectiveException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonDirectiveException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonDirectiveException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "user-defined directive, transform or macro", EXPECTED_TYPES, env);
-    }
-
-    NonDirectiveException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "user-defined directive, transform or macro", EXPECTED_TYPES, tip, env);
-    }
-
-    NonDirectiveException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "user-defined directive, transform or macro", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java
deleted file mode 100644
index 5614b23..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateHashModelEx} value was expected, but the value had a different type.
- */
-public class NonExtendedHashException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateHashModelEx.class };
-    
-    public NonExtendedHashException(Environment env) {
-        super(env, "Expecting extended hash value here");
-    }
-
-    public NonExtendedHashException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonExtendedHashException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonExtendedHashException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "extended hash", EXPECTED_TYPES, env);
-    }
-
-    NonExtendedHashException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "extended hash", EXPECTED_TYPES, tip, env);
-    }
-
-    NonExtendedHashException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "extended hash", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java
deleted file mode 100644
index 4c69b05..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNodeModelEx;
-
-/**
- * Indicates that a {@link TemplateNodeModelEx} value was expected, but the value had a different type.
- */
-public class NonExtendedNodeException extends UnexpectedTypeException {
-
-    private static final Class<?>[] EXPECTED_TYPES = new Class[] { TemplateNodeModelEx.class };
-    
-    public NonExtendedNodeException(Environment env) {
-        super(env, "Expecting extended node value here");
-    }
-
-    public NonExtendedNodeException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonExtendedNodeException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonExtendedNodeException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "extended node", EXPECTED_TYPES, env);
-    }
-
-    NonExtendedNodeException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "extended node", EXPECTED_TYPES, tip, env);
-    }
-
-    NonExtendedNodeException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "extended node", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java
deleted file mode 100644
index 2ae7fdd..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonFunctionException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateFunctionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateFunctionModel} value was expected, but the value had a different type.
- */
-public class NonFunctionException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateFunctionModel.class };
-    
-    public NonFunctionException(Environment env) {
-        super(env, "Expecting method value here");
-    }
-
-    public NonFunctionException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonFunctionException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonFunctionException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "function", EXPECTED_TYPES, env);
-    }
-
-    NonFunctionException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "function", EXPECTED_TYPES, tip, env);
-    }
-
-    NonFunctionException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "function", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonHashException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonHashException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonHashException.java
deleted file mode 100644
index eb56312..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonHashException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateHashModel} value was expected, but the value had a different type.
- */
-public class NonHashException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateHashModel.class };
-    
-    public NonHashException(Environment env) {
-        super(env, "Expecting hash value here");
-    }
-
-    public NonHashException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonHashException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonHashException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "hash", EXPECTED_TYPES, env);
-    }
-
-    NonHashException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "hash", EXPECTED_TYPES, tip, env);
-    }
-
-    NonHashException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "hash", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java
deleted file mode 100644
index cdc0728..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Indicates that a {@link TemplateMarkupOutputModel} value was expected, but the value had a different type.
- */
-public class NonMarkupOutputException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateMarkupOutputModel.class };
-    
-    public NonMarkupOutputException(Environment env) {
-        super(env, "Expecting markup output value here");
-    }
-
-    public NonMarkupOutputException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonMarkupOutputException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonMarkupOutputException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "markup output", EXPECTED_TYPES, env);
-    }
-
-    NonMarkupOutputException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "markup output", EXPECTED_TYPES, tip, env);
-    }
-
-    NonMarkupOutputException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "markup output", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonNamespaceException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonNamespaceException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonNamespaceException.java
deleted file mode 100644
index 1433e02..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonNamespaceException.java
+++ /dev/null
@@ -1,61 +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.model.TemplateModel;
-
-/**
- * Indicates that a {@link Environment.Namespace} value was expected, but the value had a different type.
- */
-class NonNamespaceException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { Environment.Namespace.class };
-    
-    public NonNamespaceException(Environment env) {
-        super(env, "Expecting namespace value here");
-    }
-
-    public NonNamespaceException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonNamespaceException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonNamespaceException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "namespace", EXPECTED_TYPES, env);
-    }
-
-    NonNamespaceException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "namespace", EXPECTED_TYPES, tip, env);
-    }
-
-    NonNamespaceException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "namespace", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonNodeException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonNodeException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonNodeException.java
deleted file mode 100644
index f6e693f..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonNodeException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNodeModel;
-
-/**
- * Indicates that a {@link TemplateNodeModel} value was expected, but the value had a different type.
- */
-public class NonNodeException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateNodeModel.class };
-    
-    public NonNodeException(Environment env) {
-        super(env, "Expecting node value here");
-    }
-
-    public NonNodeException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonNodeException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonNodeException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "node", EXPECTED_TYPES, env);
-    }
-
-    NonNodeException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "node", EXPECTED_TYPES, tip, env);
-    }
-
-    NonNodeException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "node", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonNumericalException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonNumericalException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonNumericalException.java
deleted file mode 100644
index e943bad..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonNumericalException.java
+++ /dev/null
@@ -1,84 +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.model.TemplateCallableModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-
-/**
- * Indicates that a {@link TemplateNumberModel} value was expected, but the value had a different type.
- */
-public class NonNumericalException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateNumberModel.class };
-
-    public NonNumericalException(Environment env) {
-        super(env, "Expecting numerical value here");
-    }
-
-    public NonNumericalException(String description, Environment env) {
-        super(env, description);
-    }
- 
-    NonNumericalException(_ErrorDescriptionBuilder description, Environment env) {
-        super(env, description);
-    }
-
-    NonNumericalException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "number", EXPECTED_TYPES, env);
-    }
-
-    NonNumericalException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "number", EXPECTED_TYPES, tip, env);
-    }
-
-    NonNumericalException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "number", EXPECTED_TYPES, tips, env);
-    }
-
-    NonNumericalException(
-            String assignmentTargetVarName, TemplateModel model, String[] tips, Environment env)
-            throws InvalidReferenceException {
-        super(assignmentTargetVarName, model, "number", EXPECTED_TYPES, tips, env);
-    }
-
-
-    NonNumericalException(
-            TemplateCallableModel callableModel, int argArrayIndex,
-            TemplateModel model, String[] tips, Environment env)
-            throws InvalidReferenceException {
-        super(callableModel, argArrayIndex, model, "number", EXPECTED_TYPES, tips, env);
-    }
-
-    static NonNumericalException newMalformedNumberException(ASTExpression blamed, String text, Environment env) {
-        return new NonNumericalException(
-                new _ErrorDescriptionBuilder("Can't convert this string to number: ", new _DelayedJQuote(text))
-                .blame(blamed),
-                env);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceException.java
deleted file mode 100644
index 738bb35..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceException.java
+++ /dev/null
@@ -1,62 +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.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-
-/**
- * Indicates that a {@link TemplateSequenceModel} value was expected, but the value had a different type.
- */
-public class NonSequenceException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] { TemplateSequenceModel.class };
-    
-    public NonSequenceException(Environment env) {
-        super(env, "Expecting sequence value here");
-    }
-
-    public NonSequenceException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonSequenceException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonSequenceException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "sequence", EXPECTED_TYPES, env);
-    }
-
-    NonSequenceException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, "sequence", EXPECTED_TYPES, tip, env);
-    }
-
-    NonSequenceException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "sequence", EXPECTED_TYPES, tips, env);
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java
deleted file mode 100644
index c229808..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonSequenceOrCollectionException.java
+++ /dev/null
@@ -1,90 +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.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.util._CollectionUtils;
-
-/**
- * Indicates that a {@link TemplateSequenceModel} or {@link TemplateCollectionModel} value was expected, but the value
- * had a different type.
- */
-public class NonSequenceOrCollectionException extends UnexpectedTypeException {
-
-    private static final Class[] EXPECTED_TYPES = new Class[] {
-        TemplateSequenceModel.class, TemplateCollectionModel.class
-    };
-    private static final String ITERABLE_SUPPORT_HINT = "The problematic value is a java.lang.Iterable. Using "
-            + "DefaultObjectWrapper(..., iterableSupport=true) as the objectWrapper setting of the FreeMarker "
-            + "configuration should solve this.";
-    
-    public NonSequenceOrCollectionException(Environment env) {
-        super(env, "Expecting sequence or collection value here");
-    }
-
-    public NonSequenceOrCollectionException(String description, Environment env) {
-        super(env, description);
-    }
-
-    NonSequenceOrCollectionException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonSequenceOrCollectionException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        this(blamed, model, _CollectionUtils.EMPTY_OBJECT_ARRAY, env);
-    }
-
-    NonSequenceOrCollectionException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        this(blamed, model, new Object[] { tip }, env);
-    }
-
-    NonSequenceOrCollectionException(
-            ASTExpression blamed, TemplateModel model, Object[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, "sequence or collection", EXPECTED_TYPES, extendTipsIfIterable(model, tips), env);
-    }
-    
-    private static Object[] extendTipsIfIterable(TemplateModel model, Object[] tips) {
-        if (isWrappedIterable(model)) {
-            final int tipsLen = tips != null ? tips.length : 0;
-            Object[] extendedTips = new Object[tipsLen + 1];
-            for (int i = 0; i < tipsLen; i++) {
-                extendedTips[i] = tips[i];
-            }
-            extendedTips[tipsLen] = ITERABLE_SUPPORT_HINT;
-            return extendedTips;
-        } else {
-            return tips;
-        }
-    }
-
-    public static boolean isWrappedIterable(TemplateModel model) {
-        return model instanceof WrapperTemplateModel
-                && ((WrapperTemplateModel) model).getWrappedObject() instanceof Iterable;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
deleted file mode 100644
index ec02f73..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringException.java
+++ /dev/null
@@ -1,84 +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.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCallableModel;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-
-/**
- * Indicates that a {@link TemplateScalarModel} value was expected (or maybe something that can be automatically coerced
- * to that), but the value had a different type.
- */
-public class NonStringException extends UnexpectedTypeException {
-
-    static final String STRING_COERCABLE_TYPES_DESC
-            = "string or something automatically convertible to string (number, date or boolean)";
-    
-    static final Class[] STRING_COERCABLE_TYPES = new Class[] {
-        TemplateScalarModel.class, TemplateNumberModel.class, TemplateDateModel.class, TemplateBooleanModel.class
-    };
-
-    private static final Class<?>[] EXPECTED_TYPES = { TemplateScalarModel.class };
-
-    private static final String DEFAULT_DESCRIPTION
-            = "Expecting " + NonStringException.STRING_COERCABLE_TYPES_DESC + " value here";
-
-    public NonStringException(Environment env) {
-        super(env, DEFAULT_DESCRIPTION);
-    }
-
-    public NonStringException(String description, Environment env) {
-        super(env, description);
-    }
- 
-    NonStringException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonStringException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, env);
-    }
-
-    NonStringException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, tip, env);
-    }
-
-    NonStringException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, NonStringException.STRING_COERCABLE_TYPES_DESC, STRING_COERCABLE_TYPES, tips, env);
-    }
-
-    NonStringException(
-            TemplateCallableModel callableModel, int argArrayIndex,
-            TemplateModel model, String[] tips, Environment env)
-            throws InvalidReferenceException {
-        super(callableModel, argArrayIndex, model, "string", EXPECTED_TYPES, tips, env);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java
deleted file mode 100644
index ddeb811..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/NonStringOrTemplateOutputException.java
+++ /dev/null
@@ -1,78 +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.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-
-/**
- * Indicates that a {@link TemplateScalarModel} (or maybe something that can be automatically coerced
- * to that) or {@link TemplateMarkupOutputModel} value was expected, but the value had a different type.
- */
-public class NonStringOrTemplateOutputException extends UnexpectedTypeException {
-
-    static final String STRING_COERCABLE_TYPES_OR_TOM_DESC
-            = NonStringException.STRING_COERCABLE_TYPES_DESC + ", or \"template output\"";
-    
-    static final Class[] STRING_COERCABLE_TYPES_AND_TOM;
-    static {
-        STRING_COERCABLE_TYPES_AND_TOM = new Class[NonStringException.STRING_COERCABLE_TYPES.length + 1];
-        int i;
-        for (i = 0; i < NonStringException.STRING_COERCABLE_TYPES.length; i++) {
-            STRING_COERCABLE_TYPES_AND_TOM[i] = NonStringException.STRING_COERCABLE_TYPES[i];
-        }
-        STRING_COERCABLE_TYPES_AND_TOM[i] = TemplateMarkupOutputModel.class;
-    }
-
-    private static final String DEFAULT_DESCRIPTION
-            = "Expecting " + NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC + " value here";
-
-    public NonStringOrTemplateOutputException(Environment env) {
-        super(env, DEFAULT_DESCRIPTION);
-    }
-
-    public NonStringOrTemplateOutputException(String description, Environment env) {
-        super(env, description);
-    }
- 
-    NonStringOrTemplateOutputException(Environment env, _ErrorDescriptionBuilder description) {
-        super(env, description);
-    }
-
-    NonStringOrTemplateOutputException(
-            ASTExpression blamed, TemplateModel model, Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC, STRING_COERCABLE_TYPES_AND_TOM, env);
-    }
-
-    NonStringOrTemplateOutputException(
-            ASTExpression blamed, TemplateModel model, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(blamed, model, NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC, STRING_COERCABLE_TYPES_AND_TOM, tip, env);
-    }
-
-    NonStringOrTemplateOutputException(
-            ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException {
-        super(blamed, model, NonStringOrTemplateOutputException.STRING_COERCABLE_TYPES_OR_TOM_DESC, STRING_COERCABLE_TYPES_AND_TOM, tips, env);
-    }
-        
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateException.java
index 06cb20f..bcbb645 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateException.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateException.java
@@ -39,6 +39,7 @@ public class TemplateException extends Exception {
             = "FTL stack trace (\"~\" means nesting-related):";
 
     // Set in constructor:
+    // TODO [FM3] These all must be final, or else tha class is not thread safe
     private transient _ErrorDescriptionBuilder descriptionBuilder;
     private final transient Environment env;
     private final transient ASTExpression blamedExpression;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java
deleted file mode 100644
index 10401fd..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java
+++ /dev/null
@@ -1,171 +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.model.TemplateCallableModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * The type of a value differs from what was expected.
- */
-public class UnexpectedTypeException extends TemplateException {
-    
-    public UnexpectedTypeException(Environment env, String description) {
-        super(description, env);
-    }
-
-    UnexpectedTypeException(Environment env, _ErrorDescriptionBuilder description) {
-        super(null, env, null, description);
-    }
-
-    UnexpectedTypeException(
-            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Environment env)
-            throws InvalidReferenceException {
-        super(null, env, blamed, newDescriptionBuilder(
-                blamed,
-                null,
-                null, -1,
-                model, expectedTypesDesc, expectedTypes,
-                env));
-    }
-
-    UnexpectedTypeException(
-            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, String tip,
-            Environment env)
-            throws InvalidReferenceException {
-        super(null, env, blamed, newDescriptionBuilder(
-                blamed,
-                null,
-                null, -1,
-                model, expectedTypesDesc, expectedTypes,
-                env)
-                .tip(tip));
-    }
-
-    UnexpectedTypeException(
-            ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Object[] tips,
-            Environment env)
-            throws InvalidReferenceException {
-        super(null, env, blamed, newDescriptionBuilder(
-                blamed,
-                null,
-                null, -1,
-                model, expectedTypesDesc, expectedTypes, env)
-                .tips(tips));
-    }
-
-     /**
-      * Used for assignments that use {@code +=} and such.
-      */
-    UnexpectedTypeException(
-            String blamedAssignmentTargetVarName, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes,
-            Object[] tips,
-            Environment env)
-            throws InvalidReferenceException {
-        super(null, env, null, newDescriptionBuilder(
-                null,
-                blamedAssignmentTargetVarName,
-                null, -1,
-                model, expectedTypesDesc, expectedTypes, env).tips(tips));
-    }
-
-    /**
-     * Used when the value of a directive/function argument has a different type than that the directive/function
-     * expects.
-     */
-    UnexpectedTypeException(
-            TemplateCallableModel callableModel, int argArrayIndex,
-            TemplateModel model, String expectedTypesDesc, Class[] expectedTypes,
-            Object[] tips,
-            Environment env)
-            throws InvalidReferenceException {
-        super(null, env, null, newDescriptionBuilder(
-                null,
-                null,
-                callableModel, argArrayIndex,
-                model,
-                expectedTypesDesc, expectedTypes, env).tips(tips));
-    }
-
-    private static _ErrorDescriptionBuilder newDescriptionBuilder(
-            ASTExpression blamed, String blamedAssignmentTargetVarName,
-            TemplateCallableModel callableModel, int argArrayIndex,
-            TemplateModel model, String expectedTypesDesc, Class<? extends TemplateModel>[] expectedTypes, Environment env)
-            throws InvalidReferenceException {
-        if (model == null) {
-            throw InvalidReferenceException.getInstance(blamed, env);
-        }
-
-        _ErrorDescriptionBuilder errorDescBuilder = new _ErrorDescriptionBuilder(
-                callableModel == null
-                        ? unexpectedTypeErrorDescription(
-                            expectedTypesDesc,
-                            blamed,
-                            blamedAssignmentTargetVarName,
-                            model)
-                        : unexpectedTypeErrorDescription(
-                                expectedTypesDesc,
-                                blamed,
-                                callableModel, argArrayIndex,
-                                model))
-                .blame(blamed).showBlamer(true);
-        if (model instanceof _UnexpectedTypeErrorExplainerTemplateModel) {
-            Object[] tip = ((_UnexpectedTypeErrorExplainerTemplateModel) model).explainTypeError(expectedTypes);
-            if (tip != null) {
-                errorDescBuilder.tip(tip);
-            }
-        }
-        return errorDescBuilder;
-    }
-
-    private static Object[] unexpectedTypeErrorDescription(
-            String expectedTypesDesc,
-            ASTExpression blamed,
-            String blamedAssignmentTargetVarName,
-            TemplateModel model) {
-        return new Object[] {
-                "Expected ", new _DelayedAOrAn(expectedTypesDesc), ", but ", (
-                        blamedAssignmentTargetVarName != null
-                                ? new Object[] {
-                                        "assignment target variable ",
-                                        new _DelayedJQuote(blamedAssignmentTargetVarName) }
-                        : blamed != null
-                                ? "this"
-                        : "the expression"
-                ),
-                " has evaluated to ",
-                new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(model)),
-                (blamed != null ? ":" : ".")};
-    }
-
-    private static Object[] unexpectedTypeErrorDescription(
-            String expectedTypesDesc,
-            ASTExpression blamed,
-            TemplateCallableModel callableModel, int argArrayIndex,
-            TemplateModel actualValue) {
-        // TODO
-        return new Object[]{
-                blamed, " expects ", new _DelayedAOrAn(expectedTypesDesc), " as its ", argArrayIndex, " arg, but it "
-                + "was " + new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(actualValue)),
-                (blamed != null ? ":" : ".")
-        };
-    }
-
-}


[3/3] incubator-freemarker git commit: FREEMARKER-65: Utilities for argument validation: Work in progress. Changes in this commit:

Posted by dd...@apache.org.
FREEMARKER-65: Utilities for argument validation: Work in progress. Changes in this commit:

- Added org.apache.freemarker.core.util.CallableUtils (published API), to help in implementing (and invoking) TemplateCallableModel-s.
- Removed UnexpectedTypeException (a `TemplateException subclass) and its subclasses (NonStringException, etc.),
  using simple TemplateException instead. Catching these exceptions specifically wasn't very useful, while they
  have bloated the public API (and the code).


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/f231e64f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/f231e64f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/f231e64f

Branch: refs/heads/3
Commit: f231e64fe8e4e9a2786966506aeef79d0dd36fcd
Parents: e941aed
Author: ddekany <dd...@apache.org>
Authored: Wed Aug 16 17:21:41 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Thu Aug 17 01:24:25 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |   5 +-
 ...a8DefaultObjectWrapperBridgeMethodsTest.java |   6 +-
 .../impl/Java8DefaultObjectWrapperTest.java     |  10 +-
 .../freemarker/core/OutputFormatTest.java       |   2 +-
 .../core/TemplateCallableModelTest.java         |   1 +
 .../impl/AbstractParallelIntrospectionTest.java |   4 +-
 .../model/impl/DefaultObjectWrapperTest.java    |  10 +-
 .../core/model/impl/EnumModelsTest.java         |   4 +-
 .../core/model/impl/StaticModelsTest.java       |   4 +-
 .../core/templatesuite/models/MultiModel2.java  |   4 +-
 .../core/userpkg/AllFeaturesDirective.java      |   2 +-
 .../core/userpkg/AllFeaturesFunction.java       |   2 +-
 .../templates/boolean-formatting.ftl            |   2 +-
 .../freemarker/core/ASTDirAssignment.java       |   9 +-
 .../apache/freemarker/core/ASTDirInclude.java   |  14 +-
 .../org/apache/freemarker/core/ASTDirList.java  |  23 +-
 .../apache/freemarker/core/ASTDirRecurse.java   |   6 +-
 .../org/apache/freemarker/core/ASTDirVisit.java |   5 +-
 .../freemarker/core/ASTDynamicTopLevelCall.java |   5 +-
 .../freemarker/core/ASTExpAddOrConcat.java      |  78 ++-
 .../org/apache/freemarker/core/ASTExpDot.java   |   2 +-
 .../freemarker/core/ASTExpDynamicKeyName.java   |  69 +--
 .../freemarker/core/ASTExpFunctionCall.java     |   3 +-
 .../freemarker/core/ASTExpHashLiteral.java      |   6 +-
 .../freemarker/core/ASTExpNegateOrPlus.java     |   2 +-
 .../org/apache/freemarker/core/ASTExpRange.java |   6 -
 .../apache/freemarker/core/ASTExpression.java   |   4 +-
 .../apache/freemarker/core/BuiltInForDate.java  |  17 +-
 .../freemarker/core/BuiltInForHashEx.java       |   2 +-
 .../core/BuiltInForLegacyEscaping.java          |   3 +-
 .../freemarker/core/BuiltInForMarkupOutput.java |   2 +-
 .../apache/freemarker/core/BuiltInForNode.java  |   8 +-
 .../freemarker/core/BuiltInForNodeEx.java       |   2 +-
 .../freemarker/core/BuiltInForSequence.java     |   3 +-
 .../freemarker/core/BuiltInsForDates.java       |   4 +-
 .../core/BuiltInsForExistenceHandling.java      |   3 +-
 .../core/BuiltInsForMultipleTypes.java          |  30 +-
 .../BuiltInsForNestedContentParameters.java     |   5 +-
 .../freemarker/core/BuiltInsForNodes.java       |   5 +-
 .../freemarker/core/BuiltInsForSequences.java   |  36 +-
 .../core/BuiltInsForStringsBasic.java           |  14 +-
 .../core/BuiltInsForStringsEncoding.java        |   2 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |  11 +-
 .../core/BuiltInsForStringsRegexp.java          |   6 +-
 .../org/apache/freemarker/core/Environment.java |  36 +-
 .../apache/freemarker/core/MessageUtils.java    | 117 ++++-
 .../freemarker/core/NonBooleanException.java    |  62 ---
 .../freemarker/core/NonDateException.java       |  58 ---
 .../freemarker/core/NonDirectiveException.java  |  63 ---
 .../core/NonExtendedHashException.java          |  62 ---
 .../core/NonExtendedNodeException.java          |  62 ---
 .../freemarker/core/NonFunctionException.java   |  62 ---
 .../freemarker/core/NonHashException.java       |  62 ---
 .../core/NonMarkupOutputException.java          |  62 ---
 .../freemarker/core/NonNamespaceException.java  |  61 ---
 .../freemarker/core/NonNodeException.java       |  62 ---
 .../freemarker/core/NonNumericalException.java  |  84 ----
 .../freemarker/core/NonSequenceException.java   |  62 ---
 .../core/NonSequenceOrCollectionException.java  |  90 ----
 .../freemarker/core/NonStringException.java     |  84 ----
 .../NonStringOrTemplateOutputException.java     |  78 ---
 .../freemarker/core/TemplateException.java      |   1 +
 .../core/UnexpectedTypeException.java           | 171 -------
 .../apache/freemarker/core/_CallableUtils.java  | 490 +-----------------
 .../org/apache/freemarker/core/_CoreAPI.java    |   4 +
 .../org/apache/freemarker/core/_EvalUtils.java  |  42 +-
 .../core/model/GeneralPurposeNothing.java       |  10 +-
 .../model/impl/OverloadedFixArgsMethods.java    |   4 +-
 .../model/impl/OverloadedVarArgsMethods.java    |   4 +-
 .../core/model/impl/ResourceBundleModel.java    |   4 +-
 .../core/model/impl/SimpleMethod.java           |   4 +-
 .../freemarker/core/util/CallableUtils.java     | 496 +++++++++++++++++++
 .../core/util/TemplateLanguageUtils.java        |   6 +
 freemarker-core/src/main/javacc/FTL.jj          |   4 +-
 74 files changed, 927 insertions(+), 1921 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 0d6a14b..0c69572 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -471,7 +471,10 @@ Core / Miscellaneous
 - CallPlaceCustomDataInitializationException is not a checked exception anymore (now it extends RuntimeException)
 - Renamed `FTLUtil` to `TemplateLanguageUtils` (as the FTL name will be abandoned in FM3, and supporting multiple
   languages are planned in the future)
-
+- Removed `UnexpectedTypeException` (a `TemplateException` subclass) and its subclasses (`NonStringException`, etc.),
+  using simple `TemplateException` instead. Catching these exceptions specifically wasn't very useful, while they
+  have bloated the public API (and the code).
+- Added `org.apache.freemarker.core.util.CallableUtils`, to help in implementing (and invoking) `TemplateCallableModel`-s.
 
 Build / development process changes
 -----------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
index 62bb7af..9c87b53 100644
--- a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
+++ b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperBridgeMethodsTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.*;
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.junit.Test;
 
@@ -56,10 +56,10 @@ public class Java8DefaultObjectWrapperBridgeMethodsTest {
         JavaMethodModel m1 = (JavaMethodModel) wrapped.get("m1");
         assertEquals(
                 BridgeMethodsBean.M1_RETURN_VALUE,
-                "" + m1.execute(_CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE));
+                "" + m1.execute(CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE));
 
         JavaMethodModel m2 = (JavaMethodModel) wrapped.get("m2");
-        assertNull(m2.execute(_CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE));
+        assertNull(m2.execute(CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE));
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
index cf628ad..fcd8f23 100644
--- a/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
+++ b/freemarker-core-test-java8/src/test/java/org/apache/freemarker/core/model/impl/Java8DefaultObjectWrapperTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.*;
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateNumberModel;
@@ -129,7 +129,7 @@ public class Java8DefaultObjectWrapperTest {
             assertEquals(
                     Java8DefaultMethodsBean.NORMAL_ACTION_RETURN_VALUE,
                     ((TemplateScalarModel) action.execute(
-                            _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
                             .getAsString());
         }
         
@@ -140,7 +140,7 @@ public class Java8DefaultObjectWrapperTest {
             assertEquals(
                     Java8DefaultMethodsBean.NORMAL_ACTION_RETURN_VALUE,
                     ((TemplateScalarModel) action.execute(
-                            _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
                             .getAsString());
         }
         {
@@ -150,7 +150,7 @@ public class Java8DefaultObjectWrapperTest {
             assertEquals(
                     Java8DefaultMethodsBean.DEFAULT_METHOD_ACTION_RETURN_VALUE,
                     ((TemplateScalarModel) action.execute(
-                            _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
                             .getAsString());
         }
         {
@@ -160,7 +160,7 @@ public class Java8DefaultObjectWrapperTest {
             assertEquals(
                     Java8DefaultMethodsBean.OVERRIDDEN_DEFAULT_METHOD_ACTION_RETURN_VALUE,
                     ((TemplateScalarModel) action.execute(
-                            _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                            CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
                             .getAsString());
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
index 2bda427..51a0070 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
@@ -598,7 +598,7 @@ public class OutputFormatTest extends TemplateTest {
                 "noSuchVar", "null or missing");
         assertErrorContains(
                 "${'x'?markupString}",
-                "xpected", "markup output", "string");
+                "xpected", "markupOutput", "string");
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
index cbe1c5c..ec7b225 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
@@ -249,6 +249,7 @@ public class TemplateCallableModelTest extends TemplateTest {
         assertErrorContains("${'x'?leftPad(null)}", "?leftPad", "1st", "null");
         addTemplate("foo.ftl", "<#macro m n1></#macro>");
         assertErrorContains("<#import 'foo.ftl' as f><@f.m/>", "macro", "\"foo.ftl:m\"");
+        assertErrorContains("<#function myF(p1)></#function><#assign f2 = myF>${f2()}", "\"myF\"");
         addToDataModel("bean", new TestBean());
         assertErrorContains("${bean.intMP()}", "method", "org.apache.freemarker.test.TemplateTest$TestBean.intMP");
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
index cbccd66..49d4009 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java
@@ -22,7 +22,7 @@ package org.apache.freemarker.core.model.impl;
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateNumberModel;
@@ -117,7 +117,7 @@ public abstract class AbstractParallelIntrospectionTest extends TestCase {
             JavaMethodModel pv = (JavaMethodModel) h.get("m" + mIdx);
             final int expected = objIdx * 1000 + mIdx;
             final int got = ((TemplateNumberModel) pv.execute(
-                    _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                    CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
                     .getAsNumber().intValue();
             if (got != expected) {
                 throw new AssertionError("Method assertion failed; " +

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index a1dfba6..0f9722e 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -45,7 +45,7 @@ import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core._CoreAPI;
 import org.apache.freemarker.core.model.AdapterTemplateModel;
 import org.apache.freemarker.core.model.ObjectWrapper;
@@ -193,7 +193,7 @@ public class DefaultObjectWrapperTest {
         {
             // Check method calls, and also if the return value is wrapped with the overidden "wrap".
             final TemplateModel mr = ((JavaMethodModel) bean.get("m")).execute(
-                    _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE);
+                    CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE);
             assertEquals(Collections.singletonList(1), ow.unwrap(mr));
             assertTrue(DefaultListAdapter.class.isInstance(mr));
         }
@@ -586,7 +586,7 @@ public class DefaultObjectWrapperTest {
 
         TemplateHashModel api = (TemplateHashModel) iteratorAdapter.getAPI();
         assertFalse(((TemplateBooleanModel) ((JavaMethodModel)
-                api.get("hasNext")).execute(_CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
+                api.get("hasNext")).execute(CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE))
                 .getAsBoolean());
     }
 
@@ -677,7 +677,7 @@ public class DefaultObjectWrapperTest {
 
         TemplateHashModel api = (TemplateHashModel) enumAdapter.getAPI();
         assertFalse(((TemplateBooleanModel) ((JavaMethodModel) api.get("hasMoreElements"))
-                .execute(_CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsBoolean());
+                .execute(CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsBoolean());
     }
 
     @Test
@@ -734,7 +734,7 @@ public class DefaultObjectWrapperTest {
         TemplateHashModel apiModel = (TemplateHashModel) ((TemplateModelWithAPISupport) normalModel).getAPI();
         JavaMethodModel sizeMethod = (JavaMethodModel) apiModel.get("size");
         TemplateNumberModel r = (TemplateNumberModel) sizeMethod.execute(
-                _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE);
+                CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE);
         assertEquals(expectedSize, r.getAsNumber().intValue());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
index 7dc7879..89b6ec5 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.*;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.NonTemplateCallPlace;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -59,7 +59,7 @@ public class EnumModelsTest {
         assertEquals(((TemplateScalarModel) a).getAsString(), "ts:A");
         JavaMethodModel nameMethod = (JavaMethodModel) ((TemplateHashModel) a).get("name");
         assertEquals(((TemplateScalarModel) nameMethod.execute(
-                _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsString(),
+                CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsString(),
                 "A");
         
         assertSame(e, enums.get(E.class.getName()));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
index 2487105..241b349 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.*;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.NonTemplateCallPlace;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
@@ -66,7 +66,7 @@ public class StaticModelsTest {
         TemplateModel m = s.get("m");
         assertTrue(m instanceof JavaMethodModel);
         assertEquals(((TemplateScalarModel) ((JavaMethodModel) m).execute(
-                _CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsString(),
+                CallableUtils.EMPTY_TEMPLATE_MODEL_ARRAY, NonTemplateCallPlace.INSTANCE)).getAsString(),
                 "m OK");
         
         assertSame(s, statics.get(S.class.getName()));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
index e56c0a2..96e4a7a 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/models/MultiModel2.java
@@ -22,7 +22,7 @@ package org.apache.freemarker.core.templatesuite.models;
 import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core._CallableUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateModel;
@@ -43,7 +43,7 @@ public class MultiModel2 implements TemplateScalarModel, TemplateFunctionModel {
     public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
         StringBuilder  aResults = new StringBuilder( "Arguments are:<br />" );
         for (int i = 0; i < args.length; i++) {
-            aResults.append(_CallableUtils.getStringArgument(args, i, this));
+            aResults.append(CallableUtils.getStringArgument(args, i, this));
             aResults.append("<br />");
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
index 3931a67..9302345 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesDirective.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core.userpkg;
 
-import static org.apache.freemarker.core._CallableUtils.*;
+import static org.apache.freemarker.core.util.CallableUtils.*;
 
 import java.io.IOException;
 import java.io.Writer;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
index 261c369..e6ad111 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/AllFeaturesFunction.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core.userpkg;
 
-import static org.apache.freemarker.core._CallableUtils.*;
+import static org.apache.freemarker.core.util.CallableUtils.*;
 
 import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.ftl
index 72dc253..95bee38 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.ftl
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/boolean-formatting.ftl
@@ -47,7 +47,7 @@
 <#setting booleanFormat = 'y,n'>
 <#assign x = false>
 <#assign n = 123><#assign m = { x: 'foo', n: 'bar' }><@assertEquals actual=m['n'] + m['123'] expected='foobar' />
-<@assertFails exception="UnexpectedTypeException">${m[false]}</@>
+<@assertFails message="boolean">${m[false]}</@>
 <@assertFails message="can't compare">${x == 'false'}</@>
 <@assertFails message="can't compare">${x != 'false'}</@>
 <@assertFails message="can't convert">${booleanVsStringMethods.expectsString(x)}</@>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
index 09bf4ea..7efa95c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
@@ -123,7 +123,11 @@ final class ASTDirAssignment extends ASTDirective {
             try {
                 namespace = (Environment.Namespace) namespaceTM;
             } catch (ClassCastException e) {
-                throw new NonNamespaceException(namespaceExp, namespaceTM, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        namespaceExp, namespaceTM,
+                        "namespace",
+                        new Class[] { Environment.Namespace.class },
+                        null, env);
             }
             if (namespace == null) {
                 throw InvalidReferenceException.getInstance(namespaceExp, env);
@@ -158,7 +162,8 @@ final class ASTDirAssignment extends ASTDirective {
                 } else if (lhoValue == null) {
                     throw InvalidReferenceException.getInstance(variableName, getOperatorTypeAsString(), env);
                 } else {
-                    throw new NonNumericalException(variableName, lhoValue, null, env);
+                    throw MessageUtils.newUnexpectedAssignmentTargetTypeException(
+                            variableName, lhoValue, TemplateNumberModel.class, env);
                 }
 
                 if (operatorType == OPERATOR_TYPE_PLUS_PLUS) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
index 37cc017..76ce1f6 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
@@ -22,7 +22,6 @@ package org.apache.freemarker.core;
 import java.io.IOException;
 
 import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException;
-import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util._StringUtils;
 
 /**
@@ -45,16 +44,11 @@ final class ASTDirInclude extends ASTDirective {
         this.ignoreMissingExp = ignoreMissingExp;
         if (ignoreMissingExp != null && ignoreMissingExp.isLiteral()) {
             try {
-                try {
-                    ignoreMissingExpPrecalcedValue = Boolean.valueOf(
-                            ignoreMissingExp.evalToBoolean(template.getConfiguration()));
-                } catch (NonBooleanException e) {
-                    throw new ParseException("Expected a boolean as the value of the \"ignore_missing\" attribute",
-                            ignoreMissingExp, e);
-                }
+                ignoreMissingExpPrecalcedValue = Boolean.valueOf(
+                        ignoreMissingExp.evalToBoolean(template.getConfiguration()));
             } catch (TemplateException e) {
-                // evaluation of literals must not throw a TemplateException
-                throw new BugException(e);
+                throw new ParseException("Expected a boolean as the value of the \"ignore_missing\" attribute",
+                        ignoreMissingExp, e);
             }
         } else {
             ignoreMissingExpPrecalcedValue = null;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
index 7878e97..2182a5c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
@@ -304,17 +304,19 @@ final class ASTDirList extends ASTDirective {
                         env.visit(childBuffer);
                     }
                 }
-            } else if (listedValue instanceof TemplateHashModelEx
-                    && !NonSequenceOrCollectionException.isWrappedIterable(listedValue)) {
-                throw new NonSequenceOrCollectionException(env,
+            } else if (listedValue instanceof TemplateHashModelEx) {
+                throw new TemplateException(env,
                         new _ErrorDescriptionBuilder("The value you try to list is ",
                                 new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(listedValue)),
                                 ", thus you must declare two nested content parameters after the \"as\"; one for the "
                                 + "key, and another for the value, like ", "<#... as k, v>", ")."
                                 ));
             } else {
-                throw new NonSequenceOrCollectionException(
-                        listedExp, listedValue, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        listedExp, listedValue,
+                        MessageUtils.SEQUENCE_OR_COLLECTION,
+                        MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION,
+                        null, env);
             }
             return listNotEmpty;
         }
@@ -359,13 +361,14 @@ final class ASTDirList extends ASTDirective {
                                 do {
                                     nestedContentParam = keysIter.next();
                                     if (!(nestedContentParam instanceof TemplateScalarModel)) {
-                                        throw new NonStringException(env,
+                                        throw new TemplateException(env,
                                                 new _ErrorDescriptionBuilder(
                                                         "When listing key-value pairs of traditional hash "
                                                         + "implementations, all keys must be strings, but one of them "
                                                         + "was ",
                                                         new _DelayedAOrAn(
-                                                                new _DelayedTemplateLanguageTypeDescription(nestedContentParam)),
+                                                                new _DelayedTemplateLanguageTypeDescription(
+                                                                        nestedContentParam)),
                                                         "."
                                                         ).tip("The listed value's TemplateModel class was ",
                                                                 new _DelayedShortClassName(listedValue.getClass()),
@@ -389,15 +392,15 @@ final class ASTDirList extends ASTDirective {
                 }
             } else if (listedValue instanceof TemplateCollectionModel
                     || listedValue instanceof TemplateSequenceModel) {
-                throw new NonSequenceOrCollectionException(env,
+                throw new TemplateException(env,
                         new _ErrorDescriptionBuilder("The value you try to list is ",
                                 new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(listedValue)),
                                 ", thus you must declare only one nested content parameter after the \"as\" (there's "
                                 + "no separate key and value)."
                                 ));
             } else {
-                throw new NonExtendedHashException(
-                        listedExp, listedValue, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        listedExp, listedValue, TemplateHashModelEx.class, env);
             }
             return hashNotEmpty;
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
index 17842ec..7fe528b 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
@@ -44,7 +44,8 @@ final class ASTDirRecurse extends ASTDirective {
     ASTElement[] accept(Environment env) throws IOException, TemplateException {
         TemplateModel node = targetNode == null ? null : targetNode.eval(env);
         if (node != null && !(node instanceof TemplateNodeModel)) {
-            throw new NonNodeException(targetNode, node, "node", env);
+            throw MessageUtils.newUnexpectedOperandTypeException(
+                    targetNode, node, TemplateNodeModel.class, env);
         }
         
         TemplateModel nss = namespaces == null ? null : namespaces.eval(env);
@@ -60,7 +61,8 @@ final class ASTDirRecurse extends ASTDirective {
                 nss = ss;
             } else if (!(nss instanceof TemplateSequenceModel)) {
                 if (namespaces != null) {
-                    throw new NonSequenceException(namespaces, nss, env);
+                    throw MessageUtils.newUnexpectedOperandTypeException(
+                            namespaces, nss, TemplateSequenceModel.class, env);
                 } else {
                     // Should not occur
                     throw new TemplateException(env, "Expecting a sequence of namespaces after \"using\"");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
index fd49371..b7438a0 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirVisit.java
@@ -43,7 +43,7 @@ final class ASTDirVisit extends ASTDirective {
     ASTElement[] accept(Environment env) throws IOException, TemplateException {
         TemplateModel node = targetNode.eval(env);
         if (!(node instanceof TemplateNodeModel)) {
-            throw new NonNodeException(targetNode, node, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(targetNode, node, TemplateNodeModel.class, env);
         }
         
         TemplateModel nss = namespaces == null ? null : namespaces.eval(env);
@@ -59,7 +59,8 @@ final class ASTDirVisit extends ASTDirective {
                 nss = ss;
             } else if (!(nss instanceof TemplateSequenceModel)) {
                 if (namespaces != null) {
-                    throw new NonSequenceException(namespaces, nss, env);
+                    throw MessageUtils.newUnexpectedOperandTypeException(
+                            namespaces, nss, TemplateSequenceModel.class, env);
                 } else {
                     // Should not occur
                     throw new TemplateException(env, "Expecting a sequence of namespaces after \"using\"");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
index e4b3c17..7e713bd 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDynamicTopLevelCall.java
@@ -98,7 +98,7 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
                 nestedContentSupported = directive.isNestedContentSupported();
             } else if (callableValueTM instanceof TemplateFunctionModel) {
                 if (!allowCallingFunctions) {
-                    throw new NonDirectiveException(
+                    throw new TemplateException(
                             "Calling functions is not allowed. You can only call directives (like macros) here.", env);
                 }
                 callableValue = (TemplateCallableModel) callableValueTM;
@@ -109,7 +109,8 @@ class ASTDynamicTopLevelCall extends ASTDirective implements CallPlace  {
             } else if (callableValueTM == null) {
                 throw InvalidReferenceException.getInstance(callableValueExp, env);
             } else {
-                throw new NonDirectiveException(callableValueExp, callableValueTM, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        callableValueExp, callableValueTM, TemplateDirectiveModel.class, env);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
index 4385dac..79081e8 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpAddOrConcat.java
@@ -76,53 +76,43 @@ final class ASTExpAddOrConcat extends ASTExpression {
         } else {
             boolean hashConcatPossible
                     = leftModel instanceof TemplateHashModel && rightModel instanceof TemplateHashModel;
-            try {
-                // We try string addition first. If hash addition is possible, then instead of throwing exception
-                // we return null and do hash addition instead. (We can't simply give hash addition a priority, like
-                // with sequence addition above, as FTL strings are often also FTL hashes.)
-                Object leftOMOrStr = _EvalUtils.coerceModelToStringOrMarkup(
-                        leftModel, leftExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, null,
-                        env);
-                if (leftOMOrStr == null) {
-                    return _eval_concatenateHashes(leftModel, rightModel);
-                }
+            // We try string addition first. If hash addition is possible, then instead of throwing exception
+            // we return null and do hash addition instead. (We can't simply give hash addition a priority, like
+            // with sequence addition above, as FTL strings are often also FTL hashes.)
+            Object leftOMOrStr = _EvalUtils.coerceModelToStringOrMarkup(
+                    leftModel, leftExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, null,
+                    env);
+            if (leftOMOrStr == null) {
+                return _eval_concatenateHashes(leftModel, rightModel);
+            }
 
-                // Same trick with null return as above.
-                Object rightOMOrStr = _EvalUtils.coerceModelToStringOrMarkup(
-                        rightModel, rightExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, null,
-                        env);
-                if (rightOMOrStr == null) {
-                    return _eval_concatenateHashes(leftModel, rightModel);
-                }
+            // Same trick with null return as above.
+            Object rightOMOrStr = _EvalUtils.coerceModelToStringOrMarkup(
+                    rightModel, rightExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, null,
+                    env);
+            if (rightOMOrStr == null) {
+                return _eval_concatenateHashes(leftModel, rightModel);
+            }
 
-                if (leftOMOrStr instanceof String) {
-                    if (rightOMOrStr instanceof String) {
-                        return new SimpleScalar(((String) leftOMOrStr).concat((String) rightOMOrStr));
-                    } else { // rightOMOrStr instanceof TemplateMarkupOutputModel
-                        TemplateMarkupOutputModel<?> rightMO = (TemplateMarkupOutputModel<?>) rightOMOrStr; 
-                        return _EvalUtils.concatMarkupOutputs(parent,
-                                rightMO.getOutputFormat().fromPlainTextByEscaping((String) leftOMOrStr),
-                                rightMO);
-                    }                    
-                } else { // leftOMOrStr instanceof TemplateMarkupOutputModel 
-                    TemplateMarkupOutputModel<?> leftMO = (TemplateMarkupOutputModel<?>) leftOMOrStr; 
-                    if (rightOMOrStr instanceof String) {  // markup output
-                        return _EvalUtils.concatMarkupOutputs(parent,
-                                leftMO,
-                                leftMO.getOutputFormat().fromPlainTextByEscaping((String) rightOMOrStr));
-                    } else { // rightOMOrStr instanceof TemplateMarkupOutputModel
-                        return _EvalUtils.concatMarkupOutputs(parent,
-                                leftMO,
-                                (TemplateMarkupOutputModel<?>) rightOMOrStr);
-                    }
+            if (leftOMOrStr instanceof String) {
+                if (rightOMOrStr instanceof String) {
+                    return new SimpleScalar(((String) leftOMOrStr).concat((String) rightOMOrStr));
+                } else { // rightOMOrStr instanceof TemplateMarkupOutputModel
+                    TemplateMarkupOutputModel<?> rightMO = (TemplateMarkupOutputModel<?>) rightOMOrStr;
+                    return _EvalUtils.concatMarkupOutputs(parent,
+                            rightMO.getOutputFormat().fromPlainTextByEscaping((String) leftOMOrStr),
+                            rightMO);
                 }
-            } catch (NonStringOrTemplateOutputException e) {
-                // 2.4: Remove this catch; it's for BC, after reworking hash addition so it doesn't rely on this. But
-                // user code might throws this (very unlikely), and then in 2.3.x we did catch that too, incorrectly.
-                if (hashConcatPossible) {
-                    return _eval_concatenateHashes(leftModel, rightModel);
-                } else {
-                    throw e;
+            } else { // leftOMOrStr instanceof TemplateMarkupOutputModel
+                TemplateMarkupOutputModel<?> leftMO = (TemplateMarkupOutputModel<?>) leftOMOrStr;
+                if (rightOMOrStr instanceof String) {  // markup output
+                    return _EvalUtils.concatMarkupOutputs(parent,
+                            leftMO,
+                            leftMO.getOutputFormat().fromPlainTextByEscaping((String) rightOMOrStr));
+                } else { // rightOMOrStr instanceof TemplateMarkupOutputModel
+                    return _EvalUtils.concatMarkupOutputs(parent,
+                            leftMO,
+                            (TemplateMarkupOutputModel<?>) rightOMOrStr);
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java
index c1da34e..1e67fad 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDot.java
@@ -41,7 +41,7 @@ final class ASTExpDot extends ASTExpression {
         if (leftModel instanceof TemplateHashModel) {
             return ((TemplateHashModel) leftModel).get(key);
         }
-        throw new NonHashException(target, leftModel, env);
+        throw MessageUtils.newUnexpectedOperandTypeException(target, leftModel, TemplateHashModel.class, env);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
index 7633ea9..658be00 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpDynamicKeyName.java
@@ -58,16 +58,18 @@ final class ASTExpDynamicKeyName extends ASTExpression {
         if (keyModel instanceof RangeModel) {
             return dealWithRangeKey(targetModel, (RangeModel) keyModel, env);
         }
-        throw new UnexpectedTypeException(keyExpression, keyModel, "number, range, or string",
-                new Class[] { TemplateNumberModel.class, TemplateScalarModel.class, ASTExpRange.class }, env);
+        throw MessageUtils.newUnexpectedOperandTypeException(keyExpression, keyModel,
+                "number, range, or string",
+                new Class[] { TemplateNumberModel.class, TemplateScalarModel.class, ASTExpRange.class },
+                null, env);
     }
 
     static private Class[] NUMERICAL_KEY_LHO_EXPECTED_TYPES;
     static {
-        NUMERICAL_KEY_LHO_EXPECTED_TYPES = new Class[1 + NonStringException.STRING_COERCABLE_TYPES.length];
+        NUMERICAL_KEY_LHO_EXPECTED_TYPES = new Class[1 + MessageUtils.EXPECTED_TYPES_STRING_COERCABLE.length];
         NUMERICAL_KEY_LHO_EXPECTED_TYPES[0] = TemplateSequenceModel.class;
-        for (int i = 0; i < NonStringException.STRING_COERCABLE_TYPES.length; i++) {
-            NUMERICAL_KEY_LHO_EXPECTED_TYPES[i + 1] = NonStringException.STRING_COERCABLE_TYPES[i];
+        for (int i = 0; i < MessageUtils.EXPECTED_TYPES_STRING_COERCABLE.length; i++) {
+            NUMERICAL_KEY_LHO_EXPECTED_TYPES[i + 1] = MessageUtils.EXPECTED_TYPES_STRING_COERCABLE[i];
         }
     }
     
@@ -84,35 +86,37 @@ final class ASTExpDynamicKeyName extends ASTExpression {
                 size = Integer.MAX_VALUE;
             }
             return index < size ? tsm.get(index) : null;
-        } 
-        
+        }
+
+        String s;
         try {
-            String s = target.evalAndCoerceToPlainText(env);
-            try {
-                return new SimpleScalar(s.substring(index, index + 1));
-            } catch (IndexOutOfBoundsException e) {
-                if (index < 0) {
-                    throw new TemplateException("Negative index not allowed: ", Integer.valueOf(index));
-                }
-                if (index >= s.length()) {
-                    throw new TemplateException(
-                            "String index out of range: The index was ", Integer.valueOf(index),
-                            " (0-based), but the length of the string is only ", Integer.valueOf(s.length()) , ".");
-                }
-                throw new RuntimeException("Can't explain exception", e);
-            }
-        } catch (NonStringException e) {
-            throw new UnexpectedTypeException(
+            s = target.evalAndCoerceToPlainText(env);
+        } catch (TemplateException e) {
+            // TODO [FM3] Wrong, as we don't know why this was thrown. I think we just shouldn't coerce.
+            throw MessageUtils.newUnexpectedOperandTypeException(
                     target, targetModel,
-                    "sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC,
+                    "sequence or " + MessageUtils.STRING_COERCABLE_TYPES_DESC,
                     NUMERICAL_KEY_LHO_EXPECTED_TYPES,
                     (targetModel instanceof TemplateHashModel
-                            ? "You had a numberical value inside the []. Currently that's only supported for "
-                                    + "sequences (lists) and strings. To get a Map item with a non-string key, "
-                                    + "use myMap?api.get(myKey)."
+                            ? new Object[] { "You had a numberical value inside the []. Currently that's only "
+                                    + "supported for sequences (lists) and strings. To get a Map item with a "
+                                    + "non-string key, use myMap?api.get(myKey)." }
                             : null),
                     env);
         }
+        try {
+            return new SimpleScalar(s.substring(index, index + 1));
+        } catch (IndexOutOfBoundsException e) {
+            if (index < 0) {
+                throw new TemplateException("Negative index not allowed: ", Integer.valueOf(index));
+            }
+            if (index >= s.length()) {
+                throw new TemplateException(
+                        "String index out of range: The index was ", Integer.valueOf(index),
+                        " (0-based), but the length of the string is only ", Integer.valueOf(s.length()) , ".");
+            }
+            throw new RuntimeException("Can't explain exception", e);
+        }
     }
 
     private TemplateModel dealWithStringKey(TemplateModel targetModel, String key, Environment env)
@@ -120,7 +124,7 @@ final class ASTExpDynamicKeyName extends ASTExpression {
         if (targetModel instanceof TemplateHashModel) {
             return((TemplateHashModel) targetModel).get(key);
         }
-        throw new NonHashException(target, targetModel, env);
+        throw MessageUtils.newUnexpectedOperandTypeException(target, targetModel, TemplateHashModel.class, env);
     }
 
     private TemplateModel dealWithRangeKey(TemplateModel targetModel, RangeModel range, Environment env)
@@ -134,11 +138,12 @@ final class ASTExpDynamicKeyName extends ASTExpression {
             targetSeq = null;
             try {
                 targetStr = target.evalAndCoerceToPlainText(env);
-            } catch (NonStringException e) {
-                throw new UnexpectedTypeException(
+            } catch (TemplateException e) {
+                // TODO [FM3] Wrong, as we don't know why this was thrown. I think we just shouldn't coerce.
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, target.eval(env),
-                        "sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC,
-                        NUMERICAL_KEY_LHO_EXPECTED_TYPES, env);
+                        "sequence or " + MessageUtils.STRING_COERCABLE_TYPES_DESC,
+                        NUMERICAL_KEY_LHO_EXPECTED_TYPES, null, env);
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
index 1d60af7..07985c8 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpFunctionCall.java
@@ -61,7 +61,8 @@ final class ASTExpFunctionCall extends ASTExpression implements CallPlace {
         {
             TemplateModel functionUncasted = functionExp.eval(env);
             if (!(functionUncasted instanceof TemplateFunctionModel)) {
-                throw new NonFunctionException(functionExp, functionUncasted, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        functionExp, functionUncasted, TemplateFunctionModel.class, env);
             }
             function = (TemplateFunctionModel) functionUncasted;
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
index 32af9bb..463110e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpHashLiteral.java
@@ -49,7 +49,7 @@ final class ASTExpHashLiteral extends ASTExpression {
 
     @Override
     TemplateModel _eval(Environment env) throws TemplateException {
-        return new SequenceHash(env);
+        return new LinkedHash(env);
     }
 
     @Override
@@ -106,12 +106,12 @@ final class ASTExpHashLiteral extends ASTExpression {
     	return new ASTExpHashLiteral(clonedKeys, clonedValues);
     }
 
-    private class SequenceHash implements TemplateHashModelEx2 {
+    private class LinkedHash implements TemplateHashModelEx2 {
 
         private HashMap<String, TemplateModel> map;
         private TemplateCollectionModel keyCollection, valueCollection; // ordered lists of keys and values
 
-        SequenceHash(Environment env) throws TemplateException {
+        LinkedHash(Environment env) throws TemplateException {
             map = new LinkedHashMap<>();
             for (int i = 0; i < size; i++) {
                 ASTExpression keyExp = (ASTExpression) keys.get(i);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java
index dc41129..820e2e7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpNegateOrPlus.java
@@ -48,7 +48,7 @@ final class ASTExpNegateOrPlus extends ASTExpression {
         try {
             targetModel = (TemplateNumberModel) tm;
         } catch (ClassCastException cce) {
-            throw new NonNumericalException(target, tm, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, tm, TemplateNumberModel.class, env);
         }
         if (!isMinus) {
             return targetModel;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
index 6932827..7b86e3e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpRange.java
@@ -58,12 +58,6 @@ final class ASTExpRange extends ASTExpression {
             return new ListableRightUnboundedRangeModel(begin);
         }
     }
-    
-    // Surely this way we can tell that it won't be a boolean without evaluating the range, but why was this important?
-    @Override
-    boolean evalToBoolean(Environment env) throws TemplateException {
-        throw new NonBooleanException(this, new BoundedRangeModel(0, 0, false, false), env);
-    }
 
     @Override
     public String getCanonicalForm() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
index 97f82a5..0994685 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpression.java
@@ -113,7 +113,7 @@ abstract class ASTExpression extends ASTNode {
         if (model instanceof TemplateNumberModel) {
             return _EvalUtils.modelToNumber((TemplateNumberModel) model, this);
         } else {
-            throw new NonNumericalException(this, model, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(this, model, TemplateNumberModel.class, env);
         }
     }
     
@@ -148,7 +148,7 @@ abstract class ASTExpression extends ASTNode {
         if (model instanceof TemplateBooleanModel) {
             return ((TemplateBooleanModel) model).getAsBoolean();
         } else {
-            throw new NonBooleanException(this, model, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(this, model, TemplateBooleanModel.class, env);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index 44fda23..b47ff1a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForDate.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForDate.java
@@ -33,24 +33,11 @@ abstract class BuiltInForDate extends ASTExpBuiltIn {
             TemplateDateModel tdm = (TemplateDateModel) model;
             return calculateResult(_EvalUtils.modelToDate(tdm, target), tdm.getDateType(), env);
         } else {
-            throw newNonDateException(env, model, target);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateDateModel.class, env);
         }
     }
 
     /** 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;
-    }
+    protected abstract TemplateModel calculateResult(Date date, int dateType, Environment env) throws TemplateException;
     
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index ec21061..1988f5e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForHashEx.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForHashEx.java
@@ -31,7 +31,7 @@ abstract class BuiltInForHashEx extends ASTExpBuiltIn {
         if (model instanceof TemplateHashModelEx) {
             return calculateResult((TemplateHashModelEx) model, env);
         }
-        throw new NonExtendedHashException(target, model, env);
+        throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateHashModelEx.class, env);
     }
     
     abstract TemplateModel calculateResult(TemplateHashModelEx hashExModel, Environment env)

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index e1f46d1..6082a3d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLegacyEscaping.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForLegacyEscaping.java
@@ -20,6 +20,7 @@ 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.TemplateScalarModel;
 
 /**
  * A string built-in whose usage is banned when auto-escaping with a markup-output format is active.
@@ -39,7 +40,7 @@ abstract class BuiltInForLegacyEscaping extends BuiltInBannedWhenAutoEscaping {
             if (mo.getOutputFormat().isLegacyBuiltInBypassed(key)) {
                 return mo;
             }
-            throw new NonStringException(target, tm, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, tm, TemplateScalarModel.class, env);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index fa617c2..384e491 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForMarkupOutput.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForMarkupOutput.java
@@ -30,7 +30,7 @@ abstract class BuiltInForMarkupOutput extends ASTExpBuiltIn {
             throws TemplateException {
         TemplateModel model = target.eval(env);
         if (!(model instanceof TemplateMarkupOutputModel)) {
-            throw new NonMarkupOutputException(target, model, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateMarkupOutputModel.class, env);
         }
         return calculateResult((TemplateMarkupOutputModel) model);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index ca0cd61..afe7111 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNode.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNode.java
@@ -24,6 +24,7 @@ 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 {
@@ -31,9 +32,10 @@ abstract class BuiltInForNode extends ASTExpBuiltIn {
         if (model instanceof TemplateNodeModel) {
             return calculateResult((TemplateNodeModel) model, env);
         } else {
-            throw new NonNodeException(target, model, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateNodeModel.class, env);
         }
     }
-    abstract TemplateModel calculateResult(TemplateNodeModel nodeModel, Environment env)
-            throws TemplateModelException;
+
+    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/f231e64f/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
index 8360cbd..67d0383 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNodeEx.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNodeEx.java
@@ -29,7 +29,7 @@ abstract class BuiltInForNodeEx extends ASTExpBuiltIn {
         if (model instanceof TemplateNodeModelEx) {
             return calculateResult((TemplateNodeModelEx) model, env);
         } else {
-            throw new NonExtendedNodeException(target, model, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateNodeModelEx.class, env);
         }
     }
     abstract TemplateModel calculateResult(TemplateNodeModelEx nodeModel, Environment env)

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index 4aca088..bac6c7f 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForSequence.java
@@ -20,7 +20,6 @@
 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 {
@@ -29,7 +28,7 @@ abstract class BuiltInForSequence extends ASTExpBuiltIn {
             throws TemplateException {
         TemplateModel model = target.eval(env);
         if (!(model instanceof TemplateSequenceModel)) {
-            throw new NonSequenceException(target, model, env);
+            throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateSequenceModel.class, env);
         }
         return calculateResult((TemplateSequenceModel) model);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index c237613..f69cfe9 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForDates.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.*;
+import static org.apache.freemarker.core.util.CallableUtils.*;
 
 import java.util.Date;
 import java.util.TimeZone;
@@ -60,7 +60,7 @@ class BuiltInsForDates {
                 }
                 return new SimpleDate(_EvalUtils.modelToDate(tdm, target), dateType);
             } else {
-                throw BuiltInForDate.newNonDateException(env, model, target);
+                throw MessageUtils.newUnexpectedOperandTypeException(target, model, TemplateDateModel.class, env);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/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
index dfa7c46..f7c5258 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForExistenceHandling.java
@@ -69,8 +69,7 @@ class BuiltInsForExistenceHandling {
             }
 
             @Override
-            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
-                    throws TemplateException {
+            public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) {
                 return constant;
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
index 6b70721..048d633 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.getOptionalStringArgument;
+import static org.apache.freemarker.core.util.CallableUtils.getOptionalStringArgument;
 
 import java.util.Date;
 
@@ -44,6 +44,7 @@ import org.apache.freemarker.core.model.impl.SimpleDate;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.valueformat.TemplateDateFormat;
 import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
 import org.apache.freemarker.core.valueformat.TemplateValueFormatException;
@@ -64,10 +65,10 @@ class BuiltInsForMultipleTypes {
                 return new SimpleScalar(((TemplateBooleanModel) model).getAsBoolean()
                         ? TemplateBooleanFormat.C_TRUE : TemplateBooleanFormat.C_FALSE);
             } else {
-                throw new UnexpectedTypeException(
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, model,
                         "number or boolean", new Class[] { TemplateNumberModel.class, TemplateBooleanModel.class },
-                        env);
+                        null, env);
             }
         }
 
@@ -424,9 +425,9 @@ class BuiltInsForMultipleTypes {
         TemplateModel _eval(Environment env) throws TemplateException {
             TemplateModel tm = target.eval(env);
             if (!(tm instanceof Environment.TemplateLanguageCallable)) {
-                throw new UnexpectedTypeException(
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, tm,
-                        "macro or function", new Class[] { Environment.TemplateLanguageCallable.class },
+                        Environment.TemplateLanguageCallable.class,
                         env);
             }
             return ((Environment.TemplateLanguageCallable) tm).getNamespace();
@@ -446,7 +447,7 @@ class BuiltInsForMultipleTypes {
             } else if (model instanceof TemplateHashModelEx) {
                 size = ((TemplateHashModelEx) model).size();
             } else {
-                throw new UnexpectedTypeException(
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, model,
                         "extended-hash or sequence or extended collection",
                         new Class[] {
@@ -454,6 +455,7 @@ class BuiltInsForMultipleTypes {
                                 TemplateSequenceModel.class,
                                 TemplateCollectionModelEx.class
                         },
+                        null,
                         env);
             }
             return new SimpleNumber(size);
@@ -479,7 +481,7 @@ class BuiltInsForMultipleTypes {
                 TemplateModel result = args[argIdx];
                 if (!(result instanceof TemplateScalarModel)) {
                     // Cause usual type exception
-                    _CallableUtils.castArgumentValueToString(result, argIdx, this, true, false);
+                    CallableUtils.castArgumentValueToString(result, argIdx, this, true, false);
                 }
                 return result;
             }
@@ -526,7 +528,7 @@ class BuiltInsForMultipleTypes {
             @Override
             public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
                     throws TemplateException {
-                return formatWith(_CallableUtils.getStringArgument(args, 0, this));
+                return formatWith(CallableUtils.getStringArgument(args, 0, this));
             }
 
             @Override
@@ -606,7 +608,7 @@ class BuiltInsForMultipleTypes {
             @Override
             public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
                     throws TemplateException {
-                return get(_CallableUtils.getStringArgument(args, 0, this));
+                return get(CallableUtils.getStringArgument(args, 0, this));
             }
 
             @Override
@@ -670,15 +672,15 @@ class BuiltInsForMultipleTypes {
                 return new BooleanFormatter((TemplateBooleanModel) model, env);
             } else if (model instanceof TemplateScalarModel) {
                 return new SimpleScalar(((TemplateScalarModel) model).getAsString());
-            } else {            
-                throw new UnexpectedTypeException(
+            } else {
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, model,
                         "number, date, boolean or string",
                         new Class[] {
                             TemplateNumberModel.class, TemplateDateModel.class, TemplateBooleanModel.class,
                             TemplateScalarModel.class
                         },
-                        env);
+                        null, env);
             }
         }
     }
@@ -697,10 +699,10 @@ class BuiltInsForMultipleTypes {
                 return new SimpleScalar(((TemplateBooleanModel) model).getAsBoolean()
                         ? TemplateBooleanFormat.C_TRUE : TemplateBooleanFormat.C_FALSE);
             } else {
-                throw new UnexpectedTypeException(
+                throw MessageUtils.newUnexpectedOperandTypeException(
                         target, model,
                         "number or boolean", new Class[] { TemplateNumberModel.class, TemplateBooleanModel.class },
-                        env);
+                        null, env);
             }
         }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
index 9b35b63..3767523 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNestedContentParameters.java
@@ -18,8 +18,6 @@
  */
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.checkArgumentCount;
-
 import org.apache.freemarker.core.ASTDirList.IterationContext;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateBooleanModel;
@@ -27,6 +25,7 @@ import org.apache.freemarker.core.model.TemplateFunctionModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.SimpleNumber;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.util.CallableUtils;
 
 
 class BuiltInsForNestedContentParameters {
@@ -142,7 +141,7 @@ class BuiltInsForNestedContentParameters {
             @Override
             public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env)
                     throws TemplateException {
-                _CallableUtils.checkArgumentCount(args.length, 1, Integer.MAX_VALUE, this);
+                CallableUtils.checkArgumentCount(args.length, 1, Integer.MAX_VALUE, this);
                 return args[iterCtx.getIndex() % args.length];
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
index ae23e18..5174c3e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.getStringArgument;
+import static org.apache.freemarker.core.util.CallableUtils.getStringArgument;
 
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
@@ -47,8 +47,7 @@ class BuiltInsForNodes {
            return result;
        }
 
-        class AncestorSequence extends NativeSequence implements TemplateFunctionModel,
-                BuiltInCallable {
+        class AncestorSequence extends NativeSequence implements TemplateFunctionModel, BuiltInCallable {
 
             private static final int INITIAL_CAPACITY = 12;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/f231e64f/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
index 0e8dd2b..857e7e3 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
@@ -19,7 +19,7 @@
 
 package org.apache.freemarker.core;
 
-import static org.apache.freemarker.core._CallableUtils.*;
+import static org.apache.freemarker.core.util.CallableUtils.*;
 
 import java.io.Serializable;
 import java.text.Collator;
@@ -55,7 +55,7 @@ class BuiltInsForSequences {
     
     static class chunkBI extends BuiltInForSequence {
 
-        private class BIMethod implements TemplateFunctionModel {
+        private class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             
             private final TemplateSequenceModel tsm;
 
@@ -160,7 +160,11 @@ class BuiltInsForSequences {
             } else if (model instanceof TemplateCollectionModel) {
                 return calculateResultForColletion((TemplateCollectionModel) model);
             } else {
-                throw new NonSequenceOrCollectionException(target, model, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        target, model,
+                        MessageUtils.SEQUENCE_OR_COLLECTION,
+                        MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION,
+                        null, env);
             }
         }        
         
@@ -255,7 +259,11 @@ class BuiltInsForSequences {
             } else if (model instanceof TemplateSequenceModel) {
                 return new BIMethodForCollection(env, new CollectionAndSequence((TemplateSequenceModel) model));
             } else {
-                throw new NonSequenceOrCollectionException(target, model, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        target, model,
+                        MessageUtils.SEQUENCE_OR_COLLECTION,
+                        MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION,
+                        null, env);
             }
         }
    
@@ -302,7 +310,7 @@ class BuiltInsForSequences {
     }
 
     static class seq_containsBI extends ASTExpBuiltIn {
-        private class BIMethodForCollection implements TemplateFunctionModel {
+        private class BIMethodForCollection extends BuiltInCallableImpl implements TemplateFunctionModel {
             private TemplateCollectionModel m_coll;
             private Environment m_env;
 
@@ -332,7 +340,7 @@ class BuiltInsForSequences {
 
         }
 
-        private class BIMethodForSequence implements TemplateFunctionModel {
+        private class BIMethodForSequence extends BuiltInCallableImpl implements TemplateFunctionModel {
             private TemplateSequenceModel m_seq;
             private Environment m_env;
 
@@ -371,7 +379,11 @@ class BuiltInsForSequences {
             } else if (model instanceof TemplateCollectionModel) {
                 return new BIMethodForCollection((TemplateCollectionModel) model, env);
             } else {
-                throw new NonSequenceOrCollectionException(target, model, env);
+                throw MessageUtils.newUnexpectedOperandTypeException(
+                        target, model,
+                        MessageUtils.SEQUENCE_OR_COLLECTION,
+                        MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION,
+                        null, env);
             }
         }
     
@@ -379,7 +391,7 @@ class BuiltInsForSequences {
     
     static class seq_index_ofBI extends ASTExpBuiltIn {
         
-        private class BIMethod implements TemplateFunctionModel {
+        private class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             
             final TemplateSequenceModel m_seq;
             final TemplateCollectionModel m_col;
@@ -403,7 +415,11 @@ class BuiltInsForSequences {
                         ? (TemplateCollectionModel) model
                         : null;
                 if (m_seq == null && m_col == null) {
-                    throw new NonSequenceOrCollectionException(target, model, env);
+                    throw MessageUtils.newUnexpectedOperandTypeException(
+                            target, model,
+                            MessageUtils.SEQUENCE_OR_COLLECTION,
+                            MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION,
+                            null, env);
                 }
                 
                 m_env = env;
@@ -544,7 +560,7 @@ class BuiltInsForSequences {
     }
 
     static class sort_byBI extends sortBI {
-        class BIMethod implements TemplateFunctionModel {
+        class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel {
             TemplateSequenceModel seq;
             
             BIMethod(TemplateSequenceModel seq) {