Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 8c6b4ec41 -> 888ebe114


FREEMARKER-65: Added org.apache.freemarker.core.util.CallableUtils to help 
implementing function and directives in Java.


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

Branch: refs/heads/3
Commit: 888ebe1140d30acae6378d5235202fa755e4a8a6
Parents: 8c6b4ec
Author: ddekany <ddek...@apache.org>
Authored: Sun Aug 20 23:12:19 2017 +0200
Committer: ddekany <ddek...@apache.org>
Committed: Sun Aug 20 23:24:49 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |   1 +
 .../core/BuiltInsForMultipleTypes.java          |   3 +-
 .../core/BuiltInsForStringsBasic.java           |   5 +-
 .../core/model/TemplateCallableModel.java       |   4 +
 .../core/model/TemplateDirectiveModel.java      |   3 +
 .../core/model/TemplateFunctionModel.java       |   3 +
 .../core/model/impl/ResourceBundleModel.java    |   6 +-
 .../freemarker/core/util/CallableUtils.java     | 528 +++++++++++++++++--
 .../test/templateutil/AssertDirective.java      |  14 +-
 .../templateutil/AssertEqualsDirective.java     |  20 +-
 .../test/templateutil/AssertFailsDirective.java |  59 +--
 .../templateutil/BadParameterTypeException.java |  60 ---
 .../MissingRequiredParameterException.java      |  51 --
 .../test/templateutil/ParameterException.java   |  54 --
 .../UnsupportedParameterException.java          |  50 --
 15 files changed, 512 insertions(+), 349 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 8e08099..033fcc6 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -377,6 +377,7 @@ Core / Models and Object wrapping
   the common interface bith for wrapped Java methods and functions defined in 
the templates, or on any other ways.
   `OverloadedMethodsModel` and `SimpleMethodModel` were renamed to 
`OverloadJavaMethodModel` and  `SimpleJavaMethodModel`,
   which of course extend `TemplateFunctionModel`, but allow inocations without 
`Environment` parameter.
+- `org.apache.freemarker.core.util.CallableUtils` was added to help 
implementing function and directives in Java.
 - `TemplateModelException` was removed, replaced with `TemplateException` on 
all places, except for
   `ObjectWrapper.wrap(Object)` and `wrapAsAPI(Object)`, which now throws 
`ObjectWrappingException` instead (that extends
   `TemplateException`).

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/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 6335777..159c284 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
@@ -475,7 +475,8 @@ 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, false, null, this, true);
                 }
                 return result;
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/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 1eaeb2d..91eac72 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
@@ -705,9 +705,10 @@ class BuiltInsForStringsBasic {
                         throw newIndexGreaterThanLengthException(1, endIdx, 
len);
                     }
                     if (beginIdx > endIdx) {
-                        throw newGenericExecuteException(this,
+                        throw newGenericExecuteException(
                                 "The begin index argument, " + beginIdx
-                                + ", shouldn't be greater than the end index 
argument, " + endIdx + ".");
+                                + ", shouldn't be greater than the end index 
argument, " + endIdx + ".",
+                                this);
                     }
                     return new SimpleScalar(s.substring(beginIdx, endIdx));
                 } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
index caa612a..d287757 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCallableModel.java
@@ -19,9 +19,13 @@
 
 package org.apache.freemarker.core.model;
 
