View Javadoc

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