You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2022/07/07 19:34:56 UTC

[struts] 01/01: WW-5192 Fixes brok radio tag when using with enums

This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch WW-5192-radio
in repository https://gitbox.apache.org/repos/asf/struts.git

commit e6c1cec7f81ff29dcce506d7dcc7e2b25d4d321f
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Thu Jul 7 21:34:43 2022 +0200

    WW-5192 Fixes brok radio tag when using with enums
---
 .../java/org/apache/struts2/components/Radio.java  |   4 +
 .../test/java/org/apache/struts2/TestAction.java   |  77 +++--
 .../apache/struts2/views/jsp/AbstractTagTest.java  |  68 +++--
 .../org/apache/struts2/views/jsp/ui/RadioTest.java | 311 ++++++++++++++-------
 .../org/apache/struts2/views/jsp/ui/Radio-10.txt   |   4 +
 .../org/apache/struts2/views/jsp/ui/Radio-9.txt    |   4 +
 6 files changed, 306 insertions(+), 162 deletions(-)

diff --git a/core/src/main/java/org/apache/struts2/components/Radio.java b/core/src/main/java/org/apache/struts2/components/Radio.java
index ba5eb471f..7ef81000a 100644
--- a/core/src/main/java/org/apache/struts2/components/Radio.java
+++ b/core/src/main/java/org/apache/struts2/components/Radio.java
@@ -80,4 +80,8 @@ public class Radio extends ListUIBean {
         return true;
     }
 
+    protected Class<?> getValueClassType() {
+        return String.class;
+    }
+
 }
