FREEMARKER-55: Adding more functions.

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

Branch: refs/heads/3
Commit: 234f96ec2c2868c72f797bda42a8d0c854e63b87
Parents: d0bd43c
Author: Woonsan Ko <[email protected]>
Authored: Fri Sep 8 12:21:53 2017 -0400
Committer: Woonsan Ko <[email protected]>
Committed: Fri Sep 8 12:21:53 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateCallableModel.java    |  11 +-
 .../spring/model/BindErrorsDirective.java       |   5 +-
 .../freemarker/spring/model/EvalFunction.java   | 153 +++++++++++++++++++
 .../spring/model/MessageFunction.java           |  14 +-
 .../freemarker/spring/model/MvcUrlFunction.java |  74 +++++++++
 .../model/SpringTemplateCallableHashModel.java  |   7 +-
 .../spring/model/TransformFunction.java         | 101 ++++++++++++
 .../freemarker/spring/model/UrlFunction.java    |  86 +++++++++++
 8 files changed, 437 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/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 de95df5..0d5d6ed 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
@@ -73,14 +73,21 @@ public abstract class AbstractSpringTemplateCallableModel 
implements TemplateCal
             RequestContext requestContext, String path, boolean 
ignoreNestedPath) throws TemplateException {
         final String resolvedPath = (ignoreNestedPath) ? path : 
resolveNestedPath(env, objectWrapperAndUnwrapper, path);
         BindStatus status = requestContext.getBindStatus(resolvedPath, false);
+        return wrapObject(objectWrapperAndUnwrapper, status);
+    }
+
+    protected final Object unwrapObject(ObjectWrapperAndUnwrapper 
objectWrapperAndUnwrapper, TemplateModel model) throws TemplateException {
+        return (model != null) ? objectWrapperAndUnwrapper.unwrap(model) : 
null;
+    }
 
