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