1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.betwixt.expression;
19
20 import java.lang.reflect.Array;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Enumeration;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.NoSuchElementException;
27
28
29 /*** <p><code>IteratorExpression</code> returns an iterator over the current context.</p>
30 *
31 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
32 * @version $Revision: 438373 $
33 */
34 public class IteratorExpression implements Expression {
35
36 /*** Use this <code>Expression</code> to perform initial evaluation*/
37 private Expression expression;
38
39 /***
40 * Construct <code>IteratorExpression</code> using given expression for initial evaluation.
41 * @param expression this expression will be evaluated and the result converted to an
42 * iterator.
43 */
44 public IteratorExpression(Expression expression) {
45 this.expression = expression;
46 }
47
48 /***
49 * Returns an interator over the current context
50 * @see org.apache.commons.betwixt.expression.Expression
51 */
52 public Object evaluate(Context context) {
53
54 Object value = expression.evaluate( context );
55
56
57
58 if ( value instanceof Iterator ) {
59
60 return (Iterator) value;
61
62 } else if ( value instanceof Collection ) {
63
64 Collection collection = (Collection) value;
65 return collection.iterator();
66
67 } else if ( value instanceof Map ) {
68
69 Map map = (Map) value;
70 return map.entrySet().iterator();
71
72 } else if ( value instanceof Enumeration ) {
73
74 return new EnumerationIterator( (Enumeration) value );
75
76 } else if ( value != null ) {
77
78 Class type = value.getClass();
79 if ( type.isArray() ) {
80 return new ArrayIterator( value );
81 }
82 }
83
84
85
86 return Collections.EMPTY_LIST.iterator();
87 }
88
89 /***
90 * Do nothing
91 * @see org.apache.commons.betwixt.expression.Expression
92 */
93 public void update(Context context, String newValue) {
94
95 }
96
97 /***
98 * Returns something useful for logging
99 * @return string useful for logging
100 */
101 public String toString() {
102 return "IteratorExpression [expression=" + expression + "]";
103 }
104
105
106 /***
107 * <code>ArrayIterator</code> originated in commons-collections. Added
108 * as a private inner class to break dependency.
109 *
110 * @author James Strachan
111 * @author Mauricio S. Moura
112 * @author Michael A. Smith
113 * @author Neil O'Toole
114 * @author Stephen Colebourne
115 */
116 private static final class ArrayIterator implements Iterator {
117
118 /*** The array to iterate over */
119 protected Object array;
120
121 /*** The start index to loop from */
122 protected int startIndex = 0;
123
124 /*** The end index to loop to */
125 protected int endIndex = 0;
126
127 /*** The current iterator index */
128 protected int index = 0;
129
130
131
132 /***
133 * Constructor for use with <code>setArray</code>.
134 * <p>
135 * Using this constructor, the iterator is equivalent to an empty
136 * iterator until {@link #setArray(Object)}is called to establish the
137 * array to iterate over.
138 */
139 public ArrayIterator() {
140 super();
141 }
142
143 /***
144 * Constructs an ArrayIterator that will iterate over the values in the
145 * specified array.
146 *
147 * @param array
148 * the array to iterate over.
149 * @throws IllegalArgumentException
150 * if <code>array</code> is not an array.
151 * @throws NullPointerException
152 * if <code>array</code> is <code>null</code>
153 */
154 public ArrayIterator(final Object array) {
155 super();
156 setArray(array);
157 }
158
159 /***
160 * Constructs an ArrayIterator that will iterate over the values in the
161 * specified array from a specific start index.
162 *
163 * @param array
164 * the array to iterate over.
165 * @param startIndex
166 * the index to start iterating at.
167 * @throws IllegalArgumentException
168 * if <code>array</code> is not an array.
169 * @throws NullPointerException
170 * if <code>array</code> is <code>null</code>
171 * @throws IndexOutOfBoundsException
172 * if the index is invalid
173 */
174 public ArrayIterator(final Object array, final int startIndex) {
175 super();
176 setArray(array);
177 checkBound(startIndex, "start");
178 this.startIndex = startIndex;
179 this.index = startIndex;
180 }
181
182 /***
183 * Construct an ArrayIterator that will iterate over a range of values
184 * in the specified array.
185 *
186 * @param array
187 * the array to iterate over.
188 * @param startIndex
189 * the index to start iterating at.
190 * @param endIndex
191 * the index to finish iterating at.
192 * @throws IllegalArgumentException
193 * if <code>array</code> is not an array.
194 * @throws NullPointerException
195 * if <code>array</code> is <code>null</code>
196 * @throws IndexOutOfBoundsException
197 * if either index is invalid
198 */
199 public ArrayIterator(final Object array, final int startIndex,
200 final int endIndex) {
201 super();
202 setArray(array);
203 checkBound(startIndex, "start");
204 checkBound(endIndex, "end");
205 if (endIndex < startIndex) {
206 throw new IllegalArgumentException(
207 "End index must not be less than start index.");
208 }
209 this.startIndex = startIndex;
210 this.endIndex = endIndex;
211 this.index = startIndex;
212 }
213
214 /***
215 * Checks whether the index is valid or not.
216 *
217 * @param bound
218 * the index to check
219 * @param type
220 * the index type (for error messages)
221 * @throws IndexOutOfBoundsException
222 * if the index is invalid
223 */
224 protected void checkBound(final int bound, final String type) {
225 if (bound > this.endIndex) {
226 throw new ArrayIndexOutOfBoundsException(
227 "Attempt to make an ArrayIterator that " + type
228 + "s beyond the end of the array. ");
229 }
230 if (bound < 0) {
231 throw new ArrayIndexOutOfBoundsException(
232 "Attempt to make an ArrayIterator that " + type
233 + "s before the start of the array. ");
234 }
235 }
236
237
238
239 /***
240 * Returns true if there are more elements to return from the array.
241 *
242 * @return true if there is a next element to return
243 */
244 public boolean hasNext() {
245 return (index < endIndex);
246 }
247
248 /***
249 * Returns the next element in the array.
250 *
251 * @return the next element in the array
252 * @throws NoSuchElementException
253 * if all the elements in the array have already been
254 * returned
255 */
256 public Object next() {
257 if (hasNext() == false) {
258 throw new NoSuchElementException();
259 }
260 return Array.get(array, index++);
261 }
262
263 /***
264 * Throws {@link UnsupportedOperationException}.
265 *
266 * @throws UnsupportedOperationException
267 * always
268 */
269 public void remove() {
270 throw new UnsupportedOperationException(
271 "remove() method is not supported");
272 }
273
274
275
276 /***
277 * Gets the array that this iterator is iterating over.
278 *
279 * @return the array this iterator iterates over, or <code>null</code>
280 * if the no-arg constructor was used and
281 * {@link #setArray(Object)}has never been called with a valid
282 * array.
283 */
284 public Object getArray() {
285 return array;
286 }
287
288 /***
289 * Sets the array that the ArrayIterator should iterate over.
290 * <p>
291 * If an array has previously been set (using the single-arg constructor
292 * or this method) then that array is discarded in favour of this one.
293 * Iteration is restarted at the start of the new array. Although this
294 * can be used to reset iteration, the {@link #reset()}method is a more
295 * effective choice.
296 *
297 * @param array
298 * the array that the iterator should iterate over.
299 * @throws IllegalArgumentException
300 * if <code>array</code> is not an array.
301 * @throws NullPointerException
302 * if <code>array</code> is <code>null</code>
303 */
304 public void setArray(final Object array) {
305
306
307
308
309
310
311
312
313 this.endIndex = Array.getLength(array);
314 this.startIndex = 0;
315 this.array = array;
316 this.index = 0;
317 }
318
319 /***
320 * Resets the iterator back to the start index.
321 */
322 public void reset() {
323 this.index = this.startIndex;
324 }
325
326 }
327
328
329 /***
330 * Adapter to make {@link Enumeration Enumeration}instances appear to be
331 * {@link Iterator Iterator}instances. Originated in commons-collections.
332 * Added as a private inner class to break dependency.
333 *
334 * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
335 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall </a>
336 */
337 private static final class EnumerationIterator implements Iterator {
338
339 /*** The collection to remove elements from */
340 private Collection collection;
341
342 /*** The enumeration being converted */
343 private Enumeration enumeration;
344
345 /*** The last object retrieved */
346 private Object last;
347
348
349
350 /***
351 * Constructs a new <code>EnumerationIterator</code> that will not
352 * function until {@link #setEnumeration(Enumeration)} is called.
353 */
354 public EnumerationIterator() {
355 this(null, null);
356 }
357
358 /***
359 * Constructs a new <code>EnumerationIterator</code> that provides
360 * an iterator view of the given enumeration.
361 *
362 * @param enumeration the enumeration to use
363 */
364 public EnumerationIterator(final Enumeration enumeration) {
365 this(enumeration, null);
366 }
367
368 /***
369 * Constructs a new <code>EnumerationIterator</code> that will remove
370 * elements from the specified collection.
371 *
372 * @param enumeration the enumeration to use
373 * @param collection the collection to remove elements form
374 */
375 public EnumerationIterator(final Enumeration enumeration,
376 final Collection collection) {
377 super();
378 this.enumeration = enumeration;
379 this.collection = collection;
380 this.last = null;
381 }
382
383
384
385 /***
386 * Returns true if the underlying enumeration has more elements.
387 *
388 * @return true if the underlying enumeration has more elements
389 * @throws NullPointerException if the underlying enumeration is null
390 */
391 public boolean hasNext() {
392 return enumeration.hasMoreElements();
393 }
394
395 /***
396 * Returns the next object from the enumeration.
397 *
398 * @return the next object from the enumeration
399 * @throws NullPointerException if the enumeration is null
400 */
401 public Object next() {
402 last = enumeration.nextElement();
403 return last;
404 }
405
406 /***
407 * Removes the last retrieved element if a collection is attached.
408 * <p>
409 * Functions if an associated <code>Collection</code> is known.
410 * If so, the first occurrence of the last returned object from this
411 * iterator will be removed from the collection.
412 *
413 * @exception IllegalStateException <code>next()</code> not called.
414 * @exception UnsupportedOperationException if no associated collection
415 */
416 public void remove() {
417 if (collection != null) {
418 if (last != null) {
419 collection.remove(last);
420 } else {
421 throw new IllegalStateException(
422 "next() must have been called for remove() to function");
423 }
424 } else {
425 throw new UnsupportedOperationException(
426 "No Collection associated with this Iterator");
427 }
428 }
429
430
431
432 /***
433 * Returns the underlying enumeration.
434 *
435 * @return the underlying enumeration
436 */
437 public Enumeration getEnumeration() {
438 return enumeration;
439 }
440
441 /***
442 * Sets the underlying enumeration.
443 *
444 * @param enumeration the new underlying enumeration
445 */
446 public void setEnumeration(final Enumeration enumeration) {
447 this.enumeration = enumeration;
448 }
449 }
450
451 }