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