You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by wo...@apache.org on 2018/07/23 20:53:38 UTC
freemarker git commit: FREEMARKER-55: Adding radiobutton directive
Repository: freemarker
Updated Branches:
refs/heads/3 cb072d7dc -> d389b6cce
FREEMARKER-55: Adding radiobutton directive
Project: http://git-wip-us.apache.org/repos/asf/freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/freemarker/commit/d389b6cc
Tree: http://git-wip-us.apache.org/repos/asf/freemarker/tree/d389b6cc
Diff: http://git-wip-us.apache.org/repos/asf/freemarker/diff/d389b6cc
Branch: refs/heads/3
Commit: d389b6cce3bab38227949c454b5404aa32d79b0a
Parents: cb072d7
Author: Woonsan Ko <wo...@apache.org>
Authored: Mon Jul 23 16:53:28 2018 -0400
Committer: Woonsan Ko <wo...@apache.org>
Committed: Mon Jul 23 16:53:28 2018 -0400
----------------------------------------------------------------------
.../form/RadioButtonTemplateDirectiveModel.java | 131 +++++++++++++++++++
.../SpringFormTemplateCallableHashModel.java | 1 +
.../spring/example/mvc/users/User.java | 16 ++-
.../example/mvc/users/UserRepository.java | 3 +
.../RadioButtonTemplateDirectiveModelTest.java | 87 ++++++++++++
.../form/radiobutton-directive-usages.f3ah | 44 +++++++
6 files changed, 279 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/freemarker/blob/d389b6cc/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModel.java
new file mode 100644
index 0000000..d6b8e16
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModel.java
@@ -0,0 +1,131 @@
+/*
+ * 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.form;
+
+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.apache.freemarker.core.util.StringToIndexMap;
+import org.springframework.web.servlet.support.RequestContext;
+
+/**
+ * Provides <code>TemplateModel</code> for data-binding-aware HTML '{@code <input type="radio"/>}' element.
+ * This tag is provided for completeness if the application relies on a
+ * <code>org.springframework.web.servlet.support.RequestDataValueProcessor</code>.
+ * <P>
+ * This directive supports the following parameters:
+ * <UL>
+ * <LI>
+ * ... TODO ...
+ * </LI>
+ * </UL>
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ * <@form.radio 'user.gender' value='U'/>Unspecified
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code><form:button /></code> JSP Tag Library, this directive
+ * does not support <code>htmlEscape</code> parameter. It always renders HTML's without escaping
+ * because it is much easier to control escaping in FreeMarker Template expressions.
+ * </P>
+ */
+
+class RadioButtonTemplateDirectiveModel extends AbstractSingleCheckedElementTemplateDirectiveModel {
+
+ public static final String NAME = "radiobutton";
+
+ private static final int NAMED_ARGS_OFFSET = AbstractHtmlInputElementTemplateDirectiveModel.ARGS_LAYOUT
+ .getPredefinedNamedArgumentsEndIndex();
+
+ private static final int VALUE_PARAM_IDX = NAMED_ARGS_OFFSET;
+ private static final String VALUE_PARAM_NAME = "value";
+
+ private static final int LABEL_PARAM_IDX = NAMED_ARGS_OFFSET + 1;
+ private static final String LABEL_PARAM_NAME = "label";
+
+ protected static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(1, false,
+ StringToIndexMap.of(AbstractHtmlInputElementTemplateDirectiveModel.ARGS_LAYOUT.getPredefinedNamedArgumentsMap(),
+ new StringToIndexMap.Entry(VALUE_PARAM_NAME, VALUE_PARAM_IDX),
+ new StringToIndexMap.Entry(LABEL_PARAM_NAME, LABEL_PARAM_IDX)),
+ true);
+
+ private String value;
+ private String label;
+
+ protected RadioButtonTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) {
+ super(request, response);
+ }
+
+ @Override
+ public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+ return ARGS_LAYOUT;
+ }
+
+ @Override
+ public boolean isNestedContentSupported() {
+ return false;
+ }
+
+ @Override
+ protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env,
+ ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext)
+ throws TemplateException, IOException {
+ value = CallableUtils.getOptionalStringArgument(args, VALUE_PARAM_IDX, this);
+ label = CallableUtils.getOptionalStringArgument(args, LABEL_PARAM_IDX, this);
+
+ super.executeInternal(args, callPlace, out, env, objectWrapperAndUnwrapper, requestContext);
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String getLabel() {
+ return label;
+ }
+
+ @Override
+ protected void writeAdditionalDetails(Environment env, TagOutputter tagOut) throws TemplateException, IOException {
+ tagOut.writeAttribute("type", getInputType());
+ Object resolvedValue = evaluate("value", getValue());
+ renderFromValue(env, resolvedValue, tagOut);
+ }
+
+ @Override
+ protected String getInputType() {
+ return "radio";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/freemarker/blob/d389b6cc/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
index 680fe29..a2750dc 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java
@@ -58,6 +58,7 @@ public final class SpringFormTemplateCallableHashModel implements TemplateHashMo
modelsMap.put(ErrorsTemplateDirectiveModel.NAME, new ErrorsTemplateDirectiveModel(request, response));
modelsMap.put(CheckboxTemplateDirectiveModel.NAME, new CheckboxTemplateDirectiveModel(request, response));
modelsMap.put(CheckboxesTemplateDirectiveModel.NAME, new CheckboxesTemplateDirectiveModel(request, response));
+ modelsMap.put(RadioButtonTemplateDirectiveModel.NAME, new RadioButtonTemplateDirectiveModel(request, response));
}
@Override
http://git-wip-us.apache.org/repos/asf/freemarker/blob/d389b6cc/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 56330e6..9bfa43d 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
@@ -28,6 +28,7 @@ public class User {
private String email;
private String firstName;
private String lastName;
+ private String gender = "U"; // 'F': female, 'M': male, 'U': unspecified, ...
private Date birthDate;
private String description;
private String favoriteSport;
@@ -77,6 +78,14 @@ public class User {
this.lastName = lastName;
}
+ public String getGender() {
+ return gender;
+ }
+
+ public void setGender(String gender) {
+ this.gender = gender;
+ }
+
public Date getBirthDate() {
return birthDate;
}
@@ -125,8 +134,9 @@ public class User {
@Override
public String toString() {
- return super.toString() + " {id=" + id + ", firstName='" + firstName + "', lastName='" + lastName + "', email='"
- + email + "', birthDate='" + birthDate + "', description='" + description + "', favoriteSport='"
- + favoriteSport + "', receiveNewsletter=" + receiveNewsletter + ", favoriteFood=" + favoriteFood + "}";
+ return super.toString() + " {id=" + id + ", firstName='" + firstName + "', lastName='" + lastName
+ + "', gender='" + gender + ", email='" + email + "', birthDate='" + birthDate + "', description='"
+ + description + "', favoriteSport='" + favoriteSport + "', receiveNewsletter=" + receiveNewsletter
+ + ", favoriteFood=" + favoriteFood + "}";
}
}
http://git-wip-us.apache.org/repos/asf/freemarker/blob/d389b6cc/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 57af289..667ee50 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
@@ -50,6 +50,7 @@ public class UserRepository {
user.setPassword("johnpass");
user.setFirstName("John");
user.setLastName("Doe");
+ user.setGender("M");
Calendar birthDate = Calendar.getInstance();
birthDate.set(Calendar.YEAR, 1973);
birthDate.set(Calendar.MONTH, Calendar.JANUARY);
@@ -68,6 +69,7 @@ public class UserRepository {
user.setPassword("janepass");
user.setFirstName("Jane");
user.setLastName("Doe");
+ user.setGender("F");
birthDate = Calendar.getInstance();
birthDate.set(Calendar.YEAR, 1970);
birthDate.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -135,6 +137,7 @@ public class UserRepository {
clone.setEmail(source.getEmail());
clone.setFirstName(source.getFirstName());
clone.setLastName(source.getLastName());
+ clone.setGender(source.getGender());
clone.setBirthDate(source.getBirthDate());
clone.setDescription(source.getDescription());
clone.setFavoriteSport(source.getFavoriteSport());
http://git-wip-us.apache.org/repos/asf/freemarker/blob/d389b6cc/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModelTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModelTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModelTest.java
new file mode 100644
index 0000000..7c3d209
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/RadioButtonTemplateDirectiveModelTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.form;
+
+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;
+
+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;
+
+@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 RadioButtonTemplateDirectiveModelTest {
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+ mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
+ }
+
+ @Test
+ public void testSingleCheckboxWithFemaleUser() throws Exception {
+ final User user = userRepository.getUserByEmail("jane@example.com");
+ mockMvc.perform(get("/users/{userId}/", user.getId()).param("viewName", "test/model/form/radiobutton-directive-usages")
+ .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender1' and @name='gender' and @value='F']").exists())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender1' and @name='gender' and @value='F']/@checked").string("checked"))
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender2' and @name='gender' and @value='M']").exists())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender2' and @name='gender' and @value='M']/@checked").doesNotExist())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender3' and @name='gender' and @value='U']").exists())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender3' and @name='gender' and @value='U']/@checked").doesNotExist());
+ }
+
+ @Test
+ public void testSingleCheckboxWithMaleUser() throws Exception {
+ final User user = userRepository.getUserByEmail("john@example.com");
+ mockMvc.perform(get("/users/{userId}/", user.getId()).param("viewName", "test/model/form/radiobutton-directive-usages")
+ .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender1' and @name='gender' and @value='F']").exists())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender1' and @name='gender' and @value='F']/@checked").doesNotExist())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender2' and @name='gender' and @value='M']").exists())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender2' and @name='gender' and @value='M']/@checked").string("checked"))
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender3' and @name='gender' and @value='U']").exists())
+ .andExpect(xpath("//form[@id='form1']//input[@type='radio' and @id='gender3' and @name='gender' and @value='U']/@checked").doesNotExist());
+ }
+}
http://git-wip-us.apache.org/repos/asf/freemarker/blob/d389b6cc/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobutton-directive-usages.f3ah
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobutton-directive-usages.f3ah b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobutton-directive-usages.f3ah
new file mode 100644
index 0000000..ca4192d
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/radiobutton-directive-usages.f3ah
@@ -0,0 +1,44 @@
+<#--
+ 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>
+
+ <h1>Form 1</h1>
+ <hr/>
+ <form id="form1">
+ <table>
+ <tr>
+ <th>User:</th>
+ <td>
+ ${user.firstName} ${user.lastName} (${user.email})
+ </td>
+ </tr>
+ <tr>
+ <th>Gender</th>
+ <td>
+ <@form.radiobutton "user.gender" value="F" />Female
+ <@form.radiobutton "user.gender" value="M" />Male
+ <@form.radiobutton "user.gender" value="U" />Unspecified
+ </td>
+ </tr>
+ </table>
+ </form>
+
+</body>
+</html>