You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/09/13 09:49:02 UTC

[01/36] incubator-freemarker git commit: FREEMARKER-55: trying with a directive to replace spring:bind jsp tag.

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 b63e44880 -> 5f6a9174d


FREEMARKER-55: trying with a directive to replace spring:bind jsp tag.


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

Branch: refs/heads/3
Commit: 6709117683e29b34a6e155cac30a160f86f95745
Parents: e3e7f1e
Author: Woonsan Ko <wo...@apache.org>
Authored: Sat Aug 26 01:17:07 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Sat Aug 26 01:17:07 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/BindDirective.java  | 120 +++++++++++++++++++
 .../spring/web/view/FreeMarkerView.java         |  15 +++
 2 files changed, 135 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/67091176/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
new file mode 100644
index 0000000..967344d
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -0,0 +1,120 @@
+/*
+ * 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.io.IOException;
+import java.io.Writer;
+
+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.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.BeanModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.apache.freemarker.core.util.StringToIndexMap;
+import org.springframework.web.servlet.support.BindStatus;
+import org.springframework.web.servlet.support.RequestContext;
+import org.springframework.web.servlet.view.AbstractTemplateView;
+
+public class BindDirective implements TemplateDirectiveModel {
+
+    public static final String STATUS_VARIABLE_NAME = "status";
+
+    /**
+     * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
+     */
+    private static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
+
+    private static final int PATH_PARAM_IDX = 0;
+    private static final int IGNORE_NESTED_PATH_PARAM_IDX = 1;
+
+    private static final String IGNORE_NESTED_PATH_PARAM_NAME = "ignoreNestedPath";
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
+            1,
+            true,
+            StringToIndexMap.of(
+                    IGNORE_NESTED_PATH_PARAM_NAME, IGNORE_NESTED_PATH_PARAM_IDX
+            ),
+            false
+    );
+
+    private final HttpServletRequest request;
+    private final HttpServletResponse response;
+
+    public BindDirective(HttpServletRequest request, HttpServletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    @Override
+    public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+            throws TemplateException, IOException {
+        final ObjectWrapper objectWrapper = env.getObjectWrapper();
+
+        if (!(objectWrapper instanceof DefaultObjectWrapper)) {
+            throw new TemplateException("The ObjectWrapper of environment wasn't instance of " + DefaultObjectWrapper.class.getName());
+        }
+
+        TemplateModel model = env.getDataModel().get(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
+        RequestContext requestContext = (RequestContext) ((DefaultObjectWrapper) objectWrapper).unwrap(model);
+
+        String resolvedPath = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this);
+        boolean ignoreNestedPath = CallableUtils.getOptionalBooleanArgument(args, IGNORE_NESTED_PATH_PARAM_IDX, this,
+                false);
+
+        if (!ignoreNestedPath) {
+            resolvedPath = resolveNestedPath(resolvedPath);
+        }
+
+        BindStatus status = requestContext.getBindStatus(resolvedPath);
+        env.setLocalVariable(STATUS_VARIABLE_NAME, new BeanModel(status, (DefaultObjectWrapper) objectWrapper));
+
+        callPlace.executeNestedContent(null, out, env);
+    }
+
+    @Override
+    public boolean isNestedContentSupported() {
+        return true;
+    }
+
+    @Override
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+    private String resolveNestedPath(final String path) {
+        String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);
+
+        if (nestedPath != null && !path.startsWith(nestedPath)
+                && !path.equals(nestedPath.substring(0, nestedPath.length() - 1))) {
+            return nestedPath + path;
+        }
+
+        return path;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/67091176/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
index e827db5..1e94a97 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
@@ -25,15 +25,20 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.servlet.AllHttpScopesHashModel;
 import org.apache.freemarker.servlet.FreemarkerServlet;
 import org.apache.freemarker.servlet.HttpRequestHashModel;
 import org.apache.freemarker.servlet.HttpRequestParametersHashModel;
 import org.apache.freemarker.servlet.HttpSessionHashModel;
+import org.apache.freemarker.servlet.IncludePage;
 import org.apache.freemarker.servlet.ServletContextHashModel;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
+import org.apache.freemarker.spring.model.BindDirective;
 
 /**
  * FreeMarker template based view implementation, with being able to provide a {@link ServletContextHashModel}
@@ -134,6 +139,10 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
 
         model.putUnlistedModel(FreemarkerServlet.KEY_JSP_TAGLIBS, getTaglibFactory());
 
+        model.putUnlistedModel(FreemarkerServlet.KEY_INCLUDE, new IncludePage(request, response));
+
+        model.putUnlistedModel("spring", createSpringCallableHashModel(objectWrapper, request, response));
+
         model.putAll(map);
 
         return model;
@@ -165,4 +174,10 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
         return sessionModel;
     }
 
+    private TemplateHashModelEx2 createSpringCallableHashModel(final ObjectWrapper objectWrapper,
+            final HttpServletRequest request, final HttpServletResponse response) {
+        final SimpleHash springCallableHash = new SimpleHash(objectWrapper);
+        springCallableHash.put("bind", new BindDirective(request, response));
+        return springCallableHash;
+    }
 }


[18/36] incubator-freemarker git commit: FREEMARKER-55: Adding url function

Posted by dd...@apache.org.
FREEMARKER-55: Adding url function


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

Branch: refs/heads/3
Commit: 8bdc5fdcf6507a00113e8f396cdf9a90c8c3857c
Parents: 6f025c7
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 15:45:46 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 15:45:46 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/UrlFunction.java    | 189 ++++++++++++++++++-
 1 file changed, 179 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8bdc5fdc/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
index 402e74d..fe1b1fd 100644
--- 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
@@ -19,6 +19,14 @@
 
 package org.apache.freemarker.spring.model;
 
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -27,9 +35,16 @@ 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.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateHashModelEx2.KeyValuePairIterator;
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateStringModel;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
+import org.apache.freemarker.core.util._KeyValuePair;
 import org.springframework.web.servlet.support.RequestContext;
+import org.springframework.web.servlet.support.RequestDataValueProcessor;
+import org.springframework.web.util.UriUtils;
 
 /**
  * A <code>TemplateFunctionModel</code> providing functionality equivalent to the Spring Framework's
@@ -51,20 +66,21 @@ public class UrlFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final int VALUE_PARAM_IDX = 0;
     private static final int CONTEXT_PARAM_IDX = 1;
+    private static final int PARAMS_PARAM_IDX = 2;
 
     private static final String CONTEXT_PARAM_NAME = "context";
 
+    /**
+     * Absolute URL pattern. e.g, http(s)://example.com, mailto:john@example.com, tel:123-456-7890.
+     */
+    private static final Pattern ABS_URL_PATTERN = Pattern.compile("^((([A-Za-z]+?:)?\\/\\/)|[A-Za-z]+:)[\\w.-]+");
 
-    // TODO: How to deal with parameters (spring:param tags)??
+    private static final String URL_TEMPLATE_DELIMITER_PREFIX = "{";
 
+    private static final String URL_TEMPLATE_DELIMITER_SUFFIX = "}";
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT =
-            ArgumentArrayLayout.create(
-                    1,
-                    false,
-                    StringToIndexMap.of(CONTEXT_PARAM_NAME, CONTEXT_PARAM_IDX),
-                    false
-                    );
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false,
+            StringToIndexMap.of(CONTEXT_PARAM_NAME, CONTEXT_PARAM_IDX), true);
 
     public UrlFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
@@ -74,8 +90,75 @@ public class UrlFunction extends AbstractSpringTemplateFunctionModel {
     public TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException {
-        // TODO
-        return null;
+        final String value = CallableUtils.getStringArgument(args, VALUE_PARAM_IDX, this);
+        final String context = CallableUtils.getOptionalStringArgument(args, CONTEXT_PARAM_IDX, this);
+
+        List<_KeyValuePair<String, String>> params = null;
+        final TemplateHashModelEx2 paramsHashModel = (TemplateHashModelEx2) args[PARAMS_PARAM_IDX];
+
+        if (!paramsHashModel.isEmptyHash()) {
+            params = new ArrayList<>();
+
+            TemplateHashModelEx2.KeyValuePair pair;
+            TemplateModel paramNameModel;
+            TemplateModel paramValueModel;
+            String paramName;
+            String paramValue;
+
+            for (KeyValuePairIterator pairIt = paramsHashModel.keyValuePairIterator(); pairIt.hasNext();) {
+                pair = pairIt.next();
+                paramNameModel = pair.getKey();
+                paramValueModel = pair.getValue();
+
+                if ((paramNameModel instanceof TemplateStringModel)
+                        && (paramValueModel instanceof TemplateStringModel)) {
+                    paramName = ((TemplateStringModel) paramNameModel).getAsString();
+                    paramValue = ((TemplateStringModel) paramValueModel).getAsString();
+
+                    if (paramName.isEmpty()) {
+                        CallableUtils.newArgumentValueException(PARAMS_PARAM_IDX,
+                                "Parameter name must be a non-blank string.", this);
+                    }
+
+                    params.add(new _KeyValuePair<String, String>(paramName, paramValue));
+                } else {
+                    CallableUtils.newArgumentValueException(PARAMS_PARAM_IDX,
+                            "Parameter name and value must be string.", this);
+                }
+            }
+        }
+
+        final UrlType urlType = determineUrlType(value);
+
+        StringBuilder urlBuilder = new StringBuilder();
+
+        if (urlType == UrlType.CONTEXT_RELATIVE) {
+            if (context == null) {
+                urlBuilder.append(getRequest().getContextPath());
+            } else if (context.endsWith("/")) {
+                urlBuilder.append(context.substring(0, context.length() - 1));
+            } else {
+                urlBuilder.append(context);
+            }
+        }
+
+        Set<String> templateParams = new HashSet<>();
+        urlBuilder.append(replaceUriTemplateParams(value, params, templateParams));
+        urlBuilder.append(createQueryString(params, templateParams, (urlBuilder.indexOf("?") == -1)));
+
+        String urlString = urlBuilder.toString();
+
+        if (urlType != UrlType.ABSOLUTE) {
+            urlString = getResponse().encodeURL(urlString);
+        }
+
+        RequestDataValueProcessor processor = requestContext.getRequestDataValueProcessor();
+
+        if ((processor != null) && (getRequest() instanceof HttpServletRequest)) {
+            urlString = processor.processUrl(getRequest(), urlString);
+        }
+
+        return wrapObject(objectWrapperAndUnwrapper, urlString);
     }
 
     @Override
@@ -83,4 +166,90 @@ public class UrlFunction extends AbstractSpringTemplateFunctionModel {
         return ARGS_LAYOUT;
     }
 
+    private UrlType determineUrlType(final String value) {
+        Matcher m = ABS_URL_PATTERN.matcher(value);
+
+        if (m.matches()) {
+            return UrlType.ABSOLUTE;
+        } else if (value.startsWith("/")) {
+            return UrlType.CONTEXT_RELATIVE;
+        } else {
+            return UrlType.RELATIVE;
+        }
+    }
+
+    private String replaceUriTemplateParams(String uri, List<_KeyValuePair<String, String>> params, Set<String> usedParams)
+            throws TemplateException {
+        final String encoding = getResponse().getCharacterEncoding();
+
+        String paramName;
+        String paramValue;
+
+        for (_KeyValuePair<String, String> pair : params) {
+            paramName = pair.getKey();
+            paramValue = pair.getValue();
+
+            String template = URL_TEMPLATE_DELIMITER_PREFIX + paramName + URL_TEMPLATE_DELIMITER_SUFFIX;
+
+            if (uri.contains(template)) {
+                usedParams.add(paramName);
+
+                try {
+                    uri = uri.replace(template, UriUtils.encodePath(paramValue, encoding));
+                } catch (UnsupportedEncodingException e) {
+                    CallableUtils.newGenericExecuteException("Cannot encode URI. " + e, this);
+                }
+            } else {
+                template = URL_TEMPLATE_DELIMITER_PREFIX + '/' + paramName + URL_TEMPLATE_DELIMITER_SUFFIX;
+
+                if (uri.contains(template)) {
+                    usedParams.add(paramName);
+
+                    try {
+                        uri = uri.replace(template, UriUtils.encodePathSegment(paramValue, encoding));
+                    } catch (UnsupportedEncodingException e) {
+                        CallableUtils.newGenericExecuteException("Cannot encode URI. " + e, this);
+                    }
+                }
+            }
+        }
+
+        return uri;
+    }
+
+    private String createQueryString(List<_KeyValuePair<String, String>> params, Set<String> usedParams, boolean includeQueryStringDelimiter)
+            throws TemplateException {
+        final String encoding = getResponse().getCharacterEncoding();
+        final StringBuilder queryStringBuilder = new StringBuilder();
+
+        String paramName;
+        String paramValue;
+
+        for (_KeyValuePair<String, String> pair : params) {
+            paramName = pair.getKey();
+            paramValue = pair.getValue();
+
+            if (!usedParams.contains(paramName)) {
+                queryStringBuilder
+                        .append((includeQueryStringDelimiter && queryStringBuilder.length() == 0) ? "?" : "&");
+
+                try {
+                    queryStringBuilder.append(UriUtils.encodeQueryParam(paramName, encoding));
+
+                    if (paramValue != null) {
+                        queryStringBuilder.append('=');
+                        queryStringBuilder.append(UriUtils.encodeQueryParam(paramValue, encoding));
+                    }
+                } catch (UnsupportedEncodingException e) {
+                    CallableUtils.newGenericExecuteException("Cannot encode query parameter. " + e, this);
+                }
+            }
+        }
+
+        return queryStringBuilder.toString();
+    }
+
+    private enum UrlType {
+        CONTEXT_RELATIVE, RELATIVE, ABSOLUTE
+    }
 }


[10/36] incubator-freemarker git commit: FREEMARKER-55: read message args from positioned vargs.

Posted by dd...@apache.org.
FREEMARKER-55: read message args from positioned vargs.


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

Branch: refs/heads/3
Commit: 8f5eaaaf2df1914fef461802f2d7b511d4a99870
Parents: 3b83475
Author: Woonsan Ko <wo...@apache.org>
Authored: Sun Sep 3 00:08:31 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Sun Sep 3 00:08:31 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/MessageFunction.java     | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8f5eaaaf/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 d440963..7bee8c1 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
@@ -19,6 +19,7 @@
 
 package org.apache.freemarker.spring.model;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -29,7 +30,9 @@ 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.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;
@@ -41,6 +44,7 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final int CODE_PARAM_IDX = 0;
     private static final int MESSAGE_PARAM_IDX = 1;
+    private static final int MESSAGE_ARGS_PARAM_IDX = 2;
 
     private static final String MESSAGE_PARAM_NAME = "message";
 
@@ -71,8 +75,17 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
         final String code = CallableUtils.getStringArgument(args, CODE_PARAM_IDX, this);
 
         if (code != null && !code.isEmpty()) {
+            final TemplateCollectionModel messageArgsModel = (TemplateCollectionModel) args[MESSAGE_ARGS_PARAM_IDX];
             List<Object> msgArgumentList = null;
-            // TODO: How to read message arguments from the varags?
+
+            if (!messageArgsModel.isEmptyCollection()) {
+                msgArgumentList = new ArrayList<>();
+                TemplateModel msgArgModel;
+                for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext(); ) {
+                    msgArgModel = tit.next();
+                    msgArgumentList.add(objectWrapperAndUnwrapper.unwrap(msgArgModel));
+                }
+            }
 
             // TODO: Is it okay to set the default value to null to avoid NoSuchMessageException from Spring MessageSource?
             message = messageSource.getMessage(code, (msgArgumentList == null) ? null : msgArgumentList.toArray(),


[08/36] incubator-freemarker git commit: Merge branch '3' into feature/FREEMARKER-55-2

Posted by dd...@apache.org.
Merge branch '3' into feature/FREEMARKER-55-2


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

Branch: refs/heads/3
Commit: f1e8a4df1a745164bcf48ab9a92cec5beb598bc2
Parents: 63f42c3 aec8e66
Author: Woonsan Ko <wo...@apache.org>
Authored: Sat Sep 2 22:11:45 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Sat Sep 2 22:11:45 2017 -0400

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |  77 ++--
 .../core/FM2ASTToFM3SourceConverter.java        |   8 +
 .../converter/FM2ToFM3ConverterTest.java        |   4 +
 .../freemarker/core/DirectiveCallPlaceTest.java |   2 +-
 .../model/impl/DefaultObjectWrapperTest.java    | 123 ++++---
 .../model/impl/RestrictedObjectWrapperTest.java |   6 +-
 .../templatesuite/CoreTemplateTestSuite.java    |  12 +-
 .../templatesuite/models/AllTemplateModels.java |  45 +--
 .../core/templatesuite/models/BooleanHash1.java |   4 +-
 .../core/templatesuite/models/BooleanHash2.java |   4 +-
 .../core/templatesuite/models/BooleanList1.java |  17 +-
 .../core/templatesuite/models/BooleanList2.java |  15 +-
 .../models/HashAndStringModel.java              |  29 +-
 .../core/templatesuite/models/Listables.java    |   4 +-
 .../core/templatesuite/models/MultiModel1.java  |  18 +-
 .../core/templatesuite/models/MultiModel3.java  |   2 +-
 .../core/templatesuite/models/MultiModel4.java  |  21 +-
 .../core/templatesuite/models/MultiModel5.java  |  19 +-
 .../core/userpkg/TestTemplateCallableModel.java |   2 +-
 .../templatesuite/expected/type-builtins.txt    |  28 +-
 .../templatesuite/templates/range-common.ftl    | 314 ----------------
 .../core/templatesuite/templates/range.ftl      | 308 +++++++++++++++-
 .../templatesuite/templates/type-builtins.ftl   |  16 +-
 .../org/apache/freemarker/core/ASTDirList.java  |  41 +--
 .../freemarker/core/ASTDirMacroOrFunction.java  |   3 +-
 .../apache/freemarker/core/ASTDirNested.java    |   8 +-
 .../freemarker/core/ASTExpAddOrConcat.java      | 108 ++++--
 .../apache/freemarker/core/ASTExpBuiltIn.java   |   8 +-
 .../freemarker/core/ASTExpBuiltInVariable.java  |   2 +-
 .../apache/freemarker/core/ASTExpDefault.java   |  92 +++--
 .../freemarker/core/ASTExpDynamicKeyName.java   |  35 +-
 .../freemarker/core/ASTExpHashLiteral.java      |  10 +-
 .../freemarker/core/ASTExpListLiteral.java      |  44 +--
 .../org/apache/freemarker/core/ASTExpRange.java |   2 +-
 .../apache/freemarker/core/ASTExpression.java   |  20 +-
 .../freemarker/core/BoundedRangeModel.java      |  38 +-
 .../freemarker/core/BuiltInForIterable.java     |  43 +++
 .../freemarker/core/BuiltInForSequence.java     |   6 +-
 .../freemarker/core/BuiltInsForHashes.java      |  12 +-
 .../core/BuiltInsForMultipleTypes.java          |  51 +--
 .../freemarker/core/BuiltInsForNodes.java       |   3 +-
 .../freemarker/core/BuiltInsForSequences.java   | 359 +++++++------------
 .../core/BuiltInsForStringsBasic.java           |   4 +-
 .../freemarker/core/BuiltInsForStringsMisc.java |   3 +-
 .../core/BuiltInsForStringsRegexp.java          |  97 ++---
 .../org/apache/freemarker/core/Environment.java |  32 +-
 .../core/InvalidReferenceException.java         |   2 +-
 .../core/ListableRightUnboundedRangeModel.java  |  94 -----
 .../apache/freemarker/core/MessageUtils.java    |  21 +-
 .../freemarker/core/NativeCollection.java       |  55 +++
 .../freemarker/core/NativeCollectionEx.java     |  72 ----
 .../apache/freemarker/core/NativeHashEx2.java   |   8 +-
 .../apache/freemarker/core/NativeSequence.java  |  39 +-
 .../core/NativeStringArraySequence.java         |  27 +-
 .../core/NativeStringCollectionCollection.java  |  74 ++++
 .../NativeStringCollectionCollectionEx.java     |  78 ----
 .../core/NativeStringListSequence.java          |  27 +-
 .../core/NativeTemplateModelIterator.java       |  44 +++
 .../org/apache/freemarker/core/RangeModel.java  |  17 +-
 .../core/RightUnboundedRangeModel.java          |  74 +++-
 .../org/apache/freemarker/core/_EvalUtils.java  |  11 +-
 .../core/debug/RmiDebugModelImpl.java           |  10 +-
 .../core/debug/RmiDebuggedEnvironmentImpl.java  |   6 +-
 .../core/model/EmptyCollectionExModel.java      |  43 ---
 .../core/model/EmptyCollectionModel.java        |  47 +++
 .../freemarker/core/model/EmptyHashModel.java   |   4 +-
 .../core/model/EmptyIterableModel.java          |  31 ++
 .../core/model/EmptyIteratorModel.java          |   2 +-
 .../core/model/EmptySequenceModel.java          |  12 +-
 .../core/model/GeneralPurposeNothing.java       |  25 +-
 .../core/model/TemplateCollectionModel.java     |  34 +-
 .../core/model/TemplateCollectionModelEx.java   |  45 ---
 .../core/model/TemplateDirectiveModel.java      |   2 +-
 .../core/model/TemplateHashModel.java           |   2 +-
 .../core/model/TemplateHashModelEx.java         |  11 +-
 .../core/model/TemplateIterableModel.java       |  49 +++
 .../core/model/TemplateModelIterator.java       |  23 +-
 .../core/model/TemplateSequenceModel.java       |  25 +-
 .../freemarker/core/model/impl/BeanModel.java   |  18 +-
 .../core/model/impl/ClassBasedModelFactory.java |   2 +-
 .../core/model/impl/ClassIntrospector.java      |   6 +-
 .../core/model/impl/CollectionAdapter.java      |  88 -----
 .../core/model/impl/CollectionAndSequence.java  | 111 ------
 .../core/model/impl/DefaultArrayAdapter.java    | 258 ++++++++++++-
 .../model/impl/DefaultEnumerationAdapter.java   |  12 +-
 .../core/model/impl/DefaultIterableAdapter.java |  10 +-
 .../core/model/impl/DefaultIteratorAdapter.java |  12 +-
 .../core/model/impl/DefaultListAdapter.java     |  42 +--
 .../core/model/impl/DefaultMapAdapter.java      |  10 +-
 .../impl/DefaultNonListCollectionAdapter.java   |  12 +-
 .../core/model/impl/DefaultObjectWrapper.java   |  61 ++--
 .../DefaultUnassignableIteratorAdapter.java     |  59 ---
 .../freemarker/core/model/impl/HashAdapter.java | 181 ----------
 .../core/model/impl/IterableAndSequence.java    |  82 +++++
 .../IteratorToTemplateModelIteratorAdapter.java |  52 +++
 .../core/model/impl/ResourceBundleModel.java    |   6 +-
 .../core/model/impl/SequenceAdapter.java        |  68 ----
 .../impl/SequenceTemplateModelIterator.java     |  63 ++++
 .../freemarker/core/model/impl/SetAdapter.java  |  32 --
 .../core/model/impl/SimpleCollection.java       |  93 ++---
 .../freemarker/core/model/impl/SimpleHash.java  |   5 +-
 .../core/model/impl/SimpleIterable.java         | 134 +++++++
 .../core/model/impl/SimpleSequence.java         |  31 +-
 .../impl/SingleItemTemplateModelIterator.java   |  48 +++
 .../freemarker/core/model/impl/StaticModel.java |   4 +-
 .../impl/TemplateCollectionModelAdapter.java    |  77 ++++
 .../model/impl/TemplateHashModelAdapter.java    | 185 ++++++++++
 .../impl/TemplateIterableModelAdapter.java      |  57 +++
 .../impl/TemplateModelIteratorAdapter.java      |  65 ++++
 .../model/impl/TemplateModelListSequence.java   |  33 +-
 .../impl/TemplateSequenceModelAdapter.java      |  88 +++++
 .../model/impl/TemplateSetModelAdapter.java     |  32 ++
 .../freemarker/core/util/CallableUtils.java     |  26 +-
 .../apache/freemarker/core/util/DeepUnwrap.java |  18 +-
 .../freemarker/core/util/StringToIndexMap.java  |   6 +-
 .../core/util/TemplateLanguageUtils.java        |   8 +-
 freemarker-core/src/main/javacc/FTL.jj          |   4 +-
 .../freemarker/dom/AttributeNodeModel.java      |   2 +-
 .../freemarker/dom/CharacterDataNodeModel.java  |   2 +-
 .../apache/freemarker/dom/DocumentModel.java    |   2 +-
 .../freemarker/dom/DocumentTypeModel.java       |   2 +-
 .../org/apache/freemarker/dom/ElementModel.java |   7 +-
 .../apache/freemarker/dom/NodeListModel.java    |  34 +-
 .../org/apache/freemarker/dom/NodeModel.java    |  32 +-
 .../org/apache/freemarker/dom/PINodeModel.java  |   2 +-
 .../dom/SunInternalXalanXPathSupport.java       |   2 +-
 .../freemarker/dom/XalanXPathSupport.java       |   2 +-
 .../servlet/HttpRequestHashModel.java           |   8 +-
 .../servlet/HttpRequestParametersHashModel.java |  68 ++--
 .../servlet/HttpSessionHashModel.java           |   2 +-
 .../servlet/ServletContextHashModel.java        |   2 +-
 .../servlet/jsp/FreeMarkerPageContext.java      |   4 +
 .../freemarker/servlet/jsp/JspTagModelBase.java |   2 +-
 .../freemarker/servlet/jsp/TaglibFactory.java   |   4 +-
 .../expected/attributes-modernModels.txt        |   4 +-
 .../basic/WEB-INF/expected/attributes.txt       |   4 +-
 .../org/apache/freemarker/test/TestUtils.java   |   7 +-
 137 files changed, 3085 insertions(+), 2319 deletions(-)
----------------------------------------------------------------------



[03/36] incubator-freemarker git commit: FREEMARKER-55: use nested content parameter instead of variable

Posted by dd...@apache.org.
FREEMARKER-55: use nested content parameter instead of variable


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

Branch: refs/heads/3
Commit: b656f1e1f7c1b0875526f2224db1fc793e1172d2
Parents: 19a3277
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Aug 29 14:58:34 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Aug 29 14:58:34 2017 -0400

----------------------------------------------------------------------
 .../apache/freemarker/spring/model/BindDirective.java | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b656f1e1/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index 9ca7c6a..fb68384 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -91,15 +91,11 @@ public class BindDirective implements TemplateDirectiveModel {
             resolvedPath = resolveNestedPath(resolvedPath);
         }
 
-        final TemplateModel oldStatusModel = env.getVariable(STATUS_VARIABLE_NAME);
-
-        try {
-            BindStatus status = requestContext.getBindStatus(resolvedPath);
-            env.setVariable(STATUS_VARIABLE_NAME, new BeanModel(status, (DefaultObjectWrapper) objectWrapper));
-            callPlace.executeNestedContent(null, out, env);
-        } finally {
-            env.setVariable(STATUS_VARIABLE_NAME, oldStatusModel);
-        }
+        //TODO: how to deal with htmlEscape when invoking #getBindStatus()?
+        BindStatus status = requestContext.getBindStatus(resolvedPath);
+        TemplateModel[] nestedContentArgs = new TemplateModel[] {
+                new BeanModel(status, (DefaultObjectWrapper) objectWrapper) };
+        callPlace.executeNestedContent(nestedContentArgs, out, env);
     }
 
     @Override


