1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (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.mozilla.org/MPL/MPL-1.1.html
     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  import org.as2lib.core.BasicClass;
    18  import org.as2lib.env.except.IllegalArgumentException;
    19  import org.as2lib.env.except.IllegalStateException;
    20  import org.as2lib.data.holder.Iterator;
    21  import org.as2lib.data.holder.NoSuchElementException;
    22  
    23  /**
    24   * {@code ArrayIterator} can be used to iterate over arrays.
    25   *
    26   * <p>The usage of this iterator is quite simple. There is one method to check
    27   * whether there are more elements left to iterate over {@link #hasNext}, one method
    28   * to get the next element {@link #next} and one to remove the current element
    29   * {@link #remove}.
    30   * 
    31   * <p>Example:
    32   * <code>
    33   *   var iterator:Iterator = new ArrayIterator(["value1", "value2", "value3"]);
    34   *   while (iterator.hasNext()) {
    35   *       trace(iterator.next());
    36   *   }
    37   * </code>
    38   * <p>Output:
    39   * <pre>
    40   *   value1
    41   *   value2
    42   *   value3
    43   * </pre>
    44   *
    45   * @author Simon Wacker
    46   * @author Michael Herrmann
    47   * @author Martin Heidegger
    48   */
    49   class org.as2lib.data.holder.array.ArrayIterator extends BasicClass implements Iterator {
    50  	
    51  	/** The target data holder. */
    52  	private var t:Array;
    53  	
    54  	/** The current index of the iteration. */
    55  	private var i:Number;
    56  	
    57  	/**
    58  	 * Constructs a new {@code ArrayIterator} instance.
    59  	 * 
    60  	 * @param target the array to iterate over
    61  	 * @throws IllegalArgumentException if the passed-in {@code target} array is
    62  	 * {@code null} or {@code undefined}
    63  	 */
    64  	public function ArrayIterator(target:Array) {
    65  		// IllegalArgumentException if the passed array is not available.
    66  		if (!target) throw new IllegalArgumentException("Argument 'target' [" + target + "] must not be 'null' nor 'undefined'.", this, arguments);
    67  
    68  		// Usual handling of the arguments.
    69  		this.t = target;
    70  		i = -1;
    71  		
    72  		// Prepare of fast internal replacement
    73  		var t:Array = target;
    74  		var g:Number = -1;
    75  		var p:Object = ArrayIterator.prototype;
    76  		var s:ArrayIterator = this;
    77  		
    78  		// Replacement of internal methods as performance upgrade.
    79  		// - Only if this class is used, else the OOP functionality would be broken.
    80  		// - With more than 50 elements, else this method would be slower
    81  		if (this["__proto__"] == p && t.length > 50) {
    82  			
    83  			// Replace for .next() if .hasNext was not called
    84  			var y:Function = function() {
    85  				if(g < t.length-1) {
    86  					arguments.callee = p.next;
    87  					throw new NoSuchElementException("There is no more element.", this, arguments);
    88  				}
    89  				return t[++g];
    90  			};
    91  			// Replace for .next() if .hasNext was called and there is something next
    92  			var x:Function = function() {
    93  				s.next = y;
    94  				return t[++g];
    95  			};
    96  			// Replace for .next() if .hasNext found that there is no next
    97  			var z:Function = function() {
    98  				s.next = y;
    99  				arguments.callee = p.next;
   100  				throw new NoSuchElementException("There is no more element.", this, arguments);
   101  			};
   102  			// .next replacement
   103  			next = y;
   104  			// .hasNext replacement
   105  			hasNext = function() {
   106  				if(g < t.length-1) {
   107  					s.next = x;
   108  					return true;
   109  				} else {
   110  					s.next = z;
   111  					return false; 
   112  				}
   113  			};
   114  			// .remove replacement
   115  			remove = function() {
   116  				if (g < 0) {
   117  					arguments.callee = p.remove;
   118  					throw new IllegalStateException("You tried to remove an element before calling the 'next' method. There is thus no element selected to remove.", this, arguments);
   119  				}
   120  				t.splice(g--, 1);
   121  			};
   122  		}
   123  	}
   124  	
   125  	/**
   126  	 * Returns whether there exists another element to iterate over.
   127  	 * 
   128  	 * @return {@code true} if there is at least one lement left to iterate over
   129  	 */
   130  	public function hasNext(Void):Boolean {
   131  		return (i < t.length-1);
   132  	}
   133  	
   134  	/**
   135  	 * Returns the next element of the array.
   136  	 * 
   137  	 * @return the next element of the array
   138  	 * @throws NoSuchElementException if there is no next element
   139  	 */
   140  	public function next(Void) {
   141  		if (!hasNext()) {
   142  			throw new NoSuchElementException("There is no more element.", this, arguments);
   143  		}
   144  		return t[++i];
   145  	}
   146  	
   147  	/**
   148  	 * Removes the currently selected element from this iterator and from the array this
   149  	 * iterator iterates over.
   150  	 * 
   151  	 * @throws IllegalStateException if you try to remove an element when none is selected
   152  	 */
   153  	public function remove(Void):Void {
   154  		if (i < 0) {
   155  			throw new IllegalStateException("You tried to remove an element before calling the 'next' method. There is thus no element selected to remove.", this, arguments);
   156  		}
   157  		t.splice(i--, 1);
   158  	}
   159  	
   160  }
   161