| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| MapUtil |
|
| 0.0;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.utils |
| 31 | { | |
| 32 | import org.as3collections.ICollection; | |
| 33 | import org.as3collections.IIterator; | |
| 34 | import org.as3collections.IListMap; | |
| 35 | import org.as3collections.IMap; | |
| 36 | import org.as3collections.IMapEntry; | |
| 37 | import org.as3collections.ISortedMap; | |
| 38 | import org.as3collections.maps.TypedListMap; | |
| 39 | import org.as3collections.maps.TypedMap; | |
| 40 | import org.as3collections.maps.TypedSortedMap; | |
| 41 | import org.as3utils.BooleanUtil; | |
| 42 | import org.as3utils.NumberUtil; | |
| 43 | import org.as3utils.ReflectionUtil; | |
| 44 | ||
| 45 | import flash.errors.IllegalOperationError; | |
| 46 | ||
| 47 | /** | |
| 48 | * A utility class to work with implementations of the <code>IMap</code> interface. | |
| 49 | * | |
| 50 | * @author Flávio Silva | |
| 51 | */ | |
| 52 | public class MapUtil | |
| 53 | { | |
| 54 | /** | |
| 55 | * <code>MapUtil</code> is a static class and shouldn't be instantiated. | |
| 56 | * | |
| 57 | * @throws IllegalOperationError <code>MapUtil</code> is a static class and shouldn't be instantiated. | |
| 58 | */ | |
| 59 | public function MapUtil() | |
| 60 | 1 | { |
| 61 | 1 | throw new IllegalOperationError("MapUtil is a static class and shouldn't be instantiated."); |
| 62 | } | |
| 63 | ||
| 64 | /** | |
| 65 | * Performs an arbitrary, specific evaluation of equality between this object and the <code>other</code> object. | |
| 66 | * If one of the maps or both maps are <code>null</code> it will be returned <code>false</code>. | |
| 67 | * <p>Two different objects are considered equal if:</p> | |
| 68 | * <p> | |
| 69 | * <ul><li>object A and object B are instances of the same class (i.e. if they have <b>exactly</b> the same type)</li> | |
| 70 | * <li>object A contains all mappings of object B</li> | |
| 71 | * <li>object B contains all mappings of object A</li> | |
| 72 | * <li>mappings have exactly the same order</li> | |
| 73 | * </ul></p> | |
| 74 | * <p>This implementation <b>takes care</b> of the order of the mappings in the maps. | |
| 75 | * So, for two maps are equal the order of entries returned by the iterator object must be equal.</p> | |
| 76 | * | |
| 77 | * @param map1 the first map. | |
| 78 | * @param map2 the second map. | |
| 79 | * @return <code>true</code> if the arbitrary evaluation considers the objects equal. | |
| 80 | */ | |
| 81 | public static function equalConsideringOrder(map1:IMap, map2:IMap): Boolean | |
| 82 | { | |
| 83 | 1 | if (!map1 || !map2) return false; |
| 84 | 1 | if (map1 == map2) return true; |
| 85 | ||
| 86 | 1 | if (!ReflectionUtil.classPathEquals(map1, map2)) return false; |
| 87 | 1 | if (map1.size() != map2.size()) return false; |
| 88 | ||
| 89 | 1 | var itEntryList1:IIterator = map1.entryCollection().iterator(); |
| 90 | 1 | var itEntryList2:IIterator = map2.entryCollection().iterator(); |
| 91 | var mapEntry1:IMapEntry; | |
| 92 | var mapEntry2:IMapEntry; | |
| 93 | ||
| 94 | 1 | while (itEntryList1.hasNext()) |
| 95 | { | |
| 96 | 1 | mapEntry1 = itEntryList1.next(); |
| 97 | 1 | mapEntry2 = itEntryList2.next(); |
| 98 | ||
| 99 | 1 | if (!mapEntry1.equals(mapEntry2)) return false; |
| 100 | } | |
| 101 | ||
| 102 | 1 | return true; |
| 103 | } | |
| 104 | ||
| 105 | /** | |
| 106 | * Performs an arbitrary, specific evaluation of equality between this object and the <code>other</code> object. | |
| 107 | * If one of the maps or both maps are <code>null</code> it will be returned <code>false</code>. | |
| 108 | * <p>Two different objects are considered equal if:</p> | |
| 109 | * <p> | |
| 110 | * <ul><li>object A and object B are instances of the same class (i.e. if they have <b>exactly</b> the same type)</li> | |
| 111 | * <li>object A contains all mappings of object B</li> | |
| 112 | * <li>object B contains all mappings of object A</li> | |
| 113 | * </ul></p> | |
| 114 | * <p>This implementation <b>does not takes care</b> of the order of the mappings in the map.</p> | |
| 115 | * | |
| 116 | * @param map1 the first map. | |
| 117 | * @param map2 the second map. | |
| 118 | * @return <code>true</code> if the arbitrary evaluation considers the objects equal. | |
| 119 | */ | |
| 120 | public static function equalNotConsideringOrder(map1:IMap, map2:IMap): Boolean | |
| 121 | { | |
| 122 | 1 | if (!map1 || !map2) return false; |
| 123 | 1 | if (map1 == map2) return true; |
| 124 | ||
| 125 | 1 | if (!ReflectionUtil.classPathEquals(map1, map2)) return false; |
| 126 | 1 | if (map1.size() != map2.size()) return false; |
| 127 | ||
| 128 | 1 | var itMap1:IIterator = map1.entryCollection().iterator(); |
| 129 | 1 | var entryListMap2:ICollection = map2.entryCollection(); |
| 130 | ||
| 131 | // because maps has same size | |
| 132 | // it's not necessary to perform bidirectional validation | |
| 133 | // i.e. if map1 contains all entries of map2 | |
| 134 | // consequently map2 contains all entries of map1 | |
| 135 | 1 | while (itMap1.hasNext()) |
| 136 | { | |
| 137 | 1 | if (!entryListMap2.contains(itMap1.next())) return false; |
| 138 | } | |
| 139 | ||
| 140 | 1 | return true; |
| 141 | } | |
| 142 | ||
| 143 | /** | |
| 144 | * Feeds argument <code>map</code> with argument <code>list</code>. | |
| 145 | * <p>The name of the nodes become keys and the values of the nodes become values of the <code>IMap</code> object.</p> | |
| 146 | * | |
| 147 | * @example | |
| 148 | * | |
| 149 | * <listing version="3.0"> | |
| 150 | * import org.as3collections.maps.HashMap; | |
| 151 | * import org.as3collections.utils.MapUtil; | |
| 152 | * | |
| 153 | * var map:IMap = new HashMap(); | |
| 154 | * var xml:XML = <index><key1>value1</key1><key2>value2</key2></index>; | |
| 155 | * | |
| 156 | * MapUtil.feedMapFromXmlList(map, xml.children()); | |
| 157 | * | |
| 158 | * trace(map); // [key1=value1,key2=value2] | |
| 159 | * </listing> | |
| 160 | * | |
| 161 | * @param map the map to be fed. | |
| 162 | * @param list the list to retrieve entries. | |
| 163 | * @param typeCoercion if <code>true</code> performs a type coercion to Boolean if some String is "true" or "false", or a type coercion to Number if some String is a Number <code>(i.e. !isNaN(Number(string)) == true)</code>. | |
| 164 | */ | |
| 165 | public static function feedMapWithXmlList(map:IMap, list:XMLList, typeCoercion:Boolean = true): void | |
| 166 | { | |
| 167 | 1 | if (!map) throw new ArgumentError("Argument <map> must not be null."); |
| 168 | 1 | if (!list) return; |
| 169 | ||
| 170 | var nodeName:*; | |
| 171 | var nodeValue:*; | |
| 172 | ||
| 173 | 1 | for each (var node:XML in list) |
| 174 | { | |
| 175 | 1 | nodeName = node.localName(); |
| 176 | ||
| 177 | 1 | if (node.hasComplexContent()) |
| 178 | { | |
| 179 | 0 | if (!isNaN(node.children().length()) && node.children().length() > 0) |
| 180 | { | |
| 181 | 0 | nodeValue = feedMapWithXmlList(map, node.children(), typeCoercion); |
| 182 | } | |
| 183 | else | |
| 184 | { | |
| 185 | 0 | nodeValue = node; |
| 186 | } | |
| 187 | } | |
| 188 | else | |
| 189 | { | |
| 190 | 1 | nodeValue = node.toString(); |
| 191 | } | |
| 192 | ||
| 193 | 1 | if (typeCoercion) |
| 194 | { | |
| 195 | 1 | if (BooleanUtil.isBooleanString(nodeName)) |
| 196 | { | |
| 197 | 0 | nodeName = BooleanUtil.string2Boolean(nodeName); |
| 198 | } | |
| 199 | 1 | else if (NumberUtil.isNumber(Number(nodeName))) |
| 200 | { | |
| 201 | 0 | nodeName = Number(nodeName); |
| 202 | } | |
| 203 | ||
| 204 | 1 | if (BooleanUtil.isBooleanString(nodeValue)) |
| 205 | { | |
| 206 | 1 | nodeValue = BooleanUtil.string2Boolean(nodeValue); |
| 207 | } | |
| 208 | 1 | else if (NumberUtil.isNumber(Number(nodeValue))) |
| 209 | { | |
| 210 | 1 | nodeValue = Number(nodeValue); |
| 211 | } | |
| 212 | } | |
| 213 | ||
| 214 | 1 | map.put(nodeName, nodeValue); |
| 215 | } | |
| 216 | 1 | } |
| 217 | ||
| 218 | /** | |
| 219 | * Returns a new <code>TypedMap</code> with the <code>wrapMap</code> argument wrapped. | |
| 220 | * | |
| 221 | * @param wrapMap the target map to be wrapped by the <code>TypedMap</code>. | |
| 222 | * @param typeKeys the type of the keys allowed by the returned <code>TypedMap</code>. | |
| 223 | * @param typeValues the type of the values allowed by the returned <code>TypedMap</code>. | |
| 224 | * @throws ArgumentError if the <code>wrapMap</code> argument is <code>null</code>. | |
| 225 | * @throws ArgumentError if the <code>typeKeys</code> argument is <code>null</code>. | |
| 226 | * @throws ArgumentError if the <code>typeValues</code> argument is <code>null</code>. | |
| 227 | * @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. | |
| 228 | * @return a new <code>TypedMap</code> with the <code>wrapMap</code> argument wrapped. | |
| 229 | */ | |
| 230 | public static function getTypedMap(wrapMap:IMap, typeKeys:*, typeValues:*): TypedMap | |
| 231 | { | |
| 232 | 1 | return new TypedMap(wrapMap, typeKeys, typeValues); |
| 233 | } | |
| 234 | ||
| 235 | /** | |
| 236 | * Returns a new <code>TypedListMap</code> with the <code>wrapMap</code> argument wrapped. | |
| 237 | * | |
| 238 | * @param wrapMap the target map to be wrapped by the <code>TypedListMap</code>. | |
| 239 | * @param typeKeys the type of the keys allowed by the returned <code>TypedListMap</code>. | |
| 240 | * @param typeValues the type of the values allowed by the returned <code>TypedListMap</code>. | |
| 241 | * @throws ArgumentError if the <code>wrapMap</code> argument is <code>null</code>. | |
| 242 | * @throws ArgumentError if the <code>typeKeys</code> argument is <code>null</code>. | |
| 243 | * @throws ArgumentError if the <code>typeValues</code> argument is <code>null</code>. | |
| 244 | * @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. | |
| 245 | * @return a new <code>TypedListMap</code> with the <code>wrapMap</code> argument wrapped. | |
| 246 | */ | |
| 247 | public static function getTypedListMap(wrapMap:IListMap, typeKeys:*, typeValues:*): TypedListMap | |
| 248 | { | |
| 249 | 1 | return new TypedListMap(wrapMap, typeKeys, typeValues); |
| 250 | } | |
| 251 | ||
| 252 | /** | |
| 253 | * Returns a new <code>TypedSortedMap</code> with the <code>wrapMap</code> argument wrapped. | |
| 254 | * | |
| 255 | * @param wrapMap the target map to be wrapped by the <code>TypedSortedMap</code>. | |
| 256 | * @param typeKeys the type of the keys allowed by the returned <code>TypedSortedMap</code>. | |
| 257 | * @param typeValues the type of the values allowed by the returned <code>TypedSortedMap</code>. | |
| 258 | * @throws ArgumentError if the <code>wrapMap</code> argument is <code>null</code>. | |
| 259 | * @throws ArgumentError if the <code>typeKeys</code> argument is <code>null</code>. | |
| 260 | * @throws ArgumentError if the <code>typeValues</code> argument is <code>null</code>. | |
| 261 | * @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. | |
| 262 | * @return a new <code>TypedSortedMap</code> with the <code>wrapMap</code> argument wrapped. | |
| 263 | */ | |
| 264 | public static function getTypedSortedMap(wrapMap:ISortedMap, typeKeys:*, typeValues:*): TypedSortedMap | |
| 265 | { | |
| 266 | 1 | return new TypedSortedMap(wrapMap, typeKeys, typeValues); |
| 267 | } | |
| 268 | ||
| 269 | /** | |
| 270 | * Returns the string representation of the <code>map</code> argument. | |
| 271 | * | |
| 272 | * @param map the target map. | |
| 273 | * @return the string representation of the target map. | |
| 274 | */ | |
| 275 | public static function toString(map:IMap): String | |
| 276 | { | |
| 277 | 1 | var s:String = "["; |
| 278 | 1 | var it:IIterator = map.iterator(); |
| 279 | var value :*; | |
| 280 | ||
| 281 | 1 | while (it.hasNext()) |
| 282 | { | |
| 283 | 1 | value = it.next(); |
| 284 | ||
| 285 | 1 | s += it.pointer() + "=" + value; |
| 286 | 1 | if (it.hasNext()) s += ","; |
| 287 | } | |
| 288 | ||
| 289 | 1 | s += "]"; |
| 290 | ||
| 291 | 1 | return s; |
| 292 | } | |
| 293 | ||
| 294 | } | |
| 295 | ||
| 296 | } |