1 /*
2 * $Id: OptionsCollectionTag.java 56513 2004-11-03 19:20:47Z niallp $
3 *
4 * Copyright 2002-2004 The Apache Software Foundation.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.apache.struts.taglib.html;
20
21 import java.lang.reflect.InvocationTargetException;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Enumeration;
25 import java.util.Iterator;
26 import java.util.Map;
27
28 import javax.servlet.jsp.JspException;
29 import javax.servlet.jsp.tagext.TagSupport;
30
31 import org.apache.commons.beanutils.PropertyUtils;
32 import org.apache.struts.util.IteratorAdapter;
33 import org.apache.struts.taglib.TagUtils;
34 import org.apache.struts.util.MessageResources;
35
36 /**
37 * Tag for creating multiple <select> options from a collection. The
38 * collection may be part of the enclosing form, or may be independent of
39 * the form. Each element of the collection must expose a 'label' and a
40 * 'value', the property names of which are configurable by attributes of
41 * this tag.
42 * <p>
43 * The collection may be an array of objects, a Collection, an Enumeration,
44 * an Iterator, or a Map.
45 * <p>
46 * <b>NOTE</b> - This tag requires a Java2 (JDK 1.2 or later) platform.
47 *
48 * @version $Rev: 56513 $ $Date: 2004-11-03 19:20:47 +0000 (Wed, 03 Nov 2004) $
49 * @since Struts 1.1
50 */
51 public class OptionsCollectionTag extends TagSupport {
52
53 // ----------------------------------------------------- Instance Variables
54
55 /**
56 * The message resources for this package.
57 */
58 protected static MessageResources messages =
59 MessageResources.getMessageResources(Constants.Package + ".LocalStrings");
60
61 // ------------------------------------------------------------- Properties
62
63 /**
64 * Should the label values be filtered for HTML sensitive characters?
65 */
66 protected boolean filter = true;
67
68 public boolean getFilter() {
69 return filter;
70 }
71
72 public void setFilter(boolean filter) {
73 this.filter = filter;
74 }
75
76 /**
77 * The name of the bean property containing the label.
78 */
79 protected String label = "label";
80
81 public String getLabel() {
82 return label;
83 }
84
85 public void setLabel(String label) {
86 this.label = label;
87 }
88
89 /**
90 * The name of the bean containing the values collection.
91 */
92 protected String name = Constants.BEAN_KEY;
93
94 public String getName() {
95 return name;
96 }
97
98 public void setName(String name) {
99 this.name = name;
100 }
101
102 /**
103 * The name of the property to use to build the values collection.
104 */
105 protected String property = null;
106
107 public String getProperty() {
108 return property;
109 }
110
111 public void setProperty(String property) {
112 this.property = property;
113 }
114
115 /**
116 * The style associated with this tag.
117 */
118 private String style = null;
119
120 public String getStyle() {
121 return style;
122 }
123
124 public void setStyle(String style) {
125 this.style = style;
126 }
127
128 /**
129 * The named style class associated with this tag.
130 */
131 private String styleClass = null;
132
133 public String getStyleClass() {
134 return styleClass;
135 }
136
137 public void setStyleClass(String styleClass) {
138 this.styleClass = styleClass;
139 }
140
141 /**
142 * The name of the bean property containing the value.
143 */
144 protected String value = "value";
145
146 public String getValue() {
147 return value;
148 }
149
150 public void setValue(String value) {
151 this.value = value;
152 }
153
154 // --------------------------------------------------------- Public Methods
155
156 /**
157 * Process the start of this tag.
158 *
159 * @exception JspException if a JSP exception has occurred
160 */
161 public int doStartTag() throws JspException {
162
163 // Acquire the select tag we are associated with
164 SelectTag selectTag = (SelectTag) pageContext.getAttribute(Constants.SELECT_KEY);
165
166 if (selectTag == null) {
167 JspException e = new JspException(messages.getMessage("optionsCollectionTag.select"));
168 TagUtils.getInstance().saveException(pageContext, e);
169 throw e;
170 }
171
172 // Acquire the collection containing our options
173 Object collection = TagUtils.getInstance().lookup(pageContext, name, property, null);
174
175 if (collection == null) {
176 JspException e =
177 new JspException(messages.getMessage("optionsCollectionTag.collection"));
178 TagUtils.getInstance().saveException(pageContext, e);
179 throw e;
180 }
181
182 // Acquire an iterator over the options collection
183 Iterator iter = getIterator(collection);
184
185 StringBuffer sb = new StringBuffer();
186
187 // Render the options
188 while (iter.hasNext()) {
189
190 Object bean = iter.next();
191 Object beanLabel = null;
192 Object beanValue = null;
193
194 // Get the label for this option
195 try {
196 beanLabel = PropertyUtils.getProperty(bean, label);
197 if (beanLabel == null) {
198 beanLabel = "";
199 }
200 } catch (IllegalAccessException e) {
201 JspException jspe =
202 new JspException(messages.getMessage("getter.access", label, bean));
203 TagUtils.getInstance().saveException(pageContext, jspe);
204 throw jspe;
205 } catch (InvocationTargetException e) {
206 Throwable t = e.getTargetException();
207 JspException jspe =
208 new JspException(messages.getMessage("getter.result", label, t.toString()));
209 TagUtils.getInstance().saveException(pageContext, jspe);
210 throw jspe;
211 } catch (NoSuchMethodException e) {
212 JspException jspe =
213 new JspException(messages.getMessage("getter.method", label, bean));
214 TagUtils.getInstance().saveException(pageContext, jspe);
215 throw jspe;
216 }
217
218 // Get the value for this option
219 try {
220 beanValue = PropertyUtils.getProperty(bean, value);
221 if (beanValue == null) {
222 beanValue = "";
223 }
224 } catch (IllegalAccessException e) {
225 JspException jspe =
226 new JspException(messages.getMessage("getter.access", value, bean));
227 TagUtils.getInstance().saveException(pageContext, jspe);
228 throw jspe;
229 } catch (InvocationTargetException e) {
230 Throwable t = e.getTargetException();
231 JspException jspe =
232 new JspException(messages.getMessage("getter.result", value, t.toString()));
233 TagUtils.getInstance().saveException(pageContext, jspe);
234 throw jspe;
235 } catch (NoSuchMethodException e) {
236 JspException jspe =
237 new JspException(messages.getMessage("getter.method", value, bean));
238 TagUtils.getInstance().saveException(pageContext, jspe);
239 throw jspe;
240 }
241
242 String stringLabel = beanLabel.toString();
243 String stringValue = beanValue.toString();
244
245 // Render this option
246 addOption(sb, stringLabel, stringValue, selectTag.isMatched(stringValue));
247 }
248
249 TagUtils.getInstance().write(pageContext, sb.toString());
250
251 return SKIP_BODY;
252 }
253
254 /**
255 * Release any acquired resources.
256 */
257 public void release() {
258 super.release();
259 filter = true;
260 label = "label";
261 name = Constants.BEAN_KEY;
262 property = null;
263 style = null;
264 styleClass = null;
265 value = "value";
266 }
267
268 // ------------------------------------------------------ Protected Methods
269
270 /**
271 * Add an option element to the specified StringBuffer based on the
272 * specified parameters.
273 *<p>
274 * Note that this tag specifically does not support the
275 * <code>styleId</code> tag attribute, which causes the HTML
276 * <code>id</code> attribute to be emitted. This is because the HTML
277 * specification states that all "id" attributes in a document have to be
278 * unique. This tag will likely generate more than one <code>option</code>
279 * element element, but it cannot use the same <code>id</code> value. It's
280 * conceivable some sort of mechanism to supply an array of <code>id</code>
281 * values could be devised, but that doesn't seem to be worth the trouble.
282 *
283 * @param sb StringBuffer accumulating our results
284 * @param value Value to be returned to the server for this option
285 * @param label Value to be shown to the user for this option
286 * @param matched Should this value be marked as selected?
287 */
288 protected void addOption(StringBuffer sb, String label, String value, boolean matched) {
289
290 sb.append("<option value=\"");
291 if (filter) {
292 sb.append(TagUtils.getInstance().filter(value));
293 } else {
294 sb.append(value);
295 }
296 sb.append("\"");
297 if (matched) {
298 sb.append(" selected=\"selected\"");
299 }
300 if (style != null) {
301 sb.append(" style=\"");
302 sb.append(style);
303 sb.append("\"");
304 }
305 if (styleClass != null) {
306 sb.append(" class=\"");
307 sb.append(styleClass);
308 sb.append("\"");
309 }
310
311 sb.append(">");
312
313 if (filter) {
314 sb.append(TagUtils.getInstance().filter(label));
315 } else {
316 sb.append(label);
317 }
318
319 sb.append("</option>\r\n");
320
321 }
322
323 /**
324 * Return an iterator for the options collection.
325 *
326 * @param collection Collection to be iterated over
327 *
328 * @exception JspException if an error occurs
329 */
330 protected Iterator getIterator(Object collection) throws JspException {
331
332 if (collection.getClass().isArray()) {
333 collection = Arrays.asList((Object[]) collection);
334 }
335
336 if (collection instanceof Collection) {
337 return (((Collection) collection).iterator());
338
339 } else if (collection instanceof Iterator) {
340 return ((Iterator) collection);
341
342 } else if (collection instanceof Map) {
343 return (((Map) collection).entrySet().iterator());
344
345 } else if (collection instanceof Enumeration) {
346 return new IteratorAdapter((Enumeration) collection);
347
348 } else {
349 throw new JspException(
350 messages.getMessage("optionsCollectionTag.iterator", collection.toString()));
351 }
352 }
353
354 }