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 }