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.env.overload.Overload;
    19  import org.as2lib.env.except.IllegalArgumentException;
    20  import org.as2lib.env.reflect.ReflectConfig;
    21  import org.as2lib.env.reflect.ClassInfo;
    22  import org.as2lib.env.reflect.PackageMemberInfo;
    23  import org.as2lib.env.reflect.PackageMemberFilter;
    24  import org.as2lib.env.reflect.algorithm.PackageAlgorithm;
    25  import org.as2lib.env.reflect.algorithm.PackageMemberAlgorithm;
    26  import org.as2lib.util.StringUtil;
    27  
    28  /**
    29   * {@code PackageInfo} represents a real package in the Flash environment. This class
    30   * is used to get specific information about the package it represents.
    31   *
    32   * <p>You can use the static search methods {@link #forName} and {@link #forPackage} to
    33   * get package infos for specific packages.
    34   *
    35   * <p>If you for example have a package you wanna get information about you first must
    36   * retrieve the appropriate {@code PackageInfo} instance and you can then use its
    37   * methods to get the wanted information.
    38   * 
    39   * <code>
    40   *   var packageInfo:PackageInfo = PackageInfo.forPackage(org.as2lib.core);
    41   *   trace("Package full name: " + packageInfo.getFullName());
    42   *   trace("Parent package name: " + packageInfo.getParent().getName());
    43   *   trace("Member classes: " + packageInfo.getMemberClasses());
    44   *   trace("Member packages: " + packageInfo.getMemberPackages());
    45   * </code>
    46   *
    47   * @author Simon Wacker
    48   */
    49  class org.as2lib.env.reflect.PackageInfo extends BasicClass implements PackageMemberInfo {
    50  	
    51  	/** The algorithm to find packages. */
    52  	private static var packageAlgorithm:PackageAlgorithm;
    53  	
    54  	/** The algorithm to find members of packages, that are classes and packages. */
    55  	private static var packageMemberAlgorithm:PackageMemberAlgorithm;
    56  	
    57  	/** Stores the root package of the package hierarchy. */
    58  	private static var rootPackage:PackageInfo;
    59  	
    60  	/**
    61  	 * Returns the package info corresponding to the passed-in {@code packageName}.
    62  	 *
    63  	 * <p>The passed-in {@code packageName} must be composed of the preceding path and
    64  	 * the actual package name, that means it must be fully qualified. For example
    65  	 * {@code "org.as2lib.core"}.
    66  	 *
    67  	 * <p>This method first checks whether the package is already contained in the
    68  	 * cache.
    69  	 *
    70  	 * @param packageName the fully qualified name of the package to find
    71  	 * @return the package info corresponding to the passed-in {@code packageName}
    72  	 * @throws IllegalArgumentException if the passed-in {@code packageName} is {@code null},
    73  	 * {@code undefined} or an empty string or if the object corresponding to the passed-in
    74  	 * {@code packageName} is not of type {@code "object"}
    75  	 * @throws PackageNotFoundException if a package with the passed-in {@code packageName}
    76  	 * could not be found
    77  	 */
    78  	public static function forName(packageName:String):PackageInfo {
    79  		return getPackageAlgorithm().executeByName(packageName);
    80  	}
    81  	
    82  	/**
    83  	 * Returns the package info corresponding to the passed-in {@code package}.
    84  	 *
    85  	 * <p>This method first checks whether the package info is already contained in the
    86  	 * cache.
    87  	 *
    88  	 * @param package the package you wanna get the package info for
    89  	 * @return the package info corresponding to the passed-in {@code package}
    90  	 * @throws IllegalArgumentException if the passed-in {@code package} is {@code null}
    91  	 * or {@code undefined}
    92  	 */
    93  	public static function forPackage(package):PackageInfo {
    94  		// _global == null results in true, as well does _global == undefined because of that === is used
    95  		if (package === null || package === undefined) {
    96  			throw new IllegalArgumentException("Argument 'package' [" + package + "] must not be 'null' nor 'undefined'.", eval("th" + "is"), arguments);
    97  		}
    98  		var packageInfo:PackageInfo = ReflectConfig.getCache().getPackage(package);
    99  		if (packageInfo) return packageInfo;
   100  		return ReflectConfig.getCache().addPackage(new PackageInfo(package));
   101  	}
   102  	
   103  	/**
   104  	 * Sets the algorithm used to find packages.
   105  	 *
   106  	 * <p>If {@code newPackageAlgorithm} is {@code null} or {@code undefined},
   107  	 * {@link #getPackageAlgorithm} will return the default package algorithm.
   108  	 *
   109  	 * @param newPackageAlgorithm the new algorithm to find packages
   110  	 * @see #getPackageAlgorithm
   111  	 */
   112  	public static function setPackageAlgorithm(newPackageAlgorithm:PackageAlgorithm):Void {
   113  		packageAlgorithm = newPackageAlgorithm;
   114  	}
   115  	
   116  	/**
   117  	 * Returns the algorithm used to find packages.
   118  	 *
   119  	 * <p>Either the algorithm set via {@link #setPackageAlgorithm} method will be
   120  	 * returned or the default one which is an instance of class {@link PackageAlgorithm}.
   121  	 *
   122  	 * @return the set or the default package algorithm
   123  	 * @see #setPackageAlgorithm
   124  	 */
   125  	public static function getPackageAlgorithm(Void):PackageAlgorithm {
   126  		if (!packageAlgorithm) packageAlgorithm = new PackageAlgorithm();
   127  		return packageAlgorithm;
   128  	}
   129  	
   130  	/**
   131  	 * Sets the algorithm used to find members of packages.
   132  	 *
   133  	 * <p>Members of packages are classes, interfaces and packages.
   134  	 *
   135  	 * <p>If {@code newPackageMemberAlgorithm} is {@code null} or {@code undefined},
   136  	 * {@link #getPackageMemberAlgorithm} will return the default package member
   137  	 * algorithm.
   138  	 *
   139  	 * @param newPackageMemberAlgorithm the new algorithm to find members of packages
   140  	 * @see #getPackageMemberAlgorithm
   141  	 */
   142  	public static function setPackageMemberAlgorithm(newPackageMemberAlgorithm:PackageMemberAlgorithm):Void {
   143  		packageMemberAlgorithm = newPackageMemberAlgorithm;
   144  	}
   145  	
   146  	/**
   147  	 * Returns the member algorithm used to find members of packages.
   148  	 *
   149  	 * <p>Either the algorithm set via {@link #setPackageMemberAlgorithm} will be
   150  	 * returned or the default one which is an instance of class {@link PackageMemberAlgorithm}.
   151  	 *
   152  	 * @return the set or the default member algorithm
   153  	 * @see #setPackageMemberAlgorithm
   154  	 */
   155  	public static function getPackageMemberAlgorithm(Void):PackageMemberAlgorithm {
   156  		if (!packageMemberAlgorithm) packageMemberAlgorithm = new PackageMemberAlgorithm();
   157  		return packageMemberAlgorithm;
   158  	}
   159  	
   160  	/**
   161  	 * Returns the root package of the package hierarchy.
   162  	 *
   163  	 * <p>If you do not set a custom root package via the {@code #setRootPackage}
   164  	 * method, the default root package is returned that refers to {@code _global}.
   165  	 *
   166  	 * @return the root package of the package hierarchy.
   167  	 * @see #setRootPackage
   168  	 */
   169  	public static function getRootPackage(Void):PackageInfo {
   170  		if (!rootPackage) rootPackage = new PackageInfo(_global, "_global", null);
   171  		return rootPackage;
   172  	}
   173  	
   174  	/**
   175  	 * Sets the new root package of the package hierarchy.
   176  	 *
   177  	 * <p>If the passed-in {@code newRootPackage} argument is {@code null} or
   178  	 * {@code undefined} the {@code #getRootPackage} method will return the default
   179  	 * root package.
   180  	 *
   181  	 * @param newRootPackage the new root package of the package hierarchy
   182  	 * @see #getRootPackage
   183  	 */
   184  	public static function setRootPackage(newRootPackage:PackageInfo):Void {
   185  		rootPackage = newRootPackage;
   186  	}
   187  	
   188  	/** The name of this package. */
   189  	private var name:String;
   190  	
   191  	/** The fully qualified name of this package. */
   192  	private var fullName:String;
   193  	
   194  	/** The actual package this instance represents. */
   195  	private var package;
   196  	
   197  	/** The parent of this package. */
   198  	private var parent:PackageInfo;
   199  	
   200  	/** The members of this package. */
   201  	private var members:Array;
   202  	
   203  	/**
   204  	 * Constructs a new {@code PackageInfo} instance.
   205  	 *
   206  	 * <p>Note that you do not have to pass-in the concrete {@code package}. But if you
   207  	 * do not pass it in some methods cannot do their job correctly.
   208  	 * 
   209  	 * <p>If you do not pass-in the {@code name} or the {@code parent} they are resolved
   210  	 * lazily when requested using the passed-in {@code package}.
   211  	 *
   212  	 * @param package the actual package this instance represents
   213  	 * @param name (optional) the name of the package
   214  	 * @param parent (optional) the parent package
   215  	 */
   216  	public function PackageInfo(package,
   217  								name:String,  
   218  							  	parent:PackageInfo) {
   219  		this.package = package;
   220  		this.name = name;
   221  		this.parent = parent;
   222  	}
   223  	
   224  	/**
   225  	 * Returns the name of the represented package.
   226  	 *
   227  	 * <p>This does not include the package's path/namespace. If this package info
   228  	 * represented for example the {@code org.as2lib.core} package the returned
   229  	 * name would be {@code "core"}.
   230  	 *
   231  	 * @return the name of the represented package
   232  	 * @see #getFullName
   233  	 */
   234  	public function getName(Void):String {
   235  		if (name === undefined) initNameAndParent();
   236  		return name;
   237  	}
   238  	
   239  	/**
   240  	 * Returns the fully qualified name of the represented package. This means the name
   241  	 * of the package plus its package path/namespace.
   242  	 *
   243  	 * <p>The path is not included if:
   244  	 * <ul>
   245  	 *   <li>The {@link #getParent} method returns {@code null} or {@code undefined}.</li>
   246  	 *   <li>
   247  	 *     The {@link #getParent} method returns the root package, that means its 
   248  	 *     {@link #isRoot} method returns {@code true}.
   249  	 *   </li>
   250  	 * </ul>
   251  	 *
   252  	 * @return the fully qualified name of the package
   253  	 */
   254  	public function getFullName(Void):String {
   255  		if (fullName === undefined) {
   256  			if (getParent().isRoot() || isRoot()) {
   257  				return (fullName = getName());
   258  			}
   259  			fullName = getParent().getFullName() + "." + getName();
   260  		}
   261  		return fullName;
   262  	}
   263  	
   264  	/**
   265  	 * Returns the actual package this instance represents.
   266  	 *
   267  	 * @return the actual package
   268  	 */
   269  	public function getPackage(Void) {
   270  		return package;
   271  	}
   272  	
   273  	/**
   274  	 * Returns the parent of the represented package.
   275  	 *
   276  	 * <p>The parent is the package the represented package is contained in / a member
   277  	 * of. The parent of the package {@code org.as2lib.core} is {@code org.as2lib}.
   278  	 *
   279  	 * @return the parent of the represented package
   280  	 */
   281  	public function getParent(Void):PackageInfo {
   282  		if (parent === undefined) initNameAndParent();
   283  		return parent;
   284  	}
   285  	
   286  	/**
   287  	 * Initializes the name and the parent of the represented package.
   288  	 *
   289  	 * <p>This is done using the result of an execution of the package algorithm
   290  	 * returned by the static {@link #getPackageAlgorithm} method.
   291  	 */
   292  	private function initNameAndParent(Void):Void {
   293  		var info = getPackageAlgorithm().execute(getPackage());
   294  		if (name === undefined) name = info.name == null ? null : info.name;
   295  		if (parent === undefined) parent = info.parent == null ? null : info.parent;
   296  	}
   297  	
   298  	/**
   299  	 * @overload #getMembersByFlag
   300  	 * @overload #getMembersByFilter
   301  	 */
   302  	public function getMembers():Array {
   303  		var o:Overload = new Overload(this);
   304  		o.addHandler([], getMembersByFlag);
   305  		o.addHandler([Boolean], getMembersByFlag);
   306  		o.addHandler([PackageMemberFilter], getMembersByFilter);
   307  		return o.forward(arguments);
   308  	}
   309  	
   310  	/**
   311  	 * Returns an array containing {@link PackageMemberInfo} instances representing the
   312  	 * members of the package and maybe the ones of the sub-packages.
   313  	 *
   314  	 * <p>The members of the package are all types and packages contained in the
   315  	 * represented package.
   316  	 *
   317  	 * <p>If {@code filterSubPackages} is {@code null} or {@code undefined} it is
   318  	 * interpreted as {@code true}, that means sub-packages' package members will be
   319  	 * filtered/excluded from the result by default.
   320  	 *
   321  	 * <p>{@code null} will be returned if
   322  	 * <ul>
   323  	 *   <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li>
   324  	 *   <li>
   325  	 *     The {@code execute} method of the algorithm returned by
   326  	 *     {@link #getPackageMemberAlgorithm} returns {@code null} or {@code undefined}.
   327  	 *   </li>
   328  	 * </ul>
   329  	 *
   330  	 * @param filterSubPackages (optional) determines whether to filter the sub-packages'
   331  	 * members
   332  	 * @return an array containing the members of the represented package
   333  	 */
   334  	public function getMembersByFlag(filterSubPackages:Boolean):Array {
   335  		// not just "== null" because "_global == null" evaluates to "true"
   336  		if (getPackage() === null || getPackage() === undefined) return null;
   337  		if (filterSubPackages == null) filterSubPackages = true;
   338  		if (members === undefined) {
   339  			members = getPackageMemberAlgorithm().execute(this);
   340  		}
   341  		var result:Array = members.concat();
   342  		if (!filterSubPackages) {
   343  			var subPackages:Array = members["packages"];
   344  			for (var i:Number = 0; i < subPackages.length; i++) {
   345  				result = result.concat(PackageInfo(subPackages[i]).getMembersByFlag(filterSubPackages));
   346  			}
   347  		}
   348  		return result;
   349  	}
   350  	
   351  	/**
   352  	 * Returns an array containing {@link PackageMemberInfo} instances representing the
   353  	 * members of the package and sub-packages that are not filtered/excluded.
   354  	 *
   355  	 * <p>The members of this package are all types and packages contained in the
   356  	 * represented package.
   357  	 *
   358  	 * <p>The {@link PackageMemberFilter#filter} method of the passed-in {@code packageMemberFilter}
   359  	 * is invoked for every package member to determine whether it shall be contained
   360  	 * in the result.
   361  	 *
   362  	 * <p>If the passed-in {@code packageMemberFilter} is {@code null} or {@code undefined}
   363  	 * the result of the invocation of {@link #getMembersByFlag} with argument {@code true}
   364  	 * will be returned.
   365  	 *
   366  	 * <p>{@code null} will be returned if:
   367  	 * <ul>
   368  	 *   <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li>
   369  	 *   <li>
   370  	 *     The {@code execute} method of the algorithm returned by
   371  	 *     {@link #getPackageMemberAlgorithm} returns {@code null} or {@code undefined}.
   372  	 *   </li>
   373  	 * </ul>
   374  	 *
   375  	 * @param packageMemberFilter the filter that filters unwanted package members out
   376  	 * @return an array containing the remaining members of the represented package
   377  	 */
   378  	 public function getMembersByFilter(packageMemberFilter:PackageMemberFilter):Array {
   379  		// not just "== null" because "_global == null" evaluates to "true"
   380  		if (getPackage() === null || getPackage() === undefined) return null;
   381  		if (!packageMemberFilter) return getMembersByFlag(true);
   382  		var result:Array = getMembersByFlag(packageMemberFilter.filterSubPackages());
   383  		for (var i:Number = 0; i < result.length; i++) {
   384  			if (packageMemberFilter.filter(PackageMemberInfo(result[i]))) {
   385  				result.splice(i, 1);
   386  				i--;
   387  			}
   388  		}
   389  		return result;
   390  	}
   391  	
   392  	/**
   393  	 * @overload #getMemberClassesByFlag
   394  	 * @overload #getMemberClassesByFilter
   395  	 */
   396  	public function getMemberClasses():Array {
   397  		var o:Overload = new Overload(this);
   398  		o.addHandler([], getMemberClassesByFlag);
   399  		o.addHandler([Boolean], getMemberClassesByFlag);
   400  		o.addHandler([PackageMemberFilter], getMemberClassesByFilter);
   401  		return o.forward(arguments);
   402  	}
   403  	
   404  	/**
   405  	 * Returns an array containing {@link ClassInfo} instances representing the member
   406  	 * classes of the package and maybe the ones of the sub-packages.
   407  	 *
   408  	 * <p>If {@code filterSubPackages} is {@code null} or {@code undefined} it is
   409  	 * interpreted as {@code true}, this means that sub-packages' classes are filtered
   410  	 * by default.
   411  	 *
   412  	 * <p>{@code null} will be returned if:
   413  	 * <ul>
   414  	 *   <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li>
   415  	 *   <li>
   416  	 *     The {@code execute} method of the algorithm returned by
   417  	 *     {@link #getPackageMemberAlgorithm} returns {@code null} or {@code undefined}.
   418  	 *   </li>
   419  	 * </ul>
   420  	 *
   421  	 * @param filterSubPackages (optional) determines whether to filter/exclude the
   422  	 * sub-packages' member classes
   423  	 * @return an array containing the member classes of the represented package
   424  	 */
   425  	public function getMemberClassesByFlag(filterSubPackages:Boolean):Array {
   426  		// not just "== null" because "_global == null" evaluates to "true"
   427  		if (getPackage() === null || getPackage() === undefined) return null;
   428  		if (filterSubPackages == null) filterSubPackages = true;
   429  		if (members === undefined) {
   430  			members = getPackageMemberAlgorithm().execute(this);
   431  		}
   432  		var result:Array = members["classes"].concat();
   433  		if (!filterSubPackages) {
   434  			var subPackages:Array = members["packages"];
   435  			for (var i:Number = 0; i < subPackages.length; i++) {
   436  				result = result.concat(PackageInfo(subPackages[i]).getMemberClassesByFlag(filterSubPackages));
   437  			}
   438  		}
   439  		return result;
   440  	}
   441  	
   442  	/**
   443  	 * Returns an array containing {@link ClassInfo} instances representing the class
   444  	 * members of the package and sub-packages that are not filtered/excluded.
   445  	 *
   446  	 * <p>The {@link PackageMemberFilter#filter} method of the passed-in {@code classFilter}
   447  	 * is invoked for every member class to determine whether it shall be contained in
   448  	 * the result.
   449  	 *
   450  	 * <p>If the passed-in {@code clasFilter} is {@code null} or {@code undefined} the
   451  	 * result of an invocation of {@link #getMemberClassesByFlag} with argument {@code true}
   452  	 * will be returned.
   453  	 *
   454  	 * <p>{@code null} will be returned if:
   455  	 * <ul>
   456  	 *   <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li>
   457  	 *   <li>
   458  	 *     The {@code execute} method of the algorithm returned by
   459  	 *     {@link #getPackageMemberAlgorithm} returns {@code null} or {@code undefined}.
   460  	 *   </li>
   461  	 * </ul>
   462  	 *
   463  	 * @param classFilter the filter that filters unwanted member classes out
   464  	 * @return an array containing the remaining member classes of the represented
   465  	 * package
   466  	 */
   467  	 public function getMemberClassesByFilter(classFilter:PackageMemberFilter):Array {
   468  		// not just "== null" because "_global == null" evaluates to "true"
   469  		if (getPackage() === null || getPackage() === undefined) return null;
   470  		if (!classFilter) return getMemberClassesByFlag(true);
   471  		var result:Array = getMemberClassesByFlag(classFilter.filterSubPackages());
   472  		for (var i:Number = 0; i < result.length; i++) {
   473  			if (classFilter.filter(ClassInfo(result[i]))) {
   474  				result.splice(i, 1);
   475  				i--;
   476  			}
   477  		}
   478  		return result;
   479  	}
   480  	
   481  	/**
   482  	 * @overload #getMemberPackagesByFlag
   483  	 * @overload #getMemberPackagesByFilter
   484  	 */
   485  	public function getMemberPackages():Array {
   486  		var o:Overload = new Overload(this);
   487  		o.addHandler([], getMemberPackagesByFlag);
   488  		o.addHandler([Boolean], getMemberPackagesByFlag);
   489  		o.addHandler([PackageMemberFilter], getMemberPackagesByFilter);
   490  		return o.forward(arguments);
   491  	}
   492  	
   493  	/**
   494  	 * Returns an array containing {@link PackageInfo} instances representing the member
   495  	 * packages of the package and maybe the ones of the sub-packages.
   496  	 *
   497  	 * <p>If {@code filterSubPackages} is {@code null} or {@code undefined} it is
   498  	 * interpreted as {@code true}, this means sub-packages' packages are filtered
   499  	 * by default.
   500  	 *
   501  	 * <p>{@code null} will be returned if:
   502  	 * <ul>
   503  	 *   <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li>
   504  	 *   <li>
   505  	 *     The {@code execute} method of the algorithm returned by
   506  	 *     {@link #getPackageMemberAlgorithm} returns {@code null} or {@code undefined}.
   507  	 *   </li>
   508  	 * </ul>
   509  	 *
   510  	 * @param filterSubPackages (optional) determines whether the sub-packages' member
   511  	 * packages shall be filtered/excluded from or included in the result
   512  	 * @return an array containing the member packages of the represented package
   513  	 */
   514  	public function getMemberPackagesByFlag(filterSubPackages:Boolean):Array {
   515  		// not just "== null" because "_global == null" evaluates to "true"
   516  		if (getPackage() === null || getPackage() === undefined) return null;
   517  		if (filterSubPackages == null) filterSubPackages = true;
   518  		if (members === undefined) {
   519  			members = getPackageMemberAlgorithm().execute(this);
   520  		}
   521  		var result:Array = members["packages"].concat();
   522  		if (!filterSubPackages) {
   523  			var subPackages:Array = members["packages"];
   524  			for (var i:Number = 0; i < subPackages.length; i++) {
   525  				result = result.concat(PackageInfo(subPackages[i]).getMemberPackagesByFlag(filterSubPackages));
   526  			}
   527  		}
   528  		return result;
   529  	}
   530  	
   531  	/**
   532  	 * Returns an array containing {@link PackageInfo} instances representing the
   533  	 * package members of the package and sub-packages that are not filtered/excluded.
   534  	 *
   535  	 * <p>The {@link PackageMemberFilter#filter} method of the passed-in {@code packageFilter}
   536  	 * is invoked for every member package to determine whether it shall be contained
   537  	 * in the result.
   538  	 *
   539  	 * <p>If the passed-in {@code packageFilter} is {@code null} or {@code undefined}
   540  	 * the result of the invocation of {@link #getMemberPackagesByFlag} with argument
   541  	 * {@code true} will be returned.
   542  	 *
   543  	 * <p>{@code null} will be returned if:
   544  	 * <ul>
   545  	 *   <li>The {@link #getPackage} method returns {@code null} or {@code undefined}.</li>
   546  	 *   <li>
   547  	 *     The {@code execute} method of the algorithm returned by
   548  	 *     {@link #getPackageMemberAlgorithm} returns {@code null} or {@code undefined}.
   549  	 *   </li>
   550  	 * </ul>
   551  	 *
   552  	 * @param packageFilter the filter that filters unwanted member packages out
   553  	 * @return an array containing the remaining member packages of the represented
   554  	 * package
   555  	 */
   556  	 public function getMemberPackagesByFilter(packageFilter:PackageMemberFilter):Array {
   557  		// not just "== null" because "_global == null" evaluates to "true"
   558  		if (getPackage() === null || getPackage() === undefined) return null;
   559  		if (!packageFilter) return getMemberPackagesByFlag(true);
   560  		var result:Array = getMemberPackagesByFlag(packageFilter.filterSubPackages());
   561  		for (var i:Number = 0; i < result.length; i++) {
   562  			if (packageFilter.filter(PackageInfo(result[i]))) {
   563  				result.splice(i, 1);
   564  				i--;
   565  			}
   566  		}
   567  		return result;
   568  	}
   569  	
   570  	/**
   571  	 * @overload #getMemberByName
   572  	 * @overload #getMemberByMember
   573  	 */
   574  	public function getMember():PackageMemberInfo {
   575  		var o:Overload = new Overload(this);
   576  		o.addHandler([String], getMemberByName);
   577  		o.addHandler([Object], getMemberByMember);
   578  		return o.forward(arguments);
   579  	}
   580  	
   581  	/**
   582  	 * Returns the package member info corresponding to the passed-in {@code memberName}.
   583  	 *
   584  	 * <p>If the package member with the passed-in {@code memberName} cannot be found
   585  	 * directly in the represented package its sub-packages are searched through.
   586  	 *
   587  	 * <p>{@code null} will be returned if:
   588  	 * <ul>
   589  	 *   <li>The {@link #getMembers} method returns {@code null} or {@code undefined}.</li>
   590  	 *   <li>The passed-in {@code memberName} is {@code null} or {@code undefined}.</li>
   591  	 *   <li>There is no member with the passed-in {@code memberName}.</li>
   592  	 * </ul>
   593  	 *
   594  	 * @param memberName the name of the member to return
   595  	 * @return the member corresponding to the passed-in {@code memberName}
   596  	 */
   597  	public function getMemberByName(memberName:String):PackageMemberInfo {
   598  		if (memberName == null) return null;
   599  		if (getMembersByFlag(true)) {
   600  			if (members[memberName]) return members[memberName];
   601  			var subPackages:Array = members["packages"];
   602  			for (var i:Number = 0; i < subPackages.length; i++) {
   603  				var member:PackageMemberInfo = PackageInfo(subPackages[i]).getMemberByName(memberName);
   604  				if (member) return member;
   605  			}
   606  		}
   607  		return null;
   608  	}
   609  	
   610  	/**
   611  	 * Returns the package member info corresponding to the passed-in
   612  	 * {@code concreteMember}.
   613  	 *
   614  	 * <p>If the package member corresponding to the passed-in {@code concreteMember}
   615  	 * cannot be found directly in the represented package its sub-packages are
   616  	 * searched through.
   617  	 *
   618  	 * <p>{@code null} will be returned if:
   619  	 * <ul>
   620  	 *   <li>The {@link #getMembers} method returns {@code null} or {@code undefined}.</li>
   621  	 *   <li>The passed-in {@code concreteMember} is {@code null} or {@code undefined}.</li>
   622  	 *   <li>The member could not be found.</li>
   623  	 * </ul>
   624  	 *
   625  	 * @param concreteMember the concrete member to find
   626  	 * @return the package member info instance corresponding to the {@code concreteMember}
   627  	 */
   628  	public function getMemberByMember(concreteMember):PackageMemberInfo {
   629  		if (concreteMember == null) return null;
   630  		if (typeof(concreteMember) == "function") {
   631  			return getMemberClassByClass(concreteMember);
   632  		} else {
   633  			return getMemberPackageByPackage(concreteMember);
   634  		}
   635  	}
   636  	
   637  	/**
   638  	 * @overload #getMemberClassByName
   639  	 * @overload #getMemberClassByClass
   640  	 */
   641  	public function getMemberClass(clazz):ClassInfo {
   642  		var o:Overload = new Overload(this);
   643  		o.addHandler([String], getMemberClassByName);
   644  		o.addHandler([Function], getMemberClassByClass);
   645  		return o.forward(arguments);
   646  	}
   647  	
   648  	/**
   649  	 * Returns the class info corresponding to the passed-in {@code className}.
   650  	 *
   651  	 * <p>If the member class with the passed-in {@code className} cannot be found
   652  	 * directly in the represented package its sub-packages are searched through.
   653  	 *
   654  	 * <p>{@code null} will be returned if:
   655  	 * <ul>
   656  	 *   <li>The passed-in {@code className} is {@code null} or {@code undefined}.</li>
   657  	 *   <li>There is no class with the passed-in {@code className}.</li>
   658  	 * </ul>
   659  	 *
   660  	 * @param className the name of the class
   661  	 * @return the class info corresponding to the passed-in {@code className}
   662  	 */
   663  	public function getMemberClassByName(className:String):ClassInfo {
   664  		if (className == null) return null;
   665  		if (getMemberClassesByFlag(true)) {
   666  			if (members["classes"][className]) return members["classes"][className];
   667  		}
   668  		var subPackages:Array = getMemberPackagesByFlag(true);
   669  		if (subPackages) {
   670  			for (var i:Number = 0; i < subPackages.length; i++) {
   671  				var clazz:ClassInfo = PackageInfo(subPackages[i]).getMemberClassByName(className);
   672  				if (clazz) return clazz;
   673  			}
   674  		}
   675  		return null;
   676  	}
   677  	
   678  	/**
   679  	 * Returns the class info corresponding to the passed-in {@code concreteClass}.
   680  	 *
   681  	 * <p>If the member class corresponding to the passed-in {@code concreteClass}
   682  	 * cannot be found directly in the represented package its sub-packages are
   683  	 * searched through.
   684  	 *
   685  	 * <p>{@code null} will be returned if:
   686  	 * <ul>
   687  	 *   <li>The passed-in {@code concreteClass} is {@code null} or {@code undefined}.</li>
   688  	 *   <li>
   689  	 *     There is no class matching the passed-in {@code concreteClass} in this
   690  	 *     package or any sub-packages.</li>
   691  	 * </ul>
   692  	 *
   693  	 * @param concreteClass the concrete class a corresponding class info shall be
   694  	 * returned
   695  	 * @return the class info corresponding to the passed-in {@code concreteClass}
   696  	 */
   697  	public function getMemberClassByClass(concreteClass:Function):ClassInfo {
   698  		if (!concreteClass) return null;
   699  		var classes:Array = getMemberClassesByFlag(true);
   700  		if (classes) {
   701  			for (var i:Number = 0; i < classes.length; i++) {
   702  				var clazz:ClassInfo = classes[i];
   703  				if (clazz.getType().valueOf() == concreteClass.valueOf()) {
   704  					return clazz;
   705  				}
   706  			}
   707  		}
   708  		var subPackages:Array = getMemberPackagesByFlag(true);
   709  		if (subPackages) {
   710  			for (var i:Number = 0; i < subPackages.length; i++) {
   711  				var clazz:ClassInfo = PackageInfo(subPackages[i]).getMemberClassByClass(concreteClass);
   712  				if (clazz) return clazz;
   713  			}
   714  		}
   715  		return null;
   716  	}
   717  	
   718  	/**
   719  	 * @overload #getMemberPackageByName
   720  	 * @overload #getMemberPackageByPackage
   721  	 */
   722  	public function getMemberPackage(package):PackageInfo {
   723  		var o:Overload = new Overload(this);
   724  		o.addHandler([String], getMemberPackageByName);
   725  		o.addHandler([Object], getMemberPackageByPackage);
   726  		return o.forward(arguments);
   727  	}
   728  	
   729  	/**
   730  	 * Returns the package info corresponding to the passed-in {@code packageName}.
   731  	 *
   732  	 * <p>If the member package with the passed-in {@code packageName} cannot be found
   733  	 * directly in the represented package its sub-packages are searched through.
   734  	 * 
   735  	 * <p>{@code null} will be returned if:
   736  	 * <ul>
   737  	 *   <li>The passed-in {@code packageName} is {@code null} or {@code undefined}.</li>
   738  	 *   <li>The {@link #getMemberPackages} method returns {@code null}.</li>
   739  	 *   <li>There is no package with the given {@code packageName}.</li>
   740  	 * </ul>
   741  	 *
   742  	 * @param packageName the name of the package
   743  	 * @return the package info corresponding to the passed-in {@code packageName}
   744  	 */
   745  	public function getMemberPackageByName(packageName:String):PackageInfo {
   746  		if (packageName == null) return null;
   747  		if (getMemberPackagesByFlag(true)) {
   748  			if (members["packages"][packageName]) return members["packages"][packageName];
   749  			var subPackages:Array = members["packages"];
   750  			for (var i:Number = 0; i < subPackages.length; i++) {
   751  				var package:PackageInfo = PackageInfo(subPackages[i]).getMemberPackageByName(packageName);
   752  				if (package) return package;
   753  			}
   754  		}
   755  		return null;
   756  	}
   757  	
   758  	/**
   759  	 * Returns the package info corresponding to the passed-in {@code concretePackage}.
   760  	 *
   761  	 * <p>If the member package corresponding to the passed-in {@code concretePackage}
   762  	 * cannot be found directly in the represented package its sub-packages are
   763  	 * searched through.
   764  	 * 
   765  	 * <p>{@code null} will be returned if:
   766  	 * <ul>
   767  	 *   <li>The passed-in {@code concretePackage} is {@code null} or {@code undefined}.</li>
   768  	 *   <li>The {@link #getMemberPackages} method returns {@code null}.</li>
   769  	 *   <li>A package matching the passed-in {@code concretePackage} could not be found.</li>
   770  	 * </ul>
   771  	 *
   772  	 * @param concretePackage the concrete package the corresponding package info shall
   773  	 * be returned for
   774  	 * @return the package info corresponding to the passed-in {@code concretePackage}
   775  	 */
   776  	public function getMemberPackageByPackage(concretePackage):PackageInfo {
   777  		if (concretePackage == null) return null;
   778  		var packages:Array = getMemberPackagesByFlag(true);
   779  		if (packages) {
   780  			for (var i:Number = 0; i < packages.length; i++) {
   781  				var package:PackageInfo = packages[i];
   782  				if (package.getPackage().valueOf() == concretePackage.valueOf()) {
   783  					return package;
   784  				}
   785  			}
   786  			for (var i:Number = 0; i < packages.length; i++) {
   787  				var package:PackageInfo = PackageInfo(packages[i]).getMemberPackageByPackage(concretePackage);
   788  				if (package) return package;
   789  			}
   790  		}
   791  		return null;
   792  	}
   793  	
   794  	/**
   795  	 * Returns whether this package is a root package.
   796  	 *
   797  	 * <p>It is supposed to be a root package when its parent is {@code null}.
   798  	 *
   799  	 * @return {@code true} if this package info represents a root package else {@code false}
   800  	 */
   801  	public function isRoot(Void):Boolean {
   802  		return !getParent();
   803  	}
   804  	
   805  	/** 
   806  	 * Returns {@code true} if this package is the parent package of the passed-in
   807  	 * {@code package}.
   808  	 * 
   809  	 * <p>{@code false} will be returned if:
   810  	 * <ul>
   811  	 *   <li>The passed-in {@code package} is not a parent package of this package.</li>
   812  	 *   <li>The passed-in {@code package} is {@code null} or {@code undefined}.</li>
   813  	 *   <li>The passed-in {@code package} equals this {@code package}.</li>
   814  	 *   <li>The passed-in {@code package}'s {@code isRoot} method returns {@code true}.</li>
   815  	 * </ul>
   816  	 * 
   817  	 * @param package package this package may be a parent of
   818  	 * @return {@code true} if this package is the parent of the passed-in {@code package}
   819  	 */
   820  	public function isParentPackage(package:PackageInfo):Boolean {
   821  		if (!package) return false;
   822  		if (package == this) return false;
   823  		while (package) {
   824  			if (package.isRoot()) return false;
   825  			package = package.getParent();
   826  			if (package == this) return true;
   827  		}
   828  		return false;
   829  	}
   830  	
   831  	/**
   832  	 * Returns the string representation of this instance.
   833  	 * 
   834  	 * <p>The string representation is constructed as follows:
   835  	 * <pre>
   836  	 *   [reflection fullyQualifiedNameOfReflectedPackage]
   837  	 * </pre>
   838  	 * 
   839  	 * @param displayContent (optional) a {@code Boolean} that determines whether to
   840  	 * render this package's content recursively {@code true} or not {@code false}
   841  	 * @return this instance's string representation
   842  	 */
   843  	public function toString():String {
   844  		var result:String = "[reflection " + getFullName();
   845  		if (arguments[0] == true) {
   846  			var members:Array = getMembers();
   847  			for (var i:Number = 0; i < members.length; i++) {
   848  				result += "\n" + StringUtil.addSpaceIndent(members[i].toString(true), 2);
   849  			}
   850  			if (members.length > 0) {
   851  				result += "\n";
   852  			}
   853  		}
   854  
   855  		return (result + "]");
   856  	}
   857  	
   858  }