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  }