View Javadoc

1   /*
2    * $Id: IterateTag.java 421129 2006-07-12 05:13:54Z wsmoak $
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.logic;
19  
20  import org.apache.struts.taglib.TagUtils;
21  import org.apache.struts.util.IteratorAdapter;
22  import org.apache.struts.util.MessageResources;
23  
24  import javax.servlet.jsp.JspException;
25  import javax.servlet.jsp.tagext.BodyTagSupport;
26  
27  import java.lang.reflect.Array;
28  
29  import java.util.ArrayList;
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   * Custom tag that iterates the elements of a collection, which can be either
38   * an attribute or the property of an attribute.  The collection can be any of
39   * the following:  an array of objects, an Enumeration, an Iterator, a
40   * Collection (which includes Lists, Sets and Vectors), or a Map (which
41   * includes Hashtables) whose elements will be iterated over.
42   *
43   * @version $Rev: 421129 $ $Date: 2004-11-03 14:20:47 -0500 (Wed, 03 Nov 2004)
44   *          $
45   */
46  public class IterateTag extends BodyTagSupport {
47      /***
48       * The message resources for this package.
49       */
50      protected static MessageResources messages =
51          MessageResources.getMessageResources(
52              "org.apache.struts.taglib.logic.LocalStrings");
53  
54      // ----------------------------------------------------- Instance Variables
55  
56      /***
57       * Iterator of the elements of this collection, while we are actually
58       * running.
59       */
60      protected Iterator iterator = null;
61  
62      /***
63       * The number of elements we have already rendered.
64       */
65      protected int lengthCount = 0;
66  
67      /***
68       * The actual length value (calculated in the start tag).
69       */
70      protected int lengthValue = 0;
71  
72      /***
73       * The actual offset value (calculated in the start tag).
74       */
75      protected int offsetValue = 0;
76  
77      /***
78       * Has this tag instance been started?
79       */
80      protected boolean started = false;
81  
82      // ------------------------------------------------------------- Properties
83  
84      /***
85       * The collection over which we will be iterating.
86       */
87      protected Object collection = null;
88  
89      /***
90       * The name of the scripting variable to be exposed.
91       */
92      protected String id = null;
93  
94      /***
95       * The name of the scripting variable to be exposed as the current index.
96       */
97      protected String indexId = null;
98  
99      /***
100      * The length value or attribute name (<=0 means no limit).
101      */
102     protected String length = null;
103 
104     /***
105      * The name of the collection or owning bean.
106      */
107     protected String name = null;
108 
109     /***
110      * The starting offset (zero relative).
111      */
112     protected String offset = null;
113 
114     /***
115      * The property name containing the collection.
116      */
117     protected String property = null;
118 
119     /***
120      * The scope of the bean specified by the name property, if any.
121      */
122     protected String scope = null;
123 
124     /***
125      * The Java class of each exposed element of the collection.
126      */
127     protected String type = null;
128 
129     public Object getCollection() {
130         return (this.collection);
131     }
132 
133     public void setCollection(Object collection) {
134         this.collection = collection;
135     }
136 
137     public String getId() {
138         return (this.id);
139     }
140 
141     public void setId(String id) {
142         this.id = id;
143     }
144 
145     /***
146      * <p>Return the zero-relative index of the current iteration through the
147      * loop.  If you specify an <code>offset</code>, the first iteration
148      * through the loop will have that value; otherwise, the first iteration
149      * will return zero.</p>
150      *
151      * <p>This property is read-only, and gives nested custom tags access to
152      * this information.  Therefore, it is <strong>only</strong> valid in
153      * between calls to <code>doStartTag()</code> and <code>doEndTag()</code>.
154      * </p>
155      */
156     public int getIndex() {
157         if (started) {
158             return ((offsetValue + lengthCount) - 1);
159         } else {
160             return (0);
161         }
162     }
163 
164     public String getIndexId() {
165         return (this.indexId);
166     }
167 
168     public void setIndexId(String indexId) {
169         this.indexId = indexId;
170     }
171 
172     public String getLength() {
173         return (this.length);
174     }
175 
176     public void setLength(String length) {
177         this.length = length;
178     }
179 
180     public String getName() {
181         return (this.name);
182     }
183 
184     public void setName(String name) {
185         this.name = name;
186     }
187 
188     public String getOffset() {
189         return (this.offset);
190     }
191 
192     public void setOffset(String offset) {
193         this.offset = offset;
194     }
195 
196     public String getProperty() {
197         return (this.property);
198     }
199 
200     public void setProperty(String property) {
201         this.property = property;
202     }
203 
204     public String getScope() {
205         return (this.scope);
206     }
207 
208     public void setScope(String scope) {
209         this.scope = scope;
210     }
211 
212     public String getType() {
213         return (this.type);
214     }
215 
216     public void setType(String type) {
217         this.type = type;
218     }
219 
220     // --------------------------------------------------------- Public Methods
221 
222     /***
223      * Construct an iterator for the specified collection, and begin looping
224      * through the body once per element.
225      *
226      * @throws JspException if a JSP exception has occurred
227      */
228     public int doStartTag() throws JspException {
229         // Acquire the collection we are going to iterate over
230         Object collection = this.collection;
231 
232         if (collection == null) {
233             collection =
234                 TagUtils.getInstance().lookup(pageContext, name, property,
235                     scope);
236         }
237 
238         if (collection == null) {
239             JspException e =
240                 new JspException(messages.getMessage("iterate.collection"));
241 
242             TagUtils.getInstance().saveException(pageContext, e);
243             throw e;
244         }
245 
246         // Construct an iterator for this collection
247         if (collection.getClass().isArray()) {
248             try {
249                 // If we're lucky, it is an array of objects
250                 // that we can iterate over with no copying
251                 iterator = Arrays.asList((Object[]) collection).iterator();
252             } catch (ClassCastException e) {
253                 // Rats -- it is an array of primitives
254                 int length = Array.getLength(collection);
255                 ArrayList c = new ArrayList(length);
256 
257                 for (int i = 0; i < length; i++) {
258                     c.add(Array.get(collection, i));
259                 }
260 
261                 iterator = c.iterator();
262             }
263         } else if (collection instanceof Collection) {
264             iterator = ((Collection) collection).iterator();
265         } else if (collection instanceof Iterator) {
266             iterator = (Iterator) collection;
267         } else if (collection instanceof Map) {
268             iterator = ((Map) collection).entrySet().iterator();
269         } else if (collection instanceof Enumeration) {
270             iterator = new IteratorAdapter((Enumeration) collection);
271         } else {
272             JspException e =
273                 new JspException(messages.getMessage("iterate.iterator"));
274 
275             TagUtils.getInstance().saveException(pageContext, e);
276             throw e;
277         }
278 
279         // Calculate the starting offset
280         if (offset == null) {
281             offsetValue = 0;
282         } else {
283             try {
284                 offsetValue = Integer.parseInt(offset);
285             } catch (NumberFormatException e) {
286                 Integer offsetObject =
287                     (Integer) TagUtils.getInstance().lookup(pageContext,
288                         offset, null);
289 
290                 if (offsetObject == null) {
291                     offsetValue = 0;
292                 } else {
293                     offsetValue = offsetObject.intValue();
294                 }
295             }
296         }
297 
298         if (offsetValue < 0) {
299             offsetValue = 0;
300         }
301 
302         // Calculate the rendering length
303         if (length == null) {
304             lengthValue = 0;
305         } else {
306             try {
307                 lengthValue = Integer.parseInt(length);
308             } catch (NumberFormatException e) {
309                 Integer lengthObject =
310                     (Integer) TagUtils.getInstance().lookup(pageContext,
311                         length, null);
312 
313                 if (lengthObject == null) {
314                     lengthValue = 0;
315                 } else {
316                     lengthValue = lengthObject.intValue();
317                 }
318             }
319         }
320 
321         if (lengthValue < 0) {
322             lengthValue = 0;
323         }
324 
325         lengthCount = 0;
326 
327         // Skip the leading elements up to the starting offset
328         for (int i = 0; i < offsetValue; i++) {
329             if (iterator.hasNext()) {
330                 iterator.next();
331             }
332         }
333 
334         // Store the first value and evaluate, or skip the body if none
335         if (iterator.hasNext()) {
336             Object element = iterator.next();
337 
338             if (element == null) {
339                 pageContext.removeAttribute(id);
340             } else {
341                 pageContext.setAttribute(id, element);
342             }
343 
344             lengthCount++;
345             started = true;
346 
347             if (indexId != null) {
348                 pageContext.setAttribute(indexId, new Integer(getIndex()));
349             }
350 
351             return (EVAL_BODY_TAG);
352         } else {
353             return (SKIP_BODY);
354         }
355     }
356 
357     /***
358      * Make the next collection element available and loop, or finish the
359      * iterations if there are no more elements.
360      *
361      * @throws JspException if a JSP exception has occurred
362      */
363     public int doAfterBody() throws JspException {
364         // Render the output from this iteration to the output stream
365         if (bodyContent != null) {
366             TagUtils.getInstance().writePrevious(pageContext,
367                 bodyContent.getString());
368             bodyContent.clearBody();
369         }
370 
371         // Decide whether to iterate or quit
372         if ((lengthValue > 0) && (lengthCount >= lengthValue)) {
373             return (SKIP_BODY);
374         }
375 
376         if (iterator.hasNext()) {
377             Object element = iterator.next();
378 
379             if (element == null) {
380                 pageContext.removeAttribute(id);
381             } else {
382                 pageContext.setAttribute(id, element);
383             }
384 
385             lengthCount++;
386 
387             if (indexId != null) {
388                 pageContext.setAttribute(indexId, new Integer(getIndex()));
389             }
390 
391             return (EVAL_BODY_TAG);
392         } else {
393             return (SKIP_BODY);
394         }
395     }
396 
397     /***
398      * Clean up after processing this enumeration.
399      *
400      * @throws JspException if a JSP exception has occurred
401      */
402     public int doEndTag() throws JspException {
403         // Clean up our started state
404         started = false;
405         iterator = null;
406 
407         // Continue processing this page
408         return (EVAL_PAGE);
409     }
410 
411     /***
412      * Release all allocated resources.
413      */
414     public void release() {
415         super.release();
416 
417         iterator = null;
418         lengthCount = 0;
419         lengthValue = 0;
420         offsetValue = 0;
421 
422         id = null;
423         collection = null;
424         length = null;
425         name = null;
426         offset = null;
427         property = null;
428         scope = null;
429         started = false;
430     }
431 }