diff --git a/core/src/test/java/org/apache/struts2/TestAction.java b/core/src/test/java/org/apache/struts2/TestAction.java
index 35b48b320..5543b4462 100644
--- a/core/src/test/java/org/apache/struts2/TestAction.java
+++ b/core/src/test/java/org/apache/struts2/TestAction.java
@@ -27,11 +27,12 @@ import com.opensymphony.xwork2.validator.annotations.Validations;
 import com.opensymphony.xwork2.validator.annotations.ValidatorType;
 import org.apache.struts2.views.jsp.ui.User;
 
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
-
-/**
- */
 public class TestAction extends ActionSupport {
 
     private static final long serialVersionUID = -8891365561914451494L;
@@ -50,22 +51,22 @@ public class TestAction extends ActionSupport {
     private SomeEnum status = SomeEnum.COMPLETED;
     private Float floatNumber;
     private Long id;
+    private List<SomeEnum> enumList;
+    private List<Integer> intList;
 
-    private final Map<String, String> texts = new HashMap<String, String>();
+    private final Map<String, String> texts = new HashMap<>();
 
     /**
      * Define a text resource within this action that will be returned by the getText methods
-     * here before delegating to the default TextProvider
-     *
-     * call
-     * @param key
-     * @param value
+     * here before delegating to the default TextProvider call
      */
     public void setText(String key, String value) {
         this.texts.put(key, value);
     }
 
-    /** Returns the test value if defined otherwise delegates to the default TextProvider */
+    /**
+     * Returns the test value if defined otherwise delegates to the default TextProvider
+     */
     public String getText(String key) {
         if (this.texts.containsKey(key)) {
             return this.texts.get(key);
@@ -73,8 +74,10 @@ public class TestAction extends ActionSupport {
         return super.getText(key);
     }
 
-    /** This is the method invoked by the {@link org.apache.struts2.util.TextProviderHelper}.
-     * Returns the test value if defined otherwise delegates to the default TextProvider */
+    /**
+     * This is the method invoked by the {@link org.apache.struts2.util.TextProviderHelper}.
+     * Returns the test value if defined otherwise delegates to the default TextProvider
+     */
     public String getText(String key, String defaultValue, List<?> args, ValueStack stack) {
         if (this.texts.containsKey(key)) {
             return this.texts.get(key);
@@ -180,20 +183,21 @@ public class TestAction extends ActionSupport {
     }
 
     @Validations(
-            requiredFields = {
-            		@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
-            },
-            requiredStrings = {
-            		@RequiredStringValidator(type = ValidatorType.SIMPLE, fieldName = "result", message = "You must enter a value for field.")
-            }
+        requiredFields = {
+            @RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
+        },
+        requiredStrings = {
+            @RequiredStringValidator(type = ValidatorType.SIMPLE, fieldName = "result", message = "You must enter a value for field.")
+        }
     )
     public String annotatedExecute1() throws Exception {
         return Action.SUCCESS;
     }
+
     @Validations(
-            requiredFields = {
-            		@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
-            }
+        requiredFields = {
+            @RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "status", message = "You must enter a value for field.")
+        }
     )
     public String annotatedExecute2() throws Exception {
         return Action.SUCCESS;
@@ -207,16 +211,16 @@ public class TestAction extends ActionSupport {
         return INPUT;
     }
 
-	public SomeEnum getStatus() {
-		return status;
-	}
+    public SomeEnum getStatus() {
+        return status;
+    }
 
-	public void setStatus(SomeEnum status) {
-		this.status = status;
-	}
+    public void setStatus(SomeEnum status) {
+        this.status = status;
+    }
 
     public List<SomeEnum> getStatusList() {
-    	return Arrays.asList(SomeEnum.values());
+        return Arrays.asList(SomeEnum.values());
     }
 
     public Float getFloatNumber() {
@@ -235,4 +239,19 @@ public class TestAction extends ActionSupport {
         this.id = id;
     }
 
+    public List<SomeEnum> getEnumList() {
+        return enumList;
+    }
+
+    public void setEnumList(List<SomeEnum> enumList) {
+        this.enumList = enumList;
+    }
+
+    public List<Integer> getIntList() {
+        return intList;
+    }
+
+    public void setIntList(List<Integer> intList) {
+        this.intList = intList;
+    }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
index c22f90404..42e6f7e92 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/AbstractTagTest.java
@@ -18,36 +18,31 @@
  */
 package org.apache.struts2.views.jsp;
 
-import java.io.File;
-import java.io.StringWriter;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.jsp.JspWriter;
-
+import com.mockobjects.dynamic.Mock;
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.util.ValueStack;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
 import org.apache.struts2.TestAction;
 import org.apache.struts2.dispatcher.ApplicationMap;
 import org.apache.struts2.dispatcher.Dispatcher;
-import org.apache.struts2.dispatcher.MockDispatcher;
 import org.apache.struts2.dispatcher.HttpParameters;
+import org.apache.struts2.dispatcher.MockDispatcher;
 import org.apache.struts2.dispatcher.RequestMap;
 import org.apache.struts2.dispatcher.SessionMap;
 
-import com.mockobjects.dynamic.Mock;
-import com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.util.ValueStack;
-
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspWriter;
+import java.io.File;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Base class to extend for unit testing UI Tags.
- *
  */
 public abstract class AbstractTagTest extends StrutsInternalTestCase {
     protected Action action;
@@ -62,7 +57,7 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
     protected StrutsMockHttpServletRequest request;
     protected StrutsMockPageContext pageContext;
     protected HttpServletResponse response;
-    
+
     protected Mock mockContainer;
 
     /**
@@ -114,11 +109,11 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
         Dispatcher.setInstance(du);
         session = new SessionMap<>(request);
         Map<String, Object> extraContext = du.createContextMap(new RequestMap(request),
-                HttpParameters.create(request.getParameterMap()).build(),
-                session,
-                new ApplicationMap(pageContext.getServletContext()),
-                request,
-                response);
+            HttpParameters.create(request.getParameterMap()).build(),
+            session,
+            new ApplicationMap(pageContext.getServletContext()),
+            request,
+            response);
         // let's not set the locale -- there is a test that checks if Dispatcher actually picks this up...
         // ... but generally we want to just use no locale (let it stay system default)
         extraContext = ActionContext.of(extraContext).withLocale(null).getContextMap();
@@ -149,18 +144,18 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
     }
 
     /**
-     * Compare if two component tags are considered equal according to their fields as accessed 
+     * Compare if two component tags are considered equal according to their fields as accessed
      * via reflection.
-     * 
-     * Utilizes {@link EqualsBuilder#reflectionEquals(java.lang.Object, java.lang.Object, boolean)} to perform 
+     * <p>
+     * Utilizes {@link EqualsBuilder#reflectionEquals(java.lang.Object, java.lang.Object, boolean)} to perform
      * the check, and compares transient fields as well.  This may fail when run while a security manager is
      * active, due to a need to user reflection.
-     * 
+     * <p>
      * This method may be useful for checking if the state of a tag is what is expected after a given set of operations,
      * or after clearing state such as for calls involving {@link StrutsBodyTagSupport#clearTagStateForTagPoolingServers()}
      * has taken place following {@link StrutsBodyTagSupport#doEndTag()} processing.  When making comparisons, keep in mind the
      * pageContext and parent Tag state are not cleared by clearTagStateForTagPoolingServers().
-     * 
+     *
      * @param tag1 the first {@link StrutsBodyTagSupport} to compare against the other.
      * @param tag2 the second {@link StrutsBodyTagSupport} to compare against the other.
      * @return true if the Tags are equal based on field comparisons by reflection, false otherwise.
@@ -170,15 +165,16 @@ public abstract class AbstractTagTest extends StrutsInternalTestCase {
     }
 
     /**
-     * Helper method to simplify setting the performClearTagStateForTagPoolingServers state for a 
-     * {@link ComponentTagSupport} tag's {@link Component} to match expectations for the test.
-     * 
+     * Helper method to simplify setting the performClearTagStateForTagPoolingServers state for a
+     * {@link ComponentTagSupport} tag's {@link import org.apache.struts2.components.Component} to match expectations
+     * for the test.
+     * <p>
      * The component reference is not available to the tag until after the doStartTag() method is called.
-     * We need to ensure the component's {@link Component#performClearTagStateForTagPoolingServers} state matches
-     * what we set for the Tag when a non-default (true) value is used, so this method accesses the component instance,
-     * sets the value specified and forces the tag's parameters to be repopulated again.
-     * 
-     * @param tag The ComponentTagSupport tag upon whose component we will set the performClearTagStateForTagPoolingServers state.
+     * We need to ensure the component's {@link import org.apache.struts2.components.Component#performClearTagStateForTagPoolingServers}
+     * state matches what we set for the Tag when a non-default (true) value is used, so this method accesses
+     * the component instance, sets the value specified and forces the tag's parameters to be repopulated again.
+     *
+     * @param tag                                      The ComponentTagSupport tag upon whose component we will set the performClearTagStateForTagPoolingServers state.
      * @param performClearTagStateForTagPoolingServers true to clear tag state, false otherwise
      */
     protected void setComponentTagClearTagState(ComponentTagSupport tag, boolean performClearTagStateForTagPoolingServers) {
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
index 8a8cbaa0f..72e4cbf06 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/RadioTest.java
@@ -18,77 +18,82 @@
  */
 package org.apache.struts2.views.jsp.ui;
 
+import org.apache.struts2.SomeEnum;
 import org.apache.struts2.TestAction;
 import org.apache.struts2.views.jsp.AbstractUITagTest;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
 
 /**
+ *
  */
 public class RadioTest extends AbstractUITagTest {
-	
-	public void testMapWithBooleanAsKey() throws Exception {
-		TestAction testAction = (TestAction) action;
-		
-		HashMap map = new LinkedHashMap();
-		map.put(Boolean.TRUE, "male");
-		map.put(Boolean.FALSE, "female");
-		testAction.setMap(map);
-		
-		RadioTag tag = new RadioTag();
-		tag.setPageContext(pageContext);
-		tag.setLabel("mylabel");
-		tag.setName("myname");
-		tag.setValue("%{true}");
-		tag.setList("map");
-		
-		tag.doStartTag();
-		tag.doEndTag();
-		
-		verify(RadioTag.class.getResource("Radio-3.txt"));
-
-                // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
-                RadioTag freshTag = new RadioTag();
-                freshTag.setPageContext(pageContext);
-                assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
-                        "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                        strutsBodyTagsAreReflectionEqual(tag, freshTag));
-	}
-
-	public void testMapWithBooleanAsKey_clearTagStateSet() throws Exception {
-		TestAction testAction = (TestAction) action;
-
-		HashMap map = new LinkedHashMap();
-		map.put(Boolean.TRUE, "male");
-		map.put(Boolean.FALSE, "female");
-		testAction.setMap(map);
-
-		RadioTag tag = new RadioTag();
-                tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
-		tag.setPageContext(pageContext);
-		tag.setLabel("mylabel");
-		tag.setName("myname");
-		tag.setValue("%{true}");
-		tag.setList("map");
-
-		tag.doStartTag();
-                setComponentTagClearTagState(tag, true);  // Ensure component tag state clearing is set true (to match tag).
-		tag.doEndTag();
-
-		verify(RadioTag.class.getResource("Radio-3.txt"));
-
-                // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
-                RadioTag freshTag = new RadioTag();
-                freshTag.setPerformClearTagStateForTagPoolingServers(true);
-                freshTag.setPageContext(pageContext);
-                assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
-                        "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                        strutsBodyTagsAreReflectionEqual(tag, freshTag));
-	}
+
+    public void testMapWithBooleanAsKey() throws Exception {
+        TestAction testAction = (TestAction) action;
+
+        Map<Boolean, String> map = new LinkedHashMap<>();
+        map.put(Boolean.TRUE, "male");
+        map.put(Boolean.FALSE, "female");
+        testAction.setMap(map);
+
+        RadioTag tag = new RadioTag();
+        tag.setPageContext(pageContext);
+        tag.setLabel("mylabel");
+        tag.setName("myname");
+        tag.setValue("%{true}");
+        tag.setList("map");
+
+        tag.doStartTag();
+        tag.doEndTag();
+
+        verify(RadioTag.class.getResource("Radio-3.txt"));
+
+        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+        RadioTag freshTag = new RadioTag();
+        freshTag.setPageContext(pageContext);
+        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
+                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
+    }
+
+    public void testMapWithBooleanAsKey_clearTagStateSet() throws Exception {
+        TestAction testAction = (TestAction) action;
+
+        Map<Boolean, String> map = new LinkedHashMap<>();
+        map.put(Boolean.TRUE, "male");
+        map.put(Boolean.FALSE, "female");
+        testAction.setMap(map);
+
+        RadioTag tag = new RadioTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
+        tag.setPageContext(pageContext);
+        tag.setLabel("mylabel");
+        tag.setName("myname");
+        tag.setValue("%{true}");
+        tag.setList("map");
+
+        tag.doStartTag();
+        setComponentTagClearTagState(tag, true);  // Ensure component tag state clearing is set true (to match tag).
+        tag.doEndTag();
+
+        verify(RadioTag.class.getResource("Radio-3.txt"));
+
+        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+        RadioTag freshTag = new RadioTag();
+        freshTag.setPerformClearTagStateForTagPoolingServers(true);
+        freshTag.setPageContext(pageContext);
+        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
+                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
+    }
 
     public void testMapChecked() throws Exception {
         TestAction testAction = (TestAction) action;
@@ -103,7 +108,7 @@ public class RadioTest extends AbstractUITagTest {
         tag.setPageContext(pageContext);
         tag.setLabel("mylabel");
         tag.setName("myname");
-        tag.setValue("\"1\"");
+        tag.setValue("1");
         tag.setList("map");
         tag.setListKey("key");
         tag.setListValue("value");
@@ -118,7 +123,7 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testMapChecked_clearTagStateSet() throws Exception {
@@ -135,7 +140,7 @@ public class RadioTest extends AbstractUITagTest {
         tag.setPageContext(pageContext);
         tag.setLabel("mylabel");
         tag.setName("myname");
-        tag.setValue("\"1\"");
+        tag.setValue("1");
         tag.setList("map");
         tag.setListKey("key");
         tag.setListValue("value");
@@ -152,14 +157,122 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
+    }
+
+    public void testMapCheckedUsingEnum() throws Exception {
+        TestAction testAction = (TestAction) action;
+
+        List<SomeEnum> enumList = new ArrayList<>(Arrays.asList(SomeEnum.values()));
+        testAction.setEnumList(enumList);
+
+        RadioTag tag = new RadioTag();
+        tag.setTheme("simple");
+        tag.setPageContext(pageContext);
+        tag.setName("status");
+        tag.setValue("INIT");
+        tag.setList("enumList");
+
+        tag.doStartTag();
+        tag.doEndTag();
+
+        verify(RadioTag.class.getResource("Radio-9.txt"));
+
+        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+        RadioTag freshTag = new RadioTag();
+        freshTag.setPageContext(pageContext);
+        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
+                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
+    }
+
+    public void testMapCheckedUsingEnum_clearTagStateSet() throws Exception {
+        TestAction testAction = (TestAction) action;
+
+        List<SomeEnum> enumList = new ArrayList<>(Arrays.asList(SomeEnum.values()));
+        testAction.setEnumList(enumList);
+
+        RadioTag tag = new RadioTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
+        tag.setTheme("simple");
+        tag.setPageContext(pageContext);
+        tag.setName("status");
+        tag.setValue("INIT");
+        tag.setList("enumList");
+
+        tag.doStartTag();
+        tag.doEndTag();
+
+        verify(RadioTag.class.getResource("Radio-9.txt"));
+
+        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+        RadioTag freshTag = new RadioTag();
+        freshTag.setPerformClearTagStateForTagPoolingServers(true);
+        freshTag.setPageContext(pageContext);
+        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
+                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
+    }
+
+    public void testMapCheckedUsingInteger() throws Exception {
+        TestAction testAction = (TestAction) action;
+
+        List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2));
+        testAction.setIntList(intList);
+
+        RadioTag tag = new RadioTag();
+        tag.setTheme("simple");
+        tag.setPageContext(pageContext);
+        tag.setName("status");
+        tag.setValue("2");
+        tag.setList("intList");
+
+        tag.doStartTag();
+        tag.doEndTag();
+
+        verify(RadioTag.class.getResource("Radio-10.txt"));
+
+        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+        RadioTag freshTag = new RadioTag();
+        freshTag.setPageContext(pageContext);
+        assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
+                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
+    }
+
+    public void testMapCheckedUsingInt_clearTagStateSet() throws Exception {
+        TestAction testAction = (TestAction) action;
+
+        List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2));
+        testAction.setIntList(intList);
+
+        RadioTag tag = new RadioTag();
+        tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
+        tag.setTheme("simple");
+        tag.setPageContext(pageContext);
+        tag.setName("status");
+        tag.setValue("2");
+        tag.setList("intList");
+
+        tag.doStartTag();
+        tag.doEndTag();
+
+        verify(RadioTag.class.getResource("Radio-10.txt"));
+
+        // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+        RadioTag freshTag = new RadioTag();
+        freshTag.setPerformClearTagStateForTagPoolingServers(true);
+        freshTag.setPageContext(pageContext);
+        assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
+                "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testMapCheckedNull() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
 
-        HashMap map = new HashMap();
+        Map<String, String> map = new HashMap<>();
         map.put("1", "One");
         map.put("2", "Two");
         testAction.setMap(map);
@@ -181,14 +294,14 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testMapCheckedNull_clearTagStateSet() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
 
-        HashMap map = new HashMap();
+        Map<String, String> map = new HashMap<>();
         map.put("1", "One");
         map.put("2", "Two");
         testAction.setMap(map);
@@ -213,15 +326,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testSimple() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
 
         RadioTag tag = new RadioTag();
@@ -243,15 +356,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testSimple_clearTagStateSet() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
 
         RadioTag tag = new RadioTag();
@@ -276,13 +389,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testSimpleWithStringMap() throws Exception {
-        final Map<String, String> myMap = new TreeMap<String, String>();
+        final Map<String, String> myMap = new TreeMap<>();
         myMap.put("name", "Std.");
-        stack.push(new HashMap() {{ put ("myMap", myMap); }});
+        stack.push(new HashMap<String, Map<String, String>>() {{
+            put("myMap", myMap);
+        }});
 
         RadioTag tag = new RadioTag();
         tag.setPageContext(pageContext);
@@ -298,13 +413,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testSimpleWithStringMap_clearTagStateSet() throws Exception {
-        final Map<String, String> myMap = new TreeMap<String, String>();
+        final Map<String, String> myMap = new TreeMap<>();
         myMap.put("name", "Std.");
-        stack.push(new HashMap() {{ put ("myMap", myMap); }});
+        stack.push(new HashMap<String, Map<String, String>>() {{
+            put("myMap", myMap);
+        }});
 
         RadioTag tag = new RadioTag();
         tag.setPerformClearTagStateForTagPoolingServers(true);  // Explicitly request tag state clearing.
@@ -323,15 +440,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testSimpleWithLabelSeparator() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
 
         RadioTag tag = new RadioTag();
@@ -354,15 +471,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testSimpleWithLabelSeparator_clearTagStateSet() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
 
         RadioTag tag = new RadioTag();
@@ -388,27 +505,27 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testGenericSimple() throws Exception {
         RadioTag tag = new RadioTag();
         prepareTagGeneric(tag);
-        verifyGenericProperties(tag, "simple", new String[]{"id","value"});
+        verifyGenericProperties(tag, "simple", new String[]{"id", "value"});
     }
 
     public void testGenericXhtml() throws Exception {
         RadioTag tag = new RadioTag();
         prepareTagGeneric(tag);
-        verifyGenericProperties(tag, "xhtml", new String[]{"id","value"});
+        verifyGenericProperties(tag, "xhtml", new String[]{"id", "value"});
     }
 
     public void testDynamicAttributes() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
 
         RadioTag tag = new RadioTag();
@@ -431,15 +548,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testDynamicAttributes_clearTagStateSet() throws Exception {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
 
         RadioTag tag = new RadioTag();
@@ -465,7 +582,7 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testNotExistingListValueKey() throws Exception {
@@ -487,7 +604,7 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     public void testNotExistingListValueKey_clearTagStateSet() throws Exception {
@@ -512,15 +629,15 @@ public class RadioTest extends AbstractUITagTest {
         freshTag.setPageContext(pageContext);
         assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set.  " +
                 "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
-                strutsBodyTagsAreReflectionEqual(tag, freshTag));
+            strutsBodyTagsAreReflectionEqual(tag, freshTag));
     }
 
     private void prepareTagGeneric(RadioTag tag) {
         TestAction testAction = (TestAction) action;
         testAction.setFoo("bar");
         testAction.setList(new String[][]{
-                {"hello", "world"},
-                {"foo", "bar"}
+            {"hello", "world"},
+            {"foo", "bar"}
         });
         tag.setList("list");
         tag.setListKey("top[0]");
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-10.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-10.txt
new file mode 100644
index 000000000..c4292bb8c
--- /dev/null
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-10.txt
@@ -0,0 +1,4 @@
+<input type="radio" name="status" id="status1" value="1"/>
+<label for="status1">1</label>
+<input type="radio" name="status" id="status2" checked="checked" value="2"/>
+<label for="status2">2</label>
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-9.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-9.txt
new file mode 100644
index 000000000..14c14b388
--- /dev/null
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-9.txt
@@ -0,0 +1,4 @@
+<input type="radio" name="status" id="statusINIT" checked="checked" value="INIT"/>
+<label for="statusINIT">INIT</label>
+<input type="radio" name="status" id="statusCOMPLETED" value="COMPLETED"/>
+<label for="statusCOMPLETED">COMPLETED</label>