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.data.holder.Stack;
    19  import org.as2lib.data.holder.Iterator;
    20  import org.as2lib.data.holder.ProtectedIterator;
    21  import org.as2lib.data.holder.array.ArrayIterator;
    22  import org.as2lib.data.holder.EmptyDataHolderException;
    23  import org.as2lib.util.Stringifier;
    24  import org.as2lib.data.holder.stack.StackStringifier;
    25  
    26  /**
    27   * {@code SimpleStack} holds data in a 'last-in, first-out' manner.
    28   * 
    29   * <p>It is a simple implementation of the {@code Stack} interface and realizes all
    30   * its basic concepts.
    31   * 
    32   * <p>'last-in, first-out' means that the last value that has been pushed to the
    33   * stack is the first that is popped from the stack.
    34   *
    35   * <p>The usage of a stack is quite simple. You have one method to push values,
    36   * {@link #push}, and one method to pop values, {@link #pop}. You can also peek at
    37   * the top of the stack to see what's the last value that has been pushed to the
    38   * stack without removing it {@link #peek}.
    39   * 
    40   * <p>If you want to iterate over the values of the stack you can either use the
    41   * iterator returned by the {@link #iterator} method or the array that contains the
    42   * stack's values returned by the {@link #toArray} method.
    43   * 
    44   * <p>The two methods {@link #isEmpty} and {@link #size} let you find out whether
    45   * the stack contains values and how many values it contains.
    46   * 
    47   * <p>You can modify the string representation that is returned by the {@link #toString}
    48   * method using the static {@link #setStringifier} method.
    49   *
    50   * <p>Example:
    51   * <code>
    52   *   // the stack is constructed somewhere
    53   *   var stack:Stack = new SimpleStack();
    54   *   stack.push("value1");
    55   *   stack.push("value2");
    56   *   stack.push("value3");
    57   *   // the stack is used
    58   *   trace(stack.peek());
    59   *   while (!stack.isEmpty()) {
    60   *       trace(stack.pop());
    61   *   }
    62   * </code>
    63   *
    64   * <p>Output:
    65   * <pre>
    66   *   value3
    67   *   value3
    68   *   value2
    69   *   value1
    70   * </pre>
    71   * 
    72   * <p>You can alternatively pass-in the content of the stack on construction.
    73   * <code>
    74   *   var stack:Stack = new SimpleStack(["value1", "value2", "value3"]);
    75   *   // ..
    76   * </code>
    77   *
    78   * @author Simon Wacker
    79   */
    80  class org.as2lib.data.holder.stack.SimpleStack extends BasicClass implements Stack {
    81  	
    82  	/** Stringifies stacks. */
    83  	private static var stringifier:Stringifier;
    84  	
    85  	/** Contains the inserted values. */
    86  	private var values:Array;
    87  	
    88  	/**
    89  	 * Returns the stringifier that stringifies stacks.
    90  	 *
    91  	 * <p>If no stringifier has been set manually the default stringifier will be returned
    92  	 * which is an instance of class {@link StackStringifier}.
    93  	 * 
    94  	 * @return the stringifier that stringifies stacks
    95  	 */
    96  	public static function getStringifier(Void):Stringifier {
    97  		if (!stringifier) stringifier = new StackStringifier();
    98  		return stringifier;
    99  	}
   100  	
   101  	/**
   102  	 * Sets the new stringifier that stringifies stacks.
   103  	 *
   104  	 * <p>If the passed-in {@code stackStringifier} is {@code null} or {@code undefined}
   105  	 * the static {@link #getStringifier} method will return the default stringifier.
   106  	 * 
   107  	 * @param stackStringifier the new stack stringifier
   108  	 */
   109  	public static function setStringifier(stackStringifier:Stringifier):Void {
   110  		stringifier = stackStringifier;
   111  	}
   112  	
   113  	/**
   114  	 * Constructs a new {@code SimpleStack} instance.
   115  	 *
   116  	 * <p>The stack steps through the passed-in {@code source} beginning at position
   117  	 * 0 and pushes all contained elements to this stack.
   118  	 * 
   119  	 * <code>
   120  	 *   var stack:SimpleStack = new SimpleStack([1, 2, 3]);
   121   	 *   while (!stack.isEmpty()) {
   122  	 * 	     trace(stack.pop());
   123  	 *   }
   124  	 * </code>
   125  	 *
   126  	 * <p>The output is made in the following order: 3, 2, 1
   127  	 * 
   128  	 * @param source (optional) an array that contains values to populate this stack with
   129  	 */
   130  	public function SimpleStack(source:Array) {
   131  		if (source) {
   132  			values = source.concat();
   133  		} else {
   134  			values = new Array();
   135  		}
   136  	}
   137  	
   138  	/**
   139  	 * Pushes the passed-in {@code value} to this stack.
   140  	 *
   141  	 * <p>{@code null} or {@code undefined} values are allowed.
   142  	 *
   143  	 * @param value the value to push to this stack
   144  	 */
   145  	public function push(value):Void {
   146  		values.push(value);
   147  	}
   148  	
   149  	/**
   150  	 * Removes and returns the lastly pushed value.
   151  	 *
   152  	 * @return the lastly pushed value
   153  	 * @throws EmptyDataHolderException if this stack is empty
   154  	 */
   155  	public function pop(Void) {
   156  		if (isEmpty()) {
   157  			throw new EmptyDataHolderException("You tried to pop an element from an empty Stack.", this, arguments);
   158  		}
   159  		return values.pop();
   160  	}
   161  	
   162  	/**
   163  	 * Returns the lastly pushed value without removing it.
   164  	 *
   165  	 * @return the lastly pushed value
   166  	 * @throws EmptyDataHolderException if this stack is empty
   167  	 */
   168  	public function peek(Void) {
   169  		if (isEmpty()) {
   170  			throw new EmptyDataHolderException("You tried to peek an element from an empty Stack.", this, arguments);
   171  		}
   172  		return values[values.length-1];
   173  	}
   174  	
   175  	/**
   176  	 * Returns an iterator to iterate over the values of this stack.
   177  	 *
   178  	 * @return an iterator to iterate over this stack
   179  	 * @see #toArray
   180  	 */
   181  	public function iterator(Void):Iterator {
   182  		var reversedValues:Array = values.slice();
   183  		reversedValues.reverse();
   184  		return (new ProtectedIterator(new ArrayIterator(reversedValues)));
   185  	}
   186  	
   187  	/**
   188  	 * Returns whether this stack is empty.
   189  	 *
   190  	 * @return {@code true} if this stack is empty else {@code false}
   191  	 */
   192  	public function isEmpty(Void):Boolean {
   193  		return (values.length < 1);
   194  	}
   195  	
   196  	/**
   197  	 * Returns the number of pushed values.
   198  	 *
   199  	 * @return the number of pushed values
   200  	 * @see #push
   201  	 */
   202  	public function size(Void):Number {
   203  		return values.length;
   204  	}
   205  	
   206  	/**
   207  	 * Returns the array representation of this stack.
   208  	 * 
   209  	 * <p>The elements are copied onto the array in a 'last-in, first-out' order, similar
   210  	 * to the order of the elements returned by a succession of calls to the {@link #pop}
   211  	 * method.
   212  	 *
   213  	 * @return the array representation of this stack
   214  	 */
   215  	public function toArray(Void):Array {
   216  		var result:Array = values.concat();
   217  		result.reverse();
   218  		return result;
   219  	}
   220  	
   221  	/**
   222  	 * Returns the string representation of this stack.
   223  	 * 
   224  	 * <p>The string representation is obtained using the stringifier returned by the
   225  	 * static {@link #getStringifier} method.
   226  	 *
   227  	 * @return the string representation of this stack
   228  	 */
   229  	public function toString():String {
   230  		return getStringifier().execute(this);
   231  	}
   232  	
   233  }