Coverage Report - org.as3collections.maps.TypedMap
 
Classes in this File Line Coverage Branch Coverage Complexity
TypedMap
100%
71/71
N/A
0
 
 1  
 /*
 2  
  * Licensed under the MIT License
 3  
  * 
 4  
  * Copyright 2010 (c) Flávio Silva, http://flsilva.com
 5  
  *
 6  
  * Permission is hereby granted, free of charge, to any person
 7  
  * obtaining a copy of this software and associated documentation
 8  
  * files (the "Software"), to deal in the Software without
 9  
  * restriction, including without limitation the rights to use,
 10  
  * copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  
  * copies of the Software, and to permit persons to whom the
 12  
  * Software is furnished to do so, subject to the following
 13  
  * conditions:
 14  
  *
 15  
  * The above copyright notice and this permission notice shall be
 16  
  * included in all copies or substantial portions of the Software.
 17  
  *
 18  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 19  
  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 20  
  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 21  
  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 22  
  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 23  
  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 24  
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 25  
  * OTHER DEALINGS IN THE SOFTWARE.
 26  
  * 
 27  
  * http://www.opensource.org/licenses/mit-license.php
 28  
  */
 29  
 
 30  1
 package org.as3collections.maps 
 31  
 {
 32  
         import org.as3collections.AbstractListMap;
 33  
         import org.as3collections.ICollection;
 34  
         import org.as3collections.IIterator;
 35  
         import org.as3collections.IMap;
 36  
         import org.as3collections.IMapEntry;
 37  
         import org.as3collections.utils.MapUtil;
 38  
         import org.as3coreaddendum.errors.ClassCastError;
 39  
         import org.as3utils.ReflectionUtil;
 40  
 
 41  
         /**
 42  
          * <code>TypedMap</code> works as a wrapper for a map.
 43  
          * It stores the <code>wrapMap</code> constructor's argument in the <code>wrappedMap</code> variable.
 44  
          * So every method call to this class is forwarded to the <code>wrappedMap</code> object.
 45  
          * The methods that need to be checked for the type of the keys and values are previously validated with the <code>validateKeyType</code>, <code>validateValueType</code> or <code>validateMap</code> method before forward the call.
 46  
          * If the type of a key or value requested to be inserted to this map is incompatible with the type of the map a <code>org.as3coreaddendum.errors.ClassCastError</code> is thrown.
 47  
          * The calls that are forwarded to the <code>wrappedMap</code> returns the return of the <code>wrappedMap</code> call.
 48  
          * <p><code>TypedMap</code> does not allow <code>null</code> keys or values.</p>
 49  
          * 
 50  
          * @example
 51  
          * 
 52  
          * <listing version="3.0">
 53  
          * import org.as3collections.IMap;
 54  
          * import org.as3collections.maps.ArrayListMap;
 55  
          * import org.as3collections.maps.TypedMap;
 56  
          * 
 57  
          * var map1:IMap = new ArrayListMap();
 58  
          * 
 59  
          * map1.put("e", 1)            // null
 60  
          * map1.put("d", 2)            // null
 61  
          * map1.put("c", 3)            // null
 62  
          * map1.put("b", 4)            // null
 63  
          * map1.put("a", 5)            // null
 64  
          * 
 65  
          * map1                        // {e=1,d=2,c=3,b=4,a=5}
 66  
          * map1.size()                 // 5
 67  
          * 
 68  
          * var map2:IMap = new TypedMap(map1, String, Number); // you can use this way
 69  
          * 
 70  
          * //var map2:IMap = MapUtil.getTypedMap(map1, String, Number); // or you can use this way
 71  
          * 
 72  
          * map2                        // {e=1,d=2,c=3,b=4,a=5}
 73  
          * map2.size()                 // 5
 74  
          * 
 75  
          * map2.equals(map1)           // false
 76  
          * 
 77  
          * map2.put("f", 6)            // null
 78  
          * map2                        // {e=1,d=2,c=3,b=4,a=5,f=6}
 79  
          * map2.size()                 // 6
 80  
          * 
 81  
          * map2.put("g", "h")          // ClassCastError: Invalid value type. value: h | type: String | expected value type: Number
 82  
          * map2.put(7, 8)              // ClassCastError: Invalid key type. key: 7 | type: int | expected key type: String
 83  
          * </listing>
 84  
          * 
 85  
          * @see org.as3collections.utils.MapUtil#getTypedMap() MapUtil.getTypedMap()
 86  
          * @author Flávio Silva
 87  
          */
 88  
         public class TypedMap implements IMap
 89  
         {
 90  
                 private var _typeKeys: *;
 91  
                 private var _typeValues: *;
 92  
                 private var _wrappedMap: IMap;
 93  
 
 94  
                 /**
 95  
                  * @inheritDoc
 96  
                  */
 97  
                 public function get allKeysEquatable(): Boolean { return _wrappedMap.allKeysEquatable; }
 98  
 
 99  
                 /**
 100  
                  * @inheritDoc
 101  
                  */
 102  
                 public function get allValuesEquatable(): Boolean { return _wrappedMap.allValuesEquatable; }
 103  
 
 104  
                 /**
 105  
                  * Defines the acceptable type of the keys by this map.
 106  
                  */
 107  
                 public function get typeKeys(): * { return _typeKeys; }
 108  
 
 109  
                 /**
 110  
                  * Defines the acceptable type of the values by this map.
 111  
                  */
 112  
                 public function get typeValues(): * { return _typeValues; }
 113  
 
 114  
                 /**
 115  
                  * @private
 116  
                  */
 117  
                 protected function get wrappedMap(): IMap { return _wrappedMap; }
 118  
 
 119  
                 /**
 120  
                  * Constructor, creates a new <code>TypedMap</code> object.
 121  
                  * 
 122  
                  * @param         wrapMap         the target map to wrap.
 123  
                  * @param         typeKeys        the type of the keys allowed by this map.
 124  
                  * @param         typeValues        the type of the values allowed by this map.
 125  
                  * @throws         ArgumentError          if the <code>wrapMap</code> argument is <code>null</code>.
 126  
                  * @throws         ArgumentError          if the <code>typeKeys</code> argument is <code>null</code>.
 127  
                  * @throws         ArgumentError          if the <code>typeValues</code> argument is <code>null</code>.
 128  
                  * @throws         org.as3coreaddendum.errors.ClassCastError                  if the types of one or more keys or values in the <code>wrapMap</code> argument are incompatible with the <code>typeKeys</code> or <code>typeValues</code> argument.
 129  
                  */
 130  
                 public function TypedMap(wrapMap:IMap, typeKeys:*, typeValues:*)
 131  1
                 {
 132  1
                         if (!wrapMap) throw new ArgumentError("The 'wrapMap' argument must not be 'null'.");
 133  1
                         if (typeKeys == null) throw new ArgumentError("The 'typeKeys' argument must not be 'null'.");
 134  1
                         if (typeValues == null) throw new ArgumentError("The 'typeValues' argument must not be 'null'.");
 135  
                         
 136  1
                         _typeKeys = typeKeys;
 137  1
                         _typeValues = typeValues;
 138  
                         
 139  1
                         validateMap(wrapMap);
 140  1
                         _wrappedMap = wrapMap;
 141  1
                 }
 142  
 
 143  
                 /**
 144  
                  * Forwards the call to <code>wrappedMap.clear</code>.
 145  
                  */
 146  
                 public function clear(): void
 147  
                 {
 148  1
                         _wrappedMap.clear();
 149  1
                 }
 150  
 
 151  
                 /**
 152  
                  * Creates and return a new <code>TypedMap</code> object with the clone of the <code>wrappedMap</code> object.
 153  
                  * 
 154  
                  * @return         a new <code>TypedMap</code> object with the clone of the <code>wrappedMap</code> object.
 155  
                   */
 156  
                 public function clone(): *
 157  
                 {
 158  1
                         return new TypedMap(_wrappedMap.clone(), _typeKeys, _typeValues);
 159  
                 }
 160  
 
 161  
                 /**
 162  
                  * Forwards the call to <code>wrappedMap.containsKey</code>.
 163  
                  * 
 164  
                  * @param          key
 165  
                  * @return         the return of the call <code>wrappedMap.containsKey</code>.
 166  
                  */
 167  
                 public function containsKey(key:*): Boolean
 168  
                 {
 169  1
                         return _wrappedMap.containsKey(key);
 170  
                 }
 171  
 
 172  
                 /**
 173  
                  * Forwards the call to <code>wrappedMap.containsValue</code>.
 174  
                  * 
 175  
                  * @param          value
 176  
                  * @return         the return of the call <code>wrappedMap.containsValue</code>.
 177  
                  */
 178  
                 public function containsValue(value:*): Boolean
 179  
                 {
 180  1
                         return _wrappedMap.containsValue(value);
 181  
                 }
 182  
 
 183  
                 /**
 184  
                  * Forwards the call to <code>wrappedMap.entryCollection</code>.
 185  
                  * 
 186  
                  * @return
 187  
                   */
 188  
                 public function entryCollection(): ICollection
 189  
                 {
 190  1
                         return wrappedMap.entryCollection();
 191  
                 }
 192  
 
 193  
                 /**
 194  
                  * This method uses <code>MapUtil.equalNotConsideringOrder</code> or <code>MapUtil.equalConsideringOrder</code> method to perform equality, sending this map and <code>other</code> argument.
 195  
                  * <p>If <code>wrappedMap</code> is of type <code>AbstractListMap</code> then <code>MapUtil.equalConsideringOrder</code> method is used.
 196  
                  * Otherwise <code>MapUtil.equalNotConsideringOrder</code> method is used.</p>
 197  
                  * 
 198  
                  * @param          other         the object to be compared for equality.
 199  
                  * @return         <code>true</code> if the arbitrary evaluation considers the objects equal.
 200  
                  * @see         org.as3collections.utils.MapUtil#equalConsideringOrder() MapUtil.equalConsideringOrder()
 201  
                  * @see         org.as3collections.utils.MapUtil#equalNotConsideringOrder() MapUtil.equalNotConsideringOrder()
 202  
                  */
 203  
                 public function equals(other:*): Boolean
 204  
                 {
 205  1
                         if (this == other) return true;
 206  1
                         if (!ReflectionUtil.classPathEquals(this, other)) return false;
 207  
                         
 208  1
                         var otherMap:TypedMap = other as TypedMap;
 209  1
                         if (typeKeys != otherMap.typeKeys || typeValues != otherMap.typeValues) return false;
 210  
                         
 211  1
                         if (wrappedMap is AbstractListMap)
 212  
                         {
 213  
                                 // takes care of order
 214  1
                                 return MapUtil.equalConsideringOrder(this, other);
 215  
                         }
 216  
                         else
 217  
                         {
 218  
                                 // do not take care of order
 219  1
                                 return MapUtil.equalNotConsideringOrder(this, other);
 220  
                         }
 221  
                 }
 222  
 
 223  
                 /**
 224  
                  * Forwards the call to <code>wrappedMap.getKeys</code>.
 225  
                  * 
 226  
                  * @return
 227  
                   */
 228  
                 public function getKeys(): ICollection
 229  
                 {
 230  1
                         return wrappedMap.getKeys();
 231  
                 }
 232  
 
 233  
                 /**
 234  
                  * Forwards the call to <code>wrappedMap.getValue</code>.
 235  
                  * 
 236  
                  * @param key
 237  
                  */
 238  
                 public function getValue(key:*): *
 239  
                 {
 240  1
                         return wrappedMap.getValue(key);
 241  
                 }
 242  
 
 243  
                 /**
 244  
                  * Forwards the call to <code>wrappedMap.getValues</code>.
 245  
                  * 
 246  
                  * @return
 247  
                   */
 248  
                 public function getValues(): ICollection
 249  
                 {
 250  1
                         return wrappedMap.getValues();
 251  
                 }
 252  
 
 253  
                 /**
 254  
                  * Forwards the call to <code>wrappedMap.isEmpty</code>.
 255  
                  * 
 256  
                  * @return
 257  
                   */
 258  
                 public function isEmpty(): Boolean
 259  
                 {
 260  1
                         return wrappedMap.isEmpty();
 261  
                 }
 262  
 
 263  
                 /**
 264  
                  * Forwards the call to <code>wrappedMap.iterator</code>.
 265  
                  * 
 266  
                  * @return
 267  
                   */
 268  
                 public function iterator(): IIterator
 269  
                 {
 270  1
                         return wrappedMap.iterator();
 271  
                 }
 272  
 
 273  
                 /**
 274  
                  * The key and value are validated with the <code>validateKeyType</code> and <code>validateValueType</code> methods to be forwarded to <code>wrappedMap.put</code>.
 275  
                  * 
 276  
                  * @param          key         key with which the specified value is to be associated.
 277  
                  * @param          value         value to be associated with the specified key.
 278  
                  * @throws         org.as3coreaddendum.errors.ClassCastError                  if the type of the specified key or value is incompatible with this map.
 279  
                  * @return         the return of the call <code>wrappedMap.put</code>.
 280  
                  */
 281  
                 public function put(key:*, value:*): *
 282  
                 {
 283  1
                         validateKey(key);
 284  1
                         validateValue(value);
 285  1
                         return _wrappedMap.put(key, value);
 286  
                 }
 287  
 
 288  
                 /**
 289  
                  * The map is validated with the <code>validateMap</code> method to be forwarded to <code>wrappedMap.putAll</code>.
 290  
                  * 
 291  
                  * @param map
 292  
                  */
 293  
                 public function putAll(map:IMap): void
 294  
                 {
 295  1
                         validateMap(map);
 296  1
                         _wrappedMap.putAll(map);
 297  1
                 }
 298  
 
 299  
                 /**
 300  
                  * The objects is validated to be forwarded to <code>wrappedMap.putAllByObject</code>.
 301  
                  * 
 302  
                  * @param o
 303  
                  */
 304  
                 public function putAllByObject(o:Object): void
 305  
                 {
 306  1
                         var map:IMap = new HashMap();
 307  1
                         map.putAllByObject(o);
 308  1
                         validateMap(map);
 309  
                         
 310  1
                         _wrappedMap.putAllByObject(o);
 311  1
                 }
 312  
 
 313  
                 /**
 314  
                  * The entry is validated with the <code>validateKeyType</code> and <code>validateValueType</code> methods to be forwarded to <code>wrappedMap.putEntry</code>.
 315  
                  * 
 316  
                  * @param entry
 317  
                  */
 318  
                 public function putEntry(entry:IMapEntry): *
 319  
                 {
 320  1
                         validateKey(entry.key);
 321  1
                         validateValue(entry.value);
 322  1
                         return _wrappedMap.putEntry(entry);
 323  
                 }
 324  
 
 325  
                 /**
 326  
                  * Forwards the call to <code>wrappedMap.remove</code>.
 327  
                  * 
 328  
                  * @param key
 329  
                  */
 330  
                 public function remove(key:*): *
 331  
                 {
 332  1
                         return _wrappedMap.remove(key);
 333  
                 }
 334  
 
 335  
                 /**
 336  
                  * Forwards the call to <code>wrappedMap.removeAll</code>.
 337  
                  * 
 338  
                  * @param keys
 339  
                  * @return
 340  
                  */
 341  
                 public function removeAll(keys:ICollection): Boolean
 342  
                 {
 343  1
                         return _wrappedMap.removeAll(keys);
 344  
                 }
 345  
 
 346  
                 /**
 347  
                  * Forwards the call to <code>wrappedMap.retainAll</code>.
 348  
                  * 
 349  
                  * @param keys
 350  
                  * @return
 351  
                  */
 352  
                 public function retainAll(keys:ICollection): Boolean
 353  
                 {
 354  1
                         return _wrappedMap.retainAll(keys);
 355  
                 }
 356  
 
 357  
                 /**
 358  
                  * Forwards the call to <code>wrappedMap.size</code>.
 359  
                  * 
 360  
                  * @return
 361  
                   */
 362  
                 public function size(): int
 363  
                 {
 364  1
                         return _wrappedMap.size();
 365  
                 }
 366  
 
 367  
                 /**
 368  
                  * Returns the string representation of this instance.
 369  
                  * 
 370  
                  * @return         the string representation of this instance.
 371  
                   */
 372  
                 public function toString():String 
 373  
                 {
 374  1
                         return MapUtil.toString(this);
 375  
                 }
 376  
 
 377  
                 /**
 378  
                  * @private
 379  
                  */
 380  
                 protected function isValidKey(key:*): Boolean
 381  
                 {
 382  1
                         return key is _typeKeys;
 383  
                 }
 384  
                 
 385  
                 /**
 386  
                  * @private
 387  
                  */
 388  
                 protected function isValidValue(value:*): Boolean
 389  
                 {
 390  1
                         return value is _typeValues;
 391  
                 }
 392  
                 
 393  
                 /**
 394  
                  * @private
 395  
                  */
 396  
                 protected function validateMap(map:IMap): void
 397  
                 {
 398  1
                         if (!map || map.isEmpty()) return;
 399  
                         
 400  1
                         var it:IIterator = map.iterator();
 401  
                         var key:*;
 402  
                         var value:*;
 403  
                         
 404  1
                         while (it.hasNext())
 405  
                         {
 406  1
                                 value = it.next();
 407  1
                                 key = it.pointer();
 408  
                                 
 409  1
                                 validateKey(key);
 410  1
                                 validateValue(value);
 411  
                         }
 412  1
                 }
 413  
                 
 414  
                 /**
 415  
                  * @private
 416  
                  */
 417  
                 protected function validateKey(key:*): void
 418  
                 {
 419  1
                         if (isValidKey(key)) return;
 420  
                         
 421  1
                         var error:ClassCastError = getInvalidKeyError(key);
 422  1
                         throw error;
 423  
                 }
 424  
                 
 425  
                 /**
 426  
                  * @private
 427  
                  */
 428  
                 protected function validateValue(value:*): void
 429  
                 {
 430  1
                         if (isValidValue(value)) return;
 431  
                         
 432  1
                         var error:ClassCastError = getInvalidValueError(value);
 433  1
                         throw error;
 434  
                 }
 435  
                 
 436  
                 /**
 437  
                  * @private
 438  
                  */
 439  
                 private function getInvalidKeyError(key:*): ClassCastError
 440  
                 {
 441  1
                         var message:String = "Invalid key type. key: <" + key + ">\n";
 442  1
                         message += "key type: <" + ReflectionUtil.getClassPath(key) + ">\n";
 443  1
                         message += "expected type: <" + ReflectionUtil.getClassPath(_typeKeys) + ">";
 444  
                         
 445  1
                         return new ClassCastError(message);
 446  
                 }
 447  
                 
 448  
                 /**
 449  
                  * @private
 450  
                  */
 451  
                 private function getInvalidValueError(value:*): ClassCastError
 452  
                 {
 453  1
                         var message:String = "Invalid value type. value: <" + value + ">\n";
 454  1
                         message += "value type: <" + ReflectionUtil.getClassPath(value) + ">\n";
 455  1
                         message += "expected type: <" + ReflectionUtil.getClassPath(_typeValues) + ">";
 456  
                         
 457  1
                         return new ClassCastError(message);
 458  
                 }
 459  
 
 460  
         }
 461  
 
 462  
 }