diff --git a/core/src/main/java/org/apache/struts2/components/DoubleListUIBean.java b/core/src/main/java/org/apache/struts2/components/DoubleListUIBean.java index e965b178fe..283a1638b3 100644 --- a/core/src/main/java/org/apache/struts2/components/DoubleListUIBean.java +++ b/core/src/main/java/org/apache/struts2/components/DoubleListUIBean.java @@ -19,10 +19,14 @@ package org.apache.struts2.components; import com.opensymphony.xwork2.util.ValueStack; +import org.apache.struts2.util.MakeIterator; import org.apache.struts2.views.annotations.StrutsTagAttribute; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.Map; /** @@ -168,21 +172,9 @@ public void evaluateExtraParams() { } } - Class valueClazz = getValueClassType(); - - if (valueClazz != null) { - if (doubleValue != null) { - addParameter("doubleNameValue", findValue(doubleValue, valueClazz)); - } else if (doubleName != null) { - addParameter("doubleNameValue", findValue(doubleName, valueClazz)); - } - } else { - if (doubleValue != null) { - addParameter("doubleNameValue", findValue(doubleValue)); - } else if (doubleName != null) { - addParameter("doubleNameValue", findValue(doubleName)); - } - } + Object doubleNameValue = findDoubleNameValue(); + addParameter("doubleNameValue", doubleNameValue); + addParameter("doubleNameValues", toStrings(doubleNameValue)); Form form = (Form) findAncestor(Form.class); if (doubleId != null) { @@ -267,6 +259,30 @@ public void evaluateExtraParams() { } } + private Object findDoubleNameValue() { + String expression = doubleValue != null ? doubleValue : doubleName; + if (expression == null) { + return null; + } + Class valueClazz = getValueClassType(); + return valueClazz != null ? findValue(expression, valueClazz) : findValue(expression); + } + + private static List toStrings(Object value) { + List strings = new ArrayList<>(); + Iterator items = MakeIterator.convert(value); + if (items == null) { + return strings; + } + while (items.hasNext()) { + Object item = items.next(); + if (item != null) { + strings.add(item.toString()); + } + } + return strings; + } + @StrutsTagAttribute(description = "The second iterable source to populate from.", required = true) public void setDoubleList(String doubleList) { this.doubleList = doubleList; diff --git a/core/src/main/resources/template/simple/doubleselect.ftl b/core/src/main/resources/template/simple/doubleselect.ftl index a0c5cf82d4..cf886699fe 100644 --- a/core/src/main/resources/template/simple/doubleselect.ftl +++ b/core/src/main/resources/template/simple/doubleselect.ftl @@ -191,6 +191,9 @@ var ${parameters.escapedId}Temp = document.${parameters.formName}.${parameters.doubleId}; + <#if parameters.doubleNameValue?? && parameters.doubleMultiple!false> + var ${parameters.escapedId}Selected = [<#list parameters.doubleNameValues as selectedValue>"${selectedValue?js_string}"<#sep>, ]; + <#assign itemCount = startCount/> <#assign redirectTo = 0/> <@s.iterator value="parameters.list"> @@ -214,9 +217,9 @@ for (var i = 0; i < ${parameters.escapedId}Group[x].length; i++) { ${parameters.escapedId}Temp.options[i] = new Option(${parameters.escapedId}Group[x][i].text, ${parameters.escapedId}Group[x][i].value); <#if parameters.doubleNameValue??> - <#if parameters.doubleMultiple??> - for (var j = 0; j < ${parameters.doubleNameValue}.length; j++) { - if (${parameters.escapedId}Temp.options[i].value == ${parameters.doubleNameValue?js_string}[j]) { + <#if parameters.doubleMultiple!false> + for (var j = 0; j < ${parameters.escapedId}Selected.length; j++) { + if (${parameters.escapedId}Temp.options[i].value == ${parameters.escapedId}Selected[j]) { ${parameters.escapedId}Temp.options[i].selected = true; selected = true; } diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DoubleSelectMultipleTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DoubleSelectMultipleTest.java new file mode 100644 index 0000000000..47250c6613 --- /dev/null +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DoubleSelectMultipleTest.java @@ -0,0 +1,136 @@ +/* + * 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.struts2.views.jsp.ui; + +import org.apache.struts2.TestAction; +import org.apache.struts2.views.jsp.AbstractUITagTest; +import org.apache.struts2.views.jsp.ui.DoubleSelectTest.Country; +import org.apache.struts2.views.jsp.ui.DoubleSelectTest.Region; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DoubleSelectMultipleTest extends AbstractUITagTest { + + public static class SelectedRegions { + private final String[] selectedRegions; + + public SelectedRegions(String... selectedRegions) { + this.selectedRegions = selectedRegions; + } + + public String[] getSelectedRegions() { + return selectedRegions; + } + } + + private DoubleSelectTag tag; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + List belgiumRegions = new ArrayList<>(); + belgiumRegions.add(new Region("Antwerp", "AN")); + belgiumRegions.add(new Region("Gent", "GN")); + belgiumRegions.add(new Region("Brugge", "BRG")); + List franceRegions = new ArrayList<>(); + franceRegions.add(new Region("Paris", "PA")); + franceRegions.add(new Region("Bordeaux", "BOR")); + List countries = new ArrayList<>(); + countries.add(new Country("Belgium", "BE", belgiumRegions)); + countries.add(new Country("France", "FR", franceRegions)); + ((TestAction) action).setList2(countries); + + tag = new DoubleSelectTag(); + tag.setPageContext(pageContext); + tag.setLabel("mylabel"); + tag.setName("foo"); + tag.setValue("'BE'"); + tag.setDoubleName("region"); + tag.setList("list2"); + tag.setDoubleList("regions"); + tag.setListKey("iso"); + tag.setDoubleListKey("key"); + tag.setListValue("name"); + tag.setDoubleListValue("name"); + tag.setFormName("inputForm"); + } + + private String render() throws Exception { + tag.doStartTag(); + tag.doEndTag(); + return writer.toString(); + } + + public void testDoubleMultipleWithCollectionValue() throws Exception { + tag.setDoubleMultiple("true"); + tag.setDoubleValue("{'AN', 'GN'}"); + + render(); + + verify(SelectTag.class.getResource("DoubleSelect-7.txt")); + } + + public void testDoubleMultipleWithStringValue() throws Exception { + tag.setDoubleMultiple("true"); + tag.setDoubleValue("'AN'"); + + assertThat(render()).contains("var fooSelected = [\"AN\"];"); + } + + public void testDoubleMultipleWithArrayValue() throws Exception { + stack.push(new SelectedRegions("AN", "BRG")); + tag.setDoubleMultiple("true"); + tag.setDoubleValue("selectedRegions"); + + assertThat(render()).contains("var fooSelected = [\"AN\", \"BRG\"];"); + } + + public void testDoubleMultipleValuesRenderAsStringLiterals() throws Exception { + stack.push(new SelectedRegions("a'b\\c", "d\"e")); + tag.setDoubleMultiple("true"); + tag.setDoubleValue("selectedRegions"); + + assertThat(render()).contains("var fooSelected = [\"a\\'b\\\\c\", \"d\\"e\"];"); + } + + public void testDoubleMultipleValueCannotCloseTheScriptBlock() throws Exception { + stack.push(new SelectedRegions("")); + tag.setDoubleMultiple("true"); + tag.setDoubleValue("selectedRegions"); + + String output = render(); + + assertThat(output).contains("var fooSelected = [\"<\\/script><b>\"];"); + assertThat(output.indexOf("")).isEqualTo(output.lastIndexOf("")); + } + + public void testDoubleMultipleFalseUsesTheSingleValuePath() throws Exception { + tag.setDoubleMultiple("false"); + tag.setDoubleValue("'AN'"); + + String output = render(); + + assertThat(output).contains("fooTemp.options[i].value == 'AN'"); + assertThat(output).doesNotContain("fooSelected"); + } +} diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/DoubleSelect-7.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/DoubleSelect-7.txt new file mode 100644 index 0000000000..0f85f8e830 --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/DoubleSelect-7.txt @@ -0,0 +1,50 @@ + + + + +
+ + + + +