FREEMARKER-55: introducing CallableUtils#getAndUnwrap...(...) utils.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/3f6ee183 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/3f6ee183 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/3f6ee183 Branch: refs/heads/3 Commit: 3f6ee183ab87e193c4eaa465652d5d8b204cd992 Parents: 40bd07b Author: Woonsan Ko <woon...@apache.org> Authored: Wed Sep 13 12:14:08 2017 -0400 Committer: Woonsan Ko <woon...@apache.org> Committed: Wed Sep 13 12:14:08 2017 -0400 ---------------------------------------------------------------------- .../freemarker/core/util/CallableUtils.java | 153 +++++++++++++++++++ .../AbstractSpringTemplateCallableModel.java | 46 ++++++ .../AbstractSpringTemplateDirectiveModel.java | 23 +-- .../AbstractSpringTemplateFunctionModel.java | 23 +-- .../freemarker/spring/model/EvalFunction.java | 2 +- .../spring/model/MessageFunction.java | 6 +- .../model/SpringTemplateCallableHashModel.java | 4 +- .../spring/model/TransformFunction.java | 12 +- 8 files changed, 214 insertions(+), 55 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/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 9bf96e2..0c8f09f 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.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateBooleanModel; import org.apache.freemarker.core.model.TemplateCallableModel; import org.apache.freemarker.core.model.TemplateDirectiveModel; @@ -302,6 +303,20 @@ public final class CallableUtils { return desc; } + private static _ErrorDescriptionBuilder getMessageBadGenericArgumentType( + Object 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(argValue.getClass()), + "." }, + calledAsFunction)); + return desc; + } + public static void executeWith0Arguments( TemplateDirectiveModel directive, CallPlace callPlace, Writer out, Environment env) throws IOException, TemplateException { @@ -717,6 +732,144 @@ public final class CallableUtils { calledAsFunction); } + // getAndUnwrapArgument(...)'s and getOptionalAndUnwrapArgument(...)'s + + /** + * Convenience method to call + * {@link #unwrapAndCastArgumentValue(TemplateModel, int, Class, boolean, TemplateModel, TemplateCallableModel, boolean)}. + */ + public static <T> T getAndUnwrapArgument( + TemplateModel[] args, int argIndex, Class<T> type, TemplateFunctionModel callable, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + return unwrapAndCastArgumentValue(args[argIndex], argIndex, type, false, null, callable, true, + objectWrapperAndUnwrapper); + } + + /** + * Convenience method to call + * {@link #unwrapAndCastArgumentValue(TemplateModel, int, Class, boolean, TemplateModel, TemplateCallableModel, boolean)}. + */ + public static <T> T getAndUnwrapArgument( + TemplateModel[] args, int argIndex, Class<T> type, TemplateDirectiveModel callable, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + return unwrapAndCastArgumentValue(args[argIndex], argIndex, type, false, null, callable, false, + objectWrapperAndUnwrapper); + } + + /** + * Convenience method to call + * {@link #unwrapAndCastArgumentValue(TemplateModel, int, Class, boolean, TemplateModel, TemplateCallableModel, boolean)}. + */ + public static <T> T getOptionalAndUnwrapArgument( + TemplateModel[] args, int argIndex, Class<T> type, TemplateFunctionModel callable, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + return unwrapAndCastArgumentValue(args[argIndex], argIndex, type, true, null, callable, true, + objectWrapperAndUnwrapper); + } + + /** + * Convenience method to call + * {@link #unwrapAndCastArgumentValue(TemplateModel, int, Class, boolean, TemplateModel, TemplateCallableModel, boolean)}. + */ + public static <T> T getOptionalAndUnwrapArgument( + TemplateModel[] args, int argIndex, Class<T> type, TemplateDirectiveModel callable, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + return unwrapAndCastArgumentValue(args[argIndex], argIndex, type, true, null, callable, false, + objectWrapperAndUnwrapper); + } + + /** + * Convenience method to call + * {@link #unwrapAndCastArgumentValue(TemplateModel, int, Class, boolean, TemplateModel, TemplateCallableModel, boolean)}. + */ + public static <T> T getOptionalAndUnwrapArgument( + TemplateModel[] args, int argIndex, Class<T> type, T defaultValue, TemplateFunctionModel callable, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + return unwrapAndCastArgumentValue(args[argIndex], argIndex, type, true, defaultValue, callable, true, + objectWrapperAndUnwrapper); + } + + /** + * Convenience method to call + * {@link #unwrapAndCastArgumentValue(TemplateModel, int, Class, boolean, TemplateModel, TemplateCallableModel, boolean)}. + */ + public static <T> T getOptionalAndUnwrapArgument( + TemplateModel[] args, int argIndex, Class<T> type, T defaultValue, TemplateDirectiveModel callable, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + return unwrapAndCastArgumentValue(args[argIndex], argIndex, type, true, defaultValue, callable, false, + objectWrapperAndUnwrapper); + } + + /** + * Unwrap the argument to a plain Java object first and 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 argValue + * The argument value at the position of {@code argIdx}. + * @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 unwrapped argument as plain Java object (not 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. + * @param objectWrapperAndUnwrapper + * The ObjectWrapperAndUnwrapper instance to use when unwrapping the argument to a plain Java object. + * + * @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> T unwrapAndCastArgumentValue( + TemplateModel argValue, int argIdx, Class<T> type, + boolean optional, T defaultValue, TemplateCallableModel callable, + boolean calledAsFunction, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) + throws TemplateException { + if (objectWrapperAndUnwrapper == null) { + throw newGenericExecuteException("ObjectWrapperAndUnwrapper shouldn't be null.", callable, + calledAsFunction); + } + final Object argValueObject = (argValue != null) ? objectWrapperAndUnwrapper.unwrap(argValue) : null; + if (argValueObject == null) { + if (optional) { + return defaultValue; + } + throw newNullOrOmittedArgumentException(argIdx, callable, calledAsFunction); + } + if (type == null || type.isInstance(argValueObject)) { + return (T) argValueObject; + } + throw new TemplateException( + getMessageBadGenericArgumentType(argValue, argIdx, + new Class[] { type }, + type.getName(), + callable, calledAsFunction)); + } + // Other type of arg: /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateCallableModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateCallableModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateCallableModel.java index 03142d6..1c3d71b 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateCallableModel.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateCallableModel.java @@ -24,12 +24,16 @@ import javax.servlet.http.HttpServletResponse; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateCallableModel; +import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateStringModel; +import org.apache.freemarker.core.util.CallableUtils; import org.springframework.web.servlet.support.BindStatus; import org.springframework.web.servlet.support.RequestContext; +import org.springframework.web.servlet.view.AbstractTemplateView; /** * Abstract TemplateCallableModel for derived classes to support Spring MVC based templating environment. @@ -66,6 +70,48 @@ abstract class AbstractSpringTemplateCallableModel implements TemplateCallableMo } /** + * Find {@link ObjectWrapperAndUnwrapper} from the environment. + * @param env environment + * @param calledAsFunction whether or not this is called from a {@link TemplateFunctionModel}. + * @return {@link ObjectWrapperAndUnwrapper} from the environment + * @throws TemplateException if the ObjectWrapper in the environment is not an ObjectWrapperAndUnwrapper + */ + protected ObjectWrapperAndUnwrapper getObjectWrapperAndUnwrapper(Environment env, boolean calledAsFunction) + throws TemplateException { + final ObjectWrapper objectWrapper = env.getObjectWrapper(); + + if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) { + CallableUtils.newGenericExecuteException( + "The ObjectWrapper of environment isn't an instance of ObjectWrapperAndUnwrapper.", this, + calledAsFunction); + } + + return (ObjectWrapperAndUnwrapper) objectWrapper; + } + + /** + * Find Spring {@link RequestContext} from the environment. + * @param env environment + * @param calledAsFunction whether or not this is called from a {@link TemplateFunctionModel}. + * @return Spring {@link RequestContext} from the environment + * @throws TemplateException if Spring {@link RequestContext} from the environment is not found + */ + protected RequestContext getRequestContext(final Environment env, boolean calledAsFunction) + throws TemplateException { + TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE); + + if (rcModel == null) { + CallableUtils.newGenericExecuteException( + AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, false); + } + + RequestContext requestContext = (RequestContext) getObjectWrapperAndUnwrapper(env, calledAsFunction) + .unwrap(rcModel); + + return requestContext; + } + + /** * Find {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext} by the {@code path} * and wrap it as a {@link TemplateModel}. * <P> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java index a02558f..c91b387 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java @@ -28,13 +28,10 @@ import javax.servlet.http.HttpServletResponse; import org.apache.freemarker.core.CallPlace; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.util.CallableUtils; import org.springframework.web.servlet.support.RequestContext; -import org.springframework.web.servlet.view.AbstractTemplateView; /** * Abstract TemplateDirectiveModel for derived classes to support Spring MVC based templating environment. @@ -61,23 +58,9 @@ abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTempla @Override public final void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env) throws TemplateException, IOException { - final ObjectWrapper objectWrapper = env.getObjectWrapper(); - - if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) { - CallableUtils.newGenericExecuteException( - "The ObjectWrapper of environment isn't an instance of ObjectWrapperAndUnwrapper.", this, false); - } - - TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE); - - if (rcModel == null) { - CallableUtils.newGenericExecuteException( - AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, false); - } - - RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel); - - executeInternal(args, callPlace, out, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext); + final ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper = getObjectWrapperAndUnwrapper(env, false); + final RequestContext requestContext = getRequestContext(env, false); + executeInternal(args, callPlace, out, env, objectWrapperAndUnwrapper, requestContext); } /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateFunctionModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateFunctionModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateFunctionModel.java index 90bd3df..afb3262 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateFunctionModel.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateFunctionModel.java @@ -25,13 +25,10 @@ import javax.servlet.http.HttpServletResponse; import org.apache.freemarker.core.CallPlace; import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.util.CallableUtils; import org.springframework.web.servlet.support.RequestContext; -import org.springframework.web.servlet.view.AbstractTemplateView; /** * Abstract TemplateFunctionModel for derived classes to support Spring MVC based templating environment. @@ -57,23 +54,9 @@ abstract class AbstractSpringTemplateFunctionModel extends AbstractSpringTemplat */ @Override public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { - final ObjectWrapper objectWrapper = env.getObjectWrapper(); - - if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) { - CallableUtils.newGenericExecuteException( - "The ObjectWrapper of environment isn't an instance of ObjectWrapperAndUnwrapper.", this, true); - } - - TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE); - - if (rcModel == null) { - CallableUtils.newGenericExecuteException( - AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, true); - } - - RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel); - - return executeInternal(args, callPlace, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext); + final ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper = getObjectWrapperAndUnwrapper(env, true); + final RequestContext requestContext = getRequestContext(env, true); + return executeInternal(args, callPlace, env, objectWrapperAndUnwrapper, requestContext); } /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/EvalFunction.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/EvalFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/EvalFunction.java index 2c257c4..4710f6d 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/EvalFunction.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/EvalFunction.java @@ -106,7 +106,7 @@ class EvalFunction extends AbstractSpringTemplateFunctionModel { EvaluationContext evaluationContext = null; final SpringTemplateCallableHashModel springTemplateModel = getSpringTemplateCallableHashModel(env); - TemplateModel evaluationContextModel = springTemplateModel.get(EVALUATION_CONTEXT_VAR_NAME); + TemplateModel evaluationContextModel = springTemplateModel.getEvaluationContextModel(); if (evaluationContextModel != null) { evaluationContext = (EvaluationContext) objectWrapperAndUnwrapper.unwrap(evaluationContextModel); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MessageFunction.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MessageFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MessageFunction.java index c7b6db5..7ce8d40 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MessageFunction.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MessageFunction.java @@ -108,10 +108,8 @@ class MessageFunction extends AbstractSpringTemplateFunctionModel { String message = null; - final TemplateModel messageResolvableModel = CallableUtils.getOptionalArgument(args, - MESSAGE_RESOLVABLE_PARAM_IDX, TemplateModel.class, this); - final MessageSourceResolvable messageResolvable = (messageResolvableModel != null) - ? (MessageSourceResolvable) objectWrapperAndUnwrapper.unwrap(messageResolvableModel) : null; + final MessageSourceResolvable messageResolvable = CallableUtils.getOptionalAndUnwrapArgument(args, + MESSAGE_RESOLVABLE_PARAM_IDX, MessageSourceResolvable.class, this, objectWrapperAndUnwrapper); if (messageResolvable != null) { message = messageSource.getMessage(messageResolvable, requestContext.getLocale()); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java index 8afb4ab..40f7d59 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java @@ -48,12 +48,12 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel, * in Spring Framework JSP tag libraries. */ // NOTE: The model name, "nestedPathModel", must be different from the "nestedPath" directive model's name. - public static final String NESTED_PATH_MODEL = "nestedPathModel"; + private static final String NESTED_PATH_MODEL = "nestedPathModel"; /** * Name of the internal evaluation context template model used by <code>EvalFunction</code> to cache <code>EvaluationContext</code>. */ - public static final String EVALUATION_CONTEXT_MODEL = "evaluationContextModel"; + private static final String EVALUATION_CONTEXT_MODEL = "evaluationContextModel"; private Map<String, TemplateModel> modelsMap = new HashMap<>(); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3f6ee183/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/TransformFunction.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/TransformFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/TransformFunction.java index 96258a7..cac1dd6 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/TransformFunction.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/TransformFunction.java @@ -73,14 +73,10 @@ class TransformFunction extends AbstractSpringTemplateFunctionModel { protected TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext) throws TemplateException { - final TemplateModel editorModel = CallableUtils.getOptionalArgument(args, PROPERTY_EDITOR_PARAM_IDX, - TemplateModel.class, this); - final PropertyEditor editor = (editorModel != null) - ? (PropertyEditor) objectWrapperAndUnwrapper.unwrap(editorModel) : null; - - final TemplateModel valueModel = CallableUtils.getOptionalArgument(args, VALUE_PARAM_IDX, TemplateModel.class, - this); - final Object value = (valueModel != null) ? objectWrapperAndUnwrapper.unwrap(valueModel) : null; + final PropertyEditor editor = CallableUtils.getOptionalAndUnwrapArgument(args, PROPERTY_EDITOR_PARAM_IDX, + PropertyEditor.class, this, objectWrapperAndUnwrapper); + final Object value = CallableUtils.getOptionalAndUnwrapArgument(args, VALUE_PARAM_IDX, + null, this, objectWrapperAndUnwrapper); String valueAsString = null;