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.env.except.IllegalArgumentException;
    18  import org.as2lib.data.holder.IndexOutOfBoundsException;
    19  import org.as2lib.data.holder.Iterator;
    20  import org.as2lib.data.holder.List;
    21  import org.as2lib.data.holder.list.AbstractList;
    22  import org.as2lib.data.holder.list.ListIterator;
    23  
    24  /**
    25   * {@code SubList} represents a part of a wrapped list. This part is specified by a
    26   * range from one index to another index. Every changes that are made to this list are
    27   * actually made to the wrapped list. You can nevertheless treat this list
    28   * implementation as any other; there is no difference in usage.
    29   * 
    30   * @author Simon Wacker
    31   */
    32  class org.as2lib.data.holder.list.SubList extends AbstractList implements List {
    33  	
    34  	/** Makes the static variables of the super-class accessible through this class. */
    35  	private static var __proto__:Function = AbstractList;
    36  	
    37  	/** The list this is a sub-list of. */
    38  	private var list:List;
    39  	
    40  	/** The start index in the main-list. */
    41  	private var offset:Number;
    42  	
    43  	/** The size of this sub-list. */
    44  	private var length:Number;
    45  	
    46  	/**
    47  	 * Constructs a new {@code SubList} instance.
    48  	 * 
    49  	 * @param list the list this is a sub-list of
    50  	 * @param fromIndex the start index of this sub-list (inclusive)
    51  	 * @param toIndex the end index of this sub-list (exclusive)
    52  	 * @throws IllegalArgumentException if argument {@code list} is {@code null} or
    53  	 * {@code undefined}
    54  	 * @throws IndexOutOfBoundsException if argument {@code fromIndex} is less than 0
    55  	 * @throws IndexOutOfBoundsException if argument {@code toIndex} is greater than
    56  	 * the size of the passed-in {@code list}
    57  	 * @throws IndexOutOfBoundsException if argument {@code fromIndex} is greater than
    58  	 * {@code toIndex}
    59  	 */
    60  	public function SubList(list:List, fromIndex:Number, toIndex:Number) {
    61  		if (!list) throw new IllegalArgumentException("Argument 'list' [" + list + "] must not be 'null' nor 'undefined'.", this, arguments);
    62  		if (fromIndex < 0) throw new IndexOutOfBoundsException("Argument 'fromIndex' [" + fromIndex + "] must not be less than 0.", this, arguments);
    63  		if (toIndex > list.size()) throw new IndexOutOfBoundsException("Argument 'toIndex' [" + toIndex + "] must not be greater than the size of the passed-in 'list' [" + list.size() + "].", this, arguments);
    64  		if (fromIndex > toIndex) throw new IndexOutOfBoundsException("Argument 'fromIndex' [" + fromIndex + "] must not be greater than argument 'toIndex' [" + toIndex + "].", this, arguments);
    65  		this.list = list;
    66  		this.offset = fromIndex;
    67  		this.length = toIndex - fromIndex;
    68  	}
    69  	
    70  	/**
    71  	 * Sets {@code value} to given {@code index} on this list.
    72  	 * 
    73  	 * @param index the index of {@code value}
    74  	 * @param value the {@code value} to set to given {@code index}
    75  	 * @return the value that was orignially at given {@code index}
    76  	 * @throws IndexOutOfBoundsException if given {@code index} is less than 0 or
    77  	 * equal to or greater than this list's size
    78  	 */
    79  	public function set(index:Number, value) {
    80  		if (index < 0 || index >= size()) {
    81  			throw new IndexOutOfBoundsException("Argument 'index' [" + index + "] is out of range, this is less than 0 or equal to or greater than this list's size [" + size() + "].", this, arguments);
    82  		}
    83  		return this.list.set(offset + index, value);
    84  	}
    85  	
    86  	/**
    87  	 * Returns the value at given {@code index}.
    88  	 * 
    89  	 * @param index the index to return the value of
    90  	 * @return the value that is at given {@code index}
    91  	 * @throws IndexOutOfBoundsException if given {@code index} is less than 0 or
    92  	 * equal to or greater than this list's size
    93  	 */
    94  	public function get(index:Number) {
    95  		if (index < 0 || index >= size()) {
    96  			throw new IndexOutOfBoundsException("Argument 'index' [" + index + "] is out of range, this is less than 0 or equal to or greater than this list's size [" + size() + "].", this, arguments);
    97  		}
    98  		return this.list.get(offset + index);
    99  	}
   100  	
   101  	/**
   102  	 * Inserts {@code value} at the given {@code index}.
   103  	 * 
   104  	 * <p>The element that is currently at the given {@code index} is shifted by one to
   105  	 * the right, as well as any subsequent elements.
   106  	 * 
   107  	 * @param index the index at which to insert the {@code value}
   108  	 * @param value the value to insert
   109  	 * @throws IndexOutOfBoundsException if the given {@code index} is not in range,
   110  	 * this is less than 0 or greater than this list's size
   111  	 */
   112  	public function insertByIndexAndValue(index:Number, value):Void {
   113  		if (index < 0 || index > size()) {
   114  			throw new IndexOutOfBoundsException("Argument 'index' [" + index + "] is out of range, this is less than 0 or greater than this list's size [" + size() + "].", this, arguments);
   115  		}
   116  		this.list.insertByIndexAndValue(offset + index, value);
   117  		this.length++;
   118  	}
   119  	
   120  	/**
   121  	 * Removes the value at given {@code index} from this list and returns it.
   122  	 * 
   123  	 * @param index the index of the value to remove
   124  	 * @return the removed value that was originally at given {@code index}
   125  	 * @throws IndexOutOfBoundsException if given {@code index} is less than 0 or
   126  	 * equal to or greater than this list's size
   127  	 */
   128  	public function removeByIndex(index:Number) {
   129  		if (index < 0 || index >= size()) {
   130  			throw new IndexOutOfBoundsException("Argument 'index' [" + index + "] is out of range, this is less than 0 or equal to or greater than this list's size [" + size() + "].", this, arguments);
   131  		}
   132  		// list.removeByIndex may throw an exception, in this case the size must not be reduced
   133  		var result = this.list.removeByIndex(offset + index);
   134  		this.length--;
   135  		return result;
   136  	}
   137  	
   138  	/**
   139  	 * Removes all values from this list.
   140  	 */
   141  	public function clear(Void):Void {
   142  		do {
   143  			this.list.removeByIndex(this.offset);
   144  		} while (--this.length > 0);
   145  	}
   146  	
   147  	/**
   148  	 * Returns the number of added values.
   149  	 * 
   150  	 * @return the number of added values
   151  	 */
   152  	public function size(Void):Number {
   153  		return this.length;
   154  	}
   155  	
   156  	/**
   157  	 * Returns the iterator to iterate over this list.
   158  	 * 
   159  	 * @return the iterator to iterate over this list
   160  	 */
   161  	public function iterator(Void):Iterator {
   162  		return new ListIterator(this);
   163  	}
   164  	
   165  	/**
   166  	 * Returns the array representation of this list.
   167  	 * 
   168  	 * @return the array representation of this list
   169  	 */
   170  	public function toArray(Void):Array {
   171  		return this.list.toArray().slice(this.offset, this.offset + this.length);
   172  	}
   173  	
   174  }