+import org.apache.freemarker.core.util.CallableUtils;
+
 /**
  * Super interface (marker interface) of {@link TemplateFunctionModel} and 
{@link TemplateDirectiveModel}; don' extended
  * (or implement) it yourself!
+ * <p>
+ * You can find utilities for implementing {@link TemplateCallableModel}-s in 
{@link CallableUtils}.
  */
 public interface TemplateCallableModel extends TemplateModel {
     //

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
index be06cba..bfdc118 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java
@@ -8,6 +8,7 @@ import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.NonTemplateCallPlace;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.impl.JavaMethodModel;
+import org.apache.freemarker.core.util.CallableUtils;
 
 /**
  * A {@link TemplateCallableModel} that progressively writes it result into 
the {@code out} object, instead of
@@ -21,6 +22,8 @@ import org.apache.freemarker.core.model.impl.JavaMethodModel;
  * <p>
  * Example usage in a template: {@code <@my.menu style="foo" 
expand=true>...</@my.menu>},
  * {@code <@my.menuItem "Some title" icon="some.jpg" />}.
+ * <p>
+ * You can find utilities for implementing {@link TemplateDirectiveModel}-s in 
{@link CallableUtils}.
  */
 public interface TemplateDirectiveModel extends TemplateCallableModel {
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
index 70824ec..3dcd10a 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateFunctionModel.java
@@ -6,6 +6,7 @@ import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.impl.JavaMethodModel;
+import org.apache.freemarker.core.util.CallableUtils;
 
 /**
  * A {@link TemplateCallableModel}, which returns its result as a {@link 
TemplateModel} at the end of its execution.
@@ -16,6 +17,8 @@ import org.apache.freemarker.core.model.impl.JavaMethodModel;
  * <p>
  * Example usage in templates: {@code < a 
href="${my.toProductURL(product.id)}">},
  * {@code <#list my.groupByFirstLetter(products, property="name") as 
productGroup>}
+ * <p>
+ * You can find utilities for implementing {@link TemplateFunctionModel}-s in 
{@link CallableUtils}.
  */
 public interface TemplateFunctionModel extends TemplateCallableModel {
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/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 aaf3702..037ff15 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
@@ -125,10 +125,10 @@ public class ResourceBundleModel extends BeanModel 
implements TemplateFunctionMo
             // Invoke format
             return new BeanAndStringModel(format(key, params), wrapper);
         } catch (MissingResourceException e) {
-            throw CallableUtils.newGenericExecuteException(this, "No such key: 
" + key,  e);
+            throw CallableUtils.newGenericExecuteException("No such key: " + 
key, this, e);
         } catch (Exception e) {
-            throw CallableUtils.newGenericExecuteException(this,
-                    "Failed to get or format message; see cause exception",  
e);
+            throw CallableUtils.newGenericExecuteException("Failed to get or 
format message; see cause exception", this,
+                    e);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/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
index 4f6dc5d..796d082 100644
--- 
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
@@ -35,6 +35,7 @@ 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.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateFunctionModel;
@@ -44,51 +45,79 @@ 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!
+ * Utilities for implementing and calling {@link TemplateCallableModel}-s 
(such as {@link TemplateDirectiveModel}-s
+ * and {@link TemplateFunctionModel}-s).
  */
 public final class CallableUtils {
 
+    /** Empty {@link TemplateModel} array, useful to avoid array creation for 
a 0 length argument list. */
     public static final TemplateModel[] EMPTY_TEMPLATE_MODEL_ARRAY = new 
TemplateModel[0];
 
     private CallableUtils() {
         //
     }
 
-    /** Convenience method for calling {@link 
#newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
+    /**
+     * Convenience method for calling
+     * {@link #newGenericExecuteException(String, TemplateCallableModel, 
boolean, Throwable)}
+     */
     public static TemplateException newGenericExecuteException(
-            TemplateFunctionModel callable, String errorDescription) {
-        return newGenericExecuteException(callable, true, errorDescription);
+            String errorDescription, TemplateFunctionModel callable) {
+        return newGenericExecuteException(errorDescription, callable, true);
     }
 
-    /** Convenience method for calling {@link 
#newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
+    /**
+     * Convenience method for calling
+     * {@link #newGenericExecuteException(String, TemplateCallableModel, 
boolean, Throwable)}
+     */
     public static TemplateException newGenericExecuteException(
-            TemplateDirectiveModel callable, String errorDescription) {
-        return newGenericExecuteException(callable, false, errorDescription);
+            String errorDescription, TemplateDirectiveModel callable) {
+        return newGenericExecuteException(errorDescription, callable, false);
     }
 
+    /**
+     * Convenience method for calling
+     * {@link #newGenericExecuteException(String, TemplateCallableModel, 
boolean, Throwable)}
+     */
     public static TemplateException newGenericExecuteException(
-            TemplateCallableModel callable, boolean calledAsFunction, String 
errorDescription) {
-        return newGenericExecuteException(callable, calledAsFunction, 
errorDescription, null);
+            String errorDescription, TemplateCallableModel callable, boolean 
calledAsFunction) {
+        return newGenericExecuteException(errorDescription, callable, 
calledAsFunction, null);
     }
 
-    /** Convenience method for calling {@link 
#newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
+    /**
+     * Convenience method for calling
+     * {@link #newGenericExecuteException(String, TemplateCallableModel, 
boolean, Throwable)}
+     */
     public static TemplateException newGenericExecuteException(
-            TemplateFunctionModel callable, String errorDescription, Throwable 
cause) {
-        return newGenericExecuteException(callable, true, errorDescription, 
cause);
+            String errorDescription, TemplateFunctionModel callable, Throwable 
cause) {
+        return newGenericExecuteException(errorDescription, callable, true, 
cause);
     }
 
-    /** Convenience method for calling {@link 
#newGenericExecuteException(TemplateCallableModel, boolean, String)}. */
+    /**
+     * Convenience method for calling
+     * {@link #newGenericExecuteException(String, TemplateCallableModel, 
boolean, Throwable)}
+     */
     public static TemplateException newGenericExecuteException(
-            TemplateDirectiveModel callable, String errorDescription, 
Throwable cause) {
-        return newGenericExecuteException(callable, false, errorDescription, 
cause);
+            String errorDescription, TemplateDirectiveModel callable, 
Throwable cause) {
+        return newGenericExecuteException(errorDescription, callable, false, 
cause);
     }
 
     /**
-     * @param errorDescription Complete sentence describing the problem. This 
will be after
-     *      {@code "When calling xxx: "}.
+     * Creates an exception related to the execution of a {@link 
TemplateCallableModel}, for which there's no more
+     * specific method in {@link CallableUtils}.
+     *
+     * @param errorDescription
+     *         Complete sentence describing the problem. This will be shown 
after {@code "When calling Foo: "}.
+     * @param callable
+     *         The {@link TemplateCallableModel} whose execution was failed.; 
required for printing proper error
+     *         message.
+     * @param calledAsFunction
+     *         Tells if the {@code callable} was called as function (as 
opposed to called as a directive), which is
+     *         needed for proper error messages. This information is needed 
because a {@link TemplateCallableModel}
+     *         might implements both {@link TemplateFunctionModel} and {@link 
TemplateDirectiveModel}.
      */
     public static TemplateException newGenericExecuteException(
-            TemplateCallableModel callable, boolean calledAsFunction, String 
errorDescription,
+            String errorDescription, TemplateCallableModel callable, boolean 
calledAsFunction,
             Throwable cause) {
         return new TemplateException(cause,
                 
_CallableUtils.getMessagePartWhenCallingSomethingColon(callable, 
calledAsFunction),
@@ -119,6 +148,7 @@ public final class CallableUtils {
         return _newArgumentValueException(argIdx, problemDescription, 
callable, calledAsFunction, null);
     }
 
+    /** Don't use it; used internally by FreeMarker, may changes anytime 
without notice. */
     // TODO [FM3] How to expose tips API?
     public static TemplateException _newArgumentValueException(
             int argIdx, String problemDescription,
@@ -214,6 +244,7 @@ public final class CallableUtils {
         return _newNullOrOmittedArgumentException(argIdx, callable, 
calledAsFunction, null);
     }
 
+    /** Don't use it; used internally by FreeMarker, may changes anytime 
without notice. */
     // TODO [FM3] How to expose tips API?
     public static TemplateException _newNullOrOmittedArgumentException(int 
argIdx, TemplateCallableModel callable,
             boolean calledAsFunction, Object[] tips) {
@@ -310,72 +341,85 @@ public final class CallableUtils {
 
     /**
      * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, 
TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, true, 
false)}.
+     * {@link #castArgumentValueToString(TemplateModel, int, boolean, String, 
TemplateCallableModel, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, false, null, 
callable, true)}.
      */
     public static String getStringArgument(
             TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
             throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, 
true, false);
+        return castArgumentValueToString(args[argIndex], argIndex, false, 
null, callable, true);
     }
 
     /**
      * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, 
TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, false, 
false)}.
+     * {@link #castArgumentValueToString(TemplateModel, int, boolean, String, 
TemplateCallableModel, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, false, null, 
callable, false)}.
      */
     public static String getStringArgument(
             TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
             throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, 
false, false);
+        return castArgumentValueToString(args[argIndex], argIndex, false, 
null, callable, false);
     }
 
     /**
      * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, 
TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, true, 
true)}.
+     * {@link #castArgumentValueToString(TemplateModel, int, boolean, String, 
TemplateCallableModel, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, true, null, 
callable, true)}.
      */
     public static String getOptionalStringArgument(
             TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
             throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, 
true, true);
+        return castArgumentValueToString(args[argIndex], argIndex, true, null, 
callable, true);
     }
 
     /**
      * Convenience method to call
-     * {@link #castArgumentValueToString(TemplateModel, int, 
TemplateCallableModel, boolean, boolean)
-     * castArgumentValueToString(args[argIndex], argIndex, callable, false, 
true)}.
+     * {@link #castArgumentValueToString(TemplateModel, int, boolean, String, 
TemplateCallableModel, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, true, null, 
callable, false)}.
      */
     public static String getOptionalStringArgument(
             TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
             throws TemplateException {
-        return castArgumentValueToString(args[argIndex], argIndex, callable, 
false, true);
+        return castArgumentValueToString(args[argIndex], argIndex, true, null, 
callable, false);
     }
 
     /**
-     * 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.
+     * Convenience method to call
+     * {@link #castArgumentValueToString(TemplateModel, int, boolean, String, 
TemplateCallableModel, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, true, defaultValue, 
callable, true)}.
+     */
+    public static String getOptionalStringArgument(
+            TemplateModel[] args, int argIndex, String defaultValue, 
TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToString(args[argIndex], argIndex, true, 
defaultValue, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToString(TemplateModel, int, boolean, String, 
TemplateCallableModel, boolean)
+     * castArgumentValueToString(args[argIndex], argIndex, true, defaultValue, 
callable, false)}.
+     */
+    public static String getOptionalStringArgument(
+            TemplateModel[] args, int argIndex, String defaultValue, 
TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToString(args[argIndex], argIndex, true, 
defaultValue, callable, false);
+    }
+
+    /**
+     * See {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel,
+     * boolean)}; this does the same, but with {@link TemplateScalarModel} as 
{@code type}, and with {@link String}
+     * return value.
      */
     public static String castArgumentValueToString(
-            TemplateModel argValue, int argIdx, TemplateCallableModel callable,
-            boolean calledAsFunction, boolean optional)
+            TemplateModel argValue, int argIdx, boolean optional, String 
defaultValue,
+            TemplateCallableModel callable, boolean calledAsFunction)
             throws TemplateException {
         if (argValue instanceof TemplateScalarModel) {
             return _EvalUtils.modelToString((TemplateScalarModel) argValue, 
null);
         }
         if (argValue == null) {
             if (optional) {
-                return null;
+                return defaultValue;
             }
             throw newNullOrOmittedArgumentException(argIdx, callable, 
calledAsFunction);
         }
@@ -383,41 +427,88 @@ public final class CallableUtils {
     }
 
     // Number arg:
-    
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToNumber(TemplateModel, int, boolean, Number, 
TemplateCallableModel, boolean)
+     * castArgumentValueToNumber(args[argIndex], argIndex, false, null, 
callable, true)}.
+     */
     public static Number getNumberArgument(
             TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
             throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, 
true, false);
+        return castArgumentValueToNumber(args[argIndex], argIndex, false, 
null, callable, true);
     }
 
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToNumber(TemplateModel, int, boolean, Number, 
TemplateCallableModel, boolean)
+     * castArgumentValueToNumber(args[argIndex], argIndex, false, null, 
callable, false)}.
+     */
     public static Number getNumberArgument(
             TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
             throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, 
false, false);
+        return castArgumentValueToNumber(args[argIndex], argIndex, false, 
null, callable, false);
     }
 
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToNumber(TemplateModel, int, boolean, Number, 
TemplateCallableModel, boolean)
+     * castArgumentValueToNumber(args[argIndex], argIndex, true, null, 
callable, true)}.
+     */
     public static Number getOptionalNumberArgument(
             TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
             throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, 
true, true);
+        return castArgumentValueToNumber(args[argIndex], argIndex, true, null, 
callable, true);
     }
 
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToNumber(TemplateModel, int, boolean, Number, 
TemplateCallableModel, boolean)
+     * castArgumentValueToNumber(args[argIndex], argIndex, true, null, 
callable, false)}.
+     */
     public static Number getOptionalNumberArgument(
             TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
             throws TemplateException {
-        return castArgumentValueToNumber(args[argIndex], argIndex, callable, 
false, true);
+        return castArgumentValueToNumber(args[argIndex], argIndex, true, null, 
callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToNumber(TemplateModel, int, boolean, Number, 
TemplateCallableModel, boolean)
+     * castArgumentValueToNumber(args[argIndex], argIndex, true, defaultValue, 
callable, true)}.
+     */
+    public static Number getOptionalNumberArgument(
+            TemplateModel[] args, int argIndex, Number defaultValue, 
TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToNumber(args[argIndex], argIndex, true, 
defaultValue, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToNumber(TemplateModel, int, boolean, Number, 
TemplateCallableModel, boolean)
+     * castArgumentValueToNumber(args[argIndex], argIndex, true, defaultValue, 
callable, false)}.
+     */
+    public static Number getOptionalNumberArgument(
+            TemplateModel[] args, int argIndex, Number defaultValue, 
TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToNumber(args[argIndex], argIndex, true, 
defaultValue, callable, false);
     }
 
+    /**
+     * See {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel,
+     * boolean)}; this does the same, but with {@link TemplateNumberModel} as 
{@code type}, and with {@link Number}
+     * return value.
+     */
     public static Number castArgumentValueToNumber(
-            TemplateModel argValue, int argIdx, TemplateCallableModel callable,
-            boolean calledAsFunction, boolean optional)
+            TemplateModel argValue, int argIdx, boolean optional, Number 
defaultValue, TemplateCallableModel callable,
+            boolean calledAsFunction)
             throws TemplateException {
         if (argValue instanceof TemplateNumberModel) {
             return _EvalUtils.modelToNumber((TemplateNumberModel) argValue, 
null);
         }
         if (argValue == null) {
             if (optional) {
-                return null;
+                return defaultValue;
             }
             throw newNullOrOmittedArgumentException(argIdx, callable, 
calledAsFunction);
         }
@@ -426,8 +517,319 @@ public final class CallableUtils {
                 calledAsFunction);
     }
 
-    // TODO boolean, etc.
 
+    // int arg:
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, 
TemplateCallableModel, boolean)
+     * castArgumentValueToInt(args[argIndex], argIndex, false, null, callable, 
true)}.
+     */
+    public static int getIntArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToInt(args[argIndex], argIndex, false, 0, 
callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, 
TemplateCallableModel, boolean)
+     * castArgumentValueToInt(args[argIndex], argIndex, false, null, callable, 
false)}.
+     */
+    public static int getIntArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
+            throws TemplateException {
+        return castArgumentValueToInt(args[argIndex], argIndex, false, 0, 
callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, 
TemplateCallableModel, boolean)
+     * castArgumentValueToInt(args[argIndex], argIndex, true, null, callable, 
true)}.
+     */
+    public static int getOptionalIntArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToInt(args[argIndex], argIndex, true, 0, 
callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, 
TemplateCallableModel, boolean)
+     * castArgumentValueToInt(args[argIndex], argIndex, true, null, callable, 
false)}.
+     */
+    public static int getOptionalIntArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
+            throws TemplateException {
+        return castArgumentValueToInt(args[argIndex], argIndex, true, 0, 
callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, 
TemplateCallableModel, boolean)
+     * castArgumentValueToInt(args[argIndex], argIndex, true, defaultValue, 
callable, true)}.
+     */
+    public static int getOptionalIntArgument(
+            TemplateModel[] args, int argIndex, int defaultValue, 
TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToInt(args[argIndex], argIndex, true, 
defaultValue, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, 
TemplateCallableModel, boolean)
+     * castArgumentValueToInt(args[argIndex], argIndex, true, defaultValue, 
callable, false)}.
+     */
+    public static int getOptionalIntArgument(
+            TemplateModel[] args, int argIndex, int defaultValue, 
TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValueToInt(args[argIndex], argIndex, true, 
defaultValue, callable, false);
+    }
+
+    /**
+     * See {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel,
+     * boolean)}; this does the same, but with {@link TemplateNumberModel} as 
{@code type} and with the restriction that
+     * the number must be convertable to {@code int} losselessly, and with 
{@link int} return value.
+     */
+    public static int castArgumentValueToInt(
+            TemplateModel argValue, int argIdx, boolean optional, int 
defaultValue, TemplateCallableModel callable,
+            boolean calledAsFunction)
+            throws TemplateException {
+        if (argValue instanceof TemplateNumberModel) {
+            Number nm = _EvalUtils.modelToNumber((TemplateNumberModel) 
argValue, null);
+            try {
+                return _NumberUtils.toIntExact(nm);
+            } catch (ArithmeticException e) {
+                throw newArgumentValueException(argIdx, "must be an integer 
(that fits into 32 bits), but "
+                                + nm + " (class: " + 
_ClassUtils.getShortClassName(nm.getClass())
+                                + ") is not a such number.",
+                        callable,
+                        calledAsFunction);
+            }
+        }
+        if (argValue == null) {
+            if (optional) {
+                return defaultValue;
+            }
+            throw newNullOrOmittedArgumentException(argIdx, callable, 
calledAsFunction);
+        }
+        throw newArgumentValueTypeException(
+                argValue, argIdx, new Class[] { TemplateNumberModel.class }, 
"number (int)", callable,
+                calledAsFunction);
+    }
+
+    // Boolean arg:
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToBoolean(TemplateModel, int, boolean, 
Boolean, TemplateCallableModel, boolean)
+     * castArgumentValueToBoolean(args[argIndex], argIndex, false, null, 
callable, true)}.
+     */
+    public static boolean getBooleanArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToBoolean(args[argIndex], argIndex, false, 
null, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToBoolean(TemplateModel, int, boolean, 
Boolean, TemplateCallableModel, boolean)
+     * castArgumentValueToBoolean(args[argIndex], argIndex, false, null, 
callable, false)}.
+     */
+    public static boolean getBooleanArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
+            throws TemplateException {
+        return castArgumentValueToBoolean(args[argIndex], argIndex, false, 
null, callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToBoolean(TemplateModel, int, boolean, 
Boolean, TemplateCallableModel, boolean)
+     * castArgumentValueToBoolean(args[argIndex], argIndex, true, null, 
callable, true)}.
+     */
+    public static Boolean getOptionalBooleanArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValueToBoolean(args[argIndex], argIndex, true, 
null, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToBoolean(TemplateModel, int, boolean, 
Boolean, TemplateCallableModel, boolean)
+     * castArgumentValueToBoolean(args[argIndex], argIndex, true, null, 
callable, false)}.
+     */
+    public static Boolean getOptionalBooleanArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable)
+            throws TemplateException {
+        return castArgumentValueToBoolean(args[argIndex], argIndex, true, 
null, callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToBoolean(TemplateModel, int, boolean, 
Boolean, TemplateCallableModel, boolean)
+     * castArgumentValueToBoolean(args[argIndex], argIndex, true, 
defaultValue, callable, true)}.
+     */
+    public static Boolean getOptionalBooleanArgument(
+            TemplateModel[] args, int argIndex, TemplateFunctionModel 
callable, Boolean defaultValue)
+            throws TemplateException {
+        return castArgumentValueToBoolean(args[argIndex], argIndex, true, 
defaultValue, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValueToBoolean(TemplateModel, int, boolean, 
Boolean, TemplateCallableModel, boolean)
+     * castArgumentValueToBoolean(args[argIndex], argIndex, true, 
defaultValue, callable, false)}.
+     */
+    public static Boolean getOptionalBooleanArgument(
+            TemplateModel[] args, int argIndex, TemplateDirectiveModel 
callable, Boolean defaultValue)
+            throws TemplateException {
+        return castArgumentValueToBoolean(args[argIndex], argIndex, true, 
defaultValue, callable, false);
+    }
+
+    /**
+     * See {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel,
+     * boolean)}; this does the same, but with {@link TemplateBooleanModel} as 
{@code type}, and with {@link Boolean}
+     * return value.
+     */
+    public static Boolean castArgumentValueToBoolean(
+            TemplateModel argValue, int argIdx, boolean optional, Boolean 
defaultValue, TemplateCallableModel callable,
+            boolean calledAsFunction)
+            throws TemplateException {
+        if (argValue instanceof TemplateBooleanModel) {
+            return ((TemplateBooleanModel) argValue).getAsBoolean();
+        }
+        if (argValue == null) {
+            if (optional) {
+                return defaultValue;
+            }
+            throw newNullOrOmittedArgumentException(argIdx, callable, 
calledAsFunction);
+        }
+        throw newArgumentValueTypeException(
+                argValue, argIdx, TemplateBooleanModel.class, callable,
+                calledAsFunction);
+    }
+
+    // Other type of arg:
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel, boolean)}
+     * castArgumentValueToSequence(args[argIndex], argIndex, callable, true, 
false, null)}.
+     */
+    public static <T extends TemplateModel> T getArgument(
+            TemplateModel[] args, int argIndex, Class<T> type, 
TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValue(args[argIndex], argIndex, type, false, null, 
callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel, boolean)}
+     * castArgumentValueToSequence(args[argIndex], argIndex, callable, false, 
false, null)}.
+     */
+    public static <T extends TemplateModel> T getArgument(
+            TemplateModel[] args, int argIndex, Class<T> type, 
TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValue(args[argIndex], argIndex, type, false, null, 
callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel, boolean)}
+     * castArgumentValueToSequence(args[argIndex], argIndex, callable, true, 
true, null)}.
+     */
+    public static <T extends TemplateModel> T getOptionalArgument(
+            TemplateModel[] args, int argIndex, Class<T> type, 
TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValue(args[argIndex], argIndex, type, true, null, 
callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel, boolean)}
+     * castArgumentValueToSequence(args[argIndex], argIndex, callable, true, 
true, null)}.
+     */
+    public static <T extends TemplateModel> T getOptionalArgument(
+            TemplateModel[] args, int argIndex, Class<T> type, 
TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValue(args[argIndex], argIndex, type, true, null, 
callable, false);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel, boolean)}
+     * castArgumentValueToSequence(args[argIndex], argIndex, callable, true, 
true, null)}.
+     */
+    public static <T extends TemplateModel> T getOptionalArgument(
+            TemplateModel[] args, int argIndex, Class<T> type, T defaultValue, 
TemplateFunctionModel callable)
+            throws TemplateException {
+        return castArgumentValue(args[argIndex], argIndex, type, true, 
defaultValue, callable, true);
+    }
+
+    /**
+     * Convenience method to call
+     * {@link #castArgumentValue(TemplateModel, int, Class, boolean, 
TemplateModel, TemplateCallableModel, boolean)}
+     * castArgumentValueToSequence(args[argIndex], argIndex, callable, true, 
true, null)}.
+     */
+    public static <T extends TemplateModel> T getOptionalArgument(
+            TemplateModel[] args, int argIndex, Class<T> type, T defaultValue, 
TemplateDirectiveModel callable)
+            throws TemplateException {
+        return castArgumentValue(args[argIndex], argIndex, type, true, 
defaultValue, callable, false);
+    }
+
+    /**
+     * Checks if the argument value is of the proper type, also, if it's 
{@code null}/omitted, in which case it can
+     * throw an exception or return a default value.
+     * <p>
+     * The point of this method is not only to decrease the boiler plate 
needed for these common checks, but also to
+     * standardize the error message content. If the checks themselves don't 
fit your needs, you should still use {@link
+     * #newArgumentValueTypeException(TemplateModel, int, Class, 
TemplateCallableModel, boolean)} and its overloads,
+     * also {@link #newNullOrOmittedArgumentException(int, 
TemplateCallableModel, boolean)} and its overloads to
+     * generate similar error messages.
+     *
+     * @param argIdx
+     *         The index in the {@code args} array (assumed to be a valid 
index. This is information is needed for
+     *         proper error messages.
+     * @param type
+     *         The expected class of the argument (usually a {@link 
TemplateModel} subinterface). {@code null} if
+     *         there are no type restrictions.
+     * @param optional
+     *         If we allow the parameter to be {@code null} or omitted.
+     * @param defaultValue
+     *         The value to return if the parameter was {@code null} or 
omitted.
+     * @param callable
+     *         The {@link TemplateCallableModel} whose argument we cast; 
required for printing proper error message.
+     * @param calledAsFunction
+     *         Tells if the {@code callable} was called as function (as 
opposed to called as a directive). This
+     *         information is needed because a {@link TemplateCallableModel} 
might implements both {@link
+     *         TemplateFunctionModel} and {@link TemplateDirectiveModel}, in 
which case this method couldn't tell if the
+     *         argument of which we are casting.
+     *
+     * @return The argument value of the proper type.
+     *
+     * @throws TemplateException
+     *         If the argument is not of the proper type or is non-optional 
yet {@code null}/omitted. The error message
+     *         describes the problem in detail, and is meant to be shown for 
the template author.
+     */
+    public static <T extends TemplateModel> T castArgumentValue(
+            TemplateModel argValue, int argIdx, Class<T> type,
+            boolean optional, T defaultValue, TemplateCallableModel callable,
+            boolean calledAsFunction)
+            throws TemplateException {
+        if (argValue == null) {
+            if (optional) {
+                return defaultValue;
+            }
+            throw newNullOrOmittedArgumentException(argIdx, callable, 
calledAsFunction);
+        }
+        if (type == null || type.isInstance(argValue)) {
+            return (T) argValue;
+        }
+        throw newArgumentValueTypeException(
+                argValue, argIdx, type, callable,
+                calledAsFunction);
+    }
+    
     // Argument count
 
     /** Convenience method for calling {@link #checkArgumentCount(int, int, 
int, TemplateCallableModel, boolean)}. */
@@ -461,8 +863,22 @@ public final class CallableUtils {
     }
 
     /**
-     * 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.
+     * 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.
+     *
+     * @param argCnt
+     *         The actual length of the argument array
+     * @param minCnt
+     *         The minimum expected number of arguments
+     * @param maxCnt
+     *         The maximum expected number of arguments. Should be equal to or 
greater than {@code minCnt}
+     * @param callable
+     *         The {@link TemplateCallableModel} whose argument we cast; 
required for printing proper error message.
+     * @param calledAsFunction
+     *         Tells if the {@code callable} was called as function (as 
opposed to called as a directive). This
+     *         information is needed because a {@link TemplateCallableModel} 
might implements both {@link
+     *         TemplateFunctionModel} and {@link TemplateDirectiveModel}, in 
which case this method couldn't tell if the
+     *         argument of which we are casting.
      */
     public static void checkArgumentCount(int argCnt, int minCnt, int maxCnt,
         TemplateCallableModel callable, boolean calledAsFunction) throws 
