Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 1DB1A200CF3 for ; Wed, 13 Sep 2017 11:49:16 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 1C2541609CE; Wed, 13 Sep 2017 09:49:16 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 8F96B1609C9 for ; Wed, 13 Sep 2017 11:49:14 +0200 (CEST) Received: (qmail 25143 invoked by uid 500); 13 Sep 2017 09:49:13 -0000 Mailing-List: contact notifications-help@freemarker.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@freemarker.incubator.apache.org Delivered-To: mailing list notifications@freemarker.incubator.apache.org Received: (qmail 25134 invoked by uid 99); 13 Sep 2017 09:49:13 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Sep 2017 09:49:13 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id 2E575185802 for ; Wed, 13 Sep 2017 09:49:13 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.222 X-Spam-Level: X-Spam-Status: No, score=-4.222 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id tcklCLfIm6zx for ; Wed, 13 Sep 2017 09:49:07 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id B808E60EEB for ; Wed, 13 Sep 2017 09:49:04 +0000 (UTC) Received: (qmail 23656 invoked by uid 99); 13 Sep 2017 09:49:03 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 13 Sep 2017 09:49:03 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id E8E3AF5816; Wed, 13 Sep 2017 09:49:02 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ddekany@apache.org To: notifications@freemarker.incubator.apache.org Date: Wed, 13 Sep 2017 09:49:14 -0000 Message-Id: <386b0bbe68e84c2f9ca9ee64b20e1e5e@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [13/36] incubator-freemarker git commit: FREEMARKER-55: Adding more spring callable models. archived-at: Wed, 13 Sep 2017 09:49:16 -0000 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 Authored: Wed Sep 6 10:01:10 2017 -0400 Committer: Woonsan Ko 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 org.springframework.web.servlet.tags.NestedPathTag#NESTED_PATH_VARIABLE_NAME - */ - 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 BindStatus 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; *

* This directive supports the following parameters: *

    - *
  • ignoreNestedPath: Set whether to ignore a nested path, if any. false by default.
  • - *
  • path: The path to the bean or bean property to bind status information for.
  • + *
  • path: The first positional parameter pointing to the bean or bean property to bind status information for.
  • + *
  • + * ignoreNestedPath: A named parameter to set whether to ignore a nested path, if any. + * false by default. + *
  • *
*

*

+ * Some valid example(s): + *

+ *
+ *   <@spring.bind "user.email"; status>
+ *     <input type="text" name="email" value="${status.value!}" />
+ *   </@spring.bind>
+ * 
+ *

* Note: Unlike Spring Framework's <spring:bind /> JSP Tag Library, this directive * does not support htmlEscape parameter. It always has BindStatus 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. *

*/ 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 TemplateModel wrapping the bind errors (type of org.springframework.validation.Errors) + * for the given name, working similarly to Spring Framework's <spring:hasBindErrors /> JSP Tag Library. + *

+ * This directive supports the following parameters: + *

    + *
  • name: The first positional parameter for the name of the bean that this directive should check.
  • + *
+ *

+ *

+ * Some valid example(s): + *

+ *
+ * <@spring.hasBindErrors "email"; errors>
+ *   <#-- nested content with using errors -->
+ * </@spring.hasBindErrors>
+ * 
+ *

+ * Note: Unlike Spring Framework's <spring:hasBindErrors /> JSP Tag Library, this directive + * does not support htmlEscape parameter. It always has an org.springframework.validation.Errors + * instance not to escape HTML's because it is much easier to control escaping in FreeMarker Template expressions + * rather than depending on directives. + *

+ */ +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 TemplateFunctionModel providing functionality equivalent to the Spring Framework's + * <spring:message /> JSP Tag Library. + * It retrieves the theme message with the given code or the resolved text by the given message parameter. + *

+ * This function supports the following parameters: + *

    + *
  • code: The first optional positional parameter. The key to use when looking up the message. + *
  • message arguments: Positional varargs after code parameter, as message arguments.
  • + *
  • message: Named parameters as MessageResolvable object.
  • + *
+ *

+ *

+ * This function requires either code parameter or message parameter at least. + *

+ *

+ * Some valid example(s): + *

+ *
+ * <#-- 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 (MessageResolvable object) -->
+ * ${spring.message(message=myMessageResolvable)}
+ * 
+ *

+ * Note: Unlike Spring Framework's <spring:message /> JSP Tag Library, this function + * does not support htmlEscape parameter. It always returns the message not to escape HTML's + * because it is much easier to control escaping in FreeMarker Template expressions. + *

+ */ 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 TemplateModel setting spring.nestedPath by the given bind path, working similarly + * to Spring Framework's <spring:nestedPath /> JSP Tag Library. + *

+ * This directive supports the following parameters: + *

    + *
  • path: The first positional parameter to set a new nested path by appending it to the existing nested path if any existing.
  • + *
+ *

+ *

+ * Some valid example(s): + *

+ *
+ *   <@spring.nestedPath "user">
+ *     <#-- nested content --/>
+ *   </@spring.nestedPath>
+ * 
+ */ +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 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 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 TemplateFunctionModel providing functionality equivalent to the Spring Framework's + * <spring:theme /> JSP Tag Library. + * It retrieves the theme message with the given code or the resolved text by the given message parameter. + *

+ * This function supports the following parameters: + *

    + *
  • code: The first optional positional parameter. The key to use when looking up the message. + *
  • message arguments: Positional varargs after code parameter, as message arguments.
  • + *
  • message: Named parameters as MessageResolvable object.
  • + *
+ *

+ *

+ * This function requires either code parameter or message parameter at least. + *

+ *

+ * Some valid example(s): + *

+ *
+ * <#-- 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 (MessageResolvable object) -->
+ * ${spring.theme(message=myMessageResolvable)}
+ * 
+ *

+ * Note: Unlike Spring Framework's <spring:theme /> JSP Tag Library, this function + * does not support htmlEscape parameter. It always returns the message not to escape HTML's + * because it is much easier to control escaping in FreeMarker Template expressions. + *

+ */ 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; - } }