KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > blandware > atleap > webapp > taglib > core > logic > IteratorTag


1 package com.blandware.atleap.webapp.taglib.core.logic;
2
3 import com.blandware.atleap.common.util.ConvertUtil;
4
5 import javax.servlet.jsp.JspException JavaDoc;
6 import javax.servlet.jsp.PageContext JavaDoc;
7 import javax.servlet.jsp.tagext.JspFragment JavaDoc;
8 import javax.servlet.jsp.tagext.SimpleTagSupport JavaDoc;
9 import java.io.IOException JavaDoc;
10 import java.io.Serializable JavaDoc;
11 import java.util.ArrayList JavaDoc;
12 import java.util.List JavaDoc;
13
14 import org.apache.struts.taglib.TagUtils;
15
16 /**
17  * <p>Replacement for JSTL ForEach tag with the same functionality.<br />
18  * The only reason of creation this tag is that there are problems of combined usages of tags, which conform to different
19  * JSP specifications (1.2 and 2.0). For example there were problems while running on IBM WebSphere when Atleap
20  * simple tag handlers have been used inside JSTL ForEach tag. To avoid such problems, we have created our own iteration tag
21  * </p>
22  * <p>This tag provides an ability to export result of each iteration to scope other than page. It's usable, for example, when
23  * some JSP is included between opening and closing iterator tags and needs to gain access to iterated items</p>
24  * <p>
25  * Before iteration <b>items</b> is converted to <code>java.util.List</code>
26  * (if <b>items</b> is not given or <code>null</code>, then resulting list
27  * contains <code>Integer</code>s with values from <b>begin</b> to <b>end</b>
28  * with step equal to <b>step</b>). Then iteration is run over
29  * that list: from <b>begin</b> index to <b>end</b> index with <b>step</b>.
30  * On each iteration, iteration status will be exported to <b>statusVar</b>,
31  * current object will be exported to <b>var</b> and body will be rendered.
32  * </p>
33  * <p>
34  * Full list of allowed attributes:
35  * <ul>
36  * <li>
37  * <b>items</b> - "collection" of items to iterate over. See
38  * {@link com.blandware.atleap.common.util.ConvertUtil#convertCollectionToList(Object)}
39  * for details.
40  * </li>
41  * <li>
42  * <b>begin</b> - integer value from which the iteration over collection will be
43  * run. Default value is 0.
44  * </li>
45  * <li>
46  * <b>end</b> - integer value to which the iteration over collection will be
47  * run. Default value is number_of_elements_in_collection - 1.
48  * </li>
49  * <li>
50  * <b>step</b> - integer value that will be added to index on each iteration.
51  * Must be positive. Default value is 1.
52  * </li>
53  * <li>
54  * <b>var</b> - name of scope variable that will accept object that corresponds
55  * to current iteration. If not specified, current object is not exported.
56  * </li>
57  * <li>
58  * <b>scope</b> - scope of variable to export current object
59  * </li>
60  * <li>
61  * <b>statusVar</b> - name of scope variable that will accept status object that
62  * describes current iteration (see {@link com.blandware.atleap.webapp.taglib.core.logic.IteratorTag.IterationStatus})
63  * </li>
64  * <li>
65  * <b>statusScope</b> - scope variable to export current iteration status
66  * </li>
67  * </ul>
68  * </p>
69  * <p>
70  * Here is an example of this tag usage:
71  * <pre>
72  * &lt;ul&gt;
73  * &lt;atleap:iterator items=${listOfStrings} var="currentString"&gt;
74  * &lt;li&gt;${currentString}&lt;/li&gt;
75  * &lt;/atleap:iterator&gt;
76  * &lt;/ul&gt;
77  * </pre>
78  * This code snippet will generate an HTML-list from list of strings which is
79  * stored in page scope under 'listOfStrings' name.
80  * </p>
81  * <p>
82  * Here's another example:
83  * <pre>
84  * &lt;atleap:iterator begin="1" end="15" step="2" var="currentOdd"&gt;
85  * ${currentOdd}
86  * &lt;/atleap:iterator&gt;
87  * </pre>
88  * This will generate a sequence of odd numbers from 1 to 15.
89  * </p>
90  * <p><a HREF="IteratorTag.java.htm"><i>View Source</i></a></p>
91  *
92  * @author Sergey Zubtcovskii <a HREF="mailto:sergey.zubtcovskii@blandware.com">&lt;sergey.zubtcovskii@blandware.com&gt;</a>
93  * @version $Revision: 1.2 $ $Date: 2005/10/12 13:35:01 $
94  * @jsp.tag name="iterator"
95  * body-content="scriptless"
96  */

