Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
IteratorExpression |
|
| 2.0434782608695654;2.043 |
1 | /* |
|
2 | * Copyright 2001-2004 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.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 | 4672 | public IteratorExpression(Expression expression) { |
44 | 4672 | this.expression = expression; |
45 | 4672 | } |
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 | // evaluate wrapped expression against context |
|
53 | 3464 | Object value = expression.evaluate( context ); |
54 | ||
55 | // based on the class of the result, |
|
56 | // return an appropriate iterator |
|
57 | 3464 | if ( value instanceof Iterator ) { |
58 | // if the value is an iterator, we're done |
|
59 | 403 | return (Iterator) value; |
60 | ||
61 | 3061 | } else if ( value instanceof Collection ) { |
62 | // if it's a collection, return an iterator for that collection |
|
63 | 2203 | Collection collection = (Collection) value; |
64 | 2203 | return collection.iterator(); |
65 | ||
66 | 858 | } else if ( value instanceof Map ) { |
67 | // if it's a map, return an iterator for the map entries |
|
68 | 143 | Map map = (Map) value; |
69 | 143 | return map.entrySet().iterator(); |
70 | ||
71 | 715 | } else if ( value instanceof Enumeration ) { |
72 | // if it's an enumeration, wrap it in an EnumerationIterator |
|
73 | 13 | return new EnumerationIterator( (Enumeration) value ); |
74 | ||
75 | 702 | } else if ( value != null ) { |
76 | // if we have an array return an ArrayIterator |
|
77 | 598 | Class type = value.getClass(); |
78 | 598 | if ( type.isArray() ) { |
79 | 312 | return new ArrayIterator( value ); |
80 | } |
|
81 | } |
|
82 | ||
83 | // we've got something we can't deal with |
|
84 | // so return an empty iterator |
|
85 | 390 | 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 | // do nothing |
|
94 | 0 | } |
95 | ||
96 | /** |
|
97 | * Returns something useful for logging |
|
98 | * @return string useful for logging |
|
99 | */ |
|
100 | public String toString() { |
|
101 | 0 | 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 | 312 | protected int startIndex = 0; |
122 | ||
123 | /** The end index to loop to */ |
|
124 | 312 | protected int endIndex = 0; |
125 | ||
126 | /** The current iterator index */ |
|
127 | 312 | protected int index = 0; |
128 | ||
129 | // Constructors |
|
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 | 0 | super(); |
140 | 0 | } |
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 | 312 | super(); |
155 | 312 | setArray(array); |
156 | 312 | } |
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 | 0 | super(); |
175 | 0 | setArray(array); |
176 | 0 | checkBound(startIndex, "start"); |
177 | 0 | this.startIndex = startIndex; |
178 | 0 | this.index = startIndex; |
179 | 0 | } |
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 | 0 | super(); |
201 | 0 | setArray(array); |
202 | 0 | checkBound(startIndex, "start"); |
203 | 0 | checkBound(endIndex, "end"); |
204 | 0 | if (endIndex < startIndex) { |
205 | 0 | throw new IllegalArgumentException( |
206 | 0 | "End index must not be less than start index."); |
207 | } |
|
208 | 0 | this.startIndex = startIndex; |
209 | 0 | this.endIndex = endIndex; |
210 | 0 | this.index = startIndex; |
211 | 0 | } |
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 | 0 | if (bound > this.endIndex) { |
225 | 0 | throw new ArrayIndexOutOfBoundsException( |
226 | 0 | "Attempt to make an ArrayIterator that " + type |
227 | 0 | + "s beyond the end of the array. "); |
228 | } |
|
229 | 0 | if (bound < 0) { |
230 | 0 | throw new ArrayIndexOutOfBoundsException( |
231 | 0 | "Attempt to make an ArrayIterator that " + type |
232 | 0 | + "s before the start of the array. "); |
233 | } |
|
234 | 0 | } |
235 | ||
236 | // Iterator interface |
|
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 | 1612 | 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 | 650 | if (hasNext() == false) { |
257 | 0 | throw new NoSuchElementException(); |
258 | } |
|
259 | 650 | return Array.get(array, index++); |
260 | } |
|
261 | ||
262 | /** |
|
263 | * Throws {@link UnsupportedOperationException}. |
|
264 | * |
|
265 | * @throws UnsupportedOperationException |
|
266 | * always |
|
267 | */ |
|
268 | public void remove() { |
|
269 | 0 | throw new UnsupportedOperationException( |
270 | 0 | "remove() method is not supported"); |
271 | } |
|
272 | ||
273 | // Properties |
|
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 | 0 | 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 | // Array.getLength throws IllegalArgumentException if the object is |
|
305 | // not |
|
306 | // an array or NullPointerException if the object is null. This call |
|
307 | // is made before saving the array and resetting the index so that |
|
308 | // the |
|
309 | // array iterator remains in a consistent state if the argument is |
|
310 | // not |
|
311 | // an array or is null. |
|
312 | 312 | this.endIndex = Array.getLength(array); |
313 | 312 | this.startIndex = 0; |
314 | 312 | this.array = array; |
315 | 312 | this.index = 0; |
316 | 312 | } |
317 | ||
318 | /** |
|
319 | * Resets the iterator back to the start index. |
|
320 | */ |
|
321 | public void reset() { |
|
322 | 0 | this.index = this.startIndex; |
323 | 0 | } |
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 | // Constructors |
|
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 | 0 | this(null, null); |
355 | 0 | } |
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 | 13 | this(enumeration, null); |
365 | 13 | } |
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 | 13 | super(); |
377 | 13 | this.enumeration = enumeration; |
378 | 13 | this.collection = collection; |
379 | 13 | this.last = null; |
380 | 13 | } |
381 | ||
382 | // Iterator interface |
|
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 | 65 | 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 | 52 | last = enumeration.nextElement(); |
402 | 52 | 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 | 0 | if (collection != null) { |
417 | 0 | if (last != null) { |
418 | 0 | collection.remove(last); |
419 | } else { |
|
420 | 0 | throw new IllegalStateException( |
421 | 0 | "next() must have been called for remove() to function"); |
422 | } |
|
423 | } else { |
|
424 | 0 | throw new UnsupportedOperationException( |
425 | 0 | "No Collection associated with this Iterator"); |
426 | } |
|
427 | 0 | } |
428 | ||
429 | // Properties |
|
430 | //----------------------------------------------------------------------- |
|
431 | /** |
|
432 | * Returns the underlying enumeration. |
|
433 | * |
|
434 | * @return the underlying enumeration |
|
435 | */ |
|
436 | public Enumeration getEnumeration() { |
|
437 | 0 | 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 | 0 | this.enumeration = enumeration; |
447 | 0 | } |
448 | } |
|
449 | ||
450 | } |