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