KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > tags > form > SelectTag


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.servlet.tags.form;
18
19 import java.util.Collection JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import javax.servlet.jsp.JspException JavaDoc;
23
24 import org.springframework.util.Assert;
25 import org.springframework.util.ObjectUtils;
26 import org.springframework.web.servlet.support.BindStatus;
27
28 /**
29  * Databinding-aware JSP tag that renders an HTML '<code>select</code>'
30  * element.
31  *
32  * <p>Inner '<code>option</code>' tags can be rendered using one of the
33  * approaches supported by the OptionWriter class.
34  *
35  * <p>Also supports the use of nested {@link OptionTag OptionTags} or
36  * (typically one) nested {@link OptionsTag}.
37  *
38  * @author Rob Harrop
39  * @since 2.0
40  * @see OptionTag
41  */

42 public class SelectTag extends AbstractHtmlInputElementTag {
43
44     /**
45      * The {@link javax.servlet.jsp.PageContext} attribute under
46      * which the bound value is exposed to inner {@link OptionTag OptionTags}.
47      */

48     public static final String JavaDoc LIST_VALUE_PAGE_ATTRIBUTE =
49             "org.springframework.web.servlet.tags.form.SelectTag.listValue";
50
51
52     /**
53      * The {@link Collection}, {@link Map} or array of objects used to generate the inner
54      * '<code>option</code>' tags.
55      */

56     private Object JavaDoc items;
57
58     /**
59      * The name of the property mapped to the '<code>value</code>' attribute
60      * of the '<code>option</code>' tag.
61      */

62     private String JavaDoc itemValue;
63
64     /**
65      * The name of the property mapped to the inner text of the
66      * '<code>option</code>' tag.
67      */

68     private String JavaDoc itemLabel;
69
70     /**
71      * The value of the HTML '<code>size</code>' attribute rendered
72      * on the final '<code>select</code>' element.
73      */

74     private String JavaDoc size;
75
76     /**
77      * Indicates whether or not the '<code>select</code>' tag allows
78      * multiple-selections.
79      */

80     private Object JavaDoc multiple = Boolean.FALSE;
81
82     /**
83      * The {@link TagWriter} instance that the output is being written.
84      * <p>Only used in conjunction with nested {@link OptionTag OptionTags}.
85      */

86     private TagWriter tagWriter;
87
88
89     /**
90      * Set the {@link Collection}, {@link Map} or array of objects used to
91      * generate the inner '<code>option</code>' tags.
92      * <p>Required when wishing to render '<code>option</code>' tags from
93      * an array, {@link Collection} or {@link Map}.
94      * <p>Typically a runtime expression.
95      * @param items the items that comprise the options of this selection
96      */

97     public void setItems(Object JavaDoc items) {
98         Assert.notNull(items, "'items' must not be null");
99         this.items = items;
100     }
101
102     /**
103      * Get the value of the '<code>items</code>' attribute.
104      * <p>May be a runtime expression.
105      */

106     protected Object JavaDoc getItems() {
107         return this.items;
108     }
109
110     /**
111      * Set the name of the property mapped to the '<code>value</code>'
112      * attribute of the '<code>option</code>' tag.
113      * <p>Required when wishing to render '<code>option</code>' tags from
114      * an array or {@link Collection}.
115      * <p>May be a runtime expression.
116      */

117     public void setItemValue(String JavaDoc itemValue) {
118         Assert.hasText(itemValue, "'itemValue' must not be empty");
119         this.itemValue = itemValue;
120     }
121
122     /**
123      * Get the value of the '<code>itemValue</code>' attribute.
124      * <p>May be a runtime expression.
125      */

126     protected String JavaDoc getItemValue() {
127         return this.itemValue;
128     }
129
130     /**
131      * Set the name of the property mapped to the label (inner text) of the
132      * '<code>option</code>' tag.
133      * <p>May be a runtime expression.
134      */

135     public void setItemLabel(String JavaDoc itemLabel) {
136         Assert.hasText(itemLabel, "'itemLabel' must not be empty");
137         this.itemLabel = itemLabel;
138     }
139
140     /**
141      * Get the value of the '<code>itemLabel</code>' attribute.
142      * <p>May be a runtime expression.
143      */

144     protected String JavaDoc getItemLabel() {
145         return this.itemLabel;
146     }
147
148     /**
149      * Set the value of the HTML '<code>size</code>' attribute rendered
150      * on the final '<code>select</code>' element.
151      * <p>May be a runtime expression.
152      * @param size the desired value of the '<code>size</code>' attribute
153      */

154     public void setSize(String JavaDoc size) {
155         Assert.hasText(size, "'size' must not be empty");
156         this.size = size;
157     }
158
159     /**
160      * Get the value of the '<code>size</code>' attribute.
161      * <p>May be a runtime expression.
162      */

163     protected String JavaDoc getSize() {
164         return this.size;
165     }
166
167     /**
168      * Set the value of the HTML '<code>multiple</code>' attribute rendered
169      * on the final '<code>select</code>' element.
170      * <p>May be a runtime expression.
171      */

172     public void setMultiple(Object JavaDoc multiple) {
173         this.multiple = multiple;
174     }
175
176     /**
177      * Get the value of the HTML '<code>multiple</code>' attribute rendered
178      * on the final '<code>select</code>' element.
179      * <p>May be a runtime expression.
180      */

181     protected Object JavaDoc getMultiple() {
182         return this.multiple;
183     }
184
185
186     /**
187      * Renders the HTML '<code>select</code>' tag to the supplied
188      * {@link TagWriter}.
189      * <p>Renders nested '<code>option</code>' tags if the
190      * {@link #setItems items} property is set, otherwise exposes the
191      * bound value for the nested {@link OptionTag OptionTags}.
192      */

193     protected int writeTagContent(TagWriter tagWriter) throws JspException JavaDoc {
194         tagWriter.startTag("select");
195         writeDefaultAttributes(tagWriter);
196
197         if (isMultiple()) {
198             tagWriter.writeAttribute("multiple", "multiple");
199         }
200
201         tagWriter.writeOptionalAttributeValue("size", getDisplayString(evaluate("size", getSize())));
202
203         Object JavaDoc items = getItems();
204         if (items != null) {
205             Object JavaDoc itemsObject = (items instanceof String JavaDoc ? evaluate("items", (String JavaDoc) items) : items);
206
207             String JavaDoc valueProperty = (getItemValue() == null ? null :
208                             ObjectUtils.getDisplayString(evaluate("itemValue", getItemValue())));
209             String JavaDoc labelProperty = (getItemLabel() == null ? null :
210                             ObjectUtils.getDisplayString(evaluate("itemLabel", getItemLabel())));
211
212             OptionWriter optionWriter = new OptionWriter(itemsObject, getBindStatus(), valueProperty, labelProperty, isHtmlEscape());
213             optionWriter.writeOptions(tagWriter);
214
215             tagWriter.endTag();
216             writeHiddenTagIfNecessary(tagWriter);
217             return EVAL_PAGE;
218         }
219         else {
220             // using nested <form:option/> tags so just expose the value in the PageContext
221
tagWriter.forceBlock();
222             this.tagWriter = tagWriter;
223             this.pageContext.setAttribute(LIST_VALUE_PAGE_ATTRIBUTE, getBindStatus());
224             return EVAL_BODY_INCLUDE;
225         }
226     }
227
228     /**
229      * If using a multi select, a hidden element is needed to make sure all
230      * items are correctly unselected on the server-side in response to a
231      * <code>null</code> post.
232      */

233     private void writeHiddenTagIfNecessary(TagWriter tagWriter) throws JspException JavaDoc {
234         if (isMultiple()) {
235             tagWriter.startTag("input");
236             tagWriter.writeAttribute("type", "hidden");
237             tagWriter.writeAttribute("name", "_" + getName());
238             tagWriter.writeAttribute("value", "1");
239             tagWriter.endTag();
240         }
241     }
242
243     private boolean isMultiple() throws JspException JavaDoc {
244         Object JavaDoc multiple = getMultiple();
245         if (Boolean.TRUE.equals(multiple)
246                 || "true".equals(multiple)
247                 || "multiple".equals(multiple)) {
248             return true;
249         }
250         else if (this.multiple instanceof String JavaDoc) {
251             Object JavaDoc evaluatedValue = evaluate("multiple", multiple);
252             return Boolean.TRUE.equals(evaluatedValue);
253         }
254         return forceMultiple();
255     }
256
257     /**
258      * Returns '<code>true</code>' if the bound value requires the
259      * resultant '<code>select</code>' tag to be multi-select.
260      */

261     private boolean forceMultiple() throws JspException JavaDoc {
262         BindStatus bindStatus = getBindStatus();
263         Class JavaDoc valueType = bindStatus.getValueType();
264         if (valueType != null && typeRequiresMultiple(valueType)) {
265             return true;
266         }
267         else if (bindStatus.getEditor() != null) {
268             Object JavaDoc editorValue = bindStatus.getEditor().getValue();
269             if (editorValue != null && typeRequiresMultiple(editorValue.getClass())) {
270                 return true;
271             }
272         }
273         return false;
274     }
275
276     /**
277      * Returns '<code>true</code>' for arrays, {@link Collection Collections}
278      * and {@link Map Maps}.
279      */

280     private static boolean typeRequiresMultiple(Class JavaDoc type) {
281         return (type.isArray() || Collection JavaDoc.class.isAssignableFrom(type) || Map JavaDoc.class.isAssignableFrom(type));
282     }
283
284     /**
285      * Closes any block tag that might have been opened when using
286      * nested {@link OptionTag options}.
287      */

288     public int doEndTag() throws JspException JavaDoc {
289         if (this.tagWriter != null) {
290             this.tagWriter.endTag();
291             writeHiddenTagIfNecessary(tagWriter);
292         }
293         return EVAL_PAGE;
294     }
295
296     /**
297      * Clears the {@link TagWriter} that might have been left over when using
298      * nested {@link OptionTag options}.
299      */

300     public void doFinally() {
301         super.doFinally();
302         this.tagWriter = null;
303         this.pageContext.removeAttribute(LIST_VALUE_PAGE_ATTRIBUTE);
304     }
305
306 }
307
Popular Tags