Coverage Report - org.apache.commons.betwixt.expression.IteratorExpression

Classes in this File Line Coverage Branch Coverage Complexity
IteratorExpression
47% 
62% 
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  
 }