diff --git a/core/src/main/java/org/apache/struts2/components/ListUIBean.java b/core/src/main/java/org/apache/struts2/components/ListUIBean.java
index 6e24e822e..ed5094198 100644
--- a/core/src/main/java/org/apache/struts2/components/ListUIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/ListUIBean.java
@@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Array;
import java.util.Collection;
+import java.util.Collections;
import java.util.Map;
/**
@@ -70,28 +71,28 @@ public abstract class ListUIBean extends UIBean {
if (list instanceof String) {
value = findValue((String) list);
- } else if (list instanceof Collection) {
- value = list;
- } else if (MakeIterator.isIterable(list)) {
- value = MakeIterator.convert(list);
- }
- if (value == null) {
- if (throwExceptionOnNullValueAttribute) {
- // will throw an exception if not found
- value = findValue((list == null) ? (String) list : list.toString(), "list",
- "The requested list key '" + list + "' could not be resolved as a collection/array/map/enumeration/iterator type. " +
- "Example: people or people.{name}");
- } else {
- // ww-1010, allows value with null value to be compatible with ww
- // 2.1.7 behaviour
- value = findValue((list == null) ? (String) list : list.toString());
+ if (value == null) {
+ if (throwExceptionOnNullValueAttribute) {
+ // will throw an exception if not found
+ value = findValue((list == null) ? (String) list : list.toString(), "list",
+ "The requested list key '" + list + "' could not be resolved as a collection/array/map/enumeration/iterator type. " +
+ "Example: people or people.{name}");
+ } else {
+ // ww-1010, allows value with null value to be compatible with ww
+ // 2.1.7 behaviour
+ value = findValue((list == null) ? (String) list : list.toString());
+ }
}
+ } else {
+ value = list;
}
- if (value instanceof Collection) {
+ if (value == null || value instanceof Iterable) {
addParameter("list", value);
- } else {
+ } else if (MakeIterator.isIterable(value)) {
addParameter("list", MakeIterator.convert(value));
+ } else {
+ addParameter("list", Collections.singletonList(value));
}
if (value instanceof Collection) {
@@ -107,6 +108,8 @@ public abstract class ListUIBean extends UIBean {
addParameter("listKey", listKey);
} else if (value instanceof Map) {
addParameter("listKey", "key");
+ } else {
+ addParameter("listKey", "top");
}
if (listValueKey != null) {
@@ -119,6 +122,8 @@ public abstract class ListUIBean extends UIBean {
addParameter("listValue", listValue);
} else if (value instanceof Map) {
addParameter("listValue", "value");
+ } else {
+ addParameter("listValue", "top");
}
if (listLabelKey != null) {
diff --git a/core/src/main/java/org/apache/struts2/components/OptGroup.java b/core/src/main/java/org/apache/struts2/components/OptGroup.java
index 58d0b9a72..95d94d26e 100644
--- a/core/src/main/java/org/apache/struts2/components/OptGroup.java
+++ b/core/src/main/java/org/apache/struts2/components/OptGroup.java
@@ -127,7 +127,7 @@ public class OptGroup extends Component {
}
@StrutsTagAttribute(description="Set the list attribute.")
- public void setList(String list) {
+ public void setList(Object list) {
internalUiBean.setList(list);
}
diff --git a/core/src/main/resources/template/simple/optgroup.ftl b/core/src/main/resources/template/simple/optgroup.ftl
index f1f8a3e9e..765591cca 100644
--- a/core/src/main/resources/template/simple/optgroup.ftl
+++ b/core/src/main/resources/template/simple/optgroup.ftl
@@ -21,27 +21,26 @@
<#if parameters.optGroupInternalListUiBeanList??>
<#assign optGroupInternalListUiBeans=parameters.optGroupInternalListUiBeanList />
<#list optGroupInternalListUiBeans as optGroupInternalListUiBean>
-
diff --git a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
index 1067ddd16..d82a6606e 100644
--- a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
+++ b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
@@ -194,6 +194,55 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
assertEquals(expected, stringWriter.toString());
}
+ public void testSequenceForSelect() throws Exception {
+ File file = new File(FreeMarkerResultTest.class.getResource("select.ftl").toURI());
+ EasyMock.expect(servletContext.getRealPath("/tutorial/org/apache/struts2/views/freemarker/select.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/select.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/select.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/select.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/optgroup.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/optgroup.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/optgroup.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/css.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/css.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/css.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/scripting-events.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/scripting-events.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/scripting-events.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/common-attributes.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/common-attributes.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/common-attributes.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/dynamic-attributes.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/dynamic-attributes.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/dynamic-attributes.ftl")).andReturn(file.getAbsolutePath());
+
+ file = new File(ClassLoaderUtil.getResource("template/simple/empty.ftl", getClass()).toURI());
+ EasyMock.expect(servletContext.getRealPath("/template/simple/empty.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/~~~simple/empty.ftl")).andReturn(file.getAbsolutePath());
+ EasyMock.expect(servletContext.getRealPath("/template/xhtml/empty.ftl")).andReturn(file.getAbsolutePath());
+
+ EasyMock.replay(servletContext);
+
+ init();
+
+ request.setRequestURI("/tutorial/test9.action");
+ ActionMapping mapping = container.getInstance(ActionMapper.class).getMapping(request, configurationManager);
+ dispatcher.serviceAction(request, response, mapping);
+ String result = stringWriter.toString();
+ assertTrue(result.contains(""));
+ assertTrue(result.contains(""));
+ assertTrue(result.contains(""));
+ assertTrue(result.contains(""));
+ assertTrue(result.contains(""));
+ assertTrue(result.contains(""));
+ }
+
private void init() throws MalformedURLException, URISyntaxException {
stringWriter = new StringWriter();
writer = new PrintWriter(stringWriter);
diff --git a/core/src/test/resources/org/apache/struts2/views/freemarker/select.ftl b/core/src/test/resources/org/apache/struts2/views/freemarker/select.ftl
new file mode 100644
index 000000000..91b84db2f
--- /dev/null
+++ b/core/src/test/resources/org/apache/struts2/views/freemarker/select.ftl
@@ -0,0 +1,28 @@
+<#--
+/*
+ * 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.
+ */
+-->
+<@s.select theme="simple" list=['a','b','c'] />
+<@s.select theme="simple" list=1 />
+<@s.select theme="simple" list={'key':'value'} />
+<@s.select theme="simple">
+<@s.optgroup label="label1" list={'optgroupKey1':'optgroupValue1','optgroupKey2':'optgroupValue2'} />
+<@s.optgroup label="label2" disabled=true list=['optgroupKey3','optgroupKey4'] />
+<@s.optgroup label="label3" disabled=true list=2 />
+@s.select>
\ No newline at end of file
diff --git a/core/src/test/resources/struts.xml b/core/src/test/resources/struts.xml
index 5b29c1e87..0b370aa3e 100644
--- a/core/src/test/resources/struts.xml
+++ b/core/src/test/resources/struts.xml
@@ -80,6 +80,12 @@
+
+
+ org/apache/struts2/views/freemarker/select.ftl
+
+
+