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.core.BasicClass; 18 import org.as2lib.Config; 19 import org.as2lib.util.ClassUtil; 20 import org.as2lib.util.ArrayUtil; 21 import org.as2lib.util.Comparable; 22 23 /** 24 * {@code ObjectUtil} contains fundamental methods to efficiently and easily work 25 * with any type of object. 26 * 27 * @author Simon Wacker 28 * @author Martin Heidegger 29 */ 30 class org.as2lib.util.ObjectUtil extends BasicClass { 31 32 /** 33 * Constant for objects of type string. 34 * 35 * @see #isTypeOf 36 */ 37 public static var TYPE_STRING:String = "string"; 38 39 /** 40 * Constant for objects for type number. 41 * 42 * @see #isTypeOf 43 */ 44 public static var TYPE_NUMBER:String = "number"; 45 46 /** 47 * Constant for objects of type object. 48 * 49 * @see #isTypeOf 50 */ 51 public static var TYPE_OBJECT:String = "object"; 52 53 /** 54 * Constant for objects of type boolean. 55 * 56 * @see #isTypeOf 57 */ 58 public static var TYPE_BOOLEAN:String = "boolean"; 59 60 /** 61 * Constant for objects of type movieclip. 62 * 63 * @see #isTypeOf 64 */ 65 public static var TYPE_MOVIECLIP:String = "movieclip"; 66 67 /** 68 * Constant for objects of type function. 69 * 70 * @see #isTypeOf 71 */ 72 public static var TYPE_FUNCTION:String = "function"; 73 74 /** 75 * Constant for the value undefined. 76 * 77 * @see #isTypeOf 78 */ 79 public static var TYPE_UNDEFINED:String = "undefined"; 80 81 /** 82 * Constant for the value null. 83 * 84 * @see #isTypeOf 85 */ 86 public static var TYPE_NULL:String = "null"; 87 88 /** 89 * Stringifies the passed-in {@code object} using the stringifier returned by the 90 * static {@link Config#getObjectStringifier} method. 91 * 92 * @param object the object to stringify 93 * @return the string representation of the passed-in {@code object} 94 */ 95 public static function stringify(object):String { 96 return Config.getObjectStringifier().execute(object); 97 } 98 99 /** 100 * Checks if the type of the passed-in {@code object} matches the passed-in 101 * {@code type}. 102 * 103 * <p>Every value (even {@code null} and {@code undefined}) matches type 104 * {@code Object}. 105 * 106 * <p>Instances as well as their primitive correspondent match the types 107 * {@code String}, {@code Number} or {@code Boolean}. 108 * 109 * @param object the object whose type to compare with the passed-in {@code type} 110 * @param type the type to use for the comparison 111 * @return {@code true} if the type of the {@code object} matches the passed-in 112 * {@code type} else {@code false} 113 */ 114 public static function typesMatch(object, type:Function):Boolean { 115 if (type === Object) { 116 return true; 117 } 118 if (isPrimitiveType(object)) { 119 var t:String = typeof(object); 120 // Workaround for former used: typesMatch(type(object), object); 121 // Casting is not a good solution, it will break if the Constructor throws a error! 122 // This solution is not the fastest but will not break by any exception. 123 if ((type === String || ClassUtil.isSubClassOf(type, String)) && t == TYPE_STRING) { 124 return true; 125 } 126 if ((type === Boolean || ClassUtil.isSubClassOf(type, Boolean)) && t == TYPE_BOOLEAN) { 127 return true; 128 } 129 if ((type === Number || ClassUtil.isSubClassOf(type, Number)) && t == TYPE_NUMBER) { 130 return true; 131 } 132 return false; 133 } else { 134 return (isInstanceOf(object, type)); 135 } 136 } 137 138 /** 139 * Compares the results of an execution of the {@code typeof} method applied to 140 * both passed-in objects. 141 * 142 * @param firstObject the first object of the comparison 143 * @param secondObject the second object of the comparison 144 * @return {@code true} if the execution of the {@code typeof} method returns the 145 * same else {@code false} 146 */ 147 public static function compareTypeOf(firstObject, secondObject):Boolean { 148 return (typeof(firstObject) == typeof(secondObject)); 149 } 150 151 /** 152 * Checks if the passed-in {@code object} is a primitive type. 153 * 154 * <p>Primitive types are strings, numbers and booleans that are not created via the 155 * new operator. For example {@code "myString"}, {@code 3} and {@code true} are 156 * primitive types, but {@code new String("myString")}, {@code new Number(3)} and 157 * {@code new Boolean(true)} are not. 158 * 159 * @param object the object to check whether it is a prmitive type 160 * @return {@code true} if {@code object} is a primitive type else {@code false} 161 */ 162 public static function isPrimitiveType(object):Boolean { 163 var t:String = typeof(object); 164 return (t == TYPE_STRING || t == TYPE_NUMBER || t == TYPE_BOOLEAN); 165 } 166 167 /** 168 * Checks if the result of an execution of the {@code typeof} method on the 169 * passed-in {@code object} matches the passed-in {@code type}. 170 * 171 * <p>All possible types are available as constants. 172 * 173 * @param object the object whose type to check 174 * @param type the string representation of the type 175 * @return {@code true} if the object is of the given {@code type} 176 * @see #TYPE_STRING 177 * @see #TYPE_NUMBER 178 * @see #TYPE_OBJECT 179 * @see #TYPE_BOOLEAN 180 * @see #TYPE_MOVIECLIP 181 * @see #TYPE_NULL 182 * @see #TYPE_UNDEFINED 183 */ 184 public static function isTypeOf(object, type:String):Boolean { 185 return (typeof(object) == type); 186 } 187 188 /** 189 * Checks if the passed-in {@code object} is an instance of the passed-in 190 * {@code type}. 191 * 192 * <p>If the passed-in {@code type} is {@code Object}, {@code true} will always be 193 * returned, because every object is an instance of {@code Object}, even {@code null} 194 * and {@code undefined}. 195 * 196 * @param object the object to check 197 * @param type the type to check whether the {@code object} is an instance of 198 * @return {@code true} if the passed-in {@code object} is an instance of the given 199 * {@code type} else {@code false} 200 */ 201 public static function isInstanceOf(object, type:Function):Boolean { 202 if (type === Object) { 203 return true; 204 } 205 return (object instanceof type); 206 } 207 208 /** 209 * Checks if the passed-in {@code object} is an explicit instance of the passed-in 210 * {@code clazz}. 211 * 212 * <p>That means that {@code true} will only be returned if the object was instantiated 213 * directly from the given {@code clazz}. 214 * 215 * @param object the object to check whether it is an explicit instance of {@code clazz} 216 * @param clazz the class to use as the basis for the check 217 * @return {@code true} if the object is an explicit instance of {@code clazz} else 218 * {@code false} 219 */ 220 public static function isExplicitInstanceOf(object, clazz:Function):Boolean { 221 if (isPrimitiveType(object)) { 222 if (clazz == String) { 223 return (typeof(object) == TYPE_STRING); 224 } 225 if (clazz == Number) { 226 return (typeof(object) == TYPE_NUMBER); 227 } 228 if (clazz == Boolean) { 229 return (typeof(object) == TYPE_BOOLEAN); 230 } 231 } 232 return (object instanceof clazz && !(object.__proto__ instanceof clazz)); 233 } 234 235 /** 236 * Checks if two passed-in parameters are equal. 237 * 238 * <p>It uses different strategies by the first passed-in {@code obj1}. 239 * <ul> 240 * <li>If {@code obj1} is a primitive it compares it with == operator.</li> 241 * <li>If {@code obj1} implements {@link Comparable} it calls {@code compare()} 242 * to compare both passed-in parameters</li> 243 * <li>Any different case compares the structure of both objects.</li> 244 * </ul> 245 * 246 * <p>It compares complex objects (that do not implement {@code Comparable}) 247 * only if they are instances of the same class. A different class (even 248 * if its only a extended class) will be handled as not equal. 249 * 250 * <p>It compares complex objects recursivly. It handles back references in 251 * a proper way. 252 * 253 * @param obj1 object to be compared 254 * @param obj2 object to compare with passed-in {@code obj1} 255 * @return {@code true} if both parameters are equal 256 */ 257 public static function compare(obj1, obj2):Boolean { 258 return compareRecursive(obj1, obj2, [], []); 259 } 260 261 /** 262 * Compares recursivly (for objects) if the structure matches. 263 * 264 * @param obj1 object to compare with a different object. 265 * @param obj2 object to be compared with {@code obj1}. 266 * @param stack1 recursive stack to check endless recursions for the {@code obj1} 267 * @param stack2 recursive stack to check endless recursions for the {@code obj2} 268 * @return {@code true} if both paramerters are equal 269 */ 270 private static function compareRecursive(obj1, obj2, stack1:Array, stack2:Array):Boolean { 271 if (typeof obj1 == "object" 272 && !( obj1 instanceof String 273 || obj1 instanceof Number 274 || obj1 instanceof Boolean)) { 275 if (obj1 === obj2) { 276 return true; 277 } 278 if (obj1 instanceof Comparable) { 279 var c:Comparable = obj1; 280 return c.compare(obj2); 281 } 282 if (obj1.__proto__ == obj2.__proto__) { 283 var index:Number = ArrayUtil.lastIndexOf(stack1, obj1); 284 if (index > -1) { 285 if (obj2 == stack2[index]) { 286 return true; 287 } 288 } 289 stack1.push(obj1); 290 stack2.push(obj2); 291 var i:String; 292 for (i in obj1) { 293 if (!compareRecursive (obj1[i], obj2[i], stack1, stack2)) { 294 return false; 295 } 296 } 297 stack1.pop(); 298 stack2.pop(); 299 return true; 300 } else { 301 return false; 302 } 303 } else { 304 return (obj1 == obj2); 305 } 306 } 307 308 /** 309 * Private constructor. 310 */ 311 private function ObjectUtil(Void) { 312 } 313 314 }