001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.betwixt.expression;
019:
020: import java.lang.reflect.Array;
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.Enumeration;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.NoSuchElementException;
027:
028: /** <p><code>IteratorExpression</code> returns an iterator over the current context.</p>
029: *
030: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
031: * @version $Revision: 438373 $
032: */
033: public class IteratorExpression implements Expression {
034:
035: /** Use this <code>Expression</code> to perform initial evaluation*/
036: private Expression expression;
037:
038: /**
039: * Construct <code>IteratorExpression</code> using given expression for initial evaluation.
040: * @param expression this expression will be evaluated and the result converted to an
041: * iterator.
042: */
043: public IteratorExpression(Expression expression) {
044: this .expression = expression;
045: }
046:
047: /**
048: * Returns an interator over the current context
049: * @see org.apache.commons.betwixt.expression.Expression
050: */
051: public Object evaluate(Context context) {
052: // evaluate wrapped expression against context
053: Object value = expression.evaluate(context);
054:
055: // based on the class of the result,
056: // return an appropriate iterator
057: if (value instanceof Iterator) {
058: // if the value is an iterator, we're done
059: return (Iterator) value;
060:
061: } else if (value instanceof Collection) {
062: // if it's a collection, return an iterator for that collection
063: Collection collection = (Collection) value;
064: return collection.iterator();
065:
066: } else if (value instanceof Map) {
067: // if it's a map, return an iterator for the map entries
068: Map map = (Map) value;
069: return map.entrySet().iterator();
070:
071: } else if (value instanceof Enumeration) {
072: // if it's an enumeration, wrap it in an EnumerationIterator
073: return new EnumerationIterator((Enumeration) value);
074:
075: } else if (value != null) {
076: // if we have an array return an ArrayIterator
077: Class type = value.getClass();
078: if (type.isArray()) {
079: return new ArrayIterator(value);
080: }
081: }
082:
083: // we've got something we can't deal with
084: // so return an empty iterator
085: return Collections.EMPTY_LIST.iterator();
086: }
087:
088: /**
089: * Do nothing
090: * @see org.apache.commons.betwixt.expression.Expression
091: */
092: public void update(Context context, String newValue) {
093: // do nothing
094: }
095:
096: /**
097: * Returns something useful for logging
098: * @return string useful for logging
099: */
100: public String toString() {
101: return "IteratorExpression [expression=" + expression + "]";
102: }
103:
104: /**
105: * <code>ArrayIterator</code> originated in commons-collections. Added
106: * as a private inner class to break dependency.
107: *
108: * @author James Strachan
109: * @author Mauricio S. Moura
110: * @author Michael A. Smith
111: * @author Neil O'Toole
112: * @author Stephen Colebourne
113: */
114: private static final class ArrayIterator implements Iterator {
115:
116: /** The array to iterate over */
117: protected Object array;
118:
119: /** The start index to loop from */
120: protected int startIndex = 0;
121:
122: /** The end index to loop to */
123: protected int endIndex = 0;
124:
125: /** The current iterator index */
126: protected int index = 0;
127:
128: // Constructors
129: // ----------------------------------------------------------------------
130: /**
131: * Constructor for use with <code>setArray</code>.
132: * <p>
133: * Using this constructor, the iterator is equivalent to an empty
134: * iterator until {@link #setArray(Object)}is called to establish the
135: * array to iterate over.
136: */
137: public ArrayIterator() {
138: super ();
139: }
140:
141: /**
142: * Constructs an ArrayIterator that will iterate over the values in the
143: * specified array.
144: *
145: * @param array
146: * the array to iterate over.
147: * @throws IllegalArgumentException
148: * if <code>array</code> is not an array.
149: * @throws NullPointerException
150: * if <code>array</code> is <code>null</code>
151: */
152: public ArrayIterator(final Object array) {
153: super ();
154: setArray(array);
155: }
156:
157: /**
158: * Constructs an ArrayIterator that will iterate over the values in the
159: * specified array from a specific start index.
160: *
161: * @param array
162: * the array to iterate over.
163: * @param startIndex
164: * the index to start iterating at.
165: * @throws IllegalArgumentException
166: * if <code>array</code> is not an array.
167: * @throws NullPointerException
168: * if <code>array</code> is <code>null</code>
169: * @throws IndexOutOfBoundsException
170: * if the index is invalid
171: */
172: public ArrayIterator(final Object array, final int startIndex) {
173: super ();
174: setArray(array);
175: checkBound(startIndex, "start");
176: this .startIndex = startIndex;
177: this .index = startIndex;
178: }
179:
180: /**
181: * Construct an ArrayIterator that will iterate over a range of values
182: * in the specified array.
183: *
184: * @param array
185: * the array to iterate over.
186: * @param startIndex
187: * the index to start iterating at.
188: * @param endIndex
189: * the index to finish iterating at.
190: * @throws IllegalArgumentException
191: * if <code>array</code> is not an array.
192: * @throws NullPointerException
193: * if <code>array</code> is <code>null</code>
194: * @throws IndexOutOfBoundsException
195: * if either index is invalid
196: */
197: public ArrayIterator(final Object array, final int startIndex,
198: final int endIndex) {
199: super ();
200: setArray(array);
201: checkBound(startIndex, "start");
202: checkBound(endIndex, "end");
203: if (endIndex < startIndex) {
204: throw new IllegalArgumentException(
205: "End index must not be less than start index.");
206: }
207: this .startIndex = startIndex;
208: this .endIndex = endIndex;
209: this .index = startIndex;
210: }
211:
212: /**
213: * Checks whether the index is valid or not.
214: *
215: * @param bound
216: * the index to check
217: * @param type
218: * the index type (for error messages)
219: * @throws IndexOutOfBoundsException
220: * if the index is invalid
221: */
222: protected void checkBound(final int bound, final String type) {
223: if (bound > this .endIndex) {
224: throw new ArrayIndexOutOfBoundsException(
225: "Attempt to make an ArrayIterator that " + type
226: + "s beyond the end of the array. ");
227: }
228: if (bound < 0) {
229: throw new ArrayIndexOutOfBoundsException(
230: "Attempt to make an ArrayIterator that " + type
231: + "s before the start of the array. ");
232: }
233: }
234:
235: // Iterator interface
236: //-----------------------------------------------------------------------
237: /**
238: * Returns true if there are more elements to return from the array.
239: *
240: * @return true if there is a next element to return
241: */
242: public boolean hasNext() {
243: return (index < endIndex);
244: }
245:
246: /**
247: * Returns the next element in the array.
248: *
249: * @return the next element in the array
250: * @throws NoSuchElementException
251: * if all the elements in the array have already been
252: * returned
253: */
254: public Object next() {
255: if (hasNext() == false) {
256: throw new NoSuchElementException();
257: }
258: return Array.get(array, index++);
259: }
260:
261: /**
262: * Throws {@link UnsupportedOperationException}.
263: *
264: * @throws UnsupportedOperationException
265: * always
266: */
267: public void remove() {
268: throw new UnsupportedOperationException(
269: "remove() method is not supported");
270: }
271:
272: // Properties
273: //-----------------------------------------------------------------------
274: /**
275: * Gets the array that this iterator is iterating over.
276: *
277: * @return the array this iterator iterates over, or <code>null</code>
278: * if the no-arg constructor was used and
279: * {@link #setArray(Object)}has never been called with a valid
280: * array.
281: */
282: public Object getArray() {
283: return array;
284: }
285:
286: /**
287: * Sets the array that the ArrayIterator should iterate over.
288: * <p>
289: * If an array has previously been set (using the single-arg constructor
290: * or this method) then that array is discarded in favour of this one.
291: * Iteration is restarted at the start of the new array. Although this
292: * can be used to reset iteration, the {@link #reset()}method is a more
293: * effective choice.
294: *
295: * @param array
296: * the array that the iterator should iterate over.
297: * @throws IllegalArgumentException
298: * if <code>array</code> is not an array.
299: * @throws NullPointerException
300: * if <code>array</code> is <code>null</code>
301: */
302: public void setArray(final Object array) {
303: // Array.getLength throws IllegalArgumentException if the object is
304: // not
305: // an array or NullPointerException if the object is null. This call
306: // is made before saving the array and resetting the index so that
307: // the
308: // array iterator remains in a consistent state if the argument is
309: // not
310: // an array or is null.
311: this .endIndex = Array.getLength(array);
312: this .startIndex = 0;
313: this .array = array;
314: this .index = 0;
315: }
316:
317: /**
318: * Resets the iterator back to the start index.
319: */
320: public void reset() {
321: this .index = this .startIndex;
322: }
323:
324: }
325:
326: /**
327: * Adapter to make {@link Enumeration Enumeration}instances appear to be
328: * {@link Iterator Iterator}instances. Originated in commons-collections.
329: * Added as a private inner class to break dependency.
330: *
331: * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
332: * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall </a>
333: */
334: private static final class EnumerationIterator implements Iterator {
335:
336: /** The collection to remove elements from */
337: private Collection collection;
338:
339: /** The enumeration being converted */
340: private Enumeration enumeration;
341:
342: /** The last object retrieved */
343: private Object last;
344:
345: // Constructors
346: //-----------------------------------------------------------------------
347: /**
348: * Constructs a new <code>EnumerationIterator</code> that will not
349: * function until {@link #setEnumeration(Enumeration)} is called.
350: */
351: public EnumerationIterator() {
352: this (null, null);
353: }
354:
355: /**
356: * Constructs a new <code>EnumerationIterator</code> that provides
357: * an iterator view of the given enumeration.
358: *
359: * @param enumeration the enumeration to use
360: */
361: public EnumerationIterator(final Enumeration enumeration) {
362: this (enumeration, null);
363: }
364:
365: /**
366: * Constructs a new <code>EnumerationIterator</code> that will remove
367: * elements from the specified collection.
368: *
369: * @param enumeration the enumeration to use
370: * @param collection the collection to remove elements form
371: */
372: public EnumerationIterator(final Enumeration enumeration,
373: final Collection collection) {
374: super ();
375: this .enumeration = enumeration;
376: this .collection = collection;
377: this .last = null;
378: }
379:
380: // Iterator interface
381: //-----------------------------------------------------------------------
382: /**
383: * Returns true if the underlying enumeration has more elements.
384: *
385: * @return true if the underlying enumeration has more elements
386: * @throws NullPointerException if the underlying enumeration is null
387: */
388: public boolean hasNext() {
389: return enumeration.hasMoreElements();
390: }
391:
392: /**
393: * Returns the next object from the enumeration.
394: *
395: * @return the next object from the enumeration
396: * @throws NullPointerException if the enumeration is null
397: */
398: public Object next() {
399: last = enumeration.nextElement();
400: return last;
401: }
402:
403: /**
404: * Removes the last retrieved element if a collection is attached.
405: * <p>
406: * Functions if an associated <code>Collection</code> is known.
407: * If so, the first occurrence of the last returned object from this
408: * iterator will be removed from the collection.
409: *
410: * @exception IllegalStateException <code>next()</code> not called.
411: * @exception UnsupportedOperationException if no associated collection
412: */
413: public void remove() {
414: if (collection != null) {
415: if (last != null) {
416: collection.remove(last);
417: } else {
418: throw new IllegalStateException(
419: "next() must have been called for remove() to function");
420: }
421: } else {
422: throw new UnsupportedOperationException(
423: "No Collection associated with this Iterator");
424: }
425: }
426:
427: // Properties
428: //-----------------------------------------------------------------------
429: /**
430: * Returns the underlying enumeration.
431: *
432: * @return the underlying enumeration
433: */
434: public Enumeration getEnumeration() {
435: return enumeration;
436: }
437:
438: /**
439: * Sets the underlying enumeration.
440: *
441: * @param enumeration the new underlying enumeration
442: */
443: public void setEnumeration(final Enumeration enumeration) {
444: this.enumeration = enumeration;
445: }
446: }
447:
448: }
|