[27/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for eval function.

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for eval function.


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

Branch: refs/heads/3
Commit: d7916709f9897c4ff8c6ae146ee191df77a43427
Parents: 9987acf
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 13:41:02 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 13:41:02 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/EvalFunction.java   | 19 ++++-
 .../spring/model/EvalFunctionTest.java          | 80 ++++++++++++++++++++
 .../spring/model/MessageFunctionTest.java       |  2 +-
 .../spring/model/ThemeFunctionTest.java         |  2 +-
 .../test/model/eval-function-basic-usages.ftl   | 46 +++++++++++
 5 files changed, 146 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d7916709/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 6ae0bb5..f155df1 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
@@ -51,9 +51,26 @@ import org.springframework.web.servlet.support.RequestContext;
  * Some valid example(s):
  * </P>
  * <PRE>
+ * &lt;#assign expression="T(java.lang.Math).max(12.34, 56.78)" /&gt;
+ * Max number: ${spring.eval(expression)}
+ * 
+ * &lt;#assign expression="user.id" /&gt;
+ * User ID: ${spring.eval(expression)!}
+ * 
+ * User ID: ${spring.eval('user.id')!}
+ * 
+ * &lt;#assign expression="user.firstName + ' ' + user.lastName" /&gt;
+ * User Name: ${spring.eval(expression)!}
+ * 
+ * &lt;#assign expression="users[0].id" /&gt;
+ * First User's ID: ${spring.eval(expression)!}
+ *
+ * &lt;#assign expression="{0,1,1,2,3,5,8,13}" /&gt;
+ * &lt;#assign numbers=spring.eval(expression) /&gt;
+ * Numbers: &lt;#list numbers as number&gt;${number}&lt;#sep&gt;, &lt;/#list&gt;
  * </PRE>
  * <P>
- * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:message /&gt;</code> JSP Tag Library, this function
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:eval /&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>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d7916709/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
new file mode 100644
index 0000000..953b571
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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 static org.hamcrest.Matchers.equalToIgnoringWhiteSpace;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class EvalFunctionTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    private MockMvc mockMvc;
+
+    private long startTimeMillis;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+
+        startTimeMillis = System.currentTimeMillis();
+        System.setProperty("EvalFunctionTest.startTimeMillis", Long.toString(startTimeMillis));
+    }
+
+    @Test
+    public void testMessageFunctionBasicUsages() throws Exception {
+        final Integer userId = userRepository.getUserIds().iterator().next();
+        final User user = userRepository.getUser(userId);
+        mockMvc.perform(get("/users/").param("viewName", "test/model/eval-function-basic-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//div[@id='maxNumber']/text()").number(56.78))
+                .andExpect(xpath("//div[@id='user-%s']/text()", userId)
+                        .string(equalToIgnoringWhiteSpace(user.getFirstName() + " " + user.getLastName())))
+                .andExpect(xpath("//div[@id='firstUserId']/text()").string(userId.toString()))
+                .andExpect(xpath("//div[@id='fibonacci']/text()").string("0, 1, 1, 2, 3, 5, 8, 13"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d7916709/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
index 5be6daf..0ddbe27 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
@@ -53,7 +53,7 @@ public class MessageFunctionTest {
     private MockMvc mockMvc;
 
     @Before
-    public void setup() {
+    public void setUp() {
         mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d7916709/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
index ed00f5e..81b186b 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
@@ -57,7 +57,7 @@ public class ThemeFunctionTest {
     private MockMvc mockMvc;
 
     @Before
-    public void setup() {
+    public void setUp() {
         mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d7916709/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/eval-function-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/eval-function-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/eval-function-basic-usages.ftl
new file mode 100644
index 0000000..b5b1dd6
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/eval-function-basic-usages.ftl
@@ -0,0 +1,46 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<#assign expression="T(java.lang.Math).max(12.34, 56.78)" />
+<div id="maxNumber">${spring.eval(expression)}</div>
+
+<ul>
+  <#list users as user>
+    <li>
+      <#assign expression="user.id" />
+      <div id="user-${spring.eval(expression)!}">
+        <#assign expression="user.firstName + ' ' + user.lastName" />
+        ${spring.eval(expression)!}
+      </div>
+    </li>
+  </#list>
+</ul>
+
+<#assign expression="users[0].id" />
+<div id="firstUserId">${spring.eval(expression)!}</div>
+
+<#assign expression="{0,1,1,2,3,5,8,13}" />
+<#assign numbers=spring.eval(expression) />
+<div id="fibonacci"><#list numbers as number>${number}<#sep>, </#list></div>
+
+</body>
+</html>


[20/36] incubator-freemarker git commit: FREEMARKER-55: code cleanups

Posted by dd...@apache.org.
FREEMARKER-55: code cleanups


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

Branch: refs/heads/3
Commit: 834abd7d6f338ee7fcc5bdaa060ce2d36495fc8b
Parents: 075bd88
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 16:50:45 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 16:50:45 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateCallableModel.java    | 17 +++----
 .../freemarker/spring/model/EvalFunction.java   | 15 +++++-
 .../spring/model/NestedPathDirective.java       | 21 ++++-----
 .../model/SpringTemplateCallableHashModel.java  | 49 +++++++++++---------
 4 files changed, 55 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/834abd7d/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 0d5d6ed..384a47b 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
@@ -27,6 +27,7 @@ import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.util.CallableUtils;
 import org.springframework.web.servlet.support.BindStatus;
@@ -95,21 +96,15 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
 
     protected abstract boolean isFunction();
 
-    protected String getCurrentNestedPath(final Environment env) throws TemplateException {
-        SpringTemplateCallableHashModel springHash = (SpringTemplateCallableHashModel) env
-                .getVariable(SpringTemplateCallableHashModel.NAME);
-        return springHash.getNestedPath();
-    }
-
-    protected void setCurrentNestedPath(final Environment env, final String nestedPath) throws TemplateException {
-        SpringTemplateCallableHashModel springHash = (SpringTemplateCallableHashModel) env
-                .getVariable(SpringTemplateCallableHashModel.NAME);
-        springHash.setNestedPath(nestedPath);
+    protected SpringTemplateCallableHashModel getSpringTemplateCallableHashModel(final Environment env)
+            throws TemplateException {
+        return (SpringTemplateCallableHashModel) env.getVariable(SpringTemplateCallableHashModel.NAME);
     }
 
     private String resolveNestedPath(final Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
             final String path) throws TemplateException {
-        String curNestedPath = getCurrentNestedPath(env);
+        final TemplateStringModel curNestedPathModel = getSpringTemplateCallableHashModel(env).getNestedPathModel();
+        final String curNestedPath = (curNestedPathModel != null) ? curNestedPathModel.getAsString() : null;
 
         if (curNestedPath != null && !path.startsWith(curNestedPath)
                 && !path.equals(curNestedPath.substring(0, curNestedPath.length() - 1))) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/834abd7d/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 01c3cc5..6ae0bb5 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
@@ -66,6 +66,8 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false, null, false);
 
+    private static final String EVALUATION_CONTEXT_VAR_NAME = "org.apache.freemarker.spring.model.EVALUATION_CONTEXT";
+
     private final ExpressionParser expressionParser = new SpelExpressionParser();
 
     public EvalFunction(HttpServletRequest request, HttpServletResponse response) {
@@ -79,8 +81,17 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
         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, objectWrapperAndUnwrapper, requestContext);
+        EvaluationContext evaluationContext = null;
+        final SpringTemplateCallableHashModel springTemplateModel = getSpringTemplateCallableHashModel(env);
+        TemplateModel evaluationContextModel = springTemplateModel.get(EVALUATION_CONTEXT_VAR_NAME);
+
+        if (evaluationContextModel != null) {
+            evaluationContext = (EvaluationContext) unwrapObject(objectWrapperAndUnwrapper, evaluationContextModel);
+        } else {
+            evaluationContext = createEvaluationContext(env, objectWrapperAndUnwrapper, requestContext);
+            evaluationContextModel = wrapObject(objectWrapperAndUnwrapper, evaluationContext);
+            springTemplateModel.setEvaluationContextModel(evaluationContextModel);
+        }
 
         final Object result = expression.getValue(evaluationContext);
         return wrapObject(objectWrapperAndUnwrapper, result);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/834abd7d/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
index 37d4370..9bd51ef 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
@@ -31,6 +31,7 @@ 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.model.TemplateStringModel;
 import org.apache.freemarker.core.util.CallableUtils;
 import org.springframework.beans.PropertyAccessor;
 import org.springframework.web.servlet.support.RequestContext;
@@ -59,13 +60,7 @@ public class NestedPathDirective extends AbstractSpringTemplateDirectiveModel {
 
     private static final int PATH_PARAM_IDX = 0;
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT =
-            ArgumentArrayLayout.create(
-                    1,
-                    false,
-                    null,
-                    false
-                    );
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false, null, false);
 
     public NestedPathDirective(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
@@ -85,14 +80,18 @@ public class NestedPathDirective extends AbstractSpringTemplateDirectiveModel {
             path += PropertyAccessor.NESTED_PROPERTY_SEPARATOR;
         }
 
-        String prevNestedPath = getCurrentNestedPath(env);
-        String newNestedPath = (prevNestedPath != null) ? prevNestedPath + path : path;
+        final SpringTemplateCallableHashModel springTemplateModel = getSpringTemplateCallableHashModel(env);
+        final TemplateStringModel prevNestedPathModel = springTemplateModel.getNestedPathModel();
+        final String prevNestedPath = (prevNestedPathModel != null) ? prevNestedPathModel.getAsString() : null;
+        final String newNestedPath = (prevNestedPath != null) ? prevNestedPath + path : path;
+        final TemplateStringModel newNestedPathModel = (TemplateStringModel) wrapObject(objectWrapperAndUnwrapper,
+                newNestedPath);
 
         try {
-            setCurrentNestedPath(env, newNestedPath);
+            springTemplateModel.setNestedPathModel(newNestedPathModel);
             callPlace.executeNestedContent(null, out, env);
         } finally {
-            setCurrentNestedPath(env, prevNestedPath);
+            springTemplateModel.setNestedPathModel(prevNestedPathModel);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/834abd7d/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 e090cd9..7315947 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
@@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.TemplateHashModel;
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.impl.SimpleString;
+import org.apache.freemarker.core.model.TemplateStringModel;
 
 /**
  * TemplateHashModel wrapper for templates using Spring directives and functions.
@@ -40,31 +40,26 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel,
 
     public static final String NAME = "spring";
 
-    public static final String NESTED_PATH = "nestedPath";
+    public static final String NESTED_PATH_MODEL = "nestedPathModel";
 
-    private Map<String, AbstractSpringTemplateCallableModel> callablesMap = new HashMap<>();
+    public static final String EVALUATION_CONTEXT_MODEL = "evaluationContextModel";
 
-    private String nestedPath;
+    private Map<String, TemplateModel> modelsMap = new HashMap<>();
 
     public SpringTemplateCallableHashModel(final HttpServletRequest request, final HttpServletResponse 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));
+        modelsMap.put(MessageFunction.NAME, new MessageFunction(request, response));
+        modelsMap.put(ThemeFunction.NAME, new ThemeFunction(request, response));
+        modelsMap.put(BindErrorsDirective.NAME, new BindErrorsDirective(request, response));
+        modelsMap.put(NestedPathDirective.NAME, new NestedPathDirective(request, response));
+        modelsMap.put(BindDirective.NAME, new BindDirective(request, response));
+        modelsMap.put(TransformFunction.NAME, new TransformFunction(request, response));
+        modelsMap.put(UrlFunction.NAME, new UrlFunction(request, response));
+        modelsMap.put(EvalFunction.NAME, new EvalFunction(request, response));
+        modelsMap.put(MvcUrlFunction.NAME, new MvcUrlFunction(request, response));
     }
 
     public TemplateModel get(String key) throws TemplateException {
-        if (NESTED_PATH.equals(key)) {
-            return (nestedPath != null) ? new SimpleString(nestedPath) : null;
-        }
-
-        return callablesMap.get(key);
+        return modelsMap.get(key);
     }
 
     @Override
@@ -72,12 +67,20 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel,
         return false;
     }
 
-    public String getNestedPath() {
-        return nestedPath;
+    public TemplateStringModel getNestedPathModel() throws TemplateException {
+        return (TemplateStringModel) get(NESTED_PATH_MODEL);
+    }
+
+    public void setNestedPathModel(TemplateStringModel nestedPathModel) {
+        modelsMap.put(NESTED_PATH_MODEL, nestedPathModel);
+    }
+
+    public TemplateModel getEvaluationContextModel() throws TemplateException {
+        return (TemplateModel) get(EVALUATION_CONTEXT_MODEL);
     }
 
-    public void setNestedPath(String nestedPath) {
-        this.nestedPath = nestedPath;
+    public void setEvaluationContextModel(TemplateModel evaluationContextModel) {
+        modelsMap.put(EVALUATION_CONTEXT_MODEL, evaluationContextModel);
     }
 
 }


[04/36] incubator-freemarker git commit: FREEMARKER-55: splitting abstract class to keep common things

Posted by dd...@apache.org.
FREEMARKER-55: splitting abstract class to keep common things


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

Branch: refs/heads/3
Commit: aeaf0307736008b2921c7efa42cf7d31b2df6dbc
Parents: b656f1e
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Aug 29 23:02:21 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Aug 29 23:02:21 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateDirectiveModel.java   | 97 ++++++++++++++++++++
 .../freemarker/spring/model/BindDirective.java  | 62 +++----------
 2 files changed, 110 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/aeaf0307/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
new file mode 100644
index 0000000..9baf3f0
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateDirectiveModel.java
@@ -0,0 +1,97 @@
+/*
+ * 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.io.IOException;
+import java.io.Writer;
+
+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.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.springframework.web.servlet.support.RequestContext;
+import org.springframework.web.servlet.view.AbstractTemplateView;
+
+public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDirectiveModel {
+
+    /**
+     * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
+     */
+    private static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
+
+    private final HttpServletRequest request;
+    private final HttpServletResponse response;
+
+    public AbstractSpringTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    @Override
+    public final void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+            throws TemplateException, IOException {
+        final ObjectWrapper objectWrapper = env.getObjectWrapper();
+
+        if (!(objectWrapper instanceof DefaultObjectWrapper)) {
+            throw new TemplateException(
+                    "The ObjectWrapper of environment wasn't instance of " + DefaultObjectWrapper.class.getName());
+        }
+
+        TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
+
+        if (rcModel == null) {
+            throw new TemplateException(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.");
+        }
+
+        RequestContext requestContext = (RequestContext) ((DefaultObjectWrapper) objectWrapper).unwrap(rcModel);
+
+        executeInternal(args, callPlace, out, env, (DefaultObjectWrapper) objectWrapper, requestContext);
+    }
+
+    protected abstract void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
+            DefaultObjectWrapper objectWrapper, RequestContext requestContext) throws TemplateException, IOException;
+
+    protected final HttpServletRequest getRequest() {
+        return request;
+    }
+
+    protected final HttpServletResponse getResponse() {
+        return response;
+    }
+
+    protected final String resolveNestedPath(final Environment env, final String path) {
+        // TODO: should read it from request or env??
+        //       or read spring.nestedPath first and read request attribute next??
+        String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);
+
+        if (nestedPath != null && !path.startsWith(nestedPath)
+                && !path.equals(nestedPath.substring(0, nestedPath.length() - 1))) {
+            return nestedPath + path;
+        }
+
+        return path;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/aeaf0307/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index fb68384..18ba1dd 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -29,8 +29,6 @@ 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.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.BeanModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
@@ -38,58 +36,35 @@ import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.springframework.web.servlet.support.BindStatus;
 import org.springframework.web.servlet.support.RequestContext;
-import org.springframework.web.servlet.view.AbstractTemplateView;
 
-public class BindDirective implements TemplateDirectiveModel {
-
-    public static final String STATUS_VARIABLE_NAME = "status";
-
-    /**
-     * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
-     */
-    private static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
+public class BindDirective extends AbstractSpringTemplateDirectiveModel {
 
     private static final int PATH_PARAM_IDX = 0;
     private static final int IGNORE_NESTED_PATH_PARAM_IDX = 1;
 
     private static final String IGNORE_NESTED_PATH_PARAM_NAME = "ignoreNestedPath";
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(
-            1,
-            true,
-            StringToIndexMap.of(
-                    IGNORE_NESTED_PATH_PARAM_NAME, IGNORE_NESTED_PATH_PARAM_IDX
-            ),
-            false
-    );
-
-    private final HttpServletRequest request;
-    private final HttpServletResponse response;
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    true,
+                    StringToIndexMap.of(IGNORE_NESTED_PATH_PARAM_NAME, IGNORE_NESTED_PATH_PARAM_IDX),
+                    false
+                    );
 
     public BindDirective(HttpServletRequest request, HttpServletResponse response) {
-        this.request = request;
-        this.response = response;
+        super(request, response);
     }
 
     @Override
-    public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
-            throws TemplateException, IOException {
-        final ObjectWrapper objectWrapper = env.getObjectWrapper();
-
-        if (!(objectWrapper instanceof DefaultObjectWrapper)) {
-            throw new TemplateException("The ObjectWrapper of environment wasn't instance of " + DefaultObjectWrapper.class.getName());
-        }
-
-        TemplateModel model = env.getDataModel().get(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
-        RequestContext requestContext = (RequestContext) ((DefaultObjectWrapper) objectWrapper).unwrap(model);
+    protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
+            DefaultObjectWrapper objectWrapper, RequestContext requestContext) throws TemplateException, IOException {
 
-        String resolvedPath = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this);
+        final String path = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this);
         boolean ignoreNestedPath = CallableUtils.getOptionalBooleanArgument(args, IGNORE_NESTED_PATH_PARAM_IDX, this,
                 false);
 
-        if (!ignoreNestedPath) {
-            resolvedPath = resolveNestedPath(resolvedPath);
-        }
+        final String resolvedPath = (ignoreNestedPath) ? path : resolveNestedPath(env, path);
 
         //TODO: how to deal with htmlEscape when invoking #getBindStatus()?
         BindStatus status = requestContext.getBindStatus(resolvedPath);
@@ -107,15 +82,4 @@ public class BindDirective implements TemplateDirectiveModel {
     public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
         return ARGS_LAYOUT;
     }
-
-    private String resolveNestedPath(final String path) {
-        String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);
-
-        if (nestedPath != null && !path.startsWith(nestedPath)
-                && !path.equals(nestedPath.substring(0, nestedPath.length() - 1))) {
-            return nestedPath + path;
-        }
-
-        return path;
-    }
 }


[15/36] incubator-freemarker git commit: FREEMARKER-55: Adding more functions.

Posted by dd...@apache.org.
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 <wo...@apache.org>
Authored: Fri Sep 8 12:21:53 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
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;
+    }
+
+}


[16/36] incubator-freemarker git commit: Merge branch '3' into feature/FREEMARKER-55-2

Posted by dd...@apache.org.
Merge branch '3' into feature/FREEMARKER-55-2


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

Branch: refs/heads/3
Commit: 9eedd86da2dc6aeac011e0e1553871599b80ab18
Parents: 234f96e b63e448
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 13:41:52 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 13:41:52 2017 -0400

----------------------------------------------------------------------
 .gitignore                                      |  10 +-
 README.md                                       |  63 ++++----
 build.gradle                                    |   8 +-
 .../org/apache/freemarker/core/ASTElement.java  |   5 +-
 .../freemarker/core/ParsingConfiguration.java   |   1 +
 .../freemarker/core/util/CallableUtils.java     |   4 +-
 freemarker-servlet/build.gradle                 |  18 +--
 .../freemarker/servlet/FreemarkerServlet.java   |   7 +-
 .../servlet/DummyMockServletContext.java        | 142 ++++++++++++++++++-
 .../spring/SpringResourceTemplateLoader.java    |   1 -
 .../spring/web/view/FreeMarkerView.java         |   2 +-
 .../spring/web/view/FreeMarkerViewResolver.java |   2 +-
 12 files changed, 207 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9eedd86d/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
----------------------------------------------------------------------


[05/36] incubator-freemarker git commit: FREEMARKER-55: simple clean up

Posted by dd...@apache.org.
FREEMARKER-55: simple clean up


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

Branch: refs/heads/3
Commit: 64adad829926532b17ce5a5965a2c40e594dd6fc
Parents: aeaf030
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Aug 29 23:07:29 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Aug 29 23:07:29 2017 -0400

----------------------------------------------------------------------
 .../java/org/apache/freemarker/spring/model/BindDirective.java    | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/64adad82/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index 18ba1dd..b7edbac 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -68,8 +68,7 @@ public class BindDirective extends AbstractSpringTemplateDirectiveModel {
 
         //TODO: how to deal with htmlEscape when invoking #getBindStatus()?
         BindStatus status = requestContext.getBindStatus(resolvedPath);
-        TemplateModel[] nestedContentArgs = new TemplateModel[] {
-                new BeanModel(status, (DefaultObjectWrapper) objectWrapper) };
+        TemplateModel[] nestedContentArgs = new TemplateModel[] { new BeanModel(status, objectWrapper) };
         callPlace.executeNestedContent(nestedContentArgs, out, env);
     }
 


[34/36] incubator-freemarker git commit: FREEMARKER-55: javadocs and cleanups

Posted by dd...@apache.org.
FREEMARKER-55: javadocs and cleanups


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

Branch: refs/heads/3
Commit: ef9ba120ebdc85d87567c1fb4200f3d752e89ab6
Parents: bd56432
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 21:53:45 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 21:53:45 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateCallableModel.java    | 49 +++++++++--------
 .../AbstractSpringTemplateDirectiveModel.java   | 33 +++++++++---
 .../AbstractSpringTemplateFunctionModel.java    | 32 ++++++++---
 .../spring/model/BindErrorsDirective.java       | 24 +++------
 .../freemarker/spring/model/EvalFunction.java   | 16 ++++--
 .../spring/model/MessageFunction.java           | 23 ++++----
 .../spring/model/NestedPathDirective.java       |  4 +-
 .../SpringFormTemplateCallableHashModel.java    | 56 --------------------
 .../model/SpringTemplateCallableHashModel.java  | 13 ++++-
 .../spring/model/TransformFunction.java         | 21 ++++----
 .../freemarker/spring/model/UrlFunction.java    |  2 +-
 .../spring/web/view/FreeMarkerView.java         |  3 --
 12 files changed, 124 insertions(+), 152 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 384a47b..641fbdb 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
@@ -28,8 +28,6 @@ import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.util.CallableUtils;
 import org.springframework.web.servlet.support.BindStatus;
 import org.springframework.web.servlet.support.RequestContext;
 
@@ -41,15 +39,28 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
     private final HttpServletRequest request;
     private final HttpServletResponse response;
 
+    /**
+     * Constructs with servlet request and response.
+     * @param request servlet request
+     * @param response servlet response
+     */
     public AbstractSpringTemplateCallableModel(HttpServletRequest request, HttpServletResponse response) {
         this.request = request;
         this.response = response;
     }
 
+    /**
+     * Return servlet request.
+     * @return servlet request
+     */
     protected final HttpServletRequest getRequest() {
         return request;
     }
 
+    /**
+     * Return servlet response.
+     * @return servlet response
+     */
     protected final HttpServletResponse getResponse() {
         return response;
     }
@@ -68,34 +79,22 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
      * @param ignoreNestedPath flag whether or not to ignore the nested path
      * @return {@link TemplateModel} wrapping a {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext}
      * by the {@code path}
-     * @throws TemplateException 
+     * @throws TemplateException if template exception occurs
      */
-    protected final TemplateModel getBindStatusTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            RequestContext requestContext, String path, boolean ignoreNestedPath) throws TemplateException {
+    protected final TemplateModel getBindStatusTemplateModel(Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, 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;
+        return (status != null) ? objectWrapperAndUnwrapper.wrap(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(object);
-        }
-
-        return null;
-    }
-
-    protected abstract boolean isFunction();
-
+    /**
+     * Return the internal TemplateHashModel wrapper for templating in Spring Framework applications.
+     * @param env environment
+     * @return the internal TemplateHashModel wrapper for templating in Spring Framework applications
+     * @throws TemplateException if template exception occurs
+     */
     protected SpringTemplateCallableHashModel getSpringTemplateCallableHashModel(final Environment env)
             throws TemplateException {
         return (SpringTemplateCallableHashModel) env.getVariable(SpringTemplateCallableHashModel.NAME);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 df6a2bc..0ce34d0 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
@@ -42,10 +42,22 @@ import org.springframework.web.servlet.view.AbstractTemplateView;
 public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTemplateCallableModel
         implements TemplateDirectiveModel {
 
+    /**
+     * Construct directive with servlet request and response.
+     * @param request servlet request
+     * @param response servlet response
+     */
     public AbstractSpringTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
     }
 
+    /**
+     * Execute this directive.
+     * <P>
+     * This method establishes Spring's <code>RequestContext</code> and invokes {@link #executeInternal(TemplateModel[], CallPlace, Writer, Environment, ObjectWrapperAndUnwrapper, RequestContext)}
+     * which must be implemented by derived directive classes.
+     * </P>
+     */
     @Override
     public final void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
             throws TemplateException, IOException {
@@ -53,15 +65,14 @@ public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSprin
 
         if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
             CallableUtils.newGenericExecuteException(
-                    "The ObjectWrapper of environment isn't an instance of ObjectWrapperAndUnwrapper.", this,
-                    isFunction());
+                    "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, isFunction());
+                    AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, false);
         }
 
         RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel);
@@ -69,11 +80,17 @@ public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSprin
         executeInternal(args, callPlace, out, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext);
     }
 
-    @Override
-    protected final boolean isFunction() {
-        return false;
-    }
-
+    /**
+     * Interal execution method that is supposed to be implemented by derived directive classes.
+     * @param args argument models
+     * @param callPlace the place where this is being called
+     * @param out output writer
+     * @param env template execution environment
+     * @param objectWrapperAndUnwrapper ObjectWrapperAndUnwrapper
+     * @param requestContext Spring RequestContext
+     * @throws TemplateException if template exception occurs
+     * @throws IOException if IO exception occurs
+     */
     protected abstract void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException, IOException;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 7851ad8..137837c 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
@@ -39,25 +39,36 @@ import org.springframework.web.servlet.view.AbstractTemplateView;
 public abstract class AbstractSpringTemplateFunctionModel extends AbstractSpringTemplateCallableModel
         implements TemplateFunctionModel {
 
+    /**
+     * Construct function with servlet request and response.
+     * @param request servlet request
+     * @param response servlet response
+     */
     public AbstractSpringTemplateFunctionModel(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
     }
 
+    /**
+     * Execute this function.
+     * <P>
+     * This method establishes Spring's <code>RequestContext</code> and invokes {@link #executeInternal(TemplateModel[], CallPlace, Environment, ObjectWrapperAndUnwrapper, RequestContext)}
+     * which must be implemented by derived function classes.
+     * </P>
+     */
     @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,
-                    isFunction());
+                    "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, isFunction());
+                    AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, true);
         }
 
         RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel);
@@ -65,11 +76,16 @@ public abstract class AbstractSpringTemplateFunctionModel extends AbstractSpring
         return executeInternal(args, callPlace, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext);
     }
 
-    @Override
-    protected final boolean isFunction() {
-        return true;
-    }
-
+    /**
+     * Interal execution method that is supposed to be implemented by derived directive classes.
+     * @param args argument models
+     * @param callPlace the place where this is being called
+     * @param env template execution environment
+     * @param objectWrapperAndUnwrapper ObjectWrapperAndUnwrapper
+     * @param requestContext Spring RequestContext
+     * @return function execution result template model
+     * @throws TemplateException if template exception occurs
+     */
     protected abstract TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 6d0c954..d775cd1 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
@@ -31,7 +31,6 @@ 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.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.util.CallableUtils;
 import org.springframework.validation.Errors;
 import org.springframework.web.servlet.support.RequestContext;
@@ -72,13 +71,7 @@ public class BindErrorsDirective extends AbstractSpringTemplateDirectiveModel {
 
     private static final int NAME_PARAM_IDX = 0;
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT =
-            ArgumentArrayLayout.create(
-                    1,
-                    false,
-                    null,
-                    false
-                    );
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false, null, false);
 
     public BindErrorsDirective(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
@@ -90,7 +83,8 @@ public class BindErrorsDirective extends AbstractSpringTemplateDirectiveModel {
                     throws TemplateException, IOException {
         final String name = CallableUtils.getStringArgument(args, NAME_PARAM_IDX, this);
 
-        final TemplateModel bindErrorsModel = getBindErrorsTemplateModel(env, objectWrapperAndUnwrapper, requestContext, name);
+        final TemplateModel bindErrorsModel = getBindErrorsTemplateModel(env, objectWrapperAndUnwrapper, requestContext,
+                name);
 
         if (bindErrorsModel != null) {
             final TemplateModel[] nestedContentArgs = new TemplateModel[] { bindErrorsModel };
@@ -108,17 +102,13 @@ public class BindErrorsDirective extends AbstractSpringTemplateDirectiveModel {
         return ARGS_LAYOUT;
     }
 
-    private final TemplateModel getBindErrorsTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            RequestContext requestContext, String name) throws TemplateException {
+    private final TemplateModel getBindErrorsTemplateModel(Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext, String name)
+                    throws TemplateException {
         final Errors errors = requestContext.getErrors(name, false);
 
         if (errors != null && errors.hasErrors()) {
-            if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
-                CallableUtils.newGenericExecuteException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.",
-                        this, isFunction());
-            }
-
-            return wrapObject(objectWrapperAndUnwrapper, errors);
+            return objectWrapperAndUnwrapper.wrap(errors);
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 f155df1..52cd62b 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
@@ -103,15 +103,16 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
         TemplateModel evaluationContextModel = springTemplateModel.get(EVALUATION_CONTEXT_VAR_NAME);
 
         if (evaluationContextModel != null) {
-            evaluationContext = (EvaluationContext) unwrapObject(objectWrapperAndUnwrapper, evaluationContextModel);
+            evaluationContext = (EvaluationContext) objectWrapperAndUnwrapper.unwrap(evaluationContextModel);
         } else {
             evaluationContext = createEvaluationContext(env, objectWrapperAndUnwrapper, requestContext);
-            evaluationContextModel = wrapObject(objectWrapperAndUnwrapper, evaluationContext);
+            evaluationContextModel = objectWrapperAndUnwrapper.wrap(evaluationContext);
             springTemplateModel.setEvaluationContextModel(evaluationContextModel);
         }
 
         final Object result = expression.getValue(evaluationContext);
-        return wrapObject(objectWrapperAndUnwrapper, result);
+
+        return (result != null) ? objectWrapperAndUnwrapper.wrap(result) : null;
     }
 
     @Override
@@ -170,8 +171,13 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
         public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
             try {
                 TemplateModel model = env.getVariable(name);
-                Object value = unwrapObject(objectWrapperAndUnwrapper, model);
-                return new TypedValue(value);
+
+                if (model != null) {
+                    Object value = objectWrapperAndUnwrapper.unwrap(model);
+                    return new TypedValue(value);
+                } else {
+                    return null;
+                }
             } catch (TemplateException e) {
                 throw new AccessException("Can't get environment variable by name, '" + name + "'.", e);
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 e64aba5..8b98652 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
@@ -84,13 +84,8 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final String MESSAGE_RESOLVABLE_PARAM_NAME = "message";
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT =
-            ArgumentArrayLayout.create(
-                    1,
-                    true,
-                    StringToIndexMap.of(MESSAGE_RESOLVABLE_PARAM_NAME, MESSAGE_RESOLVABLE_PARAM_IDX),
-                    false
-                    );
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, true,
+            StringToIndexMap.of(MESSAGE_RESOLVABLE_PARAM_NAME, MESSAGE_RESOLVABLE_PARAM_IDX), false);
 
     public MessageFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
@@ -108,10 +103,10 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
         String message = null;
 
-        final TemplateModel messageResolvableModel = CallableUtils.getOptionalArgument(args, MESSAGE_RESOLVABLE_PARAM_IDX,
-                TemplateModel.class, this);
-        final MessageSourceResolvable messageResolvable = (MessageSourceResolvable) unwrapObject(
-                objectWrapperAndUnwrapper, messageResolvableModel);
+        final TemplateModel messageResolvableModel = CallableUtils.getOptionalArgument(args,
+                MESSAGE_RESOLVABLE_PARAM_IDX, TemplateModel.class, this);
+        final MessageSourceResolvable messageResolvable = (messageResolvableModel != null)
+                ? (MessageSourceResolvable) objectWrapperAndUnwrapper.unwrap(messageResolvableModel) : null;
 
         if (messageResolvable != null) {
             message = messageSource.getMessage(messageResolvable, requestContext.getLocale());
@@ -126,9 +121,9 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
                 if (!messageArgsModel.isEmptyCollection()) {
                     msgArgumentList = new ArrayList<>();
                     TemplateModel msgArgModel;
-                    for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext(); ) {
+                    for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext();) {
                         msgArgModel = tit.next();
-                        msgArgumentList.add(unwrapObject(objectWrapperAndUnwrapper, msgArgModel));
+                        msgArgumentList.add(objectWrapperAndUnwrapper.unwrap(msgArgModel));
                     }
                 }
 
@@ -141,7 +136,7 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
             }
         }
 
-        return wrapObject(objectWrapperAndUnwrapper, message);
+        return (message != null) ? objectWrapperAndUnwrapper.wrap(message) : null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
index 9bd51ef..a743c3f 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
@@ -84,8 +84,8 @@ public class NestedPathDirective extends AbstractSpringTemplateDirectiveModel {
         final TemplateStringModel prevNestedPathModel = springTemplateModel.getNestedPathModel();
         final String prevNestedPath = (prevNestedPathModel != null) ? prevNestedPathModel.getAsString() : null;
         final String newNestedPath = (prevNestedPath != null) ? prevNestedPath + path : path;
-        final TemplateStringModel newNestedPathModel = (TemplateStringModel) wrapObject(objectWrapperAndUnwrapper,
-                newNestedPath);
+        final TemplateStringModel newNestedPathModel = (TemplateStringModel) objectWrapperAndUnwrapper
+                .wrap(newNestedPath);
 
         try {
             springTemplateModel.setNestedPathModel(newNestedPathModel);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java
deleted file mode 100644
index 4ff3552..0000000
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java
+++ /dev/null
@@ -1,56 +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.spring.model;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * TemplateHashModel wrapper for templates using Spring Form directives and functions.
- */
-public final class SpringFormTemplateCallableHashModel implements TemplateHashModel, Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    public static final String NAME = "form";
-
-    private Map<String, AbstractSpringTemplateCallableModel> callablesMap = new HashMap<>();
-
-    public SpringFormTemplateCallableHashModel(final HttpServletRequest request, final HttpServletResponse response) {
-    }
-
-    public TemplateModel get(String key) throws TemplateException {
-        return callablesMap.get(key);
-    }
-
-    @Override
-    public boolean isEmptyHash() throws TemplateException {
-        return false;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 26ae060..4e42923 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
@@ -32,16 +32,27 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateStringModel;
 
 /**
- * TemplateHashModel wrapper for templates using Spring directives and functions.
+ * TemplateHashModel wrapper for templates using Spring directives, functions and internal models.
  */
 public final class SpringTemplateCallableHashModel implements TemplateHashModel, Serializable {
 
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Spring namespace model name.
+     */
     public static final String NAME = "spring";
 
+    /**
+     * Name of the internal nested path template model, which is equivalent to the <code>nestedPath</code> pageContext attribute
+     * 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";
 
+    /**
+     * 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 Map<String, TemplateModel> modelsMap = new HashMap<>();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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 d6c381e..9d40650 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
@@ -40,14 +40,9 @@ import org.springframework.web.servlet.support.RequestContext;
  * Some valid example(s):
  * </P>
  * <PRE>
- * </PRE>
- * <P>
- * Some valid example(s):
- * </P>
- * <PRE>
- *   &lt;@spring.bind "user"; status&gt;
- *     ${spring.transform(status.editor, user.birthDate)}
- *   &lt;/@spring.bind&gt;
+ * &lt;@spring.bind "user.birthDate"; status&gt;
+ *   &lt;div id="userBirthDate"&gt;${spring.transform(status.editor, status.actualValue)}&lt;/div&gt;
+ * &lt;/@spring.bind&gt;
  * </PRE>
  * <P>
  * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:bind /&gt;</code> JSP Tag Library, this directive
@@ -74,10 +69,12 @@ public class TransformFunction extends AbstractSpringTemplateFunctionModel {
                     throws TemplateException {
         final TemplateModel editorModel = CallableUtils.getOptionalArgument(args, PROPERTY_EDITOR_PARAM_IDX,
                 TemplateModel.class, this);
-        final PropertyEditor editor = (PropertyEditor) unwrapObject(objectWrapperAndUnwrapper, editorModel);
+        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 = unwrapObject(objectWrapperAndUnwrapper, valueModel);
+        final TemplateModel valueModel = CallableUtils.getOptionalArgument(args, VALUE_PARAM_IDX, TemplateModel.class,
+                this);
+        final Object value = (valueModel != null) ? objectWrapperAndUnwrapper.unwrap(valueModel) : null;
 
         String valueAsString = null;
 
@@ -90,7 +87,7 @@ public class TransformFunction extends AbstractSpringTemplateFunctionModel {
             }
         }
 
-        return wrapObject(objectWrapperAndUnwrapper, valueAsString);
+        return (valueAsString != null) ? objectWrapperAndUnwrapper.wrap(valueAsString) : null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/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
index 2220800..ae70a45 100644
--- 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
@@ -166,7 +166,7 @@ public class UrlFunction extends AbstractSpringTemplateFunctionModel {
             urlString = processor.processUrl(getRequest(), urlString);
         }
 
-        return wrapObject(objectWrapperAndUnwrapper, urlString);
+        return objectWrapperAndUnwrapper.wrap(urlString);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ef9ba120/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
index 604cc3d..c8fcbd2 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
@@ -35,7 +35,6 @@ import org.apache.freemarker.servlet.HttpSessionHashModel;
 import org.apache.freemarker.servlet.IncludePage;
 import org.apache.freemarker.servlet.ServletContextHashModel;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
-import org.apache.freemarker.spring.model.SpringFormTemplateCallableHashModel;
 import org.apache.freemarker.spring.model.SpringTemplateCallableHashModel;
 
 /**
@@ -141,8 +140,6 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
 
         model.putUnlistedModel(SpringTemplateCallableHashModel.NAME,
                 new SpringTemplateCallableHashModel(request, response));
-        model.putUnlistedModel(SpringFormTemplateCallableHashModel.NAME,
-                new SpringFormTemplateCallableHashModel(request, response));
 
         model.putAll(map);
 


[24/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for message function.

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for message function.


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

Branch: refs/heads/3
Commit: 77b7f30c5f61158c138f42e0f61c3b47b8df6d2d
Parents: d5c31a1
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 10:58:28 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 10:58:28 2017 -0400

----------------------------------------------------------------------
 .../spring/model/MessageFunction.java           |  2 +-
 .../spring/example/mvc/users/User.java          |  6 +--
 .../example/mvc/users/UserController.java       | 31 +++++++++----
 .../example/mvc/users/UserRepository.java       | 14 +++---
 .../spring/model/MessageFunctionTest.java       | 34 ++++++++++++---
 .../views/example/users/userlist.ftl            |  2 +
 .../model/message-function-basic-usages.ftl     | 32 ++++++++++++++
 .../example/mvc/users/UsersMessages.properties  |  1 +
 .../example/mvc/users/users-mvc-context.xml     | 46 ++++++++++++++++++++
 .../model/MessageFunctionTest-context.xml       | 46 --------------------
 10 files changed, 143 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/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 94a1e0f..e64aba5 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
@@ -65,7 +65,7 @@ import org.springframework.web.servlet.support.RequestContext;
  * &lt;#-- With 'code' positional parameter and message arguments (varargs) --&gt;
  * ${spring.message("message.user.form", user.firstName, user.lastName, user.email)}
  *
- * &lt;#-- With 'message' named parameter (<code>MessageResolvable</code> object) --&gt;
+ * &lt;#-- With 'message' named parameter (assuming a <code>MessageResolvable</code> object is set to a model attribute) --&gt;
  * ${spring.message(message=myMessageResolvable)}
  * </PRE>
  * <P>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
index a1550b2..1aa0515 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
@@ -23,18 +23,18 @@ import java.util.Date;
 
 public class User {
 
-    private final String id;
+    private final Integer id;
     private String password;
     private String email;
     private String firstName;
     private String lastName;
     private Date birthDate;
 
-    public User(final String id) {
+    public User(final Integer id) {
         this.id = id;
     }
 
-    public String getId() {
+    public Integer getId() {
         return id;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
index e8c0d86..80a158d 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
@@ -23,36 +23,51 @@ import java.util.LinkedList;
 import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
 
 @Controller
 public class UserController {
 
+    private static final String DEFAULT_USER_LIST_VIEW_NAME = "example/users/userlist";
+
+    private static final String DEFAULT_USER_EDIT_VIEW_NAME = "example/users/useredit";
+
     @Autowired
     private UserRepository userRepository;
 
-    @RequestMapping(value = "/users", method = RequestMethod.GET)
-    public String listUsers(Model model) {
+    @RequestMapping(value = "/users/", method = RequestMethod.GET)
+    public String listUsers(@RequestParam(value = "viewName", required = false) String viewName, Model model) {
         List<User> users = new LinkedList<>();
 
-        for (String id : userRepository.getUserIds()) {
+        for (Integer id : userRepository.getUserIds()) {
             users.add(userRepository.getUser(id));
         }
 
         model.addAttribute("users", users);
 
-        return "example/users/userlist";
+        return (StringUtils.hasText(viewName)) ? viewName : DEFAULT_USER_LIST_VIEW_NAME;
     }
 
-    @RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
-    public String editUser(@PathVariable("id") String id, Model model) {
+    @RequestMapping(value = "/users/{id:\\d+}", method = RequestMethod.GET)
+    public String getUser(@PathVariable("id") Integer id,
+            @RequestParam(value = "viewName", required = false) String viewName, Model model) {
         User user = userRepository.getUser(id);
-        model.addAttribute("user", user);
-        return "example/users/useredit";
+
+        if (user != null) {
+            model.addAttribute("user", user);
+        } else {
+            model.addAttribute("errorMessage",
+                    new DefaultMessageSourceResolvable(new String[] { "user.error.notfound" }, new Object[] { id }));
+        }
+
+        return (StringUtils.hasText(viewName)) ? viewName : DEFAULT_USER_EDIT_VIEW_NAME;
     }
 
     public UserRepository getUserRepository() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
index 3e53d03..5049521 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
@@ -30,9 +30,9 @@ import org.springframework.stereotype.Repository;
 @Repository
 public class UserRepository {
 
-    private Map<String, User> usersMap = new ConcurrentHashMap<>();
+    private Map<Integer, User> usersMap = new ConcurrentHashMap<>();
     {
-        String id = "13c2ec8c-102c-4883-a282-3fe983e61515";
+        Integer id = 101;
         User user = new User(id);
         user.setEmail("john@example.com");
         user.setFirstName("John");
@@ -44,7 +44,7 @@ public class UserRepository {
         user.setBirthDate(birthDate.getTime());
         usersMap.put(id, user);
 
-        id = "04d6080b-2098-4eaf-90ee-7331caab5e91";
+        id = 102;
         user = new User(id);
         user.setEmail("jane@example.com");
         user.setFirstName("Jane");
@@ -57,11 +57,11 @@ public class UserRepository {
         usersMap.put(id, user);
     }
 
-    public synchronized Set<String> getUserIds() {
+    public synchronized Set<Integer> getUserIds() {
         return new TreeSet<>(usersMap.keySet());
     }
 
-    public synchronized User getUser(final String id) {
+    public synchronized User getUser(final Integer id) {
         if (id == null) {
             throw new IllegalArgumentException("ID must be non-null.");
         }
@@ -76,7 +76,7 @@ public class UserRepository {
     }
 
     public synchronized User addOrUpdateUser(final User user) {
-        final String id = user.getId();
+        final Integer id = user.getId();
         User newUser = cloneUser(user, id);
         usersMap.put(id, newUser);
         return cloneUser(newUser, id);
@@ -91,7 +91,7 @@ public class UserRepository {
         return user != null;
     }
 
-    private User cloneUser(final User source, final String id) {
+    private User cloneUser(final User source, final Integer id) {
         User clone = new User(id);
         clone.setPassword(source.getPassword());
         clone.setEmail(source.getEmail());

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
index 03c32f4..803e3de 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
@@ -20,10 +20,13 @@
 package org.apache.freemarker.spring.model;
 
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
 
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -38,12 +41,15 @@ import org.springframework.web.context.WebApplicationContext;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @WebAppConfiguration("classpath:META-INF/web-resources")
-@ContextConfiguration("MessageFunctionTest-context.xml")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
 public class MessageFunctionTest {
 
     @Autowired
     private WebApplicationContext wac;
 
+    @Autowired
+    private UserRepository userRepository;
+
     private MockMvc mockMvc;
 
     @Before
@@ -52,10 +58,26 @@ public class MessageFunctionTest {
     }
 
     @Test
-    public void getUsers() throws Exception {
-        mockMvc.perform(get("/users").accept(MediaType.parseMediaType("text/html")))
-            .andExpect(status().isOk())
-            .andExpect(content().contentTypeCompatibleWith("text/html"))
-            .andExpect(xpath("/html/head/title").string("Spring MVC Form Example - Users"));
+    public void getMessageFunctionBasicUsages() throws Exception {
+        final Integer userId = userRepository.getUserIds().iterator().next();
+        final User user = userRepository.getUser(userId);
+        mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/message-function-basic-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//div[@id='userId']/text()").string(wac.getMessage("user.id", null, null)))
+                .andExpect(xpath("//div[@id='userEmail']/text()").string(wac.getMessage("user.email", null, null)))
+                .andExpect(xpath("//div[@id='userInfoWithArgs']/text()").string(wac.getMessage("user.form.message",
+                        new Object[] { user.getFirstName(), user.getLastName(), user.getEmail() }, null)));
+    }
+
+    @Test
+    public void getMessageFunctionWithMessageSourceResolvable() throws Exception {
+        final Integer nonExistingUserId = 0;
+        mockMvc.perform(
+                get("/users/{userId}/", nonExistingUserId).param("viewName", "test/model/message-function-basic-usages")
+                        .accept(MediaType.parseMediaType("text/html")))
+                .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//div[@id='errorMessage']/text()")
+                        .string(wac.getMessage("user.error.notfound", new Object[] { nonExistingUserId }, null)));
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
index 25c757f..aef5c91 100644
--- a/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
@@ -21,6 +21,7 @@
 <head>
 <title>Spring MVC Form Example - Users</title>
 </head>
+<body>
 
 <h1>Users</h1>
 
@@ -41,4 +42,5 @@
   </tbody>
 </table>
 
+</body>
 </html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/message-function-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/message-function-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/message-function-basic-usages.ftl
new file mode 100644
index 0000000..fe1e65e
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/message-function-basic-usages.ftl
@@ -0,0 +1,32 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<#if user??>
+  <div id="userId">${spring.message("user.id")!}</div>
+  <div id="userEmail">${spring.message("user.email")!}</div>
+  <div id="userInfoWithArgs">${spring.message("user.form.message", user.firstName, user.lastName, user.email)!}</div>
+<#else>
+  <div id="errorMessage">${spring.message(message=errorMessage)!}</div>
+</#if>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
index ade76f4..497607c 100644
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
@@ -5,3 +5,4 @@ user.email=E-Mail
 user.firstName=First name
 user.lastName=Last name
 user.birthDate=Birth Date
+user.error.notfound=User not found by ID: {0}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
new file mode 100644
index 0000000..0dbc950
--- /dev/null
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+   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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:mvc="http://www.springframework.org/schema/mvc"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
+
+  <context:component-scan base-package="org.apache.freemarker.spring.example.mvc.users" />
+
+  <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
+    <property name="basename" value="classpath:org/apache/freemarker/spring/example/mvc/users/UsersMessages" />
+  </bean>
+
+  <bean id="configuration" class="org.apache.freemarker.spring.ConfigurationFactoryBean">
+    <property name="localizedTemplateLookup" value="false" />
+    <property name="templateLoader">
+      <bean class="org.apache.freemarker.spring.SpringResourceTemplateLoader">
+      </bean>
+    </property>
+  </bean>
+
+  <bean id="viewResolver" class="org.apache.freemarker.spring.web.view.FreeMarkerViewResolver">
+    <property name="configuration" ref="configuration" />
+    <property name="prefix" value="/views/" />
+    <property name="suffix" value=".ftl" />
+  </bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/77b7f30c/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
deleted file mode 100644
index 862de44..0000000
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-   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.
--->
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xmlns:mvc="http://www.springframework.org/schema/mvc"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
-                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
-                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
-  <context:component-scan base-package="org.apache.freemarker.spring.example.mvc.users" />
-
-  <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
-    <property name="basename" value="classpath:org.apache.freemarker.spring.example.mvc.users.UserMessages" />
-  </bean>
-
-  <bean id="configuration" class="org.apache.freemarker.spring.ConfigurationFactoryBean">
-    <property name="localizedTemplateLookup" value="false" />
-    <property name="templateLoader">
-      <bean class="org.apache.freemarker.spring.SpringResourceTemplateLoader">
-      </bean>
-    </property>
-  </bean>
-
-  <bean id="viewResolver" class="org.apache.freemarker.spring.web.view.FreeMarkerViewResolver">
-    <property name="configuration" ref="configuration" />
-    <property name="prefix" value="/views/" />
-    <property name="suffix" value=".ftl" />
-  </bean>
-
-</beans>


[30/36] incubator-freemarker git commit: FREEMARKER-55: adding unit test for bind directive.

Posted by dd...@apache.org.
FREEMARKER-55: adding unit test for bind directive.


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

Branch: refs/heads/3
Commit: 7a93fa0ac9d5594080b3b8749976f80030de6e4f
Parents: 4672252
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 15:02:44 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 15:02:44 2017 -0400

----------------------------------------------------------------------
 .../spring/model/BindDirectiveTest.java         | 73 ++++++++++++++++++++
 .../spring/model/EvalFunctionTest.java          |  5 --
 .../test/model/bind-directive-basic-usages.ftl  | 55 +++++++++++++++
 3 files changed, 128 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7a93fa0a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
new file mode 100644
index 0000000..cd1af42
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class BindDirectiveTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testMessageFunctionBasicUsages() throws Exception {
+        final Integer userId = userRepository.getUserIds().iterator().next();
+        final User user = userRepository.getUser(userId);
+        mockMvc.perform(get("/users/{userId}.", userId).param("viewName", "test/model/bind-directive-basic-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//input[@name='email']/@value").string(user.getEmail()))
+                .andExpect(xpath("//input[@name='firstName']/@value").string(user.getFirstName()))
+                .andExpect(xpath("//input[@name='lastName']/@value").string(user.getLastName()))
+                .andExpect(xpath("//div[@id='statusValueNotReachable']/text()").string(""));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7a93fa0a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
index 953b571..be6f6dd 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
@@ -53,14 +53,9 @@ public class EvalFunctionTest {
 
     private MockMvc mockMvc;
 
-    private long startTimeMillis;
-
     @Before
     public void setUp() {
         mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
-
-        startTimeMillis = System.currentTimeMillis();
-        System.setProperty("EvalFunctionTest.startTimeMillis", Long.toString(startTimeMillis));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7a93fa0a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/bind-directive-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/bind-directive-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/bind-directive-basic-usages.ftl
new file mode 100644
index 0000000..2f0e535
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/bind-directive-basic-usages.ftl
@@ -0,0 +1,55 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<table class="table">
+  <tbody>
+    <tr>
+      <th>E-Mail</th>
+      <td>
+        <@spring.bind "user.email"; status>
+          <input type="text" name="email" value="${status.value!}" />
+        </...@spring.bind>
+      </td>
+    </tr>
+    <tr>
+      <th>First Name</th>
+      <td>
+        <@spring.bind "user.firstName"; status>
+          <input type="text" name="firstName" value="${status.value!}" />
+        </...@spring.bind>
+      </td>
+    </tr>
+    <tr>
+      <th>Last Name</th>
+      <td>
+        <@spring.bind "user.lastName"; status>
+          <input type="text" name="lastName" value="${status.value!}" />
+        </...@spring.bind>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+<div id="statusValueNotReachable"><#if status??>${status.value!}</#if></div>
+
+</body>
+</html>


[02/36] incubator-freemarker git commit: FREEMARKER-55: using namespace variable for status variable.

Posted by dd...@apache.org.
FREEMARKER-55: using namespace variable for status variable.


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

Branch: refs/heads/3
Commit: 19a3277c5809e30243a921a2ee42d3a40ecf9ebc
Parents: 6709117
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Aug 28 23:11:12 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Aug 28 23:11:12 2017 -0400

----------------------------------------------------------------------
 .../apache/freemarker/spring/model/BindDirective.java  | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/19a3277c/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index 967344d..9ca7c6a 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -91,10 +91,15 @@ public class BindDirective implements TemplateDirectiveModel {
             resolvedPath = resolveNestedPath(resolvedPath);
         }
 
-        BindStatus status = requestContext.getBindStatus(resolvedPath);
-        env.setLocalVariable(STATUS_VARIABLE_NAME, new BeanModel(status, (DefaultObjectWrapper) objectWrapper));
-
-        callPlace.executeNestedContent(null, out, env);
+        final TemplateModel oldStatusModel = env.getVariable(STATUS_VARIABLE_NAME);
+
+        try {
+            BindStatus status = requestContext.getBindStatus(resolvedPath);
+            env.setVariable(STATUS_VARIABLE_NAME, new BeanModel(status, (DefaultObjectWrapper) objectWrapper));
+            callPlace.executeNestedContent(null, out, env);
+        } finally {
+            env.setVariable(STATUS_VARIABLE_NAME, oldStatusModel);
+        }
     }
 
     @Override


[11/36] incubator-freemarker git commit: FREEMARKER-55: use vargs for code and args

Posted by dd...@apache.org.
FREEMARKER-55: use vargs for code and args


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

Branch: refs/heads/3
Commit: b0aceddab9ac55c622fb596d2ed03444c2ff0884
Parents: 8f5eaaa
Author: Woonsan Ko <wo...@apache.org>
Authored: Sun Sep 3 00:27:08 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Sun Sep 3 00:27:08 2017 -0400

----------------------------------------------------------------------
 .../spring/model/MessageFunction.java           | 34 +++++++++++---------
 1 file changed, 18 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0acedda/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 7bee8c1..9579366 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
@@ -42,15 +42,14 @@ import org.springframework.web.servlet.support.RequestContext;
 
 public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
-    private static final int CODE_PARAM_IDX = 0;
-    private static final int MESSAGE_PARAM_IDX = 1;
-    private static final int MESSAGE_ARGS_PARAM_IDX = 2;
+    private static final int MESSAGE_PARAM_IDX = 0;
+    private static final int MESSAGE_ARGS_PARAM_IDX = 1;
 
     private static final String MESSAGE_PARAM_NAME = "message";
 
     private static final ArgumentArrayLayout ARGS_LAYOUT =
             ArgumentArrayLayout.create(
-                    1,
+                    0,
                     true,
                     StringToIndexMap.of(
                             MESSAGE_PARAM_NAME, MESSAGE_PARAM_IDX),
@@ -72,23 +71,24 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
         String message = null;
 
-        final String code = CallableUtils.getStringArgument(args, CODE_PARAM_IDX, this);
-
-        if (code != null && !code.isEmpty()) {
-            final TemplateCollectionModel messageArgsModel = (TemplateCollectionModel) args[MESSAGE_ARGS_PARAM_IDX];
-            List<Object> msgArgumentList = null;
-
-            if (!messageArgsModel.isEmptyCollection()) {
-                msgArgumentList = new ArrayList<>();
-                TemplateModel msgArgModel;
-                for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext(); ) {
-                    msgArgModel = tit.next();
+        final TemplateCollectionModel messageArgsModel = (TemplateCollectionModel) args[MESSAGE_ARGS_PARAM_IDX];
+
+        if (!messageArgsModel.isEmptyCollection()) {
+            String code = null;
+            List<Object> msgArgumentList = new ArrayList<>();
+            TemplateModel msgArgModel;
+            int i = 0;
+            for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext(); i++) {
+                msgArgModel = tit.next();
+                if (i == 0) {
+                    code = objectWrapperAndUnwrapper.unwrap(msgArgModel).toString();
+                } else {
                     msgArgumentList.add(objectWrapperAndUnwrapper.unwrap(msgArgModel));
                 }
             }
 
             // TODO: Is it okay to set the default value to null to avoid NoSuchMessageException from Spring MessageSource?
-            message = messageSource.getMessage(code, (msgArgumentList == null) ? null : msgArgumentList.toArray(),
+            message = messageSource.getMessage(code, (msgArgumentList.isEmpty()) ? null : msgArgumentList.toArray(),
                     null, requestContext.getLocale());
         } else {
             final TemplateModel messageModel = CallableUtils.getOptionalArgument(args, MESSAGE_PARAM_IDX,
@@ -97,6 +97,8 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
                 MessageSourceResolvable messageResolvable = (MessageSourceResolvable) objectWrapperAndUnwrapper
                         .unwrap(messageModel);
                 message = messageSource.getMessage(messageResolvable, requestContext.getLocale());
+            } else {
+                throw new TemplateException("Neither message code nor message resolvable was set.");
             }
         }
 


[14/36] incubator-freemarker git commit: Merge branch '3' into feature/FREEMARKER-55-2

Posted by dd...@apache.org.
Merge branch '3' into feature/FREEMARKER-55-2


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

Branch: refs/heads/3
Commit: d0bd43c8e42a9b5b2834a2d5c139401fcb8fda2d
Parents: 12f70ff 0b7d965
Author: Woonsan Ko <wo...@apache.org>
Authored: Wed Sep 6 10:02:01 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Wed Sep 6 10:02:01 2017 -0400

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              | 12 +++
 .../freemarker/core/SequenceBuiltInTest.java    | 75 ++++++++++++++++++
 .../core/cano-identifier-escaping.ftl           |  2 +-
 .../core/cano-identifier-escaping.ftl.out       |  2 +-
 .../expected/identifier-escaping.txt            |  2 +-
 .../core/templatesuite/expected/macros.txt      |  6 +-
 .../templatesuite/templates/hashliteral.ftl     |  4 +-
 .../templates/identifier-escaping.ftl           |  4 +-
 .../core/templatesuite/templates/macros.ftl     |  2 +-
 .../freemarker/core/ASTExpAddOrConcat.java      | 41 +++++-----
 .../apache/freemarker/core/ASTExpBuiltIn.java   |  3 +-
 .../freemarker/core/ASTExpHashLiteral.java      |  5 +-
 .../freemarker/core/BuiltInForSequence.java     |  3 +
 .../freemarker/core/BuiltInsForHashes.java      | 12 ++-
 .../freemarker/core/BuiltInsForSequences.java   | 47 +++++++----
 .../apache/freemarker/core/MessageUtils.java    | 10 +++
 .../apache/freemarker/core/NativeSequence.java  |  4 +
 .../freemarker/core/model/impl/BeanModel.java   |  4 +-
 .../core/model/impl/IterableAndSequence.java    | 82 --------------------
 19 files changed, 179 insertions(+), 141 deletions(-)
----------------------------------------------------------------------



[17/36] incubator-freemarker git commit: FREEMARKER-55: use Environment variables instead of pageContext in eval function.

Posted by dd...@apache.org.
FREEMARKER-55: use Environment variables instead of pageContext in eval function.


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

Branch: refs/heads/3
Commit: 6f025c79534117a0f87eb17445e1a76cf06be418
Parents: 9eedd86
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 14:13:56 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 14:13:56 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/EvalFunction.java   | 34 +++++++++++++-------
 1 file changed, 22 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6f025c79/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 a380f19..01c3cc5 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
@@ -80,7 +80,7 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
         final Expression expression = expressionParser.parseExpression(expressionString);
 
         // TODO: cache evaluationContext somewhere in request level....
-        EvaluationContext evaluationContext = createEvaluationContext(env, requestContext);
+        EvaluationContext evaluationContext = createEvaluationContext(env, objectWrapperAndUnwrapper, requestContext);
 
         final Object result = expression.getValue(evaluationContext);
         return wrapObject(objectWrapperAndUnwrapper, result);
@@ -91,10 +91,11 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
         return ARGS_LAYOUT;
     }
 
-    private EvaluationContext createEvaluationContext(final Environment env, final RequestContext requestContext) {
+    private EvaluationContext createEvaluationContext(final Environment env,
+            final ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, final RequestContext requestContext) {
         StandardEvaluationContext context = new StandardEvaluationContext();
 
-        context.addPropertyAccessor(new EnvironmentVariablesPropertyAccessor(env));
+        context.addPropertyAccessor(new EnvironmentVariablesPropertyAccessor(env, objectWrapperAndUnwrapper));
         context.addPropertyAccessor(new MapAccessor());
         context.addPropertyAccessor(new EnvironmentAccessor());
         context.setBeanResolver(new BeanFactoryResolver(requestContext.getWebApplicationContext()));
@@ -112,42 +113,51 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
         return (ConversionService) getRequest().getAttribute(ConversionService.class.getName());
     }
 
-    private static class EnvironmentVariablesPropertyAccessor implements PropertyAccessor {
+    private class EnvironmentVariablesPropertyAccessor implements PropertyAccessor {
 
         private final Environment env;
+        private final ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper;
 
-        public EnvironmentVariablesPropertyAccessor(final Environment env) {
+        public EnvironmentVariablesPropertyAccessor(final Environment env,
+                final ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper) {
             this.env = env;
+            this.objectWrapperAndUnwrapper = objectWrapperAndUnwrapper;
         }
 
         @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;
+            try {
+                return (target == null && env.getVariable(name) != null);
+            } catch (TemplateException e) {
+                throw new AccessException("Can't get environment variable by name, '" + name + "'.", e);
+            }
         }
 
         @Override
         public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
-            // TODO Auto-generated method stub
-            return null;
+            try {
+                TemplateModel model = env.getVariable(name);
+                Object value = unwrapObject(objectWrapperAndUnwrapper, model);
+                return new TypedValue(value);
+            } catch (TemplateException e) {
+                throw new AccessException("Can't get environment variable by name, '" + name + "'.", e);
+            }
         }
 
         @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
+            throw new UnsupportedOperationException();
         }
     }
 }


[29/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for url function.

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for url function.


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

Branch: refs/heads/3
Commit: 4672252edb58455d4727ad8e233c892ddf0fe5fb
Parents: e4598db
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 14:25:49 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 14:25:49 2017 -0400

----------------------------------------------------------------------
 .../spring/model/UrlFunctionTest.java           | 76 ++++++++++++++++++++
 .../test/model/url-function-basic-usages.ftl    | 62 ++++++++++++++++
 2 files changed, 138 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4672252e/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
new file mode 100644
index 0000000..c2e1ce3
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class UrlFunctionTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testThemeFunctionBasicUsages() throws Exception {
+        final Integer userId = userRepository.getUserIds().iterator().next();
+        mockMvc.perform(get("/users/").param("viewName", "test/model/url-function-basic-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//h2[@id='usersListHeader']//a/@href", userId).string("/users/"))
+                .andExpect(xpath("//h3[@id='usersListHeaderWithSortParams']//a/@href", userId)
+                        .string("/users/?sortField=birthDate&sortDirection=descending"))
+                .andExpect(xpath("//h2[@id='otherAppsUsersListHeader']//a/@href", userId).string("/otherapp/users/"))
+                .andExpect(xpath("//h3[@id='otherAppsUsersListHeaderWithSortParams']//a/@href", userId)
+                        .string("/otherapp/users/?sortField=birthDate&sortDirection=descending"))
+                .andExpect(xpath("//div[@id='user-%s']//a[@class='userIdLink']/@href", userId).string("/users/" + userId + "/"))
+                .andExpect(xpath("//div[@id='user-%s']//a[@class='userNameLink']/@href", userId).string("/users/" + userId + "/"))
+                .andExpect(xpath("//div[@id='freeMarkerManualUrl']//a/@href", userId)
+                        .string("http://freemarker.org/docs/index.html"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/4672252e/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/url-function-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/url-function-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/url-function-basic-usages.ftl
new file mode 100644
index 0000000..870ba3c
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/url-function-basic-usages.ftl
@@ -0,0 +1,62 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<h2 id="usersListHeader">
+  <#assign pathInfo="/users/" />
+  <a href="${spring.url(pathInfo)}">Users List</a>
+</h2>
+
+<h3 id="usersListHeaderWithSortParams">
+  <#assign pathInfo="/users/" />
+  <a href="${spring.url(pathInfo, sortField='birthDate', sortDirection='descending')}">Users List</a>
+</h3>
+
+<h2 id="otherAppsUsersListHeader">
+  <#assign pathInfo="/users/" />
+  <a href="${spring.url(pathInfo, context='/otherapp')}">Users List</a>
+</h2>
+
+<h3 id="otherAppsUsersListHeaderWithSortParams">
+  <#assign pathInfo="/users/" />
+  <a href="${spring.url(pathInfo, context='/otherapp', sortField='birthDate', sortDirection='descending')}">Users List</a>
+</h3>
+
+<ul>
+  <#list users as user>
+    <li>
+      <div id="user-${user.id!}">
+        <#assign pathInfo="/users/{userId}/" />
+        <a class="userIdLink" href="${spring.url(pathInfo, userId=user.id?string)}">${user.id!}</a>
+        <#assign pathInfo="/users/${user.id}/" />
+        <a class="userNameLink" href="${spring.url(pathInfo)}">${user.firstName!} ${user.lastName!}</a>
+      </div>
+    </li>
+  </#list>
+</ul>
+
+<div id="freeMarkerManualUrl">
+  <#assign pathInfo="http://freemarker.org/docs/index.html" />
+  <a href="${spring.url(pathInfo)}">Apache FreeMarker Manual</a>
+</div>
+
+</body>
+</html>


[35/36] incubator-freemarker git commit: FREEMARKER-55: Updating change logs

Posted by dd...@apache.org.
FREEMARKER-55: Updating change logs


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

Branch: refs/heads/3
Commit: 1c7c98745a87d86d3f953ed99a26e239a3bcaaaa
Parents: ef9ba12
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 22:16:09 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 22:16:09 2017 -0400

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              | 14 ++++++++++++
 .../spring/model/BindErrorsDirective.java       |  8 ++++++-
 .../freemarker/spring/model/EvalFunction.java   |  8 ++++++-
 .../spring/model/MessageFunction.java           |  9 ++++++--
 .../spring/model/NestedPathDirective.java       |  8 ++++++-
 .../spring/model/TransformFunction.java         |  8 ++++++-
 .../freemarker/spring/model/UrlFunction.java    |  9 ++++++--
 .../apache/freemarker/spring/model/package.html | 23 ++++++++++++++++++++
 .../org/apache/freemarker/spring/package.html   | 23 ++++++++++++++++++++
 .../freemarker/spring/web/view/package.html     | 23 ++++++++++++++++++++
 10 files changed, 125 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index e1bbb44..0febe03 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -476,6 +476,20 @@ This is about the Spring Framework Support (freemarker-spring): FREEMARKER-54, F
 - SpringResourceTemplateLoader, a new TemplateLoader to load templates from Spring Framework's Resources.
 - New FreeMarkerView and FreeMarkerViewResolver for MVC support. FreeMarkerView supports TaglibFactory and other
 models by default like FreemarkerServlet does.
+- Directives and Functions Support to replace Spring JSP Tag Libraries in spring.tld:
+  - <spring:htmlEscape ... /> : No need since FreeMarker Built-In's and escaping directives are better.
+  - <spring:escapeBody ... /> : No need since FreeMarker Built-In's and escaping directives are better.
+  - <spring:message ... /> : Replaced by spring.message function. e.g, ${spring.message(...)}
+  - <spring:theme ... /> : Replaced by spring.theme function. e.g, ${spring.theme(...)}
+  - <spring:argument ... /> : No need since spring.message(...) and spring.theme(...) functions support
+    positional varargs for variable length arguments.
+  - <spring:hasBindErrors ... /> : Replaced by <@spring.hasBindErrors ... /> directive.
+  - <spring:nestedPath ... /> : Replaced by <@spring.nestedPath ... /> directive.
+  - <spring:bind ... /> : Replaced by <@spring.bind ... /> directive.
+  - <spring:transform ... /> : Replaced by spring.transform(...) function.
+  - <spring:url ... /> : Replaced by spring.url(...) function.
+  - <spring:param ... /> : No need since spring.url(...) function supports named vargs for variable length parameters.
+  - <spring:eval /> : Replaced by spring.eval(...) function.
 
 Core / Miscellaneous
 ....................

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/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 d775cd1..2b73780 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
@@ -71,7 +71,13 @@ public class BindErrorsDirective extends AbstractSpringTemplateDirectiveModel {
 
     private static final int NAME_PARAM_IDX = 0;
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false, null, false);
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    null,
+                    false
+                    );
 
     public BindErrorsDirective(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/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 52cd62b..39d210e 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
@@ -81,7 +81,13 @@ public class EvalFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final int EXPRESSION_PARAM_IDX = 0;
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false, null, false);
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    null,
+                    false
+                    );
 
     private static final String EVALUATION_CONTEXT_VAR_NAME = "org.apache.freemarker.spring.model.EVALUATION_CONTEXT";
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/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 8b98652..f30e07b 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
@@ -84,8 +84,13 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final String MESSAGE_RESOLVABLE_PARAM_NAME = "message";
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, true,
-            StringToIndexMap.of(MESSAGE_RESOLVABLE_PARAM_NAME, MESSAGE_RESOLVABLE_PARAM_IDX), false);
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    true,
+                    StringToIndexMap.of(MESSAGE_RESOLVABLE_PARAM_NAME, MESSAGE_RESOLVABLE_PARAM_IDX),
+                    false
+                    );
 
     public MessageFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
index a743c3f..b24dfaf 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
@@ -60,7 +60,13 @@ public class NestedPathDirective extends AbstractSpringTemplateDirectiveModel {
 
     private static final int PATH_PARAM_IDX = 0;
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false, null, false);
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    null,
+                    false
+                    );
 
     public NestedPathDirective(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/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 9d40650..6110652 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
@@ -57,7 +57,13 @@ public class TransformFunction extends AbstractSpringTemplateFunctionModel {
     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);
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    2,
+                    false,
+                    null,
+                    false
+                    );
 
     public TransformFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/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
index ae70a45..47c83ab 100644
--- 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
@@ -87,8 +87,13 @@ public class UrlFunction extends AbstractSpringTemplateFunctionModel {
 
     private static final String URL_TEMPLATE_DELIMITER_SUFFIX = "}";
 
-    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false,
-            StringToIndexMap.of(CONTEXT_PARAM_NAME, CONTEXT_PARAM_IDX), true);
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    StringToIndexMap.of(CONTEXT_PARAM_NAME, CONTEXT_PARAM_IDX),
+                    true
+                    );
 
     public UrlFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/package.html
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/package.html b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/package.html
new file mode 100644
index 0000000..5a98e44
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/package.html
@@ -0,0 +1,23 @@
+<!--
+  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.
+-->
+<html>
+<body>
+<p>FreeMarker Directives and Functions Support for Spring Framework MVC</p>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/freemarker-spring/src/main/java/org/apache/freemarker/spring/package.html
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/package.html b/freemarker-spring/src/main/java/org/apache/freemarker/spring/package.html
new file mode 100644
index 0000000..05ba1ff
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/package.html
@@ -0,0 +1,23 @@
+<!--
+  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.
+-->
+<html>
+<body>
+<p>Spring Framework (MVC) Support</p>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/1c7c9874/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/package.html
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/package.html b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/package.html
new file mode 100644
index 0000000..98b1417
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/package.html
@@ -0,0 +1,23 @@
+<!--
+  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.
+-->
+<html>
+<body>
+<p>Spring Framework MVC View Support</p>
+</body>
+</html>


[23/36] incubator-freemarker git commit: FREEMARKER-55: unit test template code

Posted by dd...@apache.org.
FREEMARKER-55: unit test template code


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

Branch: refs/heads/3
Commit: d5c31a1b4e5753e7dbbeb1b1971c3b9bcf5643e4
Parents: 96c038d
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 23:24:37 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 23:24:37 2017 -0400

----------------------------------------------------------------------
 .../spring/example/mvc/users/UsersMessages.properties         | 7 +++++++
 .../freemarker/spring/model/MessageFunctionTest-context.xml   | 2 +-
 .../apache/freemarker/spring/model/UsersMessages.properties   | 7 -------
 3 files changed, 8 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d5c31a1b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
new file mode 100644
index 0000000..ade76f4
--- /dev/null
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
@@ -0,0 +1,7 @@
+user.form.message=Edit info for {0} {1} <{2}>
+user.id=ID
+user.password=Password
+user.email=E-Mail
+user.firstName=First name
+user.lastName=Last name
+user.birthDate=Birth Date

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d5c31a1b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
index f68f584..862de44 100644
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
@@ -26,7 +26,7 @@
   <context:component-scan base-package="org.apache.freemarker.spring.example.mvc.users" />
 
   <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
-    <property name="basename" value="classpath:org.apache.freemarker.spring.model.UserMessages" />
+    <property name="basename" value="classpath:org.apache.freemarker.spring.example.mvc.users.UserMessages" />
   </bean>
 
   <bean id="configuration" class="org.apache.freemarker.spring.ConfigurationFactoryBean">

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d5c31a1b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties
deleted file mode 100644
index ade76f4..0000000
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-user.form.message=Edit info for {0} {1} <{2}>
-user.id=ID
-user.password=Password
-user.email=E-Mail
-user.firstName=First name
-user.lastName=Last name
-user.birthDate=Birth Date


[06/36] incubator-freemarker git commit: FREEMARKER-55: code clean ups

Posted by dd...@apache.org.
FREEMARKER-55: code clean ups


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

Branch: refs/heads/3
Commit: 2de73b082f1bf7c36d616263688f77d27bc50227
Parents: 64adad8
Author: Woonsan Ko <wo...@apache.org>
Authored: Wed Aug 30 09:07:02 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Wed Aug 30 09:07:02 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateDirectiveModel.java   | 50 +++++++++++++++++---
 .../freemarker/spring/model/BindDirective.java  | 15 +++---
 2 files changed, 50 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2de73b08/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 9baf3f0..1c5332b 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
@@ -29,14 +29,18 @@ 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.ObjectWrappingException;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.springframework.web.servlet.support.BindStatus;
 import org.springframework.web.servlet.support.RequestContext;
 import org.springframework.web.servlet.view.AbstractTemplateView;
 
 public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDirectiveModel {
 
+    // TODO: namespace this into 'spring.nestedPath'??
     /**
      * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
      */
@@ -55,9 +59,9 @@ public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDi
             throws TemplateException, IOException {
         final ObjectWrapper objectWrapper = env.getObjectWrapper();
 
-        if (!(objectWrapper instanceof DefaultObjectWrapper)) {
+        if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
             throw new TemplateException(
-                    "The ObjectWrapper of environment wasn't instance of " + DefaultObjectWrapper.class.getName());
+                    "The ObjectWrapper of environment wasn't instance of ObjectWrapperAndUnwrapper.");
         }
 
         TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
@@ -66,13 +70,14 @@ public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDi
             throw new TemplateException(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.");
         }
 
-        RequestContext requestContext = (RequestContext) ((DefaultObjectWrapper) objectWrapper).unwrap(rcModel);
+        RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel);
 
-        executeInternal(args, callPlace, out, env, (DefaultObjectWrapper) objectWrapper, requestContext);
+        executeInternal(args, callPlace, out, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext);
     }
 
     protected abstract void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
-            DefaultObjectWrapper objectWrapper, RequestContext requestContext) throws TemplateException, IOException;
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+                    throws TemplateException, IOException;
 
     protected final HttpServletRequest getRequest() {
         return request;
@@ -82,7 +87,40 @@ public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDi
         return response;
     }
 
-    protected final String resolveNestedPath(final Environment env, final String path) {
+    /**
+     * Find {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext} by the {@code path}
+     * and wrap it as a {@link TemplateModel}.
+     * <P>
+     * <EM>NOTE:</EM> In FreeMarker, there is no need to depend on <code>BindStatus#htmlEscape</code> option
+     * as FreeMarker template expressions can easily set escape option by themselves.
+     * Therefore, this method always get a {@link BindStatus} with {@code htmlEscape} option set to {@code false}.
+     * @param env Environment
+     * @param objectWrapperAndUnwrapper ObjectWrapperAndUnwrapper
+     * @param requestContext Spring RequestContext
+     * @param path bind path
+     * @param ignoreNestedPath flag whether or not to ignore the nested path
+     * @return {@link TemplateModel} wrapping a {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext}
+     * by the {@code path}
+     * @throws ObjectWrappingException if fails to wrap the <code>BindStatus</code> object
+     */
+    protected final TemplateModel getBindStatusTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
+            RequestContext requestContext, String path, boolean ignoreNestedPath) throws ObjectWrappingException {
+        final String resolvedPath = (ignoreNestedPath) ? path : resolveNestedPath(env, objectWrapperAndUnwrapper, path);
+        BindStatus status = requestContext.getBindStatus(resolvedPath, false);
+
+        if (status != null) {
+            if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
+                throw new IllegalArgumentException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.");
+            }
+
+            return ((DefaultObjectWrapper) objectWrapperAndUnwrapper).wrap(status);
+        }
+
+        return null;
+    }
+
+    private String resolveNestedPath(final Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
+            final String path) {
         // TODO: should read it from request or env??
         //       or read spring.nestedPath first and read request attribute next??
         String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2de73b08/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index b7edbac..d7967c8 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -29,12 +29,10 @@ 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.model.impl.BeanModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
-import org.springframework.web.servlet.support.BindStatus;
 import org.springframework.web.servlet.support.RequestContext;
 
 public class BindDirective extends AbstractSpringTemplateDirectiveModel {
@@ -58,17 +56,16 @@ public class BindDirective extends AbstractSpringTemplateDirectiveModel {
 
     @Override
     protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
-            DefaultObjectWrapper objectWrapper, RequestContext requestContext) throws TemplateException, IOException {
-
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+                    throws TemplateException, IOException {
         final String path = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this);
         boolean ignoreNestedPath = CallableUtils.getOptionalBooleanArgument(args, IGNORE_NESTED_PATH_PARAM_IDX, this,
                 false);
 
-        final String resolvedPath = (ignoreNestedPath) ? path : resolveNestedPath(env, path);
+        TemplateModel statusModel = getBindStatusTemplateModel(env, objectWrapperAndUnwrapper, requestContext, path,
+                ignoreNestedPath);
+        TemplateModel[] nestedContentArgs = new TemplateModel[] { statusModel };
 
-        //TODO: how to deal with htmlEscape when invoking #getBindStatus()?
-        BindStatus status = requestContext.getBindStatus(resolvedPath);
-        TemplateModel[] nestedContentArgs = new TemplateModel[] { new BeanModel(status, objectWrapper) };
         callPlace.executeNestedContent(nestedContentArgs, out, env);
     }
 


[12/36] incubator-freemarker git commit: Using CallableUtils to throw exceptions; use positional param for code

Posted by dd...@apache.org.
Using CallableUtils to throw exceptions; use positional param for code


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

Branch: refs/heads/3
Commit: a8e73ec77a735b643be986e0c190205f3b7a2192
Parents: b0acedd
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 5 08:33:55 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 5 08:33:55 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateCallableModel.java    |  6 +-
 .../AbstractSpringTemplateDirectiveModel.java   | 17 ++++-
 .../AbstractSpringTemplateFunctionModel.java    | 14 +++-
 .../spring/model/MessageFunction.java           | 77 +++++++++++---------
 .../freemarker/spring/model/ThemeFunction.java  | 38 ++++++++++
 .../spring/web/view/FreeMarkerView.java         |  2 +
 6 files changed, 112 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a8e73ec7/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 431c066..b82bed1 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
@@ -28,6 +28,7 @@ import org.apache.freemarker.core.model.ObjectWrappingException;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.util.CallableUtils;
 import org.springframework.web.servlet.support.BindStatus;
 import org.springframework.web.servlet.support.RequestContext;
 
@@ -81,7 +82,8 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
 
         if (status != null) {
             if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
-                throw new IllegalArgumentException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.");
+                CallableUtils.newGenericExecuteException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.",
+                        this, isFunction());
             }
 
             return ((DefaultObjectWrapper) objectWrapperAndUnwrapper).wrap(status);
@@ -90,6 +92,8 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
         return null;
     }
 
+    protected abstract boolean isFunction();
+
     private String resolveNestedPath(final Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
             final String path) {
         // TODO: should read it from request or env??

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a8e73ec7/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 c4ed76c..df6a2bc 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
@@ -32,13 +32,15 @@ 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.
  */
-public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTemplateCallableModel implements TemplateDirectiveModel {
+public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTemplateCallableModel
+        implements TemplateDirectiveModel {
 
     public AbstractSpringTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
@@ -50,14 +52,16 @@ public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSprin
         final ObjectWrapper objectWrapper = env.getObjectWrapper();
 
         if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
-            throw new TemplateException(
-                    "The ObjectWrapper of environment wasn't instance of ObjectWrapperAndUnwrapper.");
+            CallableUtils.newGenericExecuteException(
+                    "The ObjectWrapper of environment isn't an instance of ObjectWrapperAndUnwrapper.", this,
+                    isFunction());
         }
 
         TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
 
         if (rcModel == null) {
-            throw new TemplateException(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.");
+            CallableUtils.newGenericExecuteException(
+                    AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, isFunction());
         }
 
         RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel);
@@ -65,6 +69,11 @@ public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSprin
         executeInternal(args, callPlace, out, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext);
     }
 
+    @Override
+    protected final boolean isFunction() {
+        return false;
+    }
+
     protected abstract void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException, IOException;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a8e73ec7/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 c0fe502..7851ad8 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
@@ -29,6 +29,7 @@ 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;
 
@@ -47,14 +48,16 @@ public abstract class AbstractSpringTemplateFunctionModel extends AbstractSpring
         final ObjectWrapper objectWrapper = env.getObjectWrapper();
 
         if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
-            throw new TemplateException(
-                    "The ObjectWrapper of environment wasn't instance of ObjectWrapperAndUnwrapper.");
+            CallableUtils.newGenericExecuteException(
+                    "The ObjectWrapper of environment isn't an instance of ObjectWrapperAndUnwrapper.", this,
+                    isFunction());
         }
 
         TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
 
         if (rcModel == null) {
-            throw new TemplateException(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.");
+            CallableUtils.newGenericExecuteException(
+                    AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.", this, isFunction());
         }
 
         RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel);
@@ -62,6 +65,11 @@ public abstract class AbstractSpringTemplateFunctionModel extends AbstractSpring
         return executeInternal(args, callPlace, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext);
     }
 
+    @Override
+    protected final boolean isFunction() {
+        return true;
+    }
+
     protected abstract TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a8e73ec7/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 9579366..c6d1c69 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
@@ -36,24 +36,26 @@ 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;
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceResolvable;
 import org.springframework.web.servlet.support.RequestContext;
 
 public class MessageFunction extends AbstractSpringTemplateFunctionModel {
 
-    private static final int MESSAGE_PARAM_IDX = 0;
-    private static final int MESSAGE_ARGS_PARAM_IDX = 1;
+    private static final int CODE_PARAM_IDX = 0;
+    private static final int MESSAGE_RESOLVABLE_PARAM_IDX = 1;
+    private static final int MESSAGE_ARGS_PARAM_IDX = 2;
 
-    private static final String MESSAGE_PARAM_NAME = "message";
+    private static final String MESSAGE_RESOLVABLE_PARAM_NAME = "message";
 
     private static final ArgumentArrayLayout ARGS_LAYOUT =
             ArgumentArrayLayout.create(
-                    0,
+                    1,
                     true,
-                    StringToIndexMap.of(
-                            MESSAGE_PARAM_NAME, MESSAGE_PARAM_IDX),
-                    false);
+                    StringToIndexMap.of(MESSAGE_RESOLVABLE_PARAM_NAME, MESSAGE_RESOLVABLE_PARAM_IDX),
+                    false
+                    );
 
     public MessageFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
@@ -63,42 +65,45 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
     public TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env,
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException {
-        final MessageSource messageSource = requestContext.getMessageSource();
+        final MessageSource messageSource = getMessageSource(requestContext);
 
         if (messageSource == null) {
-            throw new TemplateException("MessageSource not found.");
+            CallableUtils.newGenericExecuteException("MessageSource not found.", this);
         }
 
         String message = null;
 
-        final TemplateCollectionModel messageArgsModel = (TemplateCollectionModel) args[MESSAGE_ARGS_PARAM_IDX];
-
-        if (!messageArgsModel.isEmptyCollection()) {
-            String code = null;
-            List<Object> msgArgumentList = new ArrayList<>();
-            TemplateModel msgArgModel;
-            int i = 0;
-            for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext(); i++) {
-                msgArgModel = tit.next();
-                if (i == 0) {
-                    code = objectWrapperAndUnwrapper.unwrap(msgArgModel).toString();
-                } else {
-                    msgArgumentList.add(objectWrapperAndUnwrapper.unwrap(msgArgModel));
-                }
-            }
+        final TemplateModel messageResolvableModel = CallableUtils.getOptionalArgument(args, MESSAGE_RESOLVABLE_PARAM_IDX,
+                TemplateModel.class, this);
 
-            // TODO: Is it okay to set the default value to null to avoid NoSuchMessageException from Spring MessageSource?
-            message = messageSource.getMessage(code, (msgArgumentList.isEmpty()) ? null : msgArgumentList.toArray(),
-                    null, requestContext.getLocale());
+        if (messageResolvableModel != null) {
+            MessageSourceResolvable messageResolvable = (MessageSourceResolvable) objectWrapperAndUnwrapper
+                    .unwrap(messageResolvableModel);
+            message = messageSource.getMessage(messageResolvable, requestContext.getLocale());
         } else {
-            final TemplateModel messageModel = CallableUtils.getOptionalArgument(args, MESSAGE_PARAM_IDX,
-                    TemplateModel.class, this);
-            if (messageModel != null) {
-                MessageSourceResolvable messageResolvable = (MessageSourceResolvable) objectWrapperAndUnwrapper
-                        .unwrap(messageModel);
-                message = messageSource.getMessage(messageResolvable, requestContext.getLocale());
+            final String code = _StringUtils
+                    .emptyToNull(CallableUtils.getOptionalStringArgument(args, CODE_PARAM_IDX, this));
+
+            if (code != null) {
+                List<Object> msgArgumentList = null;
+                final TemplateCollectionModel messageArgsModel = (TemplateCollectionModel) args[MESSAGE_ARGS_PARAM_IDX];
+
+                if (!messageArgsModel.isEmptyCollection()) {
+                    msgArgumentList = new ArrayList<>();
+                    TemplateModel msgArgModel;
+                    int i = 0;
+                    for (TemplateModelIterator tit = messageArgsModel.iterator(); tit.hasNext(); i++) {
+                        msgArgModel = tit.next();
+                        msgArgumentList.add(objectWrapperAndUnwrapper.unwrap(msgArgModel));
+                    }
+                }
+
+                // Note: Pass null as default value to avoid NoSuchMessageException from Spring MessageSource
+                //       since we want to take advantage of FreeMarker's default value expressions.
+                message = messageSource.getMessage(code, (msgArgumentList == null) ? null : msgArgumentList.toArray(),
+                        null, requestContext.getLocale());
             } else {
-                throw new TemplateException("Neither message code nor message resolvable was set.");
+                CallableUtils.newNullOrOmittedArgumentException(CODE_PARAM_IDX, this);
             }
         }
 
@@ -110,4 +115,8 @@ public class MessageFunction extends AbstractSpringTemplateFunctionModel {
         return ARGS_LAYOUT;
     }
 
+    protected MessageSource getMessageSource(final RequestContext requestContext) {
+        return requestContext.getMessageSource();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a8e73ec7/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
new file mode 100644
index 0000000..b3b0653
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
@@ -0,0 +1,38 @@
+/*
+ * 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.springframework.context.MessageSource;
+import org.springframework.web.servlet.support.RequestContext;
+
+public class ThemeFunction extends MessageFunction {
+
+    public ThemeFunction(HttpServletRequest request, HttpServletResponse response) {
+        super(request, response);
+    }
+
+    protected MessageSource getMessageSource(final RequestContext requestContext) {
+        return requestContext.getTheme().getMessageSource();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a8e73ec7/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
index f34fd4b..a789f4e 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
@@ -40,6 +40,7 @@ import org.apache.freemarker.servlet.ServletContextHashModel;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
 import org.apache.freemarker.spring.model.BindDirective;
 import org.apache.freemarker.spring.model.MessageFunction;
+import org.apache.freemarker.spring.model.ThemeFunction;
 
 /**
  * FreeMarker template based view implementation, with being able to provide a {@link ServletContextHashModel}
@@ -180,6 +181,7 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
         final SimpleHash springCallableHash = new SimpleHash(objectWrapper);
         springCallableHash.put("bind", new BindDirective(request, response));
         springCallableHash.put("message", new MessageFunction(request, response));
+        springCallableHash.put("theme", new ThemeFunction(request, response));
         return springCallableHash;
     }
 }


[13/36] incubator-freemarker git commit: FREEMARKER-55: Adding more spring callable models.

Posted by dd...@apache.org.
FREEMARKER-55: Adding more spring callable models.


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

Branch: refs/heads/3
Commit: 12f70fff2db494e91bbc9926c76e529d781f4f18
Parents: a8e73ec
Author: Woonsan Ko <wo...@apache.org>
Authored: Wed Sep 6 10:01:10 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Wed Sep 6 10:01:10 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateCallableModel.java    |  38 +++---
 .../freemarker/spring/model/BindDirective.java  |  19 ++-
 .../spring/model/BindErrorsDirective.java       | 122 +++++++++++++++++++
 .../spring/model/MessageFunction.java           |  36 ++++++
 .../spring/model/NestedPathDirective.java       | 109 +++++++++++++++++
 .../SpringFormTemplateCallableHashModel.java    |  56 +++++++++
 .../model/SpringTemplateCallableHashModel.java  |  78 ++++++++++++
 .../freemarker/spring/model/ThemeFunction.java  |  36 ++++++
 .../spring/web/view/FreeMarkerView.java         |  21 +---
 9 files changed, 480 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/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 b82bed1..de95df5 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
@@ -23,8 +23,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.TemplateException;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.ObjectWrappingException;
 import org.apache.freemarker.core.model.TemplateCallableModel;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
@@ -37,12 +37,6 @@ import org.springframework.web.servlet.support.RequestContext;
  */
 public abstract class AbstractSpringTemplateCallableModel implements TemplateCallableModel {
 
-    // TODO: namespace this into 'spring.nestedPath'??
-    /**
-     * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
-     */
-    private static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
-
     private final HttpServletRequest request;
     private final HttpServletResponse response;
 
@@ -73,10 +67,10 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
      * @param ignoreNestedPath flag whether or not to ignore the nested path
      * @return {@link TemplateModel} wrapping a {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext}
      * by the {@code path}
-     * @throws ObjectWrappingException if fails to wrap the <code>BindStatus</code> object
+     * @throws TemplateException 
      */
     protected final TemplateModel getBindStatusTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            RequestContext requestContext, String path, boolean ignoreNestedPath) throws ObjectWrappingException {
+            RequestContext requestContext, String path, boolean ignoreNestedPath) throws TemplateException {
         final String resolvedPath = (ignoreNestedPath) ? path : resolveNestedPath(env, objectWrapperAndUnwrapper, path);
         BindStatus status = requestContext.getBindStatus(resolvedPath, false);
 
@@ -94,15 +88,25 @@ public abstract class AbstractSpringTemplateCallableModel implements TemplateCal
 
     protected abstract boolean isFunction();
 
+    protected String getCurrentNestedPath(final Environment env) throws TemplateException {
+        SpringTemplateCallableHashModel springHash = (SpringTemplateCallableHashModel) env
+                .getVariable(SpringTemplateCallableHashModel.NAME);
+        return springHash.getNestedPath();
+    }
+
+    protected void setCurrentNestedPath(final Environment env, final String nestedPath) throws TemplateException {
+        SpringTemplateCallableHashModel springHash = (SpringTemplateCallableHashModel) env
+                .getVariable(SpringTemplateCallableHashModel.NAME);
+        springHash.setNestedPath(nestedPath);
+    }
+
     private String resolveNestedPath(final Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            final String path) {
-        // TODO: should read it from request or env??
-        //       or read spring.nestedPath first and read request attribute next??
-        String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);
-
-        if (nestedPath != null && !path.startsWith(nestedPath)
-                && !path.equals(nestedPath.substring(0, nestedPath.length() - 1))) {
-            return nestedPath + path;
+            final String path) throws TemplateException {
+        String curNestedPath = getCurrentNestedPath(env);
+
+        if (curNestedPath != null && !path.startsWith(curNestedPath)
+                && !path.equals(curNestedPath.substring(0, curNestedPath.length() - 1))) {
+            return curNestedPath + path;
         }
 
         return path;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index 0fbbe2f..99f5f86 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -41,18 +41,31 @@ import org.springframework.web.servlet.support.RequestContext;
  * <P>
  * This directive supports the following parameters:
  * <UL>
- * <LI><code>ignoreNestedPath</code>: Set whether to ignore a nested path, if any. <code>false</code> by default.</LI>
- * <LI><code>path</code>: The path to the bean or bean property to bind status information for.</LI>
+ * <LI><code>path</code>: The first positional parameter pointing to the bean or bean property to bind status information for.</LI>
+ * <LI>
+ *   <code>ignoreNestedPath</code>: A named parameter to set whether to ignore a nested path, if any.
+ *   <code>false</code> by default.
+ * </LI>
  * </UL>
  * </P>
  * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ *   &lt;@spring.bind "user.email"; status&gt;
+ *     &lt;input type="text" name="email" value="${status.value!}" /&gt;
+ *   &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 rather than depending on directives.
+ * because it is much easier to control escaping in FreeMarker Template expressions.
  * </P>
  */
 public class BindDirective extends AbstractSpringTemplateDirectiveModel {
 
+    public static final String NAME = "bind";
+
     private static final int PATH_PARAM_IDX = 0;
     private static final int IGNORE_NESTED_PATH_PARAM_IDX = 1;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/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
new file mode 100644
index 0000000..c98db67
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindErrorsDirective.java
@@ -0,0 +1,122 @@
+/*
+ * 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.io.IOException;
+import java.io.Writer;
+
+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.ObjectWrappingException;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.springframework.validation.Errors;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * Provides <code>TemplateModel</code> wrapping the bind errors (type of <code>org.springframework.validation.Errors</code>)
+ * for the given name, working similarly to Spring Framework's <code>&lt;spring:hasBindErrors /&gt;</code> JSP Tag Library.
+ * <P>
+ * This directive supports the following parameters:
+ * <UL>
+ * <LI><code>name</code>: The first positional parameter for the name of the bean that this directive should check.</LI>
+ * </UL>
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * &lt;@spring.hasBindErrors "email"; errors&gt;
+ *   &lt;#-- nested content with using errors --&gt;
+ * &lt;/@spring.hasBindErrors&gt;
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:hasBindErrors /&gt;</code> JSP Tag Library, this directive
+ * does not support <code>htmlEscape</code> parameter. It always has an <code>org.springframework.validation.Errors</code>
+ * instance not to escape HTML's because it is much easier to control escaping in FreeMarker Template expressions
+ * rather than depending on directives.
+ * </P>
+ */
+public class BindErrorsDirective extends AbstractSpringTemplateDirectiveModel {
+
+    public static final String NAME = "hasBindErrors";
+
+    private static final int NAME_PARAM_IDX = 0;
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    null,
+                    false
+                    );
+
+    public BindErrorsDirective(HttpServletRequest request, HttpServletResponse response) {
+        super(request, response);
+    }
+
+    @Override
+    protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+                    throws TemplateException, IOException {
+        final String name = CallableUtils.getStringArgument(args, NAME_PARAM_IDX, this);
+
+        final TemplateModel bindErrorsModel = getBindErrorsTemplateModel(env, objectWrapperAndUnwrapper, requestContext, name);
+
+        if (bindErrorsModel != null) {
+            final TemplateModel[] nestedContentArgs = new TemplateModel[] { bindErrorsModel };
+            callPlace.executeNestedContent(nestedContentArgs, out, env);
+        }
+    }
+
+    @Override
+    public boolean isNestedContentSupported() {
+        return true;
+    }
+
+    @Override
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+    private final TemplateModel getBindErrorsTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
+            RequestContext requestContext, String name) throws ObjectWrappingException {
+        final Errors errors = requestContext.getErrors(name, false);
+
+        if (errors != null && errors.hasErrors()) {
+            if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
+                CallableUtils.newGenericExecuteException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.",
+                        this, isFunction());
+            }
+
+            return ((DefaultObjectWrapper) objectWrapperAndUnwrapper).wrap(errors);
+        }
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/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 c6d1c69..46547a6 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
@@ -41,8 +41,44 @@ import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceResolvable;
 import org.springframework.web.servlet.support.RequestContext;
 
+/**
+ * A <code>TemplateFunctionModel</code> providing functionality equivalent to the Spring Framework's
+ * <code>&lt;spring:message /&gt;</code> JSP Tag Library.
+ * It retrieves the theme message with the given code or the resolved text by the given <code>message</code> parameter.
+ * <P>
+ * This function supports the following parameters:
+ * <UL>
+ * <LI><code>code</code>: The first optional positional parameter. The key to use when looking up the message.
+ * <LI><code>message arguments</code>: Positional varargs after <code>code</code> parameter, as message arguments.</LI>
+ * <LI><code>message</code>: Named parameters as <code>MessageResolvable</code> object.</LI>
+ * </UL>
+ * </P>
+ * <P>
+ * This function requires either <code>code</code> parameter or <code>message</code> parameter at least.
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * &lt;#-- With 'code' positional parameter only --&gt;
+ * ${spring.message("label.user.firstName")!}
+ *
+ * &lt;#-- With 'code' positional parameter and message arguments (varargs) --&gt;
+ * ${spring.message("message.user.form", user.firstName, user.lastName, user.email)}
+ *
+ * &lt;#-- With 'message' named parameter (<code>MessageResolvable</code> object) --&gt;
+ * ${spring.message(message=myMessageResolvable)}
+ * </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 MessageFunction extends AbstractSpringTemplateFunctionModel {
 
+    public static final String NAME = "message";
+
     private static final int CODE_PARAM_IDX = 0;
     private static final int MESSAGE_RESOLVABLE_PARAM_IDX = 1;
     private static final int MESSAGE_ARGS_PARAM_IDX = 2;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
new file mode 100644
index 0000000..37d4370
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/NestedPathDirective.java
@@ -0,0 +1,109 @@
+/*
+ * 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.io.IOException;
+import java.io.Writer;
+
+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.beans.PropertyAccessor;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * Provides <code>TemplateModel</code> setting <code>spring.nestedPath</code> by the given bind path, working similarly
+ * to Spring Framework's <code>&lt;spring:nestedPath /&gt;</code> JSP Tag Library.
+ * <P>
+ * This directive supports the following parameters:
+ * <UL>
+ * <LI><code>path</code>: The first positional parameter to set a new nested path by appending it to the existing nested path if any existing.</LI>
+ * </UL>
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ *   &lt;@spring.nestedPath "user"&gt;
+ *     &lt;#-- nested content --/&gt;
+ *   &lt;/@spring.nestedPath&gt;
+ * </PRE>
+ */
+public class NestedPathDirective extends AbstractSpringTemplateDirectiveModel {
+
+    public static final String NAME = "nestedPath";
+
+    private static final int PATH_PARAM_IDX = 0;
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    null,
+                    false
+                    );
+
+    public NestedPathDirective(HttpServletRequest request, HttpServletResponse response) {
+        super(request, response);
+    }
+
+    @Override
+    protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+                    throws TemplateException, IOException {
+        String path = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this);
+
+        if (path == null) {
+            path = "";
+        }
+
+        if (!path.isEmpty() && !path.endsWith(PropertyAccessor.NESTED_PROPERTY_SEPARATOR)) {
+            path += PropertyAccessor.NESTED_PROPERTY_SEPARATOR;
+        }
+
+        String prevNestedPath = getCurrentNestedPath(env);
+        String newNestedPath = (prevNestedPath != null) ? prevNestedPath + path : path;
+
+        try {
+            setCurrentNestedPath(env, newNestedPath);
+            callPlace.executeNestedContent(null, out, env);
+        } finally {
+            setCurrentNestedPath(env, prevNestedPath);
+        }
+    }
+
+    @Override
+    public boolean isNestedContentSupported() {
+        return true;
+    }
+
+    @Override
+    public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java
new file mode 100644
index 0000000..4ff3552
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringFormTemplateCallableHashModel.java
@@ -0,0 +1,56 @@
+/*
+ * 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.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * TemplateHashModel wrapper for templates using Spring Form directives and functions.
+ */
+public final class SpringFormTemplateCallableHashModel implements TemplateHashModel, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String NAME = "form";
+
+    private Map<String, AbstractSpringTemplateCallableModel> callablesMap = new HashMap<>();
+
+    public SpringFormTemplateCallableHashModel(final HttpServletRequest request, final HttpServletResponse response) {
+    }
+
+    public TemplateModel get(String key) throws TemplateException {
+        return callablesMap.get(key);
+    }
+
+    @Override
+    public boolean isEmptyHash() throws TemplateException {
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/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
new file mode 100644
index 0000000..dd2f71c
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/SpringTemplateCallableHashModel.java
@@ -0,0 +1,78 @@
+/*
+ * 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.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleString;
+
+/**
+ * TemplateHashModel wrapper for templates using Spring directives and functions.
+ */
+public final class SpringTemplateCallableHashModel implements TemplateHashModel, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String NAME = "spring";
+
+    public static final String NESTED_PATH = "nestedPath";
+
+    private Map<String, AbstractSpringTemplateCallableModel> callablesMap = new HashMap<>();
+
+    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));
+    }
+
+    public TemplateModel get(String key) throws TemplateException {
+        if (NESTED_PATH.equals(key)) {
+            return (nestedPath != null) ? new SimpleString(nestedPath) : null;
+        }
+
+        return callablesMap.get(key);
+    }
+
+    @Override
+    public boolean isEmptyHash() throws TemplateException {
+        return false;
+    }
+
+    public String getNestedPath() {
+        return nestedPath;
+    }
+
+    public void setNestedPath(String nestedPath) {
+        this.nestedPath = nestedPath;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
index b3b0653..15a4103 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
@@ -25,8 +25,44 @@ import javax.servlet.http.HttpServletResponse;
 import org.springframework.context.MessageSource;
 import org.springframework.web.servlet.support.RequestContext;
 
+/**
+ * A <code>TemplateFunctionModel</code> providing functionality equivalent to the Spring Framework's
+ * <code>&lt;spring:theme /&gt;</code> JSP Tag Library.
+ * It retrieves the theme message with the given code or the resolved text by the given <code>message</code> parameter.
+ * <P>
+ * This function supports the following parameters:
+ * <UL>
+ * <LI><code>code</code>: The first optional positional parameter. The key to use when looking up the message.
+ * <LI><code>message arguments</code>: Positional varargs after <code>code</code> parameter, as message arguments.</LI>
+ * <LI><code>message</code>: Named parameters as <code>MessageResolvable</code> object.</LI>
+ * </UL>
+ * </P>
+ * <P>
+ * This function requires either <code>code</code> parameter or <code>message</code> parameter at least.
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * &lt;#-- With 'code' positional parameter only --&gt;
+ * ${spring.theme("label.user.firstName")!}
+ *
+ * &lt;#-- With 'code' positional parameter and message arguments (varargs) --&gt;
+ * ${spring.theme("message.user.form", user.firstName, user.lastName, user.email)}
+ *
+ * &lt;#-- With 'message' named parameter (<code>MessageResolvable</code> object) --&gt;
+ * ${spring.theme(message=myMessageResolvable)}
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:theme /&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 ThemeFunction extends MessageFunction {
 
+    public static final String NAME = "theme";
+
     public ThemeFunction(HttpServletRequest request, HttpServletResponse response) {
         super(request, response);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/12f70fff/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
index a789f4e..640cd13 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
@@ -25,11 +25,8 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
-import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
 import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
-import org.apache.freemarker.core.model.impl.SimpleHash;
 import org.apache.freemarker.servlet.AllHttpScopesHashModel;
 import org.apache.freemarker.servlet.FreemarkerServlet;
 import org.apache.freemarker.servlet.HttpRequestHashModel;
@@ -38,9 +35,8 @@ import org.apache.freemarker.servlet.HttpSessionHashModel;
 import org.apache.freemarker.servlet.IncludePage;
 import org.apache.freemarker.servlet.ServletContextHashModel;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
-import org.apache.freemarker.spring.model.BindDirective;
-import org.apache.freemarker.spring.model.MessageFunction;
-import org.apache.freemarker.spring.model.ThemeFunction;
+import org.apache.freemarker.spring.model.SpringFormTemplateCallableHashModel;
+import org.apache.freemarker.spring.model.SpringTemplateCallableHashModel;
 
 /**
  * FreeMarker template based view implementation, with being able to provide a {@link ServletContextHashModel}
@@ -143,7 +139,10 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
 
         model.putUnlistedModel(FreemarkerServlet.KEY_INCLUDE, new IncludePage(request, response));
 
-        model.putUnlistedModel("spring", createSpringCallableHashModel(objectWrapper, request, response));
+        model.putUnlistedModel(SpringTemplateCallableHashModel.NAME,
+                new SpringTemplateCallableHashModel(request, response));
+        model.putUnlistedModel(SpringFormTemplateCallableHashModel.NAME,
+                new SpringFormTemplateCallableHashModel(request, response));
 
         model.putAll(map);
 
@@ -176,12 +175,4 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
         return sessionModel;
     }
 
-    private TemplateHashModelEx2 createSpringCallableHashModel(final ObjectWrapper objectWrapper,
-            final HttpServletRequest request, final HttpServletResponse response) {
-        final SimpleHash springCallableHash = new SimpleHash(objectWrapper);
-        springCallableHash.put("bind", new BindDirective(request, response));
-        springCallableHash.put("message", new MessageFunction(request, response));
-        springCallableHash.put("theme", new ThemeFunction(request, response));
-        return springCallableHash;
-    }
 }


[33/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for transform function

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for transform function


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

Branch: refs/heads/3
Commit: bd564327db67c19a6a39b092a1be806ed4b7af08
Parents: 74a300f
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 21:11:31 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 21:11:31 2017 -0400

----------------------------------------------------------------------
 .../spring/model/TransformFunction.java         |  2 +-
 .../spring/example/mvc/users/User.java          |  6 ++
 .../example/mvc/users/UserController.java       | 18 ++++-
 .../spring/model/TransformFunctionTest.java     | 76 ++++++++++++++++++++
 .../model/transform-function-basic-usages.ftl   | 28 ++++++++
 5 files changed, 127 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd564327/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 ee3cc1c..d6c381e 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
@@ -46,7 +46,7 @@ import org.springframework.web.servlet.support.RequestContext;
  * </P>
  * <PRE>
  *   &lt;@spring.bind "user"; status&gt;
- *     ${spring.transform(status, user.birthDate)}
+ *     ${spring.transform(status.editor, user.birthDate)}
  *   &lt;/@spring.bind&gt;
  * </PRE>
  * <P>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd564327/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
index e349a48..23d31dc 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
@@ -80,4 +80,10 @@ public class User {
     public void setBirthDate(Date birthDate) {
         this.birthDate = birthDate;
     }
+
+    @Override
+    public String toString() {
+        return super.toString() + " {id=" + id + ", firstName='" + firstName + "', lastName='" + lastName + "', email='"
+                + email + "', birthDate='" + birthDate + "'}";
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd564327/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
index 43b06a4..09a416d 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
@@ -19,16 +19,22 @@
 
 package org.apache.freemarker.spring.example.mvc.users;
 
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.propertyeditors.CustomDateEditor;
 import org.springframework.context.support.DefaultMessageSourceResolvable;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.util.StringUtils;
 import org.springframework.validation.BindingResult;
 import org.springframework.validation.FieldError;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -44,6 +50,12 @@ public class UserController {
     @Autowired
     private UserRepository userRepository;
 
+    @InitBinder("user")
+    public void customizeBinding(WebDataBinder binder) {
+        binder.registerCustomEditor(Date.class, "birthDate",
+                new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
+    }
+
     @RequestMapping(value = "/users/", method = RequestMethod.GET)
     public String listUsers(@RequestParam(value = "viewName", required = false) String viewName, Model model) {
         List<User> users = new LinkedList<>();
@@ -73,8 +85,8 @@ public class UserController {
     }
 
     @RequestMapping(value = "/users/", method = RequestMethod.POST)
-    public String createUser(@RequestParam(value = "viewName", required = false) String viewName, User user,
-            BindingResult bindingResult, Model model) {
+    public String createUser(@RequestParam(value = "viewName", required = false) String viewName,
+            @ModelAttribute("user") User user, BindingResult bindingResult, Model model) {
         model.addAttribute("user", user);
 
         if (!StringUtils.hasText(user.getEmail())) {
@@ -82,6 +94,8 @@ public class UserController {
                     new String[] { "user.error.invalid.email" }, new Object[] { user.getEmail() }, "E-Mail is blank."));
         }
 
+        // No saving for now...
+
         return (StringUtils.hasText(viewName)) ? viewName : DEFAULT_USER_EDIT_VIEW_NAME;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd564327/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/TransformFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/TransformFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/TransformFunctionTest.java
new file mode 100644
index 0000000..d3fd680
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/TransformFunctionTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class TransformFunctionTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testBasicUsages() throws Exception {
+        final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        final User user = new User();
+        user.setFirstName("Paul");
+        user.setLastName("Temple");
+        user.setBirthDate(dateFormat.parse("1980-12-23"));
+        user.setEmail("paul.temple@example.com");
+
+        mockMvc.perform(post("/users/").param("viewName", "test/model/transform-function-basic-usages")
+                .param("firstName", user.getFirstName()).param("lastName", user.getLastName())
+                .param("email", user.getEmail()).param("birthDate", dateFormat.format(user.getBirthDate()))
+                .accept(MediaType.parseMediaType("text/html"))).andDo(print())
+                .andExpect(xpath("//div[@id='userBirthDate']/text()").string(dateFormat.format(user.getBirthDate())));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd564327/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/transform-function-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/transform-function-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/transform-function-basic-usages.ftl
new file mode 100644
index 0000000..0fb9e29
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/transform-function-basic-usages.ftl
@@ -0,0 +1,28 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<@spring.bind "user.birthDate"; status>
+  <div id="userBirthDate">${spring.transform(status.editor, status.actualValue)}</div>
+</...@spring.bind>
+
+</body>
+</html>
\ No newline at end of file


[32/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for BindErrorsDirective

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for BindErrorsDirective


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

Branch: refs/heads/3
Commit: 74a300f144ce99d03e021416f4f6ce7d191024af
Parents: 86a8a5f
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 17:30:02 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 17:30:02 2017 -0400

----------------------------------------------------------------------
 .../spring/model/BindErrorsDirective.java       | 10 ++-
 .../spring/example/mvc/users/User.java          |  5 +-
 .../example/mvc/users/UserController.java       | 15 ++++
 .../spring/model/BindDirectiveTest.java         |  2 +-
 .../spring/model/BindErrorsDirectiveTest.java   | 83 ++++++++++++++++++++
 .../spring/model/EvalFunctionTest.java          |  2 +-
 .../spring/model/MessageFunctionTest.java       |  4 +-
 .../spring/model/NestedPathDirectiveTest.java   |  2 +-
 .../spring/model/ThemeFunctionTest.java         |  2 +-
 .../spring/model/UrlFunctionTest.java           |  2 +-
 .../model/binderrors-directive-basic-usages.ftl | 70 +++++++++++++++++
 .../example/mvc/users/UsersMessages.properties  |  1 +
 12 files changed, 188 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/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 e35b6ee..6d0c954 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
@@ -49,8 +49,14 @@ import org.springframework.web.servlet.support.RequestContext;
  * Some valid example(s):
  * </P>
  * <PRE>
- * &lt;@spring.hasBindErrors "email"; errors&gt;
- *   &lt;#-- nested content with using errors --&gt;
+ * &lt;@spring.hasBindErrors "user"; errors&gt;
+ *   &lt;div class="errors"&gt;
+ *     &lt;#list errors.allErrors as error&gt;
+ *       &lt;div class="error"&gt;
+ *         ${spring.message(message=error)!}
+ *       &lt;/div&gt;
+ *     &lt;/#list&gt;
+ *   &lt;/div&gt;
  * &lt;/@spring.hasBindErrors&gt;
  * </PRE>
  * <P>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
index 1aa0515..e349a48 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
@@ -23,13 +23,16 @@ import java.util.Date;
 
 public class User {
 
-    private final Integer id;
+    private Integer id;
     private String password;
     private String email;
     private String firstName;
     private String lastName;
     private Date birthDate;
 
+    public User() {
+    }
+
     public User(final Integer id) {
         this.id = id;
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
index 80a158d..43b06a4 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
@@ -27,6 +27,8 @@ import org.springframework.context.support.DefaultMessageSourceResolvable;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.util.StringUtils;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -70,6 +72,19 @@ public class UserController {
         return (StringUtils.hasText(viewName)) ? viewName : DEFAULT_USER_EDIT_VIEW_NAME;
     }
 
+    @RequestMapping(value = "/users/", method = RequestMethod.POST)
+    public String createUser(@RequestParam(value = "viewName", required = false) String viewName, User user,
+            BindingResult bindingResult, Model model) {
+        model.addAttribute("user", user);
+
+        if (!StringUtils.hasText(user.getEmail())) {
+            bindingResult.addError(new FieldError("user", "email", user.getEmail(), true,
+                    new String[] { "user.error.invalid.email" }, new Object[] { user.getEmail() }, "E-Mail is blank."));
+        }
+
+        return (StringUtils.hasText(viewName)) ? viewName : DEFAULT_USER_EDIT_VIEW_NAME;
+    }
+
     public UserRepository getUserRepository() {
         return userRepository;
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
index cd1af42..b2a6cd5 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindDirectiveTest.java
@@ -58,7 +58,7 @@ public class BindDirectiveTest {
     }
 
     @Test
-    public void testMessageFunctionBasicUsages() throws Exception {
+    public void testBasicUsages() throws Exception {
         final Integer userId = userRepository.getUserIds().iterator().next();
         final User user = userRepository.getUser(userId);
         mockMvc.perform(get("/users/{userId}.", userId).param("viewName", "test/model/bind-directive-basic-usages")

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindErrorsDirectiveTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindErrorsDirectiveTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindErrorsDirectiveTest.java
new file mode 100644
index 0000000..15520cb
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/BindErrorsDirectiveTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static org.hamcrest.Matchers.equalToIgnoringWhiteSpace;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class BindErrorsDirectiveTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private MessageSource messageSource;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testBasicUsages() throws Exception {
+        final User user = new User();
+        user.setFirstName("Paul");
+        user.setLastName("Temple");
+        // set invalid email intentionally to test BindErrorsDirective...
+        user.setEmail("");
+
+        mockMvc.perform(post("/users/").param("viewName", "test/model/binderrors-directive-basic-usages")
+                .param("firstName", user.getFirstName()).param("lastName", user.getLastName())
+                .param("email", user.getEmail()).accept(MediaType.parseMediaType("text/html")))
+                .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//div[@class='error']").string(equalToIgnoringWhiteSpace(
+                        messageSource.getMessage("user.error.invalid.email", new Object[] { user.getEmail() }, null))))
+                .andExpect(xpath("//input[@name='firstName']/@value").string(user.getFirstName()))
+                .andExpect(xpath("//input[@name='lastName']/@value").string(user.getLastName()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
index be6f6dd..45d98b2 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/EvalFunctionTest.java
@@ -59,7 +59,7 @@ public class EvalFunctionTest {
     }
 
     @Test
-    public void testMessageFunctionBasicUsages() throws Exception {
+    public void testBasicUsages() throws Exception {
         final Integer userId = userRepository.getUserIds().iterator().next();
         final User user = userRepository.getUser(userId);
         mockMvc.perform(get("/users/").param("viewName", "test/model/eval-function-basic-usages")

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
index 0ddbe27..d043bfe 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
@@ -58,7 +58,7 @@ public class MessageFunctionTest {
     }
 
     @Test
-    public void testMessageFunctionBasicUsages() throws Exception {
+    public void testBasicUsages() throws Exception {
         final Integer userId = userRepository.getUserIds().iterator().next();
         final User user = userRepository.getUser(userId);
         mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/message-function-basic-usages")
@@ -71,7 +71,7 @@ public class MessageFunctionTest {
     }
 
     @Test
-    public void testMessageFunctionWithMessageSourceResolvable() throws Exception {
+    public void testWithMessageSourceResolvable() throws Exception {
         final Integer nonExistingUserId = 0;
         mockMvc.perform(
                 get("/users/{userId}/", nonExistingUserId).param("viewName", "test/model/message-function-basic-usages")

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
index d02478f..a4999f5 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
@@ -58,7 +58,7 @@ public class NestedPathDirectiveTest {
     }
 
     @Test
-    public void testMessageFunctionBasicUsages() throws Exception {
+    public void testBasicUsages() throws Exception {
         final Integer userId = userRepository.getUserIds().iterator().next();
         final User user = userRepository.getUser(userId);
         mockMvc.perform(get("/users/{userId}.", userId).param("viewName", "test/model/nestedpath-directive-basic-usages")

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
index 81b186b..804b76d 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
@@ -62,7 +62,7 @@ public class ThemeFunctionTest {
     }
 
     @Test
-    public void testThemeFunctionBasicUsages() throws Exception {
+    public void testBasicUsages() throws Exception {
         final MessageSource defaultThemeMessageSource = themeSource.getTheme("default").getMessageSource();
 
         final Integer userId = userRepository.getUserIds().iterator().next();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
index c2e1ce3..8435bfc 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/UrlFunctionTest.java
@@ -57,7 +57,7 @@ public class UrlFunctionTest {
     }
 
     @Test
-    public void testThemeFunctionBasicUsages() throws Exception {
+    public void testBasicUsages() throws Exception {
         final Integer userId = userRepository.getUserIds().iterator().next();
         mockMvc.perform(get("/users/").param("viewName", "test/model/url-function-basic-usages")
                 .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/binderrors-directive-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/binderrors-directive-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/binderrors-directive-basic-usages.ftl
new file mode 100644
index 0000000..d608615
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/binderrors-directive-basic-usages.ftl
@@ -0,0 +1,70 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<@spring.hasBindErrors "user"; errors>
+  <div class="errors">
+    <#list errors.allErrors as error>
+      <div class="error">
+        ${spring.message(message=error)!}
+      </div>
+    </#list>
+  </div>
+</...@spring.hasBindErrors>
+
+<form method="POST" action="${spring.url('/users')}">
+  <table class="table">
+    <tbody>
+      <tr>
+        <th>E-Mail</th>
+        <td>
+          <@spring.bind "user.email"; status>
+            <input type="text" name="email" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>First Name</th>
+        <td>
+          <@spring.bind "user.firstName"; status>
+            <input type="text" name="firstName" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>Last Name</th>
+        <td>
+          <@spring.bind "user.lastName"; status>
+            <input type="text" name="lastName" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <td colspan="2">
+          <input type="submit" name="save" value="Save" />
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</form>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/74a300f1/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
index 497607c..aa12742 100644
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersMessages.properties
@@ -6,3 +6,4 @@ user.firstName=First name
 user.lastName=Last name
 user.birthDate=Birth Date
 user.error.notfound=User not found by ID: {0}
+user.error.invalid.email=Invalid E-Mail address: {0}


[09/36] incubator-freemarker git commit: FREEMARKER-55: Adding initial message function.

Posted by dd...@apache.org.
FREEMARKER-55: Adding initial message function.


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

Branch: refs/heads/3
Commit: 3b83475f111019842b6070581ca310890ba199d1
Parents: f1e8a4d
Author: Woonsan Ko <wo...@apache.org>
Authored: Sat Sep 2 23:49:38 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Sat Sep 2 23:49:38 2017 -0400

----------------------------------------------------------------------
 .../AbstractSpringTemplateCallableModel.java    | 106 +++++++++++++++++++
 .../AbstractSpringTemplateDirectiveModel.java   |  70 +-----------
 .../AbstractSpringTemplateFunctionModel.java    |  69 ++++++++++++
 .../freemarker/spring/model/BindDirective.java  |  10 +-
 .../spring/model/MessageFunction.java           |  98 +++++++++++++++++
 .../spring/web/view/FreeMarkerView.java         |   2 +
 6 files changed, 282 insertions(+), 73 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3b83475f/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
new file mode 100644
index 0000000..431c066
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateCallableModel.java
@@ -0,0 +1,106 @@
+/*
+ * 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.Environment;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.ObjectWrappingException;
+import org.apache.freemarker.core.model.TemplateCallableModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.springframework.web.servlet.support.BindStatus;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * Abstract TemplateCallableModel for derived classes to support Spring MVC based templating environment.
+ */
+public abstract class AbstractSpringTemplateCallableModel implements TemplateCallableModel {
+
+    // TODO: namespace this into 'spring.nestedPath'??
+    /**
+     * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
+     */
+    private static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
+
+    private final HttpServletRequest request;
+    private final HttpServletResponse response;
+
+    public AbstractSpringTemplateCallableModel(HttpServletRequest request, HttpServletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    protected final HttpServletRequest getRequest() {
+        return request;
+    }
+
+    protected final HttpServletResponse getResponse() {
+        return response;
+    }
+
+    /**
+     * Find {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext} by the {@code path}
+     * and wrap it as a {@link TemplateModel}.
+     * <P>
+     * <EM>NOTE:</EM> In FreeMarker, there is no need to depend on <code>BindStatus#htmlEscape</code> option
+     * as FreeMarker template expressions can easily set escape option by themselves.
+     * Therefore, this method always get a {@link BindStatus} with {@code htmlEscape} option set to {@code false}.
+     * @param env Environment
+     * @param objectWrapperAndUnwrapper ObjectWrapperAndUnwrapper
+     * @param requestContext Spring RequestContext
+     * @param path bind path
+     * @param ignoreNestedPath flag whether or not to ignore the nested path
+     * @return {@link TemplateModel} wrapping a {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext}
+     * by the {@code path}
+     * @throws ObjectWrappingException if fails to wrap the <code>BindStatus</code> object
+     */
+    protected final TemplateModel getBindStatusTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
+            RequestContext requestContext, String path, boolean ignoreNestedPath) throws ObjectWrappingException {
+        final String resolvedPath = (ignoreNestedPath) ? path : resolveNestedPath(env, objectWrapperAndUnwrapper, path);
+        BindStatus status = requestContext.getBindStatus(resolvedPath, false);
+
+        if (status != null) {
+            if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
+                throw new IllegalArgumentException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.");
+            }
+
+            return ((DefaultObjectWrapper) objectWrapperAndUnwrapper).wrap(status);
+        }
+
+        return null;
+    }
+
+    private String resolveNestedPath(final Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
+            final String path) {
+        // TODO: should read it from request or env??
+        //       or read spring.nestedPath first and read request attribute next??
+        String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);
+
+        if (nestedPath != null && !path.startsWith(nestedPath)
+                && !path.equals(nestedPath.substring(0, nestedPath.length() - 1))) {
+            return nestedPath + path;
+        }
+
+        return path;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3b83475f/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 e1b34b4..c4ed76c 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
@@ -30,31 +30,18 @@ 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.ObjectWrappingException;
 import org.apache.freemarker.core.model.TemplateDirectiveModel;
 import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.springframework.web.servlet.support.BindStatus;
 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.
  */
-public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDirectiveModel {
-
-    // TODO: namespace this into 'spring.nestedPath'??
-    /**
-     * @see <code>org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME</code>
-     */
-    private static final String NESTED_PATH_VARIABLE_NAME = "nestedPath";
-
-    private final HttpServletRequest request;
-    private final HttpServletResponse response;
+public abstract class AbstractSpringTemplateDirectiveModel extends AbstractSpringTemplateCallableModel implements TemplateDirectiveModel {
 
     public AbstractSpringTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) {
-        this.request = request;
-        this.response = response;
+        super(request, response);
     }
 
     @Override
@@ -82,57 +69,4 @@ public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDi
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException, IOException;
 
-    protected final HttpServletRequest getRequest() {
-        return request;
-    }
-
-    protected final HttpServletResponse getResponse() {
-        return response;
-    }
-
-    /**
-     * Find {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext} by the {@code path}
-     * and wrap it as a {@link TemplateModel}.
-     * <P>
-     * <EM>NOTE:</EM> In FreeMarker, there is no need to depend on <code>BindStatus#htmlEscape</code> option
-     * as FreeMarker template expressions can easily set escape option by themselves.
-     * Therefore, this method always get a {@link BindStatus} with {@code htmlEscape} option set to {@code false}.
-     * @param env Environment
-     * @param objectWrapperAndUnwrapper ObjectWrapperAndUnwrapper
-     * @param requestContext Spring RequestContext
-     * @param path bind path
-     * @param ignoreNestedPath flag whether or not to ignore the nested path
-     * @return {@link TemplateModel} wrapping a {@link BindStatus} with no {@code htmlEscape} option from {@link RequestContext}
-     * by the {@code path}
-     * @throws ObjectWrappingException if fails to wrap the <code>BindStatus</code> object
-     */
-    protected final TemplateModel getBindStatusTemplateModel(Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            RequestContext requestContext, String path, boolean ignoreNestedPath) throws ObjectWrappingException {
-        final String resolvedPath = (ignoreNestedPath) ? path : resolveNestedPath(env, objectWrapperAndUnwrapper, path);
-        BindStatus status = requestContext.getBindStatus(resolvedPath, false);
-
-        if (status != null) {
-            if (!(objectWrapperAndUnwrapper instanceof DefaultObjectWrapper)) {
-                throw new IllegalArgumentException("objectWrapperAndUnwrapper is not a DefaultObjectWrapper.");
-            }
-
-            return ((DefaultObjectWrapper) objectWrapperAndUnwrapper).wrap(status);
-        }
-
-        return null;
-    }
-
-    private String resolveNestedPath(final Environment env, ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper,
-            final String path) {
-        // TODO: should read it from request or env??
-        //       or read spring.nestedPath first and read request attribute next??
-        String nestedPath = (String) request.getAttribute(NESTED_PATH_VARIABLE_NAME);
-
-        if (nestedPath != null && !path.startsWith(nestedPath)
-                && !path.equals(nestedPath.substring(0, nestedPath.length() - 1))) {
-            return nestedPath + path;
-        }
-
-        return path;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3b83475f/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
new file mode 100644
index 0000000..c0fe502
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/AbstractSpringTemplateFunctionModel.java
@@ -0,0 +1,69 @@
+/*
+ * 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.ObjectWrapper;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+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.
+ */
+public abstract class AbstractSpringTemplateFunctionModel extends AbstractSpringTemplateCallableModel
+        implements TemplateFunctionModel {
+
+    public AbstractSpringTemplateFunctionModel(HttpServletRequest request, HttpServletResponse response) {
+        super(request, response);
+    }
+
+    @Override
+    public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException {
+        final ObjectWrapper objectWrapper = env.getObjectWrapper();
+
+        if (!(objectWrapper instanceof ObjectWrapperAndUnwrapper)) {
+            throw new TemplateException(
+                    "The ObjectWrapper of environment wasn't instance of ObjectWrapperAndUnwrapper.");
+        }
+
+        TemplateModel rcModel = env.getVariable(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
+
+        if (rcModel == null) {
+            throw new TemplateException(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + " not found.");
+        }
+
+        RequestContext requestContext = (RequestContext) ((ObjectWrapperAndUnwrapper) objectWrapper).unwrap(rcModel);
+
+        return executeInternal(args, callPlace, env, (ObjectWrapperAndUnwrapper) objectWrapper, requestContext);
+    }
+
+    protected abstract TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+                    throws TemplateException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3b83475f/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index b4b8ad9..0fbbe2f 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -75,12 +75,12 @@ public class BindDirective extends AbstractSpringTemplateDirectiveModel {
             ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
                     throws TemplateException, IOException {
         final String path = CallableUtils.getStringArgument(args, PATH_PARAM_IDX, this);
-        boolean ignoreNestedPath = CallableUtils.getOptionalBooleanArgument(args, IGNORE_NESTED_PATH_PARAM_IDX, this,
-                false);
+        final boolean ignoreNestedPath = CallableUtils.getOptionalBooleanArgument(args, IGNORE_NESTED_PATH_PARAM_IDX,
+                this, false);
 
-        TemplateModel statusModel = getBindStatusTemplateModel(env, objectWrapperAndUnwrapper, requestContext, path,
-                ignoreNestedPath);
-        TemplateModel[] nestedContentArgs = new TemplateModel[] { statusModel };
+        final TemplateModel statusModel = getBindStatusTemplateModel(env, objectWrapperAndUnwrapper, requestContext,
+                path, ignoreNestedPath);
+        final TemplateModel[] nestedContentArgs = new TemplateModel[] { statusModel };
 
         callPlace.executeNestedContent(nestedContentArgs, out, env);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3b83475f/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
new file mode 100644
index 0000000..d440963
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MessageFunction.java
@@ -0,0 +1,98 @@
+/*
+ * 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.util.List;
+
+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.model.impl.SimpleString;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.apache.freemarker.core.util.StringToIndexMap;
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.web.servlet.support.RequestContext;
+
+public class MessageFunction extends AbstractSpringTemplateFunctionModel {
+
+    private static final int CODE_PARAM_IDX = 0;
+    private static final int MESSAGE_PARAM_IDX = 1;
+
+    private static final String MESSAGE_PARAM_NAME = "message";
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    true,
+                    StringToIndexMap.of(
+                            MESSAGE_PARAM_NAME, MESSAGE_PARAM_IDX),
+                    false);
+
+    public MessageFunction(HttpServletRequest request, HttpServletResponse response) {
+        super(request, response);
+    }
+
+    @Override
+    public TemplateModel executeInternal(TemplateModel[] args, CallPlace callPlace, Environment env,
+            ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+                    throws TemplateException {
+        final MessageSource messageSource = requestContext.getMessageSource();
+
+        if (messageSource == null) {
+            throw new TemplateException("MessageSource not found.");
+        }
+
+        String message = null;
+
+        final String code = CallableUtils.getStringArgument(args, CODE_PARAM_IDX, this);
+
+        if (code != null && !code.isEmpty()) {
+            List<Object> msgArgumentList = null;
+            // TODO: How to read message arguments from the varags?
+
+            // TODO: Is it okay to set the default value to null to avoid NoSuchMessageException from Spring MessageSource?
+            message = messageSource.getMessage(code, (msgArgumentList == null) ? null : msgArgumentList.toArray(),
+                    null, requestContext.getLocale());
+        } else {
+            final TemplateModel messageModel = CallableUtils.getOptionalArgument(args, MESSAGE_PARAM_IDX,
+                    TemplateModel.class, this);
+            if (messageModel != null) {
+                MessageSourceResolvable messageResolvable = (MessageSourceResolvable) objectWrapperAndUnwrapper
+                        .unwrap(messageModel);
+                message = messageSource.getMessage(messageResolvable, requestContext.getLocale());
+            }
+        }
+
+        return (message != null) ? new SimpleString(message) : null;
+    }
+
+    @Override
+    public ArgumentArrayLayout getFunctionArgumentArrayLayout() {
+        return ARGS_LAYOUT;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3b83475f/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
index 1e94a97..f34fd4b 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/web/view/FreeMarkerView.java
@@ -39,6 +39,7 @@ import org.apache.freemarker.servlet.IncludePage;
 import org.apache.freemarker.servlet.ServletContextHashModel;
 import org.apache.freemarker.servlet.jsp.TaglibFactory;
 import org.apache.freemarker.spring.model.BindDirective;
+import org.apache.freemarker.spring.model.MessageFunction;
 
 /**
  * FreeMarker template based view implementation, with being able to provide a {@link ServletContextHashModel}
@@ -178,6 +179,7 @@ public class FreeMarkerView extends AbstractFreeMarkerView {
             final HttpServletRequest request, final HttpServletResponse response) {
         final SimpleHash springCallableHash = new SimpleHash(objectWrapper);
         springCallableHash.put("bind", new BindDirective(request, response));
+        springCallableHash.put("message", new MessageFunction(request, response));
         return springCallableHash;
     }
 }


[36/36] incubator-freemarker git commit: FREEMARKER-55: Meged: Spring Directives/Functions to replace spring.tld tags (to be continued...)

Posted by dd...@apache.org.
FREEMARKER-55: Meged: Spring Directives/Functions to replace spring.tld tags (to be continued...)

Merge commit 'refs/pull/34/head' of https://github.com/apache/incubator-freemarker into 3


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

Branch: refs/heads/3
Commit: 5f6a9174de5cd89f07877ed2b53032cffcfa5b31
Parents: b63e448 1c7c987
Author: ddekany <dd...@apache.org>
Authored: Wed Sep 13 11:47:28 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Wed Sep 13 11:48:32 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |  14 +
 .../AbstractSpringTemplateCallableModel.java    | 115 ++++++++
 .../AbstractSpringTemplateDirectiveModel.java   |  98 +++++++
 .../AbstractSpringTemplateFunctionModel.java    |  93 +++++++
 .../freemarker/spring/model/BindDirective.java  | 110 ++++++++
 .../spring/model/BindErrorsDirective.java       | 123 +++++++++
 .../freemarker/spring/model/EvalFunction.java   | 203 ++++++++++++++
 .../spring/model/MessageFunction.java           | 156 +++++++++++
 .../spring/model/NestedPathDirective.java       | 114 ++++++++
 .../model/SpringTemplateCallableHashModel.java  |  96 +++++++
 .../freemarker/spring/model/ThemeFunction.java  |  72 +++++
 .../spring/model/TransformFunction.java         | 104 +++++++
 .../freemarker/spring/model/UrlFunction.java    | 268 +++++++++++++++++++
 .../apache/freemarker/spring/model/package.html |  23 ++
 .../org/apache/freemarker/spring/package.html   |  23 ++
 .../spring/web/view/FreeMarkerView.java         |   7 +
 .../freemarker/spring/web/view/package.html     |  23 ++
 .../spring/example/mvc/users/User.java          |  89 ++++++
 .../example/mvc/users/UserController.java       | 110 ++++++++
 .../example/mvc/users/UserRepository.java       | 103 +++++++
 .../spring/model/BindDirectiveTest.java         |  73 +++++
 .../spring/model/BindErrorsDirectiveTest.java   |  83 ++++++
 .../spring/model/EvalFunctionTest.java          |  75 ++++++
 .../spring/model/MessageFunctionTest.java       |  83 ++++++
 .../spring/model/NestedPathDirectiveTest.java   |  72 +++++
 .../spring/model/ThemeFunctionTest.java         |  77 ++++++
 .../spring/model/TransformFunctionTest.java     |  76 ++++++
 .../spring/model/UrlFunctionTest.java           |  76 ++++++
 .../views/example/users/useredit.ftl            |  88 ++++++
 .../views/example/users/userlist.ftl            |  46 ++++
 .../test/model/bind-directive-basic-usages.ftl  |  55 ++++
 .../model/binderrors-directive-basic-usages.ftl |  70 +++++
 .../test/model/eval-function-basic-usages.ftl   |  46 ++++
 .../model/message-function-basic-usages.ftl     |  32 +++
 .../model/nestedpath-directive-basic-usages.ftl |  55 ++++
 .../test/model/theme-function-basic-usages.ftl  |  31 +++
 .../model/transform-function-basic-usages.ftl   |  28 ++
 .../test/model/url-function-basic-usages.ftl    |  62 +++++
 .../example/mvc/users/UsersMessages.properties  |   9 +
 .../mvc/users/UsersTheme-default.properties     |   2 +
 .../example/mvc/users/users-mvc-context.xml     |  54 ++++
 41 files changed, 3137 insertions(+)
----------------------------------------------------------------------



[21/36] incubator-freemarker git commit: FREEMARKER-55: Removing mvcUrl function as it's not in earlier version of spring 4.0.x

Posted by dd...@apache.org.
FREEMARKER-55: Removing mvcUrl function as it's not in earlier version of spring 4.0.x


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

Branch: refs/heads/3
Commit: 2076c5e83786b5253ea80651f4a47ea87f263fd0
Parents: 834abd7
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 17:14:39 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 17:14:39 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/MvcUrlFunction.java | 74 --------------------
 .../model/SpringTemplateCallableHashModel.java  |  1 -
 2 files changed, 75 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/2076c5e8/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
deleted file mode 100644
index 047d5c2..0000000
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/MvcUrlFunction.java
+++ /dev/null
@@ -1,74 +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.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/2076c5e8/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 7315947..26ae060 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
@@ -55,7 +55,6 @@ public final class SpringTemplateCallableHashModel implements TemplateHashModel,
         modelsMap.put(TransformFunction.NAME, new TransformFunction(request, response));
         modelsMap.put(UrlFunction.NAME, new UrlFunction(request, response));
         modelsMap.put(EvalFunction.NAME, new EvalFunction(request, response));
-        modelsMap.put(MvcUrlFunction.NAME, new MvcUrlFunction(request, response));
     }
 
     public TemplateModel get(String key) throws TemplateException {


[26/36] incubator-freemarker git commit: FREEMARKER-55: adding unit test for theme function.

Posted by dd...@apache.org.
FREEMARKER-55: adding unit test for theme function.


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

Branch: refs/heads/3
Commit: 9987acfe0f399ad81d0373a8e7ee81e47674f614
Parents: a274d2a
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 11:33:39 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 11:33:39 2017 -0400

----------------------------------------------------------------------
 .../org/apache/freemarker/spring/model/ThemeFunction.java   | 2 ++
 .../apache/freemarker/spring/model/ThemeFunctionTest.java   | 9 +++++++--
 .../views/test/model/theme-function-basic-usages.ftl        | 5 +++++
 .../spring/example/mvc/users/UsersTheme-default.properties  | 1 +
 4 files changed, 15 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9987acfe/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
index b03055e..4f201ad 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
@@ -48,6 +48,8 @@ import org.springframework.web.servlet.support.RequestContext;
  * ${spring.theme("styleSheet")!}
  *
  * &lt;link rel="stylesheet" href="${spring.theme('styleSheet')}" type="text/css" /&gt;
+ * 
+ * &lt;div id="user" class="${spring.theme('userClass', 'selected')}"&gt;...&lt;/div&gt;
  * </PRE>
  * <P>
  * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:theme /&gt;</code> JSP Tag Library, this function

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9987acfe/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
index 637ca5f..ed00f5e 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
@@ -30,6 +30,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
 import org.springframework.http.MediaType;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -62,11 +63,15 @@ public class ThemeFunctionTest {
 
     @Test
     public void testThemeFunctionBasicUsages() throws Exception {
+        final MessageSource defaultThemeMessageSource = themeSource.getTheme("default").getMessageSource();
+
         final Integer userId = userRepository.getUserIds().iterator().next();
         mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/theme-function-basic-usages")
                 .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
                 .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
-                .andExpect(xpath("//link[@rel='stylesheet']/@href").string(
-                        themeSource.getTheme("default").getMessageSource().getMessage("styleSheet", null, null)));
+                .andExpect(xpath("//link[@rel='stylesheet']/@href")
+                        .string(defaultThemeMessageSource.getMessage("styleSheet", null, null)))
+                .andExpect(xpath("//div[@id='user']/@class")
+                        .string(defaultThemeMessageSource.getMessage("userClass", new Object[] { "selected" }, null)));
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9987acfe/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
index bc50875..efb503a 100644
--- a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
@@ -22,5 +22,10 @@
 <link rel="stylesheet" href="${spring.theme('styleSheet')}" type="text/css" />
 </head>
 <body>
+
+  <div id="user" class="${spring.theme('userClass', 'selected')}">
+    ${user.firstName!} ${user.lastName!}
+  </div>
+
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9987acfe/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
index 1d7bcc2..b89d3a0 100644
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
@@ -1 +1,2 @@
 styleSheet=/themes/style/default.css
+userClass=user-{0}


[22/36] incubator-freemarker git commit: FREEMARKER-55: unit test template code

Posted by dd...@apache.org.
FREEMARKER-55: unit test template code


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

Branch: refs/heads/3
Commit: 96c038d9ccc4a5d38cc8896370870d27af46af1f
Parents: 2076c5e
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 23:21:50 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 23:21:50 2017 -0400

----------------------------------------------------------------------
 .../spring/example/mvc/users/User.java          |  80 ++++++++++++++
 .../example/mvc/users/UserController.java       |  66 ++++++++++++
 .../example/mvc/users/UserRepository.java       | 103 +++++++++++++++++++
 .../spring/model/MessageFunctionTest.java       |  61 +++++++++++
 .../views/example/users/useredit.ftl            |  88 ++++++++++++++++
 .../views/example/users/userlist.ftl            |  44 ++++++++
 .../model/MessageFunctionTest-context.xml       |  46 +++++++++
 .../spring/model/UsersMessages.properties       |   7 ++
 8 files changed, 495 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
new file mode 100644
index 0000000..a1550b2
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java
@@ -0,0 +1,80 @@
+/*
+ * 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.example.mvc.users;
+
+import java.util.Date;
+
+public class User {
+
+    private final String id;
+    private String password;
+    private String email;
+    private String firstName;
+    private String lastName;
+    private Date birthDate;
+
+    public User(final String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public Date getBirthDate() {
+        return birthDate;
+    }
+
+    public void setBirthDate(Date birthDate) {
+        this.birthDate = birthDate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
new file mode 100644
index 0000000..e8c0d86
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserController.java
@@ -0,0 +1,66 @@
+/*
+ * 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.example.mvc.users;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@Controller
+public class UserController {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @RequestMapping(value = "/users", method = RequestMethod.GET)
+    public String listUsers(Model model) {
+        List<User> users = new LinkedList<>();
+
+        for (String id : userRepository.getUserIds()) {
+            users.add(userRepository.getUser(id));
+        }
+
+        model.addAttribute("users", users);
+
+        return "example/users/userlist";
+    }
+
+    @RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
+    public String editUser(@PathVariable("id") String id, Model model) {
+        User user = userRepository.getUser(id);
+        model.addAttribute("user", user);
+        return "example/users/useredit";
+    }
+
+    public UserRepository getUserRepository() {
+        return userRepository;
+    }
+
+    public void setUserRepository(UserRepository userRepository) {
+        this.userRepository = userRepository;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
new file mode 100644
index 0000000..3e53d03
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java
@@ -0,0 +1,103 @@
+/*
+ * 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.example.mvc.users;
+
+import java.util.Calendar;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class UserRepository {
+
+    private Map<String, User> usersMap = new ConcurrentHashMap<>();
+    {
+        String id = "13c2ec8c-102c-4883-a282-3fe983e61515";
+        User user = new User(id);
+        user.setEmail("john@example.com");
+        user.setFirstName("John");
+        user.setLastName("Doe");
+        Calendar birthDate = Calendar.getInstance();
+        birthDate.set(Calendar.YEAR, 1971);
+        birthDate.set(Calendar.MONTH, Calendar.JANUARY);
+        birthDate.set(Calendar.DATE, 5);
+        user.setBirthDate(birthDate.getTime());
+        usersMap.put(id, user);
+
+        id = "04d6080b-2098-4eaf-90ee-7331caab5e91";
+        user = new User(id);
+        user.setEmail("jane@example.com");
+        user.setFirstName("Jane");
+        user.setLastName("Doe");
+        birthDate = Calendar.getInstance();
+        birthDate.set(Calendar.YEAR, 1970);
+        birthDate.set(Calendar.MONTH, Calendar.FEBRUARY);
+        birthDate.set(Calendar.DATE, 7);
+        user.setBirthDate(birthDate.getTime());
+        usersMap.put(id, user);
+    }
+
+    public synchronized Set<String> getUserIds() {
+        return new TreeSet<>(usersMap.keySet());
+    }
+
+    public synchronized User getUser(final String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("ID must be non-null.");
+        }
+
+        User user = usersMap.get(id);
+
+        if (user != null) {
+            return cloneUser(user, user.getId());
+        }
+
+        return null;
+    }
+
+    public synchronized User addOrUpdateUser(final User user) {
+        final String id = user.getId();
+        User newUser = cloneUser(user, id);
+        usersMap.put(id, newUser);
+        return cloneUser(newUser, id);
+    }
+
+    public synchronized boolean deleteUser(final String username) {
+        if (username == null) {
+            throw new IllegalArgumentException("Username must be non-null.");
+        }
+
+        final User user = usersMap.remove(username);
+        return user != null;
+    }
+
+    private User cloneUser(final User source, final String id) {
+        User clone = new User(id);
+        clone.setPassword(source.getPassword());
+        clone.setEmail(source.getEmail());
+        clone.setFirstName(source.getFirstName());
+        clone.setLastName(source.getLastName());
+        clone.setBirthDate(source.getBirthDate());
+        return clone;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
new file mode 100644
index 0000000..03c32f4
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration("MessageFunctionTest-context.xml")
+public class MessageFunctionTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setup() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void getUsers() throws Exception {
+        mockMvc.perform(get("/users").accept(MediaType.parseMediaType("text/html")))
+            .andExpect(status().isOk())
+            .andExpect(content().contentTypeCompatibleWith("text/html"))
+            .andExpect(xpath("/html/head/title").string("Spring MVC Form Example - Users"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/useredit.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/useredit.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/useredit.ftl
new file mode 100644
index 0000000..c206e9a
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/useredit.ftl
@@ -0,0 +1,88 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<head>
+<title>Spring MVC Form Example - User Edit Form</title>
+</head>
+<body>
+
+<h1>Editing User: ${spring.eval("user.firstName + ' ' + user.lastName")}</h1>
+
+<p>${spring.message("user.form.message", user.firstName, user.lastName, user.email)}</p>
+
+<form method="POST" action="${spring.url('/usereditaction.do', context='/othercontext', param1='value1', param2='value2')}">
+  <table border="2">
+    <tbody>
+      <tr>
+        <th>${spring.message("user.id")!}</th>
+        <td>${user.id}</td>
+      </tr>
+      <tr>
+        <th>${spring.message("user.password")!}</th>
+        <td>
+          <@spring.bind "user.password"; status>
+            <input type="password" name="password" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>${spring.message("user.email")!}</th>
+        <td>
+          <@spring.bind "user.email"; status>
+            <input type="text" name="email" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>${spring.message("user.firstName")!}</th>
+        <td>
+          <@spring.bind "user.firstName"; status>
+            <input type="text" name="firstName" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>${spring.message("user.lastName")!}</th>
+        <td>
+          <@spring.bind "user.lastName"; status>
+            <input type="text" name="lastName" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>${spring.message("user.birthDate")!}</th>
+        <td>
+          <@spring.bind "user.birthDate"; status>
+            ${spring.transform(status.editor, status.actualValue)!}
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <td colspan="2">
+          <input type="submit" value="${spring.message('user.form.submit')!'Save'}" />
+          <input type="reset" value="${spring.message('user.form.submit')!'Reset'}" />
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</form>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
new file mode 100644
index 0000000..25c757f
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/example/users/userlist.ftl
@@ -0,0 +1,44 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<head>
+<title>Spring MVC Form Example - Users</title>
+</head>
+
+<h1>Users</h1>
+
+<table border="2">
+  <thead>
+    <tr>
+      <th>Name</th>
+      <th>E-Mail</th>
+    </tr>
+  </thead>
+  <tbody>
+    <#list users as user>
+    <tr>
+      <td><a href="users/${user.id}">${user.firstName} ${user.lastName}</a></td>
+      <td><a href="mailto:${user.email}">${user.email}</a></td>
+    </tr>
+    </#list>
+  </tbody>
+</table>
+
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
new file mode 100644
index 0000000..f68f584
--- /dev/null
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/MessageFunctionTest-context.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+   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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:mvc="http://www.springframework.org/schema/mvc"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
+
+  <context:component-scan base-package="org.apache.freemarker.spring.example.mvc.users" />
+
+  <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
+    <property name="basename" value="classpath:org.apache.freemarker.spring.model.UserMessages" />
+  </bean>
+
+  <bean id="configuration" class="org.apache.freemarker.spring.ConfigurationFactoryBean">
+    <property name="localizedTemplateLookup" value="false" />
+    <property name="templateLoader">
+      <bean class="org.apache.freemarker.spring.SpringResourceTemplateLoader">
+      </bean>
+    </property>
+  </bean>
+
+  <bean id="viewResolver" class="org.apache.freemarker.spring.web.view.FreeMarkerViewResolver">
+    <property name="configuration" ref="configuration" />
+    <property name="prefix" value="/views/" />
+    <property name="suffix" value=".ftl" />
+  </bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/96c038d9/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties
new file mode 100644
index 0000000..ade76f4
--- /dev/null
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/model/UsersMessages.properties
@@ -0,0 +1,7 @@
+user.form.message=Edit info for {0} {1} <{2}>
+user.id=ID
+user.password=Password
+user.email=E-Mail
+user.firstName=First name
+user.lastName=Last name
+user.birthDate=Birth Date


[28/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for url function.

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for url function.


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

Branch: refs/heads/3
Commit: e4598db9cb9c0513a36033ce4fcd39a6fa901231
Parents: d791670
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 14:25:21 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 14:25:21 2017 -0400

----------------------------------------------------------------------
 .../java/org/apache/freemarker/spring/model/UrlFunction.java     | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/e4598db9/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
index 1e966a1..2220800 100644
--- 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
@@ -55,7 +55,11 @@ import org.springframework.web.util.UriUtils;
  * </P>
  * <PRE>
  * ${spring.url('/usereditaction.do')}
+ * 
  * ${spring.url('/usereditaction.do', param1='value1', param2='value2')}
+ * 
+ * ${spring.url('/users/{userId}/edit.do', userId='123')}
+ * 
  * ${spring.url('/usereditaction.do', context='/othercontext', param1='value1', param2='value2')}
  * </PRE>
  * <P>


[31/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for nestedPath directive.

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for nestedPath directive.


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

Branch: refs/heads/3
Commit: 86a8a5f6933ccf128cae0ca46ef116717aa949be
Parents: 7a93fa0
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 15:06:04 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 15:06:04 2017 -0400

----------------------------------------------------------------------
 .../spring/model/NestedPathDirectiveTest.java   | 72 ++++++++++++++++++++
 .../model/nestedpath-directive-basic-usages.ftl | 55 +++++++++++++++
 2 files changed, 127 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/86a8a5f6/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
new file mode 100644
index 0000000..d02478f
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/NestedPathDirectiveTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.apache.freemarker.spring.example.mvc.users.User;
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class NestedPathDirectiveTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testMessageFunctionBasicUsages() throws Exception {
+        final Integer userId = userRepository.getUserIds().iterator().next();
+        final User user = userRepository.getUser(userId);
+        mockMvc.perform(get("/users/{userId}.", userId).param("viewName", "test/model/nestedpath-directive-basic-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//input[@name='email']/@value").string(user.getEmail()))
+                .andExpect(xpath("//input[@name='firstName']/@value").string(user.getFirstName()))
+                .andExpect(xpath("//input[@name='lastName']/@value").string(user.getLastName()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/86a8a5f6/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/nestedpath-directive-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/nestedpath-directive-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/nestedpath-directive-basic-usages.ftl
new file mode 100644
index 0000000..13849bf
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/nestedpath-directive-basic-usages.ftl
@@ -0,0 +1,55 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<body>
+
+<@spring.nestedPath "user">
+  <table class="table">
+    <tbody>
+      <tr>
+        <th>E-Mail</th>
+        <td>
+          <@spring.bind "email"; status>
+            <input type="text" name="email" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>First Name</th>
+        <td>
+          <@spring.bind "firstName"; status>
+            <input type="text" name="firstName" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+      <tr>
+        <th>Last Name</th>
+        <td>
+          <@spring.bind "lastName"; status>
+            <input type="text" name="lastName" value="${status.value!}" />
+          </...@spring.bind>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</...@spring.nestedPath>
+
+</body>
+</html>


[25/36] incubator-freemarker git commit: FREEMARKER-55: Adding unit test for theme function.

Posted by dd...@apache.org.
FREEMARKER-55: Adding unit test for theme function.


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

Branch: refs/heads/3
Commit: a274d2a6149b432656c5aa61eedd5b6213168378
Parents: 77b7f30
Author: Woonsan Ko <wo...@apache.org>
Authored: Tue Sep 12 11:22:15 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Tue Sep 12 11:22:15 2017 -0400

----------------------------------------------------------------------
 .../freemarker/spring/model/ThemeFunction.java  |  8 +--
 .../spring/model/MessageFunctionTest.java       |  4 +-
 .../spring/model/ThemeFunctionTest.java         | 72 ++++++++++++++++++++
 .../test/model/theme-function-basic-usages.ftl  | 26 +++++++
 .../mvc/users/UsersTheme-default.properties     |  1 +
 .../example/mvc/users/users-mvc-context.xml     |  8 +++
 6 files changed, 111 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a274d2a6/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
index 15a4103..b03055e 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/ThemeFunction.java
@@ -45,13 +45,9 @@ import org.springframework.web.servlet.support.RequestContext;
  * </P>
  * <PRE>
  * &lt;#-- With 'code' positional parameter only --&gt;
- * ${spring.theme("label.user.firstName")!}
+ * ${spring.theme("styleSheet")!}
  *
- * &lt;#-- With 'code' positional parameter and message arguments (varargs) --&gt;
- * ${spring.theme("message.user.form", user.firstName, user.lastName, user.email)}
- *
- * &lt;#-- With 'message' named parameter (<code>MessageResolvable</code> object) --&gt;
- * ${spring.theme(message=myMessageResolvable)}
+ * &lt;link rel="stylesheet" href="${spring.theme('styleSheet')}" type="text/css" /&gt;
  * </PRE>
  * <P>
  * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:theme /&gt;</code> JSP Tag Library, this function

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a274d2a6/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
index 803e3de..5be6daf 100644
--- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/MessageFunctionTest.java
@@ -58,7 +58,7 @@ public class MessageFunctionTest {
     }
 
     @Test
-    public void getMessageFunctionBasicUsages() throws Exception {
+    public void testMessageFunctionBasicUsages() throws Exception {
         final Integer userId = userRepository.getUserIds().iterator().next();
         final User user = userRepository.getUser(userId);
         mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/message-function-basic-usages")
@@ -71,7 +71,7 @@ public class MessageFunctionTest {
     }
 
     @Test
-    public void getMessageFunctionWithMessageSourceResolvable() throws Exception {
+    public void testMessageFunctionWithMessageSourceResolvable() throws Exception {
         final Integer nonExistingUserId = 0;
         mockMvc.perform(
                 get("/users/{userId}/", nonExistingUserId).param("viewName", "test/model/message-function-basic-usages")

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a274d2a6/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
new file mode 100644
index 0000000..637ca5f
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/ThemeFunctionTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;
+
+import org.apache.freemarker.spring.example.mvc.users.UserRepository;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.ui.context.ThemeSource;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration("classpath:META-INF/web-resources")
+@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" })
+public class ThemeFunctionTest {
+
+    @Autowired
+    private WebApplicationContext wac;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private ThemeSource themeSource;
+
+    private MockMvc mockMvc;
+
+    @Before
+    public void setup() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+    }
+
+    @Test
+    public void testThemeFunctionBasicUsages() throws Exception {
+        final Integer userId = userRepository.getUserIds().iterator().next();
+        mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/theme-function-basic-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//link[@rel='stylesheet']/@href").string(
+                        themeSource.getTheme("default").getMessageSource().getMessage("styleSheet", null, null)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a274d2a6/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
new file mode 100644
index 0000000..bc50875
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/theme-function-basic-usages.ftl
@@ -0,0 +1,26 @@
+<#ftl outputFormat="HTML">
+<#--
+  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.
+-->
+<html>
+<head>
+<link rel="stylesheet" href="${spring.theme('styleSheet')}" type="text/css" />
+</head>
+<body>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a274d2a6/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
new file mode 100644
index 0000000..1d7bcc2
--- /dev/null
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/UsersTheme-default.properties
@@ -0,0 +1 @@
+styleSheet=/themes/style/default.css

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a274d2a6/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
index 0dbc950..f5fcf24 100644
--- a/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
+++ b/freemarker-spring/src/test/resources/org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml
@@ -43,4 +43,12 @@
     <property name="suffix" value=".ftl" />
   </bean>
 
+  <bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
+    <property name="basenamePrefix" value="org/apache/freemarker/spring/example/mvc/users/UsersTheme-" />
+  </bean>
+
+  <bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver">
+    <property name="defaultThemeName" value="default" />
+  </bean>
+
 </beans>


[07/36] incubator-freemarker git commit: FREEMARKER-55: Adding javadocs

Posted by dd...@apache.org.
FREEMARKER-55: Adding javadocs


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

Branch: refs/heads/3
Commit: 63f42c3dfcb06b7873e7aec58c90894e8b291ced
Parents: 2de73b0
Author: Woonsan Ko <wo...@apache.org>
Authored: Wed Aug 30 09:21:55 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Wed Aug 30 09:21:55 2017 -0400

----------------------------------------------------------------------
 .../model/AbstractSpringTemplateDirectiveModel.java |  3 +++
 .../freemarker/spring/model/BindDirective.java      | 16 ++++++++++++++++
 2 files changed, 19 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/63f42c3d/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 1c5332b..e1b34b4 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
@@ -38,6 +38,9 @@ import org.springframework.web.servlet.support.BindStatus;
 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.
+ */
 public abstract class AbstractSpringTemplateDirectiveModel implements TemplateDirectiveModel {
 
     // TODO: namespace this into 'spring.nestedPath'??

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/63f42c3d/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
index d7967c8..b4b8ad9 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/BindDirective.java
@@ -35,6 +35,22 @@ import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.springframework.web.servlet.support.RequestContext;
 
+/**
+ * Provides <code>TemplateModel</code> wrapping <code>BindStatus</code> for the given bind path, working similarly
+ * to Spring Framework's <code>&lt;spring:bind /&gt;</code> JSP Tag Library.
+ * <P>
+ * This directive supports the following parameters:
+ * <UL>
+ * <LI><code>ignoreNestedPath</code>: Set whether to ignore a nested path, if any. <code>false</code> by default.</LI>
+ * <LI><code>path</code>: The path to the bean or bean property to bind status information for.</LI>
+ * </UL>
+ * </P>
+ * <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 rather than depending on directives.
+ * </P>
+ */
 public class BindDirective extends AbstractSpringTemplateDirectiveModel {
 
     private static final int PATH_PARAM_IDX = 0;


[19/36] incubator-freemarker git commit: FREEMARKER-55: fixing NPE when there's no params

Posted by dd...@apache.org.
FREEMARKER-55: fixing NPE when there's no params


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

Branch: refs/heads/3
Commit: 075bd88777888a12a194844f8b961c8bd7333a49
Parents: 8bdc5fd
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Sep 11 15:51:01 2017 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Sep 11 15:51:01 2017 -0400

----------------------------------------------------------------------
 .../java/org/apache/freemarker/spring/model/UrlFunction.java   | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/075bd887/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
index fe1b1fd..1e966a1 100644
--- 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
@@ -21,6 +21,7 @@ package org.apache.freemarker.spring.model;
 
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -53,6 +54,9 @@ import org.springframework.web.util.UriUtils;
  * Some valid example(s):
  * </P>
  * <PRE>
+ * ${spring.url('/usereditaction.do')}
+ * ${spring.url('/usereditaction.do', param1='value1', param2='value2')}
+ * ${spring.url('/usereditaction.do', context='/othercontext', param1='value1', param2='value2')}
  * </PRE>
  * <P>
  * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;spring:message /&gt;</code> JSP Tag Library, this function
@@ -93,7 +97,7 @@ public class UrlFunction extends AbstractSpringTemplateFunctionModel {
         final String value = CallableUtils.getStringArgument(args, VALUE_PARAM_IDX, this);
         final String context = CallableUtils.getOptionalStringArgument(args, CONTEXT_PARAM_IDX, this);
 
-        List<_KeyValuePair<String, String>> params = null;
+        List<_KeyValuePair<String, String>> params = Collections.emptyList();
         final TemplateHashModelEx2 paramsHashModel = (TemplateHashModelEx2) args[PARAMS_PARAM_IDX];
 
         if (!paramsHashModel.isEmptyHash()) {