97 public class IteratorTag extends SimpleTagSupport JavaDoc {
98
99     /**
100      * Collection of items we will run over. If not specified, array of integer indices will be created and iterated over
101      */

102     protected Object JavaDoc items;
103
104     /**
105      * Items object converted to the instance of <code>java.util.List</code>
106      */

107     protected List JavaDoc itemsList;
108
109     /**
110      * Zero-based number of element in collection to run from. Default value is 0
111      */

112     protected Integer JavaDoc begin;
113
114     /**
115      * Number of element in collection to run to. Default value is equal to (size of collection - 1)
116      */

117     protected Integer JavaDoc end;
118
119     /**
120      * Step of each iteration. Default value is 1
121      */

122     protected Integer JavaDoc step;
123
124     /**
125      * Name of variable to export result of each iteration
126      */

127     protected String JavaDoc var;
128
129     /**
130      * Scope to export result of each iteration to. Default value is "page"
131      */

132     protected String JavaDoc scope = "page";
133
134     /**
135      * Name of variable to export status of each iteration
136      */

137     protected String JavaDoc statusVar;
138
139     /**
140      * Scope to export status of each iteration to. Default value is "page"
141      */

142     protected String JavaDoc statusScope = "page";
143
144     /**
145      * Status of current iteration
146      */

147     protected IterationStatus status;
148
149     /**
150      * Creates new instance of IterateTag
151      */

152     public IteratorTag() {
153     }
154
155     /**
156      * Returns items
157      *
158      * @return items
159      * @see #items
160      * @jsp.attribute required="false"
161      * rtexprvalue="true"
162      * type="java.lang.Object"
163      * description="Collection of items to iterate over"
164      */

165     public Object JavaDoc getItems() {
166         return items;
167     }
168
169     /**
170      * Sets items
171      *
172      * @param items items to set
173      * @see #items
174      */

175     public void setItems(Object JavaDoc items) {
176         this.items = items;
177     }
178
179     /**
180      * Returns beginning number
181      *
182      * @return beginning number
183      * @see #begin
184      * @jsp.attribute required="false"
185      * rtexprvalue="true"
186      * type="java.lang.Integer"
187      * description="Zero-based number of element in collection to run from. Default value is 0"
188      */

189     public Integer JavaDoc getBegin() {
190         return begin;
191     }
192
193     /**
194      * Sets beginning number
195      *
196      * @param begin beginning number to set
197      * @see #begin
198      */

199     public void setBegin(Integer JavaDoc begin) {
200         this.begin = begin;
201     }
202
203     /**
204      * Returns ending number
205      *
206      * @return ending number
207      * @see #end
208      * @jsp.attribute required="false"
209      * rtexprvalue="true"
210      * type="java.lang.Integer"
211      * description="Number of element in collection to run to. Default value is equal to (size of collection - 1)"
212      */

213     public Integer JavaDoc getEnd() {
214         return end;
215     }
216
217     /**
218      * Sets ending number
219      *
220      * @param end ending number to set
221      * @see #end
222      */

223     public void setEnd(Integer JavaDoc end) {
224         this.end = end;
225     }
226
227     /**
228      * Returns step
229      *
230      * @return step
231      * @see #step
232      * @jsp.attribute required="false"
233      * rtexprvalue="true"
234      * type="java.lang.Integer"
235      * description="Step of each iteration. Default value is 1"
236      */

237     public Integer JavaDoc getStep() {
238         return step;
239     }
240
241     /**
242      * Sets step
243      *
244      * @param step step to set
245      * @see #step
246      */

247     public void setStep(Integer JavaDoc step) {
248         this.step = step;
249     }
250
251     /**
252      * Returns variable name
253      *
254      * @return variable name
255      * @see #var
256      * @jsp.attribute required="false"
257      * rtexprvalue="true"
258      * type="java.lang.String"
259      * description="Name of variable to export result of each iteration"
260      */

261     public String JavaDoc getVar() {
262         return var;
263     }
264
265     /**
266      * Sets variable name
267      *
268      * @param var variable name to set
269      * @see #var
270      */

271     public void setVar(String JavaDoc var) {
272         this.var = var;
273     }
274
275     /**
276      * Returns variable scope
277      *
278      * @return variable scope
279      * @see #scope
280      * @jsp.attribute required="false"
281      * rtexprvalue="true"
282      * type="java.lang.String"
283      * description="Scope to export result of each iteration to. Default value is 'page'"
284      */

285     public String JavaDoc getScope() {
286         return scope;
287     }
288
289     /**
290      * Sets variable scope
291      *
292      * @param scope variable scope to set
293      * @see #scope
294      */

295     public void setScope(String JavaDoc scope) {
296         this.scope = scope;
297     }
298
299     /**
300      * Returns name of status variable used to obtain some additional info about
301      * each iteration
302      *
303      * @return name of status variable
304      * @see #statusVar
305      * @jsp.attribute required="false"
306      * rtexprvalue="true"
307      * type="java.lang.String"
308      * description="Name of variable to export status of each iteration"
309      */

310     public String JavaDoc getStatusVar() {
311         return statusVar;
312     }
313
314     /**
315      * Sets name of status var used to obtain some additional info about each
316      * iteration
317      *
318      * @param statusVar name of status variable to set
319      * @see #statusVar
320      */

321     public void setStatusVar(String JavaDoc statusVar) {
322         this.statusVar = statusVar;
323     }
324
325     /**
326      * Returns status variable scope
327      *
328      * @return status variable scope
329      * @jsp.attribute required="false"
330      * @see #statusScope
331      * rtexprvalue="true"
332      * type="java.lang.String"
333      * description="Scope to export status of each iteration to. Default value is 'page'"
334      */

335     public String JavaDoc getStatusScope() {
336         return statusScope;
337     }
338
339     /**
340      * Sets status variable scope
341      *
342      * @param statusScope status variable scope to set
343      * @see #statusScope
344      */

345     public void setStatusScope(String JavaDoc statusScope) {
346         this.statusScope = statusScope;
347     }
348
349     /**
350      * Returns status of current iteration
351      *
352      * @return iteration status
353      */

354     public IterationStatus getStatus() {
355         return status;
356     }
357
358     /**
359      * Iterates over specified collection and exposes results to the specified scope
360      *
361      * @throws JspException
362      * @throws IOException
363      */

364     public void doTag() throws JspException JavaDoc, IOException JavaDoc {
365
366         PageContext JavaDoc pageContext = (PageContext JavaDoc) getJspContext();
367
368         // convert specified collection to list
369
itemsList = convertItemsToList();
370
371         // initialize indices and step
372
int begin = this.begin != null ? this.begin.intValue() : 0;
373         int end = this.end != null ? this.end.intValue() : itemsList.size() - 1;
374         int step = this.step != null ? this.step.intValue() : 1;
375
376         if (step <= 0) {
377             throw new JspException JavaDoc("Step must be positive");
378         }
379
380         // obtain JSP body
381
JspFragment JavaDoc body = getJspBody();
382         if ( body == null ) {
383             return;
384         }
385
386         // initialize status variable
387
status = new IterationStatus(this.begin, this.end, this.step);
388
389         // iterate over list
390

391         // count of current iteration
392
int count = 1;
393         for ( int i = begin; i <= end; i += step, count++ ) {
394             Object JavaDoc current = (Object JavaDoc) itemsList.get(i);
395
396             // set current object
397
status.setCurrent(current);
398
399             // set count
400
status.setCount(count);
401
402             // set index
403
status.setIndex(i);
404
405             // expose current object to the specified scope
406
TagUtils tagUtils = TagUtils.getInstance();
407             if ( var != null ) {
408                 int varScope = PageContext.PAGE_SCOPE;
409                 if ( scope != null ) {
410                     varScope = tagUtils.getScope(scope);
411                 }
412                 pageContext.setAttribute(var, current, varScope);
413             }
414
415             // expose status to the specified scope
416
if ( statusVar != null ) {
417                 int statusVarScope = PageContext.PAGE_SCOPE;
418                 if ( statusScope != null ) {
419                     statusVarScope = tagUtils.getScope(statusScope);
420                 }
421                 pageContext.setAttribute(statusVar, status, statusVarScope);
422             }
423
424             // invoke body content
425
body.invoke(null);
426         }
427     }
428
429     /**
430      * Converts specified collection to the instance of <code>java.util.List</code>
431      */

432     protected List JavaDoc convertItemsToList() {
433         List JavaDoc list = new ArrayList JavaDoc();
434         if ( items == null ) {
435             // create array of indices
436
int begin = this.begin != null ? this.begin.intValue() : 0;
437             int end = this.end != null ? this.end.intValue() : 0;
438             for ( int i = begin; i < end; i++ ) {
439                 list.add(new Integer JavaDoc(i));
440             }
441         } else {
442             list = ConvertUtil.convertCollectionToList(items);
443         }
444         return list;
445     }
446
447     /**
448      * <p>Exposes the current status of
449      * an iteration. IteratorTag provides a mechanism to
450      * return information about the current index of the iteration and
451      * convenience methods to determine whether or not the current round is
452      * either the first or last in the iteration. It also lets authors
453      * use the status object to obtain information about the iteration range,
454      * step, and current object.</p>
455      * <p>Interface LoopTagStatus has initially been created by Shawn Bayern.
456      * This has the same functionality and has been created to meet our conditions by Sergey Zubtcovskii</p>
457      *
458      * @author Shawn Bayern (JSTL team)
459      * @author Sergey Zubtcovskii <a HREF="mailto:sergey.zubtcovskii@blandware.com">&lt;sergey.zubtcovskii@blandware.com&gt;</a>
460      */

461     public class IterationStatus implements Serializable JavaDoc {
462
463         /**
464          * Begin index of iteration
465          */

466         protected Integer JavaDoc begin;
467
468         /**
469          * End index of iteration
470          */

471         protected Integer JavaDoc end;
472
473         /**
474          * Step of each iteration;
475          */

476         protected Integer JavaDoc step;
477
478         /**
479          * Index of the current round of the iteration
480          */

481         protected int index;
482
483         /**
484          * "Count" of the current round of the iteration. The
485          * count is a relative, 1-based sequence number identifying the
486          * current "round" of iteration (in context with all rounds the
487          * current iteration will perform)
488          */

489         protected int count;
490
491         /**
492          * Current item in the iteration
493          */

494         protected Object JavaDoc current;
495
496         /**
497          * Creates new instance of iteration status
498          * @param begin Begin index of iteration
499          * @param end End index of iteration
500          * @param step Step of each iteration
501          */

502         public IterationStatus(Integer JavaDoc begin, Integer JavaDoc end, Integer JavaDoc step) {
503             this.begin = begin;
504             this.end = end;
505             this.step = step;
506         }
507
508         /**
509          * Retrieves the index of the current round of the iteration. If
510          * iteration is being performed over a subset of an underlying
511          * array, java.lang.Collection, or other type, the index returned
512          * is absolute with respect to the underlying collection. Indices
513          * are 0-based.
514          *
515          * @return the 0-based index of the current round of the iteration
516          */

517         public int getIndex() {
518             return index;
519         }
520
521         /**
522          * Package-private setter used in parent class of this one
523          * @param index Index to set
524          */

525         void setIndex(int index) {
526             this.index = index;
527         }
528
529         /**
530          * <p>Retrieves the "count" of the current round of the iteration. The
531          * count is a relative, 1-based sequence number identifying the
532          * current "round" of iteration (in context with all rounds the
533          * current iteration will perform).</p>
534          *
535          * <p>As an example, an iteration with begin = 5, end = 15, and step =
536          * 5 produces the counts 1, 2, and 3 in that order.</p>
537          *
538          * @return the 1-based count of the current round of the iteration
539          */

540         public int getCount() {
541             return count;
542         }
543
544         /**
545          * Package-private setter used in parent class of this one
546          * @param count Count of current iteration
547          */

548         void setCount(int count) {
549             this.count = count;
550         }
551
552         /**
553          * Retrieves the current item in the iteration. Behaves
554          * idempotently; calling getCurrent() repeatedly should return the same
555          * Object until the iteration is advanced. (Specifically, calling
556          * getCurrent() does <b>not</b> advance the iteration.)
557          *
558          * @return the current item as an object
559          */

560         public Object JavaDoc getCurrent() {
561             return current;
562         }
563
564
565         /**
566          * Package-private setter used in parent class of this one
567          * @param current Object exposed on current iteration
568          */

569         void setCurrent(Object JavaDoc current) {
570             this.current = current;
571         }
572
573         /**
574          * Returns the value of the 'begin' attribute for the associated tag,
575          * or null if no 'begin' attribute was specified.
576          *
577          * @return the 'begin' value for the associated tag, or null
578          * if no 'begin' attribute was specified
579          */

580         public Integer JavaDoc getBegin() {
581             return begin;
582         }
583
584         /**
585          * Returns the value of the 'end' attribute for the associated tag,
586          * or null if no 'end' attribute was specified.
587          *
588          * @return the 'end' value for the associated tag, or null
589          * if no 'end' attribute was specified
590          */

591         public Integer JavaDoc getEnd() {
592             return end;
593         }
594
595         /**
596          * Returns the value of the 'step' attribute for the associated tag,
597          * or null if no 'step' attribute was specified.
598          *
599          * @return the 'step' value for the associated tag, or null
600          * if no 'step' attribute was specified
601          */

602         public Integer JavaDoc getStep() {
603             return step;
604         }
605
606         /**
607          * Returns information about whether the current round of the
608          * iteration is the first one. This current round may be the 'first'
609          * even when getIndex() != 0, for 'index' refers to the absolute
610          * index of the current 'item' in the context of its underlying
611          * collection. It is always that case that a true result from
612          * isFirst() implies getCount() == 1.
613          *
614          * @return <tt>true</tt> if the current round is the first in the
615          * iteration, <tt>false</tt> otherwise.
616          */

617         public boolean isFirst() {
618             int begin = this.begin != null ? this.begin.intValue() : 0;
619             return index == begin;
620         }
621
622         /**
623          * Returns information about whether the current round of the
624          * iteration is the last one. As with isFirst(), subsetting is
625          * taken into account. isLast() doesn't necessarily refer to the
626          * status of the underlying Iterator; it refers to whether or not
627          * the current round will be the final round of iteration for the
628          * tag associated with this LoopTagStatus.
629          *
630          * @return <tt>true</tt> if the current round is the last in the
631          * iteration, <tt>false</tt> otherwise.
632          */

633         public boolean isLast() {
634             int end = this.end != null ? this.end.intValue() : itemsList.size() - 1;
635             int step = this.step != null ? this.step.intValue() : 1;
636             return index + step > end;
637         }
638
639     }
640
641 }
642
Popular Tags