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.data.holder.map.AbstractMap;
    18  import org.as2lib.data.holder.Map;
    19  import org.as2lib.data.holder.map.ValueMapIterator;
    20  import org.as2lib.data.holder.map.KeyMapIterator;
    21  import org.as2lib.data.holder.Iterator;
    22  
    23  /**
    24   * {@code HashMap} can be used to map any type of key to any type of value.
    25   * 
    26   * <p>This class offers ordered mapping functionality. That means that the methods
    27   * {@link #getKeys} and {@link #getValues} return the keys and values in the order
    28   * they were put to the map and that the iterators returned by the methods
    29   * {@link #valueIterator} and {@link #keyIterator} also iterate over the keys and
    30   * values in the correct order.
    31   *
    32   * <p>This map offers two methods that help you find out whether it contains a
    33   * specific key or value. These two methods are {@link #containsKey} and
    34   * {@link #containsValue}.
    35   * 
    36   * <p>To get the data stored in this map you can use the {@link #getKeys},
    37   * {@link #getValues} and {@link #get} methods. If you want to iterate over the
    38   * values of this map you can use the iterators returned by the methods {@link #iterator}
    39   * or {@link #valueIterator}. If you want to iterate over the keys you can use the
    40   * iterator returned by the {@link #keyIterator} method.
    41   *
    42   * <p>To add key-value pairs to this map you can use the methods {@link #put} and
    43   * {@link #putAll}. The {@code putAll} method lets you add all key-value pairs
    44   * contained in the passed-in {@code map} to this map.
    45   * 
    46   * <p>To remove key-value pairs you can use the methods {@link #remove} and
    47   * {@link #clear}. The {@code remove} method deletes only the key-value pair
    48   * corresponding to the passed-in {@code key}, while the clear method removes all
    49   * key-value pairs.
    50   *
    51   * <p>There are two more methods you may need. The {@link #isEmpty} and the {@link #size}
    52   * method. These methods give you information about whether this map contains any
    53   * mappings and how many mappings it contains.
    54   *
    55   * <p>To change the string representation returned by the {@link #toString}
    56   * method you can set your own stringifier using the static
    57   * {@link AbstractMap#setStringifier} method.
    58   * 
    59   * <p>Example:
    60   * <code>
    61   *   // constructs the map
    62   *   var key1:Object = new Object();
    63   *   var key2:Object = new Object();
    64   *   var key3:Object = new Object();
    65   *   var map:Map = new HashMap();
    66   *   map.put(key1, "value1");
    67   *   map.put(key2, "value2");
    68   *   map.put(key3, "value3");
    69   *   // uses the map
    70   *   trace(map.get(key1));
    71   *   trace(map.get(key2));
    72   *   trace(map.get(key3));
    73   * </code>
    74   *
    75   * <p>Output:
    76   * <pre>
    77   *   value1
    78   *   value2
    79   *   value3
    80   * </pre>
    81   *
    82   * @author Simon Wacker
    83   * @author Michael Herrmann
    84   */
    85  class org.as2lib.data.holder.map.HashMap extends AbstractMap implements Map {
    86  	
    87  	/** Makes the static variables of the super-class accessible through this class. */
    88  	private static var __proto__:Function = AbstractMap;
    89  	
    90  	/** Contains the keys. */
    91  	private var keys:Array;
    92  	
    93  	/** Contains the values. */
    94  	private var values:Array;
    95  	
    96  	/**
    97  	 * Constructs a new {@code HashMap} instance.
    98  	 *
    99  	 * <p>This map iterates over the passed-in source with the for..in loop and uses the
   100  	 * variables' names as key and their values as value. Variables that are hidden from
   101  	 * for..in loops will not be added to this map.
   102  	 * 
   103  	 * @param source (optional) an object that contains key-value pairs to populate this
   104  	 * map with
   105  	 */
   106  	public function HashMap(source) {
   107  		keys = new Array();
   108  		values = new Array();
   109  		populate(source);
   110  	}
   111  
   112  	/**
   113  	 * Checks if the passed-in {@code key} exists.
   114  	 *
   115  	 * <p>That means whether a value has been mapped to it.
   116  	 *
   117  	 * @param key the key to be checked for availability
   118  	 * @return {@code true} if the {@code key} exists else {@code false}
   119  	 */
   120  	public function containsKey(key):Boolean {
   121  		return (findKey(key) > -1);
   122  	}
   123  	
   124  	/**
   125  	 * Checks if the passed-in {@code value} is mapped to a key.
   126  	 * 
   127  	 * @param value the value to be checked for availability
   128  	 * @return {@code true} if the {@code value} is mapped to a key else {@code false}
   129  	 */
   130  	public function containsValue(value):Boolean {
   131  		return (findValue(value) > -1);
   132  	}
   133  	
   134  	/**
   135  	 * Returns an array that contains all keys that have a value mapped to it.
   136  	 * 
   137  	 * @return an array that contains all keys
   138  	 */
   139  	public function getKeys(Void):Array {
   140  		return keys.slice();
   141  	}
   142  	
   143  	/**
   144  	 * Returns an array that contains all values that are mapped to a key.
   145  	 *
   146  	 * @return an array that contains all mapped values
   147  	 */
   148  	public function getValues(Void):Array {
   149  		return values.slice();
   150  	}
   151  	
   152  	/**
   153  	 * Returns the value that is mapped to the passed-in {@code key}.
   154  	 *
   155  	 * @param key the key to return the corresponding value for
   156  	 * @return the value corresponding to the passed-in {@code key}
   157  	 */
   158  	public function get(key) {
   159  		return values[findKey(key)];
   160  	}
   161  	
   162  	/**
   163  	 * Maps the given {@code key} to the {@code value}.
   164  	 *
   165  	 * <p>Both {@code key} and {@code value} are allowed to be {@code null} and
   166  	 * {@code undefined}.
   167  	 * 
   168  	 * @param key the key used as identifier for the {@code value}
   169  	 * @param value the value to map to the {@code key}
   170  	 * @return the value that was originally mapped to the {@code key} or {@code undefined}
   171  	 */
   172  	public function put(key, value) {
   173  		var result;
   174  		var i:Number = findKey(key); 
   175  		if(i < 0) {
   176  			keys.push(key);
   177  			values.push(value);
   178  		} else {
   179  			result = values[i];
   180  			values[i] = value;
   181  		}
   182  		return result;
   183  	}
   184  	
   185  	/**
   186  	 * Copies all mappings from the passed-in {@code map} to this map.
   187  	 *
   188  	 * @param map the mappings to add to this map
   189  	 */
   190  	public function putAll(map:Map):Void {
   191  		var values:Array = map.getValues();
   192  		var keys:Array = map.getKeys();
   193  		var l:Number = keys.length;
   194  		for (var i:Number = 0; i < l; i = i-(-1)) {
   195  			put(keys[i], values[i]);
   196  		}
   197  	}
   198  	
   199  	/**
   200  	 * Clears all mappings.
   201  	 */
   202  	public function clear(Void):Void {
   203  		keys = new Array();
   204  		values = new Array();
   205  	}
   206  	
   207  	/**
   208  	 * Removes the mapping from the given {@code key} to the value.
   209  	 *
   210  	 * @param key the key identifying the mapping to remove
   211  	 * @return the value that was originally mapped to the {@code key}
   212  	 */
   213  	public function remove(key) {
   214  		var i:Number = findKey(key);
   215  		if(i > -1) {
   216  			var result = values[i];
   217  			values.splice(i, 1);
   218  			keys.splice(i, 1);
   219  			return result;
   220  		}
   221  		return;
   222  	}
   223  	
   224  	/**
   225  	 * Returns an iterator to iterate over the values of this map.
   226  	 *
   227  	 * @return an iterator to iterate over the values of this map
   228  	 * @see #valueIterator
   229  	 * @see #getValues
   230  	 */
   231  	public function iterator(Void):Iterator {
   232  		return new ValueMapIterator(this);
   233  	}
   234  	
   235  	/**
   236  	 * Returns an iterator to iterate over the values of this map.
   237  	 *
   238  	 * @return an iterator to iterate over the values of this map
   239  	 * @see #iterator
   240  	 * @see #getValues
   241  	 */
   242  	public function valueIterator(Void):Iterator {
   243  		return iterator();
   244  	}
   245  	
   246  	/**
   247  	 * Returns an iterator to iterate over the keys of this map.
   248  	 *
   249  	 * @return an iterator to iterate over the keys of this map
   250  	 * @see #getKeys
   251  	 */
   252  	public function keyIterator(Void):Iterator {
   253  		return new KeyMapIterator(this);
   254  	}
   255  
   256  	/**
   257  	 * Returns the amount of mappings.
   258  	 *
   259  	 * @return the amount of mappings
   260  	 */
   261  	public function size(Void):Number {
   262  		return keys.length;
   263  	}
   264  	
   265  	/**
   266  	 * Returns whether this map contains any mappings.
   267  	 * 
   268  	 * @return {@code true} if this map contains no mappings else {@code false}
   269  	 */
   270  	public function isEmpty(Void):Boolean {
   271  		return (size() < 1);
   272  	}
   273  	
   274  	/**
   275  	 * Searches for the given {@code value} and returns the index where it is stored.
   276  	 *
   277  	 * @param value the value to search for
   278  	 * @return the index where the {@code value} is stored
   279  	 */
   280  	private function findValue(value):Number {
   281  		var l:Number = values.length;
   282  		while (values[--l] !== value && l>-1);
   283  		return l;
   284  	}
   285  	
   286  	/**
   287  	 * Searches for the given {@code key} and returns the index where it is stored.
   288  	 * 
   289  	 * @param key the key to search for
   290  	 * @return the index where the {@code key} is stored
   291  	 */
   292  	private function findKey(key):Number {
   293  		var l:Number = keys.length;
   294  		while (keys[--l] !== key && l>-1);
   295  		return l;
   296  	}
   297  	
   298  	/**
   299  	 * Returns the string representation of this map.
   300  	 *
   301  	 * <p>The string representation is obtained using the stringifier returned by the
   302  	 * static {@link AbstractMap#getStringifier} method.
   303  	 * 
   304  	 * @return the string representation of this map
   305  	 */
   306  	public function toString():String {
   307  		return getStringifier().execute(this);
   308  	}
   309  	
   310  }