View Javadoc

1   /*
2    * $Id: OptionsTag.java 376841 2006-02-10 21:01:28Z husted $
3    *
4    * Copyright 1999-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  package org.apache.struts.taglib.html;
19  
20  import org.apache.commons.beanutils.PropertyUtils;
21  import org.apache.struts.taglib.TagUtils;
22  import org.apache.struts.util.IteratorAdapter;
23  import org.apache.struts.util.MessageResources;
24  
25  import javax.servlet.jsp.JspException;
26  import javax.servlet.jsp.tagext.TagSupport;
27  
28  import java.lang.reflect.InvocationTargetException;
29  
30  import java.util.Arrays;
31  import java.util.Collection;
32  import java.util.Enumeration;
33  import java.util.Iterator;
34  import java.util.Map;
35  
36  /***
37   * Tag for creating multiple <select> options from a collection.  The
38   * associated values displayed to the user may optionally be specified by a
39   * second collection, or will be the same as the values themselves.  Each
40   * collection may be an array of objects, a Collection, an Enumeration, an
41   * Iterator, or a Map. <b>NOTE</b> - This tag requires a Java2 (JDK 1.2 or
42   * later) platform.
43   */
44  public class OptionsTag extends TagSupport {
45      /***
46       * The message resources for this package.
47       */
48      protected static MessageResources messages =
49          MessageResources.getMessageResources(Constants.Package
50              + ".LocalStrings");
51  
52      /***
53       * The name of the collection containing beans that have properties to
54       * provide both the values and the labels (identified by the
55       * <code>property</code> and <code>labelProperty</code> attributes).
56       */
57      protected String collection = null;
58  
59      /***
60       * Should the label values be filtered for HTML sensitive characters?
61       */
62      protected boolean filter = true;
63  
64      /***
65       * The name of the bean containing the labels collection.
66       */
67      protected String labelName = null;
68  
69      /***
70       * The bean property containing the labels collection.
71       */
72      protected String labelProperty = null;
73  
74      /***
75       * The name of the bean containing the values collection.
76       */
77      protected String name = null;
78  
79      /***
80       * The name of the property to use to build the values collection.
81       */
82      protected String property = null;
83  
84      /***
85       * The style associated with this tag.
86       */
87      private String style = null;
88  
89      /***
90       * The named style class associated with this tag.
91       */
92      private String styleClass = null;
93  
94      public String getCollection() {
95          return (this.collection);
96      }
97  
98      public void setCollection(String collection) {
99          this.collection = collection;
100     }
101 
102     public boolean getFilter() {
103         return filter;
104     }
105 
106     public void setFilter(boolean filter) {
107         this.filter = filter;
108     }
109 
110     public String getLabelName() {
111         return labelName;
112     }
113 
114     public void setLabelName(String labelName) {
115         this.labelName = labelName;
116     }
117 
118     public String getLabelProperty() {
119         return labelProperty;
120     }
121 
122     public void setLabelProperty(String labelProperty) {
123         this.labelProperty = labelProperty;
124     }
125 
126     public String getName() {
127         return name;
128     }
129 
130     public void setName(String name) {
131         this.name = name;
132     }
133 
134     public String getProperty() {
135         return property;
136     }
137 
138     public void setProperty(String property) {
139         this.property = property;
140     }
141 
142     public String getStyle() {
143         return style;
144     }
145 
146     public void setStyle(String style) {
147         this.style = style;
148     }
149 
150     public String getStyleClass() {
151         return styleClass;
152     }
153 
154     public void setStyleClass(String styleClass) {
155         this.styleClass = styleClass;
156     }
157 
158     /***
159      * Process the start of this tag.
160      *
161      * @throws JspException if a JSP exception has occurred
162      */
163     public int doStartTag() throws JspException {
164         return SKIP_BODY;
165     }
166 
167     /***
168      * Process the end of this tag.
169      *
170      * @throws JspException if a JSP exception has occurred
171      */
172     public int doEndTag() throws JspException {
173         // Acquire the select tag we are associated with
174         SelectTag selectTag =
175             (SelectTag) pageContext.getAttribute(Constants.SELECT_KEY);
176 
177         if (selectTag == null) {
178             throw new JspException(messages.getMessage("optionsTag.select"));
179         }
180 
181         StringBuffer sb = new StringBuffer();
182 
183         // If a collection was specified, use that mode to render options
184         if (collection != null) {
185             Iterator collIterator = getIterator(collection, null);
186 
187             while (collIterator.hasNext()) {
188                 Object bean = collIterator.next();
189                 Object value = null;
190                 Object label = null;
191 
192                 try {
193                     value = PropertyUtils.getProperty(bean, property);
194 
195                     if (value == null) {
196                         value = "";
197                     }
198                 } catch (IllegalAccessException e) {
199                     throw new JspException(messages.getMessage(
200                             "getter.access", property, collection));
201                 } catch (InvocationTargetException e) {
202                     Throwable t = e.getTargetException();
203 
204                     throw new JspException(messages.getMessage(
205                             "getter.result", property, t.toString()));
206                 } catch (NoSuchMethodException e) {
207                     throw new JspException(messages.getMessage(
208                             "getter.method", property, collection));
209                 }
210 
211                 try {
212                     if (labelProperty != null) {
213                         label = PropertyUtils.getProperty(bean, labelProperty);
214                     } else {
215                         label = value;
216                     }
217 
218                     if (label == null) {
219                         label = "";
220                     }
221                 } catch (IllegalAccessException e) {
222                     throw new JspException(messages.getMessage(
223                             "getter.access", labelProperty, collection));
224                 } catch (InvocationTargetException e) {
225                     Throwable t = e.getTargetException();
226 
227                     throw new JspException(messages.getMessage(
228                             "getter.result", labelProperty, t.toString()));
229                 } catch (NoSuchMethodException e) {
230                     throw new JspException(messages.getMessage(
231                             "getter.method", labelProperty, collection));
232                 }
233 
234                 String stringValue = value.toString();
235 
236                 addOption(sb, stringValue, label.toString(),
237                     selectTag.isMatched(stringValue));
238             }
239         }
240         // Otherwise, use the separate iterators mode to render options
241         else {
242             // Construct iterators for the values and labels collections
243             Iterator valuesIterator = getIterator(name, property);
244             Iterator labelsIterator = null;
245 
246             if ((labelName != null) || (labelProperty != null)) {
247                 labelsIterator = getIterator(labelName, labelProperty);
248             }
249 
250             // Render the options tags for each element of the values coll.
251             while (valuesIterator.hasNext()) {
252                 Object valueObject = valuesIterator.next();
253 
254                 if (valueObject == null) {
255                     valueObject = "";
256                 }
257 
258                 String value = valueObject.toString();
259                 String label = value;
260 
261                 if ((labelsIterator != null) && labelsIterator.hasNext()) {
262                     Object labelObject = labelsIterator.next();
263 
264                     if (labelObject == null) {
265                         labelObject = "";
266                     }
267 
268                     label = labelObject.toString();
269                 }
270 
271                 addOption(sb, value, label, selectTag.isMatched(value));
272             }
273         }
274 
275         TagUtils.getInstance().write(pageContext, sb.toString());
276 
277         return EVAL_PAGE;
278     }
279 
280     /***
281      * Release any acquired resources.
282      */
283     public void release() {
284         super.release();
285         collection = null;
286         filter = true;
287         labelName = null;
288         labelProperty = null;
289         name = null;
290         property = null;
291         style = null;
292         styleClass = null;
293     }
294 
295     // ------------------------------------------------------ Protected Methods
296 
297     /***
298      * Add an option element to the specified StringBuffer based on the
299      * specified parameters. <p> Note that this tag specifically does not
300      * support the <code>styleId</code> tag attribute, which causes the HTML
301      * <code>id</code> attribute to be emitted.  This is because the HTML
302      * specification states that all "id" attributes in a document have to be
303      * unique.  This tag will likely generate more than one
304      * <code>option</code> element element, but it cannot use the same
305      * <code>id</code> value.  It's conceivable some sort of mechanism to
306      * supply an array of <code>id</code> values could be devised, but that
307      * doesn't seem to be worth the trouble.
308      *
309      * @param sb      StringBuffer accumulating our results
310      * @param value   Value to be returned to the server for this option
311      * @param label   Value to be shown to the user for this option
312      * @param matched Should this value be marked as selected?
313      */
314     protected void addOption(StringBuffer sb, String value, String label,
315         boolean matched) {
316         sb.append("<option value=\"");
317 
318         if (filter) {
319             sb.append(TagUtils.getInstance().filter(value));
320         } else {
321             sb.append(value);
322         }
323 
324         sb.append("\"");
325 
326         if (matched) {
327             sb.append(" selected=\"selected\"");
328         }
329 
330         if (style != null) {
331             sb.append(" style=\"");
332             sb.append(style);
333             sb.append("\"");
334         }
335 
336         if (styleClass != null) {
337             sb.append(" class=\"");
338             sb.append(styleClass);
339             sb.append("\"");
340         }
341 
342         sb.append(">");
343 
344         if (filter) {
345             sb.append(TagUtils.getInstance().filter(label));
346         } else {
347             sb.append(label);
348         }
349 
350         sb.append("</option>\r\n");
351     }
352 
353     /***
354      * Return an iterator for the option labels or values, based on our
355      * configured properties.
356      *
357      * @param name     Name of the bean attribute (if any)
358      * @param property Name of the bean property (if any)
359      * @throws JspException if an error occurs
360      */
361     protected Iterator getIterator(String name, String property)
362         throws JspException {
363         // Identify the bean containing our collection
364         String beanName = name;
365 
366         if (beanName == null) {
367             beanName = Constants.BEAN_KEY;
368         }
369 
370         Object bean =
371             TagUtils.getInstance().lookup(pageContext, beanName, null);
372 
373         if (bean == null) {
374             throw new JspException(messages.getMessage("getter.bean", beanName));
375         }
376 
377         // Identify the collection itself
378         Object collection = bean;
379 
380         if (property != null) {
381             try {
382                 collection = PropertyUtils.getProperty(bean, property);
383 
384                 if (collection == null) {
385                     throw new JspException(messages.getMessage(
386                             "getter.property", property));
387                 }
388             } catch (IllegalAccessException e) {
389                 throw new JspException(messages.getMessage("getter.access",
390                         property, name));
391             } catch (InvocationTargetException e) {
392                 Throwable t = e.getTargetException();
393 
394                 throw new JspException(messages.getMessage("getter.result",
395                         property, t.toString()));
396             } catch (NoSuchMethodException e) {
397                 throw new JspException(messages.getMessage("getter.method",
398                         property, name));
399             }
400         }
401 
402         // Construct and return an appropriate iterator
403         if (collection.getClass().isArray()) {
404             collection = Arrays.asList((Object[]) collection);
405         }
406 
407         if (collection instanceof Collection) {
408             return (((Collection) collection).iterator());
409         } else if (collection instanceof Iterator) {
410             return ((Iterator) collection);
411         } else if (collection instanceof Map) {
412             return (((Map) collection).entrySet().iterator());
413         } else if (collection instanceof Enumeration) {
414             return new IteratorAdapter((Enumeration) collection);
415         } else {
416             throw new JspException(messages.getMessage("optionsTag.iterator",
417                     collection.toString()));
418         }
419     }
420 }