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 <woon...@apache.org>
Authored: Mon Jul 23 16:53:28 2018 -0400
Committer: Woonsan Ko <woon...@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>
+ *   &lt;@form.radio 'user.gender' value='U'/&gt;Unspecified
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;form:button /&gt;</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("j...@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("j...@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>

Reply via email to