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 <[email protected]> Authored: Wed Sep 6 10:01:10 2017 -0400 Committer: Woonsan Ko <[email protected]> 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> + * <@spring.bind "user.email"; status> + * <input type="text" name="email" value="${status.value!}" /> + * </@spring.bind> + * </PRE> + * <P> * <EM>Note:</EM> Unlike Spring Framework's <code><spring:bind /></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><spring:hasBindErrors /></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> + * <@spring.hasBindErrors "email"; errors> + * <#-- nested content with using errors --> + * </@spring.hasBindErrors> + * </PRE> + * <P> + * <EM>Note:</EM> Unlike Spring Framework's <code><spring:hasBindErrors /></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><spring:message /></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> + * <#-- With 'code' positional parameter only --> + * ${spring.message("label.user.firstName")!} + * + * <#-- With 'code' positional parameter and message arguments (varargs) --> + * ${spring.message("message.user.form", user.firstName, user.lastName, user.email)} + * + * <#-- With 'message' named parameter (<code>MessageResolvable</code> object) --> + * ${spring.message(message=myMessageResolvable)} + * </PRE> + * <P> + * <EM>Note:</EM> Unlike Spring Framework's <code><spring:message /></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><spring:nestedPath /></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> + * <@spring.nestedPath "user"> + * <#-- nested content --/> + * </@spring.nestedPath> + * </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><spring:theme /></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> + * <#-- With 'code' positional parameter only --> + * ${spring.theme("label.user.firstName")!} + * + * <#-- With 'code' positional parameter and message arguments (varargs) --> + * ${spring.theme("message.user.form", user.firstName, user.lastName, user.email)} + * + * <#-- With 'message' named parameter (<code>MessageResolvable</code> object) --> + * ${spring.theme(message=myMessageResolvable)} + * </PRE> + * <P> + * <EM>Note:</EM> Unlike Spring Framework's <code><spring:theme /></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; - } }
