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.util.StringUtil; 19 import org.as2lib.util.ClassUtil; 20 import org.as2lib.env.overload.Overload; 21 import org.as2lib.env.except.IllegalArgumentException; 22 import org.as2lib.env.reflect.ClassNotFoundException; 23 import org.as2lib.env.reflect.ReflectConfig; 24 import org.as2lib.env.reflect.PackageInfo; 25 import org.as2lib.env.reflect.TypeInfo; 26 import org.as2lib.env.reflect.PropertyInfo; 27 import org.as2lib.env.reflect.MethodInfo; 28 import org.as2lib.env.reflect.ConstructorInfo; 29 import org.as2lib.env.reflect.TypeMemberFilter; 30 import org.as2lib.env.reflect.algorithm.ClassAlgorithm; 31 import org.as2lib.env.reflect.algorithm.MethodAlgorithm; 32 import org.as2lib.env.reflect.algorithm.PropertyAlgorithm; 33 34 /** 35 * {@code ClassInfo} reflects a class and provides methods to get information about 36 * that class. 37 * 38 * <p>The static search methods {@link #forName}, {@link #forObject}, {@link #forInstance} 39 * and {@link #forClass} can be used to get class infos for specific classes. 40 * 41 * <p>If you for example want to get information about the class of a specific instance 42 * you can retrieve the appropriate {@code ClassInfo} instance and you can then use 43 * its methods to get the information you wanted. 44 * 45 * <p>Example: 46 * <code> 47 * var myInstance:MyClass = new MyClass(); 48 * var classInfo:ClassInfo = ClassInfo.forInstance(myInstance); 49 * trace("Class Name: " + classInfo.getFullName()); 50 * trace("Super Class Name: " + classInfo.getSuperType().getFullName()); 51 * trace("Declared Methods: " + classInfo.getMethods(true)); 52 * trace("Declared Properties: " + classInfo.getProperties(true)); 53 * </code> 54 * 55 * <p>Note that right now it is not possible to distinguish between interfaces and 56 * classes at run-time. Therefore are both classes and interfaces reflected by 57 * {@code ClassInfo} instances. This is going to change as soon is the differentiation 58 * is possible. 59 * 60 * @author Simon Wacker 61 */ 62 class org.as2lib.env.reflect.ClassInfo extends BasicClass implements TypeInfo { 63 64 /** The algorithm to find classes. */ 65 private static var classAlgorithm:ClassAlgorithm; 66 67 /** The algorithm to find methods of classes. */ 68 private static var methodAlgorithm:MethodAlgorithm; 69 70 /** The algorithm to find properties of classes. */ 71 private static var propertyAlgorithm:PropertyAlgorithm; 72 73 /** 74 * Returns the class info corresponding to the passed-in fully qualified 75 * {@code className}. 76 * 77 * <p>Fully qualified means that {@code className} must consist of the class's 78 * namespace (preceding package structure) as well as its name. For example 79 * {@code "org.as2lib.core.BasicClass"}. 80 * 81 * <p>This method first checks whether the class info for the class with the given 82 * {@code className} is already contained in the cache and adds it to the cache if 83 * not. 84 * 85 * @param className the fully qualified class name 86 * @return the class info reflecting the class corresponding to the {@code className} 87 * @throws IllegalArgumentException if the passed-in {@code className} is {@code null}, 88 * {@code undefined} or an empty string or if the object corresponding to the 89 * passed-in {@code className} is not of type {@code function} 90 * @throws ClassNotFoundException if a class with the passed-in {@code className} 91 * could not be found 92 */ 93 public static function forName(className:String):ClassInfo { 94 return getClassAlgorithm().executeByName(className); 95 } 96 97 /** 98 * Returns the class info corresponding to the passed-in {@code object}. 99 * 100 * <p>If the passed-in {@code object} is of type {@code function} it is supposed 101 * that it is the class you want to get the class info for. Otherwise it is supposed 102 * that the object is an instance of the class you want to get the class info for. 103 * 104 * <p>This method first checks whether the class info for the given class or for the 105 * class of the given instance is already contained in the cache and adds it to the 106 * cache if not. 107 * 108 * @param object the object you want to get the class info for 109 * @return the class info corresponding to the passed-in {@code object} 110 * @throws IllegalArgumentException if the passed-in {@code object} is {@code null} 111 * or {@code undefined} 112 * @throws ClassNotFoundException if the class corresponding to the passed-in 113 * {@code object} could not be found 114 * @see #forClass 115 * @see #forInstance 116 */ 117 public static function forObject(object):ClassInfo { 118 // not '!object' because parameter 'object' could be an empty string 119 // 'valueOf' method of 'object' may return 'null' or 'undefined' because of that strict eval is used 120 if (object === null || object === undefined) { 121 throw new IllegalArgumentException("Argument 'object' [" + object + "] must not be 'null' or 'undefined'.", eval("th" + "is"), arguments); 122 } 123 var classInfo:ClassInfo = ReflectConfig.getCache().getClass(object); 124 if (classInfo) return classInfo; 125 // not 'object instanceof Function' because that would include instances 126 // of type Function that were created using the new keyword 'new Function()'. 127 if (typeof(object) == "function") { 128 return forClass(object); 129 } 130 return forInstance(object); 131 } 132 133 /** 134 * Returns the class info corresponding to the class of the passed-in 135 * {@code instance} 136 * 137 * <p>This method first checks whether the class info for the class of the given 138 * {@code instance} is already contained in the cache and adds it to the cache if 139 * not. 140 * 141 * @param instance the instance you want to get the class info for 142 * @return the class info reflecting the class of the passed-in {@code instance} 143 * @throws IllegalArgumentException if the passed-in {@code instance} is 144 * {@code null} or {@code undefined} 145 * @throws ClassNotFoundException if the class corresponding to the passed-in 146 * {@code instance} could not be found 147 */ 148 public static function forInstance(instance):ClassInfo { 149 // not '!instance' because parameter 'instance' could be a blank string 150 if (instance === null || instance === undefined) { 151 throw new IllegalArgumentException("Argument 'instance' [" + instance + "] must not be 'null' or 'undefined'.", eval("th" + "is"), arguments); 152 } 153 var classInfo:ClassInfo = ReflectConfig.getCache().getClassByInstance(instance); 154 if (classInfo) return classInfo; 155 // if the __constructor__ is defined it most probably references the correct class 156 if (instance.__constructor__) { 157 // check if it really is the correct one 158 // it may be incorrect if the __proto__ property was set manually like myInstance.__proto__ = MyClass.prototype 159 if (instance.__constructor__.prototype == instance.__proto__) { 160 return ReflectConfig.getCache().addClass(new ClassInfo(instance.__constructor__)); 161 } 162 } 163 // if the __constructor__ is not defined or is not the correct one the constructor may be correct 164 // this is most probably true for MovieClips, TextFields etc. that have been put on the stage without 165 // linkage to any other class 166 if (instance.constructor) { 167 // check if it really is the correct one 168 // it may be incorrect if the __proto__ property was set manually like myInstance.__proto__ = MyClass.prototype 169 if (instance.constructor.prototype == instance.__proto__) { 170 return ReflectConfig.getCache().addClass(new ClassInfo(instance.constructor)); 171 } 172 } 173 // if all the above tests do not hold true we must search for the class using the instance 174 var info = getClassAlgorithm().executeByInstance(instance); 175 // info is null if the class algorithm could not find the appropriate class 176 if (info) { 177 // Would throwing an exception be more appropriate if any of the following 178 // if-statements holds true? 179 if (info.name == null) info.name = null; 180 if (!info.clazz) info.clazz = null; 181 if (!info.package) info.package = null; 182 return ReflectConfig.getCache().addClass(new ClassInfo(info.clazz, info.name, info.package)); 183 } 184 throw new ClassNotFoundException("The class corresponding to the passed-in instance '" + instance + "' could not be found.", eval("th" + "is"), arguments); 185 } 186 187 /** 188 * Returns the class info corresponding to the passed-in {@code clazz}. 189 * 190 * <p>This method first checks whether the class info for the given {@code clazz} 191 * is already contained in the cache and adds it to the cache if not. 192 * 193 * @param clazz the class you want to get the class info for 194 * @return the class info reflecting the passed-in {@code clazz} 195 * @throws IllegalArgumentException if the passed-in {@code clazz} is {@code null} 196 * or {@code undefined} 197 */ 198 public static function forClass(clazz:Function):ClassInfo { 199 if (clazz === null || clazz === undefined) { 200 throw new IllegalArgumentException("Argument 'clazz' [" + clazz + "] must not be 'null' or 'undefined'.", eval("th" + "is"), arguments); 201 } 202 var classInfo:ClassInfo = ReflectConfig.getCache().getClassByClass(clazz); 203 if (classInfo) return classInfo; 204 return ReflectConfig.getCache().addClass(new ClassInfo(clazz)); 205 } 206 207 /** 208 * Sets the algorithm used to find classes. 209 * 210 * <p>If the passed-in {@code newClassAlgorithm} is of value {@code null} or 211 * {@code undefined}, the {@link #getClassAlgorithm} method will return the default 212 * class algorithm. 213 * 214 * @param newClassAlgorithm the new class algorithm to find classes 215 * @see #getClassAlgorithm 216 */ 217 public static function setClassAlgorithm(newClassAlgorithm:ClassAlgorithm):Void { 218 classAlgorithm = newClassAlgorithm; 219 } 220 221 /** 222 * Returns the class algorithm used to find classes. 223 * 224 * <p>Either the algorithm set via the {@link #setClassAlgorithm} method will be 225 * returned or the default one which is an instance of class {@link ClassAlgorithm}. 226 * 227 * @return the set or the default class algorithm 228 * @see #setClassAlgorithm 229 */ 230 public static function getClassAlgorithm(Void):ClassAlgorithm { 231 if (!classAlgorithm) classAlgorithm = new ClassAlgorithm(); 232 return classAlgorithm; 233 } 234 235 /** 236 * Sets the algorithm used to find methods. 237 * 238 * <p>If the passed-in {@code newMethodAlgorithm} is of value {@code null} or 239 * {@code undefined}, the {@link #getMethodAlgorithm} method will return the 240 * default method algorithm. 241 * 242 * @param newMethodAlgorithm the new method algorithm to find methods 243 * @see #getMethodAlgorithm 244 */ 245 public static function setMethodAlgorithm(newMethodAlgorithm:MethodAlgorithm):Void { 246 methodAlgorithm = newMethodAlgorithm; 247 } 248 249 /** 250 * Returns the method algorithm used to find methods. 251 * 252 * <p>Either the algorithm set via the {@link #setMethodAlgorithm} method will be 253 * returned or the default one which is an instance of class {@link MethodAlgorithm}. 254 * 255 * @return the set or the default method algorithm 256 * @see #setMethodAlgorithm 257 */ 258 public static function getMethodAlgorithm(Void):MethodAlgorithm { 259 if (!methodAlgorithm) methodAlgorithm = new MethodAlgorithm(); 260 return methodAlgorithm; 261 } 262 263 /** 264 * Sets the algorithm used to find properties. 265 * 266 * <p>If the passed-in {@code newPropertyAlgorithm} is of value {@code null} or 267 * {@code undefined}, the {@link #getPropertyAlgorithm} method will return the 268 * default property algorithm. 269 * 270 * @param newPropertyAlgorithm the new property algorithm to find properties 271 * @see #getPropertyAlgorithm 272 */ 273 public static function setPropertyAlgorithm(newPropertyAlgorithm:PropertyAlgorithm):Void { 274 propertyAlgorithm = newPropertyAlgorithm; 275 } 276 277 /** 278 * Returns the property algorithm used to find properties. 279 * 280 * <p>Either the algorithm set via the {@link #setPropertyAlgorithm} method will 281 * be returned or the default one which is an instance of class 282 * {@link PropertyAlgorithm}. 283 * 284 * @return the set or the default property algorithm 285 * @see #setPropertyAlgorithm 286 */ 287 public static function getPropertyAlgorithm(Void):PropertyAlgorithm { 288 if (!propertyAlgorithm) propertyAlgorithm = new PropertyAlgorithm(); 289 return propertyAlgorithm; 290 } 291 292 /** The name of the reflected class. */ 293 private var name:String; 294 295 /** The fully qualified name of the reflected class. */ 296 private var fullName:String; 297 298 /** The reflected class. */ 299 private var clazz:Function; 300 301 /** The super class of the reflected class. */ 302 private var superClass:ClassInfo; 303 304 /** The package the reflected class is a member of. */ 305 private var package:PackageInfo; 306 307 /** The methods the reflected class declares. */ 308 private var methods:Array; 309 310 /** The properties the reflected class declares. */ 311 private var properties:Array; 312 313 /** The constructor of the reflected class. */ 314 private var classConstructor:ConstructorInfo; 315 316 /** 317 * Constructs a new {@code ClassInfo} instance. 318 * 319 * <p>Note that the argument {@code clazz} is not mandatorily necessary, although 320 * most of the methods cannot do their job correctly if it is {@code null} or 321 * {@code undefined}. 322 * 323 * <p>If you do not pass-in the {@code name} or the {@code package} they will be 324 * resolved lazily when requested using the passed-in {@code clazz}. 325 * 326 * @param clazz the class this new class info reflects 327 * @param name (optional) the name of the reflected class 328 * @param package (optional) the package the reflected class is a member of 329 */ 330 public function ClassInfo(clazz:Function, 331 name:String, 332 package:PackageInfo) { 333 this.clazz = clazz; 334 this.name = name; 335 this.package = package; 336 } 337 338 /** 339 * Returns the name of the represented class without its namespace. 340 * 341 * <p>The namespace is the package path to the class. The namespace of the class 342 * 'org.as2lib.core.BasicClass' is 'org.as2lib.core'. In this example this method 343 * would only return 'BasicClass'. 344 * 345 * @reutrn the name of the represented class 346 * @see #getFullName 347 */ 348 public function getName(Void):String { 349 if (name === undefined) initNameAndPackage(); 350 return name; 351 } 352 353 /** 354 * Returns the fully qualified name of the represented class. That means the name 355 * of the class plus its package path, namespace. 356 * 357 * <p>The path will not be included if: 358 * <ul> 359 * <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li> 360 * <li> 361 * The {@code isRoot} method of the package returned by {@link #getPackage} 362 * returns {@code true}. 363 * </li> 364 * </ul> 365 * 366 * @return the fully qualified name of the represented class 367 * @see #getName 368 */ 369 public function getFullName(Void):String { 370 if (fullName === undefined) { 371 if (getPackage().isRoot() || !getPackage()) { 372 return (fullName = getName()); 373 } 374 fullName = getPackage().getFullName() + "." + getName(); 375 } 376 return fullName; 377 } 378 379 /** 380 * Returns the actual class this class info represents. 381 * 382 * @return the represented class 383 */ 384 public function getType(Void):Function { 385 // TODO: find better way to keep concrete class up-to-date 386 // problems are that package and name must be resolved event if no update was made 387 // and that snapshots are not possible 388 /*if (getPackage().getPackage() !== undefined 389 && getPackage().getPackage() !== null 390 && getName() != null) { 391 return getPackage().getPackage()[getName()]; 392 }*/ 393 return clazz; 394 } 395 396 /** 397 * Returns the class's constructor representation. 398 * 399 * <p>You can use the returned constructor info to get the actual 400 * constructor. Note that the constructor in Flash is by default the same as the 401 * class. Thus the function returned by the {@link #getType} method and the 402 * {@code getMethod} method of the returned constructor is the same, if you did not 403 * overwrite the constructor manually after this instance was created. 404 * 405 * @return the constructor of the class 406 */ 407 public function getConstructor(Void):ConstructorInfo { 408 if (classConstructor === undefined) { 409 classConstructor = new ConstructorInfo(this); 410 } 411 return classConstructor; 412 } 413 414 /** 415 * Returns the super class of the class this instance represents. 416 * 417 * <p>The returned instance is of type {@code ClassInfo} and can thus be casted to 418 * this type. 419 * 420 * <p>{@code null} will be returned if: 421 * <ul> 422 * <li>The represented class is {@code Object}.</li> 423 * <li>The represented class has no prototype.</li> 424 * <li>The static {@link #forInstance} method returns {@code null}.</li> 425 * </ul> 426 * 427 * @return the super class of the class this instance represents or {@code null} 428 */ 429 public function getSuperType(Void):TypeInfo { 430 if (superClass === undefined) { 431 if (clazz.prototype.__proto__) { 432 superClass = forInstance(clazz.prototype); 433 } else { 434 superClass = null; 435 } 436 } 437 return superClass; 438 } 439 440 /** 441 * Creates a new instance of the represented class passing the constructor 442 * arguments. 443 * 444 * <p>{@code null} will be returned if the {@link #getType} method returns 445 * {@code null} or {@code undefined}. 446 * 447 * @param .. any number of arguments to pass-to the constructor on creation 448 * @return a new instance of this class 449 */ 450 public function newInstance() { 451 return ClassUtil.createInstance(getConstructor().getMethod(), arguments); 452 } 453 454 /** 455 * Returns the package the represented class is a member of. 456 * 457 * <p>The package of the class {@code org.as2lib.core.BasicClass} is 458 * {@code org.as2lib.core}. 459 * 460 * @return the package the represented class is a member of 461 */ 462 public function getPackage(Void):PackageInfo { 463 if (package === undefined) initNameAndPackage(); 464 return package; 465 } 466 467 /** 468 * Initializes the name and the package of the represented class. 469 * 470 * <p>This is done using the result of an execution of the class algorithm returned 471 * by the static {@link #getClassAlgorithm} method. 472 */ 473 private function initNameAndPackage(Void):Void { 474 var info = getClassAlgorithm().executeByClass(clazz); 475 if (name === undefined) name = info.name == null ? null : info.name; 476 if (package === undefined) package = info.package == null ? null : info.package; 477 } 478 479 /** 480 * Returns whether this class or any super-class implements a method with the 481 * passed-in {@code methodName}. 482 * 483 * <p>Static methods are not filtered by default. That means {@code filterStaticMethods} 484 * is by default set to {@code false}. 485 * 486 * <p>If the passed-in {@code methodName} is {@code null} or {@code undefined}, 487 * {@code false} will be returned. 488 * 489 * @param methodName the name of the method to search for 490 * @param filterStaticMethods (optional) determines whether static methods are 491 * filtered, that means excluded from the search 492 * @return {@code true} if the method exists else {@code false} 493 */ 494 public function hasMethod(methodName:String, filterStaticMethods:Boolean):Boolean { 495 if (methodName == null) return false; 496 if (filterStaticMethods == null) filterStaticMethods = false; 497 if (clazz.prototype[methodName]) return true; 498 if (filterStaticMethods) return false; 499 if (clazz[methodName]) return true; 500 var superClass:TypeInfo = getSuperType(); 501 while (superClass) { 502 if (superClass.getType()[methodName]) { 503 return true; 504 } 505 superClass = superClass.getSuperType(); 506 } 507 return false; 508 } 509 510 /** 511 * @overload #getMethodsByFlag 512 * @overload #getMethodsByFilter 513 */ 514 public function getMethods():Array { 515 var o:Overload = new Overload(this); 516 o.addHandler([], getMethodsByFlag); 517 o.addHandler([Boolean], getMethodsByFlag); 518 o.addHandler([TypeMemberFilter], getMethodsByFilter); 519 return o.forward(arguments); 520 } 521 522 /** 523 * Returns an array containing the methods represented by {@link MethodInfo} 524 * instances this type declares and maybe the ones of the super-classes. 525 * 526 * <p>The super-classes' methods are included if you {@code filterSuperTypes} is 527 * {@code false}, {@code null} or {@code undefined} and excluded/filtered if it is 528 * {@code true}. This means that by default super-classes are not filtered. 529 * 530 * <p>{@code null} will be returned if: 531 * <ul> 532 * <li>The {@link #getType} method returns {@code null} or {@code undefined}.</li> 533 * <li>The method algorithm returns {@code null} or {@code undefined}.</li> 534 * </ul> 535 * 536 * @param filterSuperClasses (optional) determines whether the super classes' methods 537 * shall be excluded/filtered 538 * @return an array containing the methods 539 */ 540 public function getMethodsByFlag(filterSuperClasses:Boolean):Array { 541 if (!clazz) return null; 542 if (methods === undefined) { 543 methods = getMethodAlgorithm().execute(this); 544 } 545 var result:Array = methods.concat(); 546 if (!filterSuperClasses) { 547 if (getSuperType() != null) { 548 result = result.concat(getSuperType().getMethodsByFlag(filterSuperClasses)); 549 } 550 } 551 return result; 552 } 553 554 /** 555 * Returns an array that contains the methods represented by {@link MethodInfo} 556 * instances, this class and super classes' declare, that are not filtered/excluded. 557 * 558 * <p>The {@link TypeMemberFilter#filter} method of the passed-in {@code methodFilter} 559 * is invoked for every method to determine whether it shall be contained in the 560 * result. The passed-in argument is of type {@code MethodInfo}. 561 * 562 * <p>If the passed-in {@code methodFilter} is {@code null} or {@code undefined} 563 * the result of an invocation of the {@link #getMethodsByFlag} method with 564 * argument {@code false} will be returned. 565 * 566 * <p>{@code null} will be returned if: 567 * <ul> 568 * <li>The {@link #getType} method returns {@code null} or {@code undefined}.</li> 569 * <li> 570 * The {@link #getMethodsByFlag} method returns {@code null} or {@code undefined}. 571 * </li> 572 * </ul> 573 * 574 * @param methodFilter the filter that filters unwanted methods out 575 * @return an array containing the declared methods that are not filtered, an empty 576 * array if no methods are declared or all were filtered or {@code null} 577 */ 578 public function getMethodsByFilter(methodFilter:TypeMemberFilter):Array { 579 if (!clazz) return null; 580 if (!methodFilter) return getMethodsByFlag(false); 581 var result:Array = getMethodsByFlag(methodFilter.filterSuperTypes()); 582 for (var i:Number = 0; i < result.length; i++) { 583 if (methodFilter.filter(result[i])) { 584 result.splice(i, 1); 585 i--; 586 } 587 } 588 return result; 589 } 590 591 /** 592 * @overload #getMethodByName 593 * @overload #getMethodByMethod 594 */ 595 public function getMethod():MethodInfo { 596 var overload:Overload = new Overload(this); 597 overload.addHandler([String], getMethodByName); 598 overload.addHandler([Function], getMethodByMethod); 599 return overload.forward(arguments); 600 } 601 602 /** 603 * Returns the method info corresponding to the passed-in {@code methodName}. 604 * 605 * <p>{@code null} will be returned if: 606 * <ul> 607 * <li>The passed-in {@code methodName} is {@code null} or {@code undefined}.</li> 608 * <li> 609 * A method with the given {@code methodName} is not declared in the represented 610 * class or any super class. 611 * </li> 612 * </ul> 613 * 614 * <p>If this class overwrites a method of any super class the, {@code MethodInfo} 615 * instance of the overwriting method will be returned. 616 * 617 * <p>The declaring type of the returned method info is not always the one 618 * represented by this class. It can also be a super class of it. 619 * 620 * @param methodName the name of the method to return 621 * @return a method info representing the method corresponding to the {@code methodName} 622 */ 623 public function getMethodByName(methodName:String):MethodInfo { 624 if (methodName == null) return null; 625 if (getMethodsByFlag(true)) { 626 if (methods[methodName]) return methods[methodName]; 627 } 628 if (getSuperType()) return getSuperType().getMethodByName(methodName); 629 return null; 630 } 631 632 /** 633 * Returns the method info corresponding to the passed-in {@code concreteMethod}. 634 * 635 * <p>{@code null} will be returned if: 636 * <ul> 637 * <li>The passed-in {@code concreteMethod} is {@code null} or {@code undefined}.</li> 638 * <li> 639 * A method matching the given {@code concreteMethod} cannot be found on the 640 * represented class or any super class. 641 * </li> 642 * </ul> 643 * 644 * <p>The declaring class of the returned method info is not always the one 645 * represented by this class. It can also be a super class of it. 646 * 647 * @param concreteMethod the concrete method the method info shall be returned for 648 * @return the method info thate represents the passed-in {@code concreteMethod} 649 */ 650 public function getMethodByMethod(concreteMethod:Function):MethodInfo { 651 if (!concreteMethod) return null; 652 var methodArray:Array = getMethodsByFlag(true); 653 if (methodArray) { 654 var l:Number = methodArray.length; 655 for (var i:Number = 0; i < l; i = i-(-1)) { 656 var method:MethodInfo = methodArray[i]; 657 if (method.getMethod().valueOf() == concreteMethod.valueOf()) { 658 return method; 659 } 660 } 661 } 662 if (getSuperType()) return getSuperType().getMethodByMethod(concreteMethod); 663 return null; 664 } 665 666 /** 667 * Returns whether this class or any super-class implements a property with the 668 * passed-in {@code propertyName}. 669 * 670 * <p>Static properties are not filtered by default. That means {@code filterStaticProperties} 671 * is by default set to {@code false}. 672 * 673 * <p>If the passed-in {@code propertyName} is {@code null} or {@code undefined}, 674 * {@code false} will be returned. 675 * 676 * @param propertyName the name of the property to search for 677 * @param filterStaticProperties (optional) determines whether static properties are 678 * filtered, that means excluded from the search 679 * @return {@code true} if the property exists else {@code false} 680 */ 681 public function hasProperty(propertyName:String, filterStaticProperties:Boolean):Boolean { 682 if (propertyName == null) return false; 683 if (filterStaticProperties == null) filterStaticProperties = false; 684 if (clazz.prototype["__get__" + propertyName]) return true; 685 if (clazz.prototype["__set__" + propertyName]) return true; 686 if (filterStaticProperties) return false; 687 if (clazz[propertyName]) return true; 688 var superClass:TypeInfo = getSuperType(); 689 while (superClass) { 690 if (superClass.getType()["__set__" + propertyName] 691 || superClass.getType()["__get__" + propertyName]) { 692 return true; 693 } 694 superClass = superClass.getSuperType(); 695 } 696 return false; 697 } 698 699 /** 700 * @overload #getPropertiesByFlag 701 * @overload #getPropertiesByFilter 702 */ 703 public function getProperties():Array { 704 var o:Overload = new Overload(this); 705 o.addHandler([], getPropertiesByFlag); 706 o.addHandler([Boolean], getPropertiesByFlag); 707 o.addHandler([TypeMemberFilter], getPropertiesByFilter); 708 return o.forward(arguments); 709 } 710 711 /** 712 * Returns an array containing the properties represented by {@link PropertyInfo} 713 * instances this class declares and maybe the ones of the super-classes. 714 * 715 * <p>The super-classes' properties are included if {@code filterSuperClasses} is 716 * {@code false}, {@code null} or {@code undefined} and excluded/filtered if it is 717 * {@code true}. This means that super-classes are by default not filtered. 718 * 719 * <p>{@code null} will be returned if: 720 * <ul> 721 * <li>The {@link #getType} method returns {@code null} or {@code undefined}.</li> 722 * <li>The property algorithm returns {@code null} or {@code undefined}.</li> 723 * </ul> 724 * 725 * @param filterSuperClasses (optional) determines whether the super classes' 726 * properties shall be excluded/filtered 727 * @return an array containing the properties 728 */ 729 public function getPropertiesByFlag(filterSuperClasses:Boolean):Array { 730 if (!clazz) return null; 731 if (properties === undefined) { 732 properties = getPropertyAlgorithm().execute(this); 733 } 734 var result:Array = properties.concat(); 735 if (!filterSuperClasses) { 736 if (getSuperType() != null) { 737 result = result.concat(ClassInfo(getSuperType()).getPropertiesByFlag(filterSuperClasses)); 738 } 739 } 740 return result; 741 } 742 743 /** 744 * Returns an array containing the properties represented by {@link PropertyInfo} 745 * instances this class and super classes' declare that are not filtered/excluded. 746 * 747 * <p>The {@link TypeMemberFilter#filter} method of the passed-in {@code propertyFilter} 748 * is invoked for every property to determine whether it shall be contained in the 749 * result. 750 * 751 * <p>If the passed-in {@code propertyFilter} is {@code null} or {@code undefined} 752 * the result of the invocation of {@link #getPropertiesByFlag} with argument 753 * {@code false} will be returned. 754 * 755 * <p>{@code null} will be returned if: 756 * <ul> 757 * <li>The {@link #getType} method returns {@code null} or {@code undefined}.</li> 758 * <li>The property algorithm returns {@code null} or {@code undefined}.</li> 759 * </ul> 760 * 761 * @param propertyFilter the filter that filters unwanted properties out 762 * @return an array containing the remaining properties 763 */ 764 public function getPropertiesByFilter(propertyFilter:TypeMemberFilter):Array { 765 if (!clazz) return null; 766 if (!propertyFilter) return getPropertiesByFlag(false); 767 var result:Array = getPropertiesByFlag(propertyFilter.filterSuperTypes()); 768 for (var i:Number = 0; i < result.length; i++) { 769 if (propertyFilter.filter(PropertyInfo(result[i]))) { 770 result.splice(i, 1); 771 i--; 772 } 773 } 774 return result; 775 } 776 777 /** 778 * @overload #getPropertyByName 779 * @overload #getPropertyByProperty 780 */ 781 public function getProperty():PropertyInfo { 782 var overload:Overload = new Overload(this); 783 overload.addHandler([String], getPropertyByName); 784 overload.addHandler([Function], getPropertyByProperty); 785 return overload.forward(arguments); 786 } 787 788 /** 789 * Returns the property info corresponding to the passed-in {@code propertyName}. 790 * 791 * <p>{@code null} will be returned if: 792 * <ul> 793 * <li>The passed-in {@code propertyName} is {@code null} or {@code undefined}.</li> 794 * <li> 795 * A property with the given {@code propertyName} does not exist on the 796 * represented class or any super class. 797 * </li> 798 * </ul> 799 * 800 * <p>If this class overwrites a property of any super class the {@code PropertyInfo} 801 * instance of the overwriting property will be returned. 802 * 803 * <p>The declaring class of the returned property info is not always the one 804 * represented by this class. It can also be a super class of it. 805 * 806 * @param propertyName the name of the property you wanna obtain 807 * @return the property info correspoinding to the passed-in {@code propertyName} 808 */ 809 public function getPropertyByName(propertyName:String):PropertyInfo { 810 if (propertyName == null) return null; 811 if (getPropertiesByFlag(true)) { 812 if (properties[propertyName]) return properties[propertyName]; 813 } 814 if (getSuperType()) return ClassInfo(getSuperType()).getPropertyByName(propertyName); 815 return null; 816 } 817 818 /** 819 * Returns the property info corresponding to the passed-in {@code concreteProperty}. 820 * 821 * <p>{@code null} will be returned if: 822 * <ul> 823 * <li>The passed-in {@code concreteProperty} is {@code null} or {@code undefined}.</li> 824 * <li> 825 * A property corresponding to the passed-in {@code concreteProperty} cannot 826 * be found on the represented class or any super class. 827 * </li> 828 * </ul> 829 * 830 * <p>The declaring class of the returned property info is not always the one 831 * represented by this class. It can also be a super class of it. 832 * 833 * @param concreteProperty the concrete property to return the corresponding 834 * property info for 835 * @return the property info correspoinding to the passed-in {@code concreteProperty} 836 */ 837 public function getPropertyByProperty(concreteProperty:Function):PropertyInfo { 838 if (concreteProperty == null) return null; 839 var propertyArray:Array = getPropertiesByFlag(true); 840 if (propertyArray) { 841 var l:Number = propertyArray.length; 842 for (var i:Number = 0; i < l; i++) { 843 var property:PropertyInfo = propertyArray[i]; 844 if (property.getGetter().getMethod().valueOf() == concreteProperty.valueOf() 845 || property.getSetter().getMethod().valueOf() == concreteProperty.valueOf()) { 846 return property; 847 } 848 } 849 } 850 if (getSuperType()) return ClassInfo(getSuperType()).getPropertyByProperty(concreteProperty); 851 return null; 852 } 853 854 /** 855 * Returns the string representation of this instance. 856 * 857 * <p>The string representation is constructed as follows: 858 * <pre> 859 * [reflection fullyQualifiedNameOfReflectedType] 860 * </pre> 861 * 862 * @param displayContent (optional) a {@code Boolean} that determines whether to 863 * render all methods {@code true} or not {@code false} 864 * @return this instance's string representation 865 */ 866 public function toString():String { 867 var result:String = "[reflection " + getFullName(); 868 if (arguments[0] == true) { 869 var methods:Array = getMethods(); 870 for (var i:Number = 0; i < methods.length; i++) { 871 result += "\n" + StringUtil.addSpaceIndent(methods[i].toString(), 2); 872 } 873 if (methods.length > 0) { 874 result += "\n"; 875 } 876 } 877 return (result + "]"); 878 } 879 880 }