-        if (status != null) {
+    protected final TemplateModel wrapObject(ObjectWrapperAndUnwrapper 
objectWrapperAndUnwrapper, Object object) throws TemplateException {
+        if (object != null) {
             if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
                 
CallableUtils.newGenericExecuteException("objectWrapperAndUnwrapper is not a 
DefaultObjectWrapper.",
                         this, isFunction());
             }
 
-            return ((DefaultObjectWrapper) 
objectWrapperAndUnwrapper).wrap(status);
+            return ((DefaultObjectWrapper) 
objectWrapperAndUnwrapper).wrap(object);
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindErrorsDirective.java
----------------------------------------------------------------------
diff --git 
a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindErrorsDirective.java
 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindErrorsDirective.java
index c98db67..e35b6ee 100644
--- 
a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindErrorsDirective.java
+++ 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindErrorsDirective.java
@@ -30,7 +30,6 @@ 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.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.ObjectWrappingException;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.util.CallableUtils;
@@ -104,7 +103,7 @@ public class BindErrorsDirective extends 
AbstractSpringTemplateDirectiveModel {
     }
 
     private final TemplateModel getBindErrorsTemplateModel(Environment env, 
ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            RequestContext requestContext, String name) throws 
ObjectWrappingException {
+            RequestContext requestContext, String name) throws 
TemplateException {
         final Errors errors = requestContext.getErrors(name, false);
 
         if (errors != null && errors.hasErrors()) {
@@ -113,7 +112,7 @@ public class BindErrorsDirective extends 
AbstractSpringTemplateDirectiveModel {
                         this, isFunction());
             }
 
-            return ((DefaultObjectWrapper) 
objectWrapperAndUnwrapper).wrap(errors);
+            return wrapObject(objectWrapperAndUnwrapper, errors);
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/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
new file mode 100644
index 0000000..a380f19
--- /dev/null
+++ 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/EvalFunction.java
@@ -0,0 +1,153 @@
+/*
+ * 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.spring.model;
+
+import javax.servlet.http.HttpServletRequest;
+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.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.context.expression.EnvironmentAccessor;
+import org.springframework.context.expression.MapAccessor;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.PropertyAccessor;
+import org.springframework.expression.TypedValue;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.StandardTypeConverter;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * A <code>TemplateFunctionModel</code> providing functionality equivalent to 
the Spring Framework's
+ * <code>&lt;spring:eval /&gt;</code> JSP Tag Library.
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:message 
/&gt;</code> JSP Tag Library, this function
+ * does not support <code>htmlEscape</code> parameter. It always returns the 
message not to escape HTML's
+ * because it is much easier to control escaping in FreeMarker Template 
expressions.
+ * </P>
+ */
+public class EvalFunction extends AbstractSpringTemplateFunctionModel {
+
+    public static final String NAME = "eval";
+
+    private static final int EXPRESSION_PARAM_IDX = 0;
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT = 
ArgumentArrayLayout.create(1, false, null, false);
+
+    private final ExpressionParser expressionParser = new 
SpelExpressionParser();
+
+    public EvalFunction(HttpServletRequest request, HttpServletResponse 
response) {
+        super(request, response);
+    }
+
+    @Override
+    public TemplateModel executeInternal(TemplateModel[] args, CallPlace 
callPlace, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, 
RequestContext requestContext)
+                    throws TemplateException {
+        final String expressionString = CallableUtils.getStringArgument(args, 
EXPRESSION_PARAM_IDX, this);
+        final Expression expression = 
expressionParser.parseExpression(expressionString);
+
+        // TODO: cache evaluationContext somewhere in request level....
+        EvaluationContext evaluationContext = createEvaluationContext(env, 
requestContext);
+
+        final Object result = expression.getValue(evaluationContext);
+        return wrapObject(objectWrapperAndUnwrapper, result);
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+    private EvaluationContext createEvaluationContext(final Environment env, 
final RequestContext requestContext) {
+        StandardEvaluationContext context = new StandardEvaluationContext();
+
+        context.addPropertyAccessor(new 
EnvironmentVariablesPropertyAccessor(env));
+        context.addPropertyAccessor(new MapAccessor());
+        context.addPropertyAccessor(new EnvironmentAccessor());
+        context.setBeanResolver(new 
BeanFactoryResolver(requestContext.getWebApplicationContext()));
+
+        ConversionService conversionService = getConversionService();
+
+        if (conversionService != null) {
+            context.setTypeConverter(new 
StandardTypeConverter(conversionService));
+        }
+
+        return context;
+    }
+
+    private ConversionService getConversionService() {
+        return (ConversionService) 
getRequest().getAttribute(ConversionService.class.getName());
+    }
+
+    private static class EnvironmentVariablesPropertyAccessor implements 
PropertyAccessor {
+
+        private final Environment env;
+
+        public EnvironmentVariablesPropertyAccessor(final Environment env) {
+            this.env = env;
+        }
+
+        @Override
+        public Class<?>[] getSpecificTargetClasses() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public boolean canRead(EvaluationContext context, Object target, 
String name) throws AccessException {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        public TypedValue read(EvaluationContext context, Object target, 
String name) throws AccessException {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public boolean canWrite(EvaluationContext context, Object target, 
String name) throws AccessException {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        public void write(EvaluationContext context, Object target, String 
name, Object newValue)
+                throws AccessException {
+            // TODO Auto-generated method stub
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/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 46547a6..94a1e0f 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
@@ -33,7 +33,6 @@ import 
org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateCollectionModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.impl.SimpleString;
 import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.apache.freemarker.core.util._StringUtils;
@@ -111,10 +110,10 @@ public class MessageFunction extends 
AbstractSpringTemplateFunctionModel {
 
         final TemplateModel messageResolvableModel = 
CallableUtils.getOptionalArgument(args, MESSAGE_RESOLVABLE_PARAM_IDX,
                 TemplateModel.class, this);
+        final MessageSourceResolvable messageResolvable = 
(MessageSourceResolvable) unwrapObject(
+                objectWrapperAndUnwrapper, messageResolvableModel);
 
-        if (messageResolvableModel != null) {
-            MessageSourceResolvable messageResolvable = 
(MessageSourceResolvable) objectWrapperAndUnwrapper
-                    .unwrap(messageResolvableModel);
+        if (messageResolvable != null) {
             message = messageSource.getMessage(messageResolvable, 
requestContext.getLocale());
         } else {
             final String code = _StringUtils
@@ -127,10 +126,9 @@ public class MessageFunction extends 
AbstractSpringTemplateFunctionModel {
                 if (!messageArgsModel.isEmptyCollection()) {
                     msgArgumentList = new ArrayList<>();
                     TemplateModel msgArgModel;
-                    int i = 0;
-                    for (TemplateModelIterator tit = 
messageArgsModel.iterator(); tit.hasNext(); i++) {
+                    for (TemplateModelIterator tit = 
messageArgsModel.iterator(); tit.hasNext(); ) {
                         msgArgModel = tit.next();
-                        
msgArgumentList.add(objectWrapperAndUnwrapper.unwrap(msgArgModel));
+                        
msgArgumentList.add(unwrapObject(objectWrapperAndUnwrapper, msgArgModel));
                     }
                 }
 
@@ -143,7 +141,7 @@ public class MessageFunction extends 
AbstractSpringTemplateFunctionModel {
             }
         }
 
-        return (message != null) ? new SimpleString(message) : null;
+        return wrapObject(objectWrapperAndUnwrapper, message);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MvcUrlFunction.java
----------------------------------------------------------------------
diff --git 
a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MvcUrlFunction.java
 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MvcUrlFunction.java
new file mode 100644
index 0000000..047d5c2
--- /dev/null
+++ 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MvcUrlFunction.java
@@ -0,0 +1,74 @@
+/*
+ * 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.spring.model;
+
+import javax.servlet.http.HttpServletRequest;
+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.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.StringToIndexMap;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * A <code>TemplateFunctionModel</code> providing functionality equivalent to 
the Spring Framework's
+ * <code>spring:mvcUrl</code> JSP Tag Library Function.
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * </PRE>
+ */
+public class MvcUrlFunction extends AbstractSpringTemplateFunctionModel {
+
+    public static final String NAME = "mvcUrl";
+
+    private static final int MAPPING_NAME_PARAM_IDX = 0;
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    null,
+                    false
+                    );
+
+    public MvcUrlFunction(HttpServletRequest request, HttpServletResponse 
response) {
+        super(request, response);
+    }
+
+    @Override
+    public TemplateModel executeInternal(TemplateModel[] args, CallPlace 
callPlace, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, 
RequestContext requestContext)
+                    throws TemplateException {
+        // TODO
+        return null;
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/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 dd2f71c..e090cd9 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
@@ -47,11 +47,16 @@ public final class SpringTemplateCallableHashModel 
implements TemplateHashModel,
     private String nestedPath;
 
     public SpringTemplateCallableHashModel(final HttpServletRequest request, 
final HttpServletResponse response) {
-        callablesMap.put(BindDirective.NAME, new BindDirective(request, 
response));
         callablesMap.put(MessageFunction.NAME, new MessageFunction(request, 
response));
         callablesMap.put(ThemeFunction.NAME, new ThemeFunction(request, 
response));
         callablesMap.put(BindErrorsDirective.NAME, new 
BindErrorsDirective(request, response));
         callablesMap.put(NestedPathDirective.NAME, new 
NestedPathDirective(request, response));
+        callablesMap.put(BindDirective.NAME, new BindDirective(request, 
response));
+
+        callablesMap.put(TransformFunction.NAME, new 
TransformFunction(request, response));
+        callablesMap.put(UrlFunction.NAME, new UrlFunction(request, response));
+        callablesMap.put(EvalFunction.NAME, new EvalFunction(request, 
response));
+        callablesMap.put(MvcUrlFunction.NAME, new MvcUrlFunction(request, 
response));
     }
 
     public TemplateModel get(String key) throws TemplateException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/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
new file mode 100644
index 0000000..ee3cc1c
--- /dev/null
+++ 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/TransformFunction.java
@@ -0,0 +1,101 @@
+/*
+ * 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.spring.model;
+
+import java.beans.PropertyEditor;
+
+import javax.servlet.http.HttpServletRequest;
+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.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * A <code>TemplateFunctionModel</code> providing functionality equivalent to 
the Spring Framework's
+ * <code>&lt;spring:transform /&gt;</code> JSP Tag Library.
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * </PRE>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ *   &lt;@spring.bind "user"; status&gt;
+ *     ${spring.transform(status, user.birthDate)}
+ *   &lt;/@spring.bind&gt;
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:bind /&gt;</code> 
JSP Tag Library, this directive
+ * does not support <code>htmlEscape</code> parameter. It always has 
<code>BindStatus</code> not to escape HTML's
+ * because it is much easier to control escaping in FreeMarker Template 
expressions.
+ * </P>
+ */
+public class TransformFunction extends AbstractSpringTemplateFunctionModel {
+
+    public static final String NAME = "transform";
+
+    private static final int PROPERTY_EDITOR_PARAM_IDX = 0;
+    private static final int VALUE_PARAM_IDX = 1;
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT = 
ArgumentArrayLayout.create(2, false, null, false);
+
+    public TransformFunction(HttpServletRequest request, HttpServletResponse 
response) {
+        super(request, response);
+    }
+
+    @Override
+    public 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 = (PropertyEditor) 
unwrapObject(objectWrapperAndUnwrapper, editorModel);
+
+        final TemplateModel valueModel = 
CallableUtils.getOptionalArgument(args, VALUE_PARAM_IDX, TemplateModel.class, 
this);
+        final Object value = unwrapObject(objectWrapperAndUnwrapper, 
valueModel);
+
+        String valueAsString = null;
+
+        if (value != null) {
+            if (editor != null) {
+                editor.setValue(value);
+                valueAsString = editor.getAsText();
+            } else {
+                valueAsString = value.toString();
+            }
+        }
+
+        return wrapObject(objectWrapperAndUnwrapper, valueAsString);
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/234f96ec/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
----------------------------------------------------------------------
diff --git 
a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
new file mode 100644
index 0000000..402e74d
--- /dev/null
+++ 
b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/UrlFunction.java
@@ -0,0 +1,86 @@
+/*
+ * 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.spring.model;
+
+import javax.servlet.http.HttpServletRequest;
+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.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.StringToIndexMap;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * A <code>TemplateFunctionModel</code> providing functionality equivalent to 
the Spring Framework's
+ * <code>&lt;spring:url /&gt;</code> JSP Tag Library.
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:message 
/&gt;</code> JSP Tag Library, this function
+ * does not support <code>htmlEscape</code> parameter. It always returns the 
message not to escape HTML's
+ * because it is much easier to control escaping in FreeMarker Template 
expressions.
+ * </P>
+ */
+public class UrlFunction extends AbstractSpringTemplateFunctionModel {
+
+    public static final String NAME = "url";
+
+    private static final int VALUE_PARAM_IDX = 0;
+    private static final int CONTEXT_PARAM_IDX = 1;
+
+    private static final String CONTEXT_PARAM_NAME = "context";
+
+
+    // TODO: How to deal with parameters (spring:param tags)??
+
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    StringToIndexMap.of(CONTEXT_PARAM_NAME, CONTEXT_PARAM_IDX),
+                    false
+                    );
+
+    public UrlFunction(HttpServletRequest request, HttpServletResponse 
response) {
+        super(request, response);
+    }
+
+    @Override
+    public TemplateModel executeInternal(TemplateModel[] args, CallPlace 
callPlace, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, 
RequestContext requestContext)
+                    throws TemplateException {
+        // TODO
+        return null;
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+}

Reply via email to