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.util.ObjectUtil;
    19  import org.as2lib.env.except.IllegalArgumentException;
    20  import org.as2lib.env.reflect.ReflectUtil;
    21  import org.as2lib.data.holder.Stack;
    22  import org.as2lib.data.holder.Iterator;
    23  
    24  /**
    25   * {@code TypedStack} is a wrapper for {@link Stack} instances that ensures that
    26   * only values of a specific type can be added to the wrapped stack.
    27   * 
    28   * <p>This class simply delegates all method invocations to the wrapped stack. If
    29   * the specific method is responsible for adding values it first checks if the values
    30   * to add are of the expected type. If they are the method invocation is forwarded,
    31   * otherwise an {@link IllegalArgumentException} is thrown.
    32   *
    33   * @author Simon Wacker
    34   */
    35  class org.as2lib.data.holder.stack.TypedStack extends BasicClass implements Stack {
    36  	
    37  	/** The wrapped Stack. */
    38  	private var stack:Stack;
    39  	
    40  	/** The type of the values that are allowed. */
    41  	private var type:Function;
    42  	
    43  	/**
    44  	 * Constructs a new {@code TypedStack} instance.
    45  	 *
    46  	 * <p>If the passed-in stack does already contain values, these values do not get
    47  	 * type-checked.
    48  	 * 
    49  	 * @param type the type of values the stack can have
    50  	 * @param stack the stack to type-check
    51  	 * @throws IllegalArgumentException if the passed-in {@code type} is {@code null}
    52  	 * or {@code undefined}
    53  	 * @throws IllegalArgumentException if {@code stack} is {@code null} or
    54  	 * {@code undefined}
    55  	 */
    56  	public function TypedStack(type:Function, stack:Stack) {
    57  		if (!type) throw new IllegalArgumentException("Argument 'type' [" + type + "] must not be 'null' nor 'undefined'.", this, arguments);
    58  		if (!stack) throw new IllegalArgumentException("Argument 'stack' [" + stack + "] must not be 'null' nor 'undefined'.", this, arguments);
    59  		this.type = type;
    60  		this.stack = stack;
    61  	}
    62  	
    63  	/**
    64  	 * Returns the type that all values in the wrapped stack have.
    65  	 * 
    66  	 * <p>This is the type passed-in on construction.
    67  	 *
    68  	 * @return the type that all values of the wrapped stack have
    69  	 */
    70  	public function getType(Void):Function {
    71  		return type;
    72  	}
    73  	
    74  	/**
    75  	 * Pushes the passed-in value to this stack.
    76  	 *
    77  	 * <p>The value is only pushed if it is of the expected type.
    78  	 * 
    79  	 * @param value the value to push to this stack
    80  	 * @throws IllegalArgumentException if the type of the passed-in {@code value} is
    81  	 * invalid
    82  	 */
    83  	public function push(value):Void {
    84  		validate(value);
    85  		stack.push(value);
    86  	}
    87  	
    88  	/**
    89  	 * Removes and returns the lastly pushed value.
    90  	 * 
    91  	 * @return the lastly pushed value
    92  	 * @throws org.as2lib.data.holder.EmptyDataHolderException if this stack is empty
    93  	 */
    94  	public function pop(Void) {
    95  		return stack.pop();
    96  	}
    97  	
    98  	/**
    99  	 * Returns the lastly pushed value without removing it.
   100  	 * 
   101  	 * @return the lastly pushed value
   102  	 * @throws org.as2lib.data.holder.EmptyDataHolderException if this stack is empty
   103  	 */
   104  	public function peek(Void) {
   105  		return stack.peek();
   106  	}
   107  	
   108  	/**
   109  	 * Returns an iterator to iterate over the values of this stack.
   110  	 *
   111  	 * @return an iterator to iterate over this stack's values
   112  	 * @see #toArray
   113  	 */
   114  	public function iterator(Void):Iterator {
   115  		return stack.iterator();
   116  	}
   117  	
   118  	/**
   119  	 * Returns whether this stack is empty.
   120  	 *
   121  	 * @return {@code true} if this stack is empty else {@code false}
   122  	 */
   123  	public function isEmpty(Void):Boolean {
   124  		return stack.isEmpty();
   125  	}
   126  	
   127  	/**
   128  	 * Returns the number of pushed values.
   129  	 *
   130  	 * @return the number of pushed values
   131  	 * @see #push
   132  	 */
   133  	public function size(Void):Number {
   134  		return stack.size();
   135  	}
   136  	
   137  	/**
   138  	 * Returns an array representation of this stack.
   139  	 * 
   140  	 * <p>The elements are copied onto the array in a 'last-in, first-out' order, similar
   141  	 * to the order of the elements returned by a succession of calls to the {@link #pop}
   142  	 * method.
   143  	 *
   144  	 * @return the array representation of this stack
   145  	 */
   146  	public function toArray(Void):Array {
   147  		return stack.toArray();
   148  	}
   149  	
   150  	/**
   151  	 * Returns the string representation of the wrapped stack.
   152  	 *
   153  	 * @return the string representation of the wrapped stack
   154  	 */
   155  	public function toString():String {
   156  		return stack.toString();
   157  	}
   158  	
   159  	/**
   160  	 * Validates the passed-in {@code value} based on its type.
   161  	 *
   162  	 * @param value the value whose type to validate
   163  	 * @throws IllegalArgumentException if the type of the value is invalid
   164  	 */
   165  	private function validate(value):Void {
   166  		if (!ObjectUtil.typesMatch(value, type)) {
   167  			throw new IllegalArgumentException("Type mismatch between value [" + value + "] and type [" + ReflectUtil.getTypeNameForType(type) + "].", this, arguments);
   168  		}
   169  	}
   170  	
   171  }