TemplateException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
index d833c13..caccdc6 100644
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
+++ 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertDirective.java
@@ -26,10 +26,9 @@ import org.apache.freemarker.core.CallPlace;
 import org.apache.freemarker.core.Environment;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.util.TemplateLanguageUtils;
+import org.apache.freemarker.core.util.CallableUtils;
 
 public class AssertDirective implements TemplateDirectiveModel {
     public static AssertDirective INSTANCE = new AssertDirective();
@@ -39,16 +38,7 @@ public class AssertDirective implements 
TemplateDirectiveModel {
     @Override
     public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, 
Environment env)
             throws TemplateException, IOException {
-        TemplateModel test = args[0];
-        if (test == null) {
-            throw new MissingRequiredParameterException("test", env);
-        }
-        if (!(test instanceof TemplateBooleanModel)) {
-            throw new AssertationFailedInTemplateException("Assertion 
failed:\n"
-                    + "The value had to be boolean, but it was of type" + 
TemplateLanguageUtils.getTypeDescription(test),
-                    env);
-        }
-        if (!((TemplateBooleanModel) test).getAsBoolean()) {
+        if (!CallableUtils.getBooleanArgument(args, 0, this)) {
             throw new AssertationFailedInTemplateException("Assertion 
failed:\n"
                     + "the value was false.",
                     env);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
index a894a7d..923ced8 100644
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
+++ 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertEqualsDirective.java
@@ -32,6 +32,7 @@ import 
org.apache.freemarker.core.model.TemplateDirectiveModel;
 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.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util._StringUtils;
 
@@ -42,12 +43,9 @@ public class AssertEqualsDirective implements 
TemplateDirectiveModel {
     private static final int ACTUAL_ARG_IDX = 0;
     private static final int EXPECTED_ARG_IDX = 1;
 
-    private static final String ACTUAL_ARG_NAME = "actual";
-    private static final String EXPECTED_ARG_NAME = "expected";
-
     private static final StringToIndexMap ARG_NAME_TO_IDX = 
StringToIndexMap.of(
-            ACTUAL_ARG_NAME, ACTUAL_ARG_IDX,
-            EXPECTED_ARG_NAME, EXPECTED_ARG_IDX);
+            "actual", ACTUAL_ARG_IDX,
+            "expected", EXPECTED_ARG_IDX);
 
     private static final ArgumentArrayLayout ARGS_LAYOUT = 
ArgumentArrayLayout.create(
             0, false,
@@ -58,16 +56,8 @@ public class AssertEqualsDirective implements 
TemplateDirectiveModel {
     @Override
     public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, 
Environment env)
             throws TemplateException, IOException {
-        TemplateModel actual = args[ACTUAL_ARG_IDX];
-        if (actual == null) {
-            throw new MissingRequiredParameterException(ACTUAL_ARG_NAME, env);
-        }
-
-        TemplateModel expected = args[EXPECTED_ARG_IDX];
-        if (expected == null) {
-            throw new MissingRequiredParameterException(EXPECTED_ARG_NAME, 
env);
-        }
-
+        TemplateModel actual = CallableUtils.getArgument(args, ACTUAL_ARG_IDX, 
null, this);
+        TemplateModel expected = CallableUtils.getArgument(args, 
ACTUAL_ARG_IDX, null, this);
         if (!env.applyEqualsOperatorLenient(actual, expected)) {
             throw new AssertationFailedInTemplateException("Assertion 
failed:\n"
                     + "Expected: " + tryUnwrap(expected) + "\n"

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
index e315ef6..9e82895 100644
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
+++ 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/AssertFailsDirective.java
@@ -29,8 +29,7 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ArgumentArrayLayout;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 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.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util._NullWriter;
 import org.apache.freemarker.core.util._StringUtils;
@@ -44,16 +43,11 @@ public class AssertFailsDirective implements 
TemplateDirectiveModel {
     private static final int EXCEPTION_ARG_IDX = 2;
     private static final int CAUSE_NESTING_LEVEL_ARG_IDX = 3;
 
-    private static final String MESSAGE_ARG_NAME = "message";
-    private static final String MESSAGE_REGEXP_ARG_NAME = "messageRegexp";
-    private static final String EXCEPTION_ARG_NAME = "exception";
-    private static final String CAUSE_NESTING_LEVEL_ARG_NAME = 
"causeNestingLevel";
-
     private static final StringToIndexMap ARG_NAME_TO_IDX = 
StringToIndexMap.of(
-            MESSAGE_ARG_NAME, MESSAGE_ARG_IDX,
-            MESSAGE_REGEXP_ARG_NAME, MESSAGE_REGEXP_ARG_IDX,
-            EXCEPTION_ARG_NAME, EXCEPTION_ARG_IDX,
-            CAUSE_NESTING_LEVEL_ARG_NAME, CAUSE_NESTING_LEVEL_ARG_IDX);
+            "message", MESSAGE_ARG_IDX,
+            "messageRegexp", MESSAGE_REGEXP_ARG_IDX,
+            "exception", EXCEPTION_ARG_IDX,
+            "causeNestingLevel", CAUSE_NESTING_LEVEL_ARG_IDX);
 
     private static final ArgumentArrayLayout ARGS_LAYOUT = 
ArgumentArrayLayout.create(
             0, false,
@@ -64,10 +58,15 @@ public class AssertFailsDirective implements 
TemplateDirectiveModel {
 
     public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, 
Environment env)
             throws TemplateException, IOException {
-        String message = getAsString(args[MESSAGE_ARG_IDX], MESSAGE_ARG_NAME, 
env);
-        Pattern messageRegexp = getAsPattern(args[MESSAGE_REGEXP_ARG_IDX], 
MESSAGE_REGEXP_ARG_NAME, env);
-        String exception = getAsString(args[EXCEPTION_ARG_IDX], 
EXCEPTION_ARG_NAME, env);
-        int causeNestingLevel = getAsInt(args[CAUSE_NESTING_LEVEL_ARG_IDX], 0, 
CAUSE_NESTING_LEVEL_ARG_NAME, env);
+        String message = CallableUtils.getOptionalStringArgument(args, 
MESSAGE_ARG_IDX, this);
+        Pattern messageRegexp;
+        {
+            String s = CallableUtils.getOptionalStringArgument(args, 
MESSAGE_REGEXP_ARG_IDX, this);
+            messageRegexp = s != null ? Pattern.compile(s, 
Pattern.CASE_INSENSITIVE) : null;
+        }
+        String exception = CallableUtils.getOptionalStringArgument(args, 
EXCEPTION_ARG_IDX, this);
+        int causeNestingLevel = CallableUtils.getOptionalIntArgument(
+                args, CAUSE_NESTING_LEVEL_ARG_IDX, 0, this);
         if (callPlace.hasNestedContent()) {
             boolean blockFailed;
             try {
@@ -144,34 +143,4 @@ public class AssertFailsDirective implements 
TemplateDirectiveModel {
         return true;
     }
 
-    private String getAsString(TemplateModel value, String paramName, 
Environment env)
-            throws TemplateException {
-        if (value == null) {
-            return null;
-        }
-        if (value instanceof TemplateScalarModel) {
-            return ((TemplateScalarModel) value).getAsString();
-        } else {
-            throw new BadParameterTypeException(paramName, "string", value, 
env);
-        }
-    }
-
-    private Pattern getAsPattern(TemplateModel value, String paramName, 
Environment env)
-            throws TemplateException {
-        String s = getAsString(value, paramName, env);
-        return s != null ? Pattern.compile(s, Pattern.CASE_INSENSITIVE) : null;
-    }
-
-    private int getAsInt(TemplateModel value, int defaultValue, String 
paramName, Environment env)
-            throws TemplateException {
-        if (value == null) {
-            return defaultValue;
-        }
-        if (value instanceof TemplateNumberModel) {
-            return ((TemplateNumberModel) value).getAsNumber().intValue(); 
-        } else {
-            throw new BadParameterTypeException(paramName, "number", value, 
env);
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/BadParameterTypeException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/BadParameterTypeException.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/BadParameterTypeException.java
deleted file mode 100644
index 34b7ec5..0000000
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/BadParameterTypeException.java
+++ /dev/null
@@ -1,60 +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.test.templateutil;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.util._StringUtils;
-
-/**
- * Indicates that a named directive/function parameter is not of the expected 
type.  
- * This is will be public and go into the org.apache.freemarker.core.ast when 
the directive/method stuff was reworked.
- */
-public class BadParameterTypeException extends ParameterException {
-
-    public BadParameterTypeException(String parameterName, String 
expectedType, TemplateModel value, Environment env) {
-        this(parameterName, expectedType, value, null, null, env);
-    }
-
-    public BadParameterTypeException(String parameterName, String 
expectedType, TemplateModel value,
-            Exception cause, Environment env) {
-        this(parameterName, expectedType, value, null, cause, env);
-    }
-
-    public BadParameterTypeException(String parameterName, String 
expectedType, TemplateModel value,
-            String description, Environment env) {
-        this(parameterName, expectedType, value, description, null, env);
-    }
-
-    public BadParameterTypeException(
-            String parameterName, String expectedType, TemplateModel value, 
String description, Exception cause, Environment env) {
-        super(parameterName,
-                "The type of the parameter " + 
_StringUtils.jQuote(parameterName) + " should be " + expectedType
-                + ", but the actual value was " + getTypeDescription(value) + 
"."
-                + (description != null ? " " + 
_StringUtils.jQuote(description) : ""),
-                cause, env);
-    }
-
-    private static String getTypeDescription(TemplateModel value) {
-        //FIXME: This should call 
EvaluationUtil.getTypeDescriptionForDebugging, but that's not visible from here 
yet.
-        return value == null ? "Null" : value.getClass().getName();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/MissingRequiredParameterException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/MissingRequiredParameterException.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/MissingRequiredParameterException.java
deleted file mode 100644
index 19b2927..0000000
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/MissingRequiredParameterException.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.test.templateutil;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.util._StringUtils;
-
-/**
- * Indicates that a named directive/function parameter is missing.  
- * This is will be public and go into the org.apache.freemarker.core.ast when 
the directive/method stuff was reworked.
- */
-class MissingRequiredParameterException extends ParameterException {
-
-    public MissingRequiredParameterException(String parameterName, Environment 
env) {
-        this(parameterName, null, null, env);
-    }
-
-    public MissingRequiredParameterException(String parameterName, Exception 
cause, Environment env) {
-        this(parameterName, null, cause, env);
-    }
-
-    public MissingRequiredParameterException(String parameterName, String 
description, Environment env) {
-        this(parameterName, description, null, env);
-    }
-
-    public MissingRequiredParameterException(String parameterName, String 
description, Exception cause, Environment env) {
-        super(parameterName,
-                "Required parameter " + _StringUtils.jQuote(parameterName) + " 
is missing, "
-                + "or the parameter value was null."
-                + (description != null ? " " + 
_StringUtils.jQuote(description) : ""),
-                cause, env);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/ParameterException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/ParameterException.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/ParameterException.java
deleted file mode 100644
index 2839ad3..0000000
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/ParameterException.java
+++ /dev/null
@@ -1,54 +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.test.templateutil;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.TemplateException;
-
-/**
- * An exception that is related to a named parameter of a directive or 
function.
- * This is will be public and go into the org.apache.freemarker.core.ast when 
the method/directive stuff was reworked.
- */
-abstract class ParameterException extends TemplateException {
-    
-    private final String parameterName;
-    
-    public ParameterException(String parameterName, Environment env) {
-        this(parameterName, null, null, env);
-    }
-
-    public ParameterException(String parameterName, Exception cause, 
Environment env) {
-        this(parameterName, null, cause, env);
-    }
-
-    public ParameterException(String parameterName, String description, 
Environment env) {
-        this(parameterName, description, null, env);
-    }
-
-    public ParameterException(String parameterName, String description, 
Exception cause, Environment env) {
-        super(description, cause, env);
-        this.parameterName = parameterName;
-    }
-
-    public String getParameterName() {
-        return parameterName;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/888ebe11/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/UnsupportedParameterException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/UnsupportedParameterException.java
 
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/UnsupportedParameterException.java
deleted file mode 100644
index 4b0e274..0000000
--- 
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/templateutil/UnsupportedParameterException.java
+++ /dev/null
@@ -1,50 +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.test.templateutil;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.util._StringUtils;
-
-/**
- * Indicates that a named directive/function parameter is unsupported (like a 
typo).  
- * This is will be public and go into the org.apache.freemarker.core.ast when 
the directive/method stuff was reworked.
- */
-class UnsupportedParameterException extends ParameterException {
-
-    public UnsupportedParameterException(String parameterName, Environment 
env) {
-        this(parameterName, null, null, env);
-    }
-
-    public UnsupportedParameterException(String parameterName, Exception 
cause, Environment env) {
-        this(parameterName, null, cause, env);
-    }
-
-    public UnsupportedParameterException(String parameterName, String 
description, Environment env) {
-        this(parameterName, description, null, env);
-    }
-
-    public UnsupportedParameterException(String parameterName, String 
description, Exception cause, Environment env) {
-        super(parameterName,
-                "Unsuppored parameter: " + _StringUtils.jQuote(parameterName)
-                + (description == null ? "." : ". " + 
_StringUtils.jQuote(description)),
-                cause, env);
-    }
-
-}

Reply via email to