KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > tools > generic > IteratorTool


1 /*
2  * Copyright 2003 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.velocity.tools.generic;
18
19 import java.util.Collection JavaDoc;
20 import java.util.Enumeration JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Vector JavaDoc;
24
25 import org.apache.velocity.util.ArrayIterator;
26 import org.apache.velocity.util.EnumerationIterator;
27
28 /**
29  * <p>
30  * A convenience tool to use with #foreach loops. It wraps a list
31  * to let the designer specify a condition to terminate the loop,
32  * and reuse the same list in different loops.
33  * </p>
34  * <p>
35  * Example of use:
36  * <pre>
37  * Java
38  * ----
39  * context.put("mill", new IteratorTool());
40  *
41  *
42  * VTL
43  * ---
44  *
45  * #set ($list = [1, 2, 3, 5, 8, 13])
46  * #set ($numbers = $mill.wrap($list))
47  *
48  * #foreach ($item in $numbers)
49  * #if ($item < 8) $numbers.more()#end
50  * #end
51  *
52  * $numbers.more()
53  *
54  *
55  * Output
56  * ------
57  *
58  * 1 2 3 5
59  * 8
60  *
61  * Example toolbox.xml config (if you want to use this with VelocityView):
62  * &lt;tool&gt;
63  * &lt;key&gt;mill&lt;/key&gt;
64  * &lt;scope&gt;request&lt;/scope&gt;
65  * &lt;class&gt;org.apache.velocity.tools.generic.IteratorTool&lt;/class&gt;
66  * &lt;/tool&gt;
67  * </pre>
68  * </p>
69  * <p>
70  * <b>Warning:</b> It is not recommended to use hasNext() with this
71  * tool as it is used to control the #foreach. Use hasMore() instead.
72  * </p>
73  *
74  * @author <a HREF="mailto:jido@respublica.fr">Denis Bredelet</a>
75  * @version $Id: IteratorTool.java,v 1.3 2004/02/18 20:11:07 nbubna Exp $
76  */

77
78 public class IteratorTool implements Iterator JavaDoc {
79
80
81     private Object JavaDoc wrapped;
82     private Iterator JavaDoc iterator;
83     private boolean wantMore;
84     private boolean cachedNext;
85     protected Object JavaDoc next;
86
87
88     /**
89      * Create a IteratorTool instance to use as tool.
90      * When it is created this way, the tool returns a new
91      * instance each time wrap() is called. This is
92      * useful when you want to allow the designers to create instances.
93      */

94     public IteratorTool()
95     {
96         this(null);
97     }
98
99
100     /**
101      * Create a IteratorTool instance to use in #foreach.
102      *
103      * @param wrapped The list to wrap.
104      */

105     public IteratorTool(Object JavaDoc wrapped)
106     {
107         internalWrap(wrapped);
108     }
109
110
111     /**
112      * Wraps a list with the tool.
113      * <br>The list can be an array, a Collection, a Map, an Iterator
114      * or an Enumeration.
115      * <br>If the list is a Map, the tool iterates over the values.
116      * <br>If the list is an Iterator or an Enumeration, the tool can
117      * be used only once.
118      *
119      * @param list The list to wrap.
120      * @return A new wrapper if this object is used as a tool, or
121      * itself if it is a wrapper.
122      */

123     public IteratorTool wrap(Object JavaDoc list)
124     {
125         if (this.wrapped == null)
126         {
127             return new IteratorTool(list);
128         }
129         else if (list != null)
130         {
131             internalWrap(list);
132             return this;
133         }
134         else
135         {
136             throw new IllegalArgumentException JavaDoc("Need a valid list to wrap");
137         }
138     }
139
140
141     /**
142      * Wraps a list with the tool. This object can therefore
143      * be used instead of the list itself in a #foreach.
144      * The list can be an array, a Collection, a Map, an
145      * Iterator or an Enumeration.
146      * <br>- If the list is a Map, the tool iterates over the values.
147      * <br>- If the list is an Iterator or an Enumeration, the tool
148      * can be used only once.
149      *
150      * @param wrapped The list to wrap.
151      * @return This object, wrapped around the list.
152      */

153     private void internalWrap(Object JavaDoc wrapped)
154     {
155         if (wrapped != null)
156         {
157             /* rip-off from org/apache/velocity/runtime/directive/ForEach.java */
158             if (wrapped.getClass().isArray())
159             {
160                 this.iterator = new ArrayIterator((Object JavaDoc[])wrapped);
161             }
162             else if (wrapped instanceof Collection JavaDoc)
163             {
164                 this.iterator = ((Collection JavaDoc)wrapped).iterator();
165             }
166             else if (wrapped instanceof Map JavaDoc)
167             {
168                 this.iterator = ((Map JavaDoc)wrapped).values().iterator();
169             }
170             else if (wrapped instanceof Iterator JavaDoc)
171             {
172                 this.iterator = (Iterator JavaDoc)wrapped;
173             }
174             else if (wrapped instanceof Enumeration JavaDoc)
175             {
176                 this.iterator = new EnumerationIterator((Enumeration JavaDoc)wrapped);
177             }
178             else
179             {
180                 /* Don't know what is the object.
181                  * Should we put it in a one-item array? */

182                 throw new IllegalArgumentException JavaDoc("Don't know how to wrap this list");
183             }
184
185             this.wrapped = wrapped;
186             this.wantMore = true;
187             this.cachedNext = false;
188         }
189         else
190         {
191             this.iterator = null;
192             this.wrapped = null;
193             this.wantMore = false;
194             this.cachedNext = false;
195         }
196     }
197
198
199     /**
200      * <p>
201      * Resets the wrapper so that it starts over at the beginning of the list.
202      * </p>
203      * <p>
204      * <b>Note to programmers:</b> This method has no effect if the wrapped
205      * object is an enumeration or an iterator.
206      */

207     public void reset()
208     {
209         if (this.wrapped != null)
210         {
211             internalWrap(this.wrapped);
212         }
213     }
214
215
216     /**
217      * <p>
218      * Gets the next object in the list. This method is called
219      * by #foreach to define $item in:
220      * <pre>
221      * #foreach( $item in $list )
222      * </pre>
223      * </p>
224      * <p>
225      * This method is not intended for template designers, but they can use
226      * them if they want to read the value of the next item without doing
227      * more().
228      * </p>
229      *
230      * @return The next item in the list.
231      * @throws NoSuchElementException if there are no more
232      * elements in the list.
233      */

234     public Object JavaDoc next()
235     {
236         if (this.wrapped == null)
237         {
238             throw new IllegalStateException JavaDoc("Use wrap() before calling next()");
239         }
240         
241         if (!this.cachedNext)
242         {
243             this.cachedNext = true;
244             this.next = this.iterator.next();
245             return this.next;
246         }
247         else
248         {
249             return this.next;
250         }
251     }
252
253     /**
254      * Returns true if there are more elements in the
255      * list and more() was called.
256      * <br>This code always return false:
257      * <pre>
258      * tool.hasNext()? tool.hasNext(): false;
259      * </pre>
260      *
261      * @return true if there are more elements, and either more()
262      * or hasNext() was called since last call.
263      */

264     public boolean hasNext()
265     {
266         if (this.wantMore)
267         {
268             /* don't want more unless more is called */
269             this.wantMore = false;
270             return hasMore();
271         }
272         else
273         {
274             /* prepare for next #foreach */
275             this.wantMore = true;
276             return false;
277         }
278     }
279
280     /**
281      * Removes the current element from the list.
282      * The current element is defined as the last element that was read
283      * from the list, either with next() or with more().
284      *
285      * @throws UnsupportedOperationException if the wrapped list
286      * iterator doesn't support this operation.
287      */

288     public void remove() throws UnsupportedOperationException JavaDoc
289     {
290         if (this.wrapped == null)
291         {
292             throw new IllegalStateException JavaDoc("Use wrap() before calling remove()");
293         }
294
295         /* Let the iterator decide whether to implement this or not */
296         this.iterator.remove();
297     }
298
299
300     /**
301      * <p>
302      * Asks for the next element in the list. This method is to be used
303      * by the template designer in #foreach loops.
304      * </p>
305      * <p>
306      * If this method is called in the body of #foreach, the loop
307      * continues as long as there are elements in the list.
308      * <br>If this method is not called the loop terminates after the
309      * current iteration.
310      * </p>
311      *
312      * @return The next element in the list, or null if there are no
313      * more elements.
314      */

315     public Object JavaDoc more()
316     {
317         this.wantMore = true;
318         if (hasMore())
319         {
320             Object JavaDoc next = next();
321             this.cachedNext = false;
322             return next;
323         }
324         else
325         {
326             return null;
327         }
328     }
329
330     /**
331      * Returns true if there are more elements in the wrapped list.
332      * <br>If this object doesn't wrap a list, the method always returns false.
333      *
334      * @return true if there are more elements in the list.
335      */

336     public boolean hasMore()
337     {
338         if (this.wrapped == null)
339         {
340             return false;
341         }
342         return cachedNext || this.iterator.hasNext();
343     }
344
345     
346     /**
347      * Puts a condition to break out of the loop.
348      * The #foreach loop will terminate after this iteration, unless more()
349      * is called after stop().
350      */

351     public void stop()
352     {
353         this.wantMore = false;
354     }
355
356
357     /**
358      * Returns this object as a String.
359      * <br>If this object is used as a tool, it just gives the class name.
360      * <br>Otherwise it appends the wrapped list to the class name.
361      *
362      * @return A string representation of this object.
363      */

364     public String JavaDoc toString()
365     {
366         StringBuffer JavaDoc out = new StringBuffer JavaDoc(this.getClass().getName());
367         if (this.wrapped != null)
368         {
369             out.append('(');
370             out.append(this.wrapped);
371             out.append(')');
372         }
373         return out.toString();
374     }
375
376
377 }
378
Popular Tags