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.reflect.PropertyInfo;
    19  import org.as2lib.env.reflect.ClassInfo;
    20  
    21  /**
    22   * {@code PropertyAlgorithm} searches for all properties of a class.
    23   *
    24   * <p>Properties are not variables but implicit getters and setters that can be
    25   * added via the {@code set} or {@code get} keyword or the {@code addProperty}
    26   * method.
    27   * 
    28   * <p>This class is mostly used internally. If you wanna obtain the properties of a
    29   * class you need its representing {@code ClassInfo}. You can then also use the
    30   * {@link ClassInfo#getProperties} method directly and do not have to make the detour
    31   * over this class. The {@link ClassInfo#getProperties} method is also easier to use
    32   * and offers some extra functionalities.
    33   *
    34   * <p>If you nevertheless want to use this class here is how it works.
    35   *
    36   * <code>
    37   *   var classInfo:ClassInfo = ClassInfo.forClass(MyClass);
    38   *   var propertyAlgorithm:PropertyAlgorithm = new PropertyAlgorithm();
    39   *   var properties:Array = propertyAlgorithm.execute(classInfo);
    40   * </code>
    41   *
    42   * <p>Refer to the {@link #execute} method for details on how to get data from the
    43   * properties array appropriately.
    44   * 
    45   * @author Simon Wacker
    46   */
    47  class org.as2lib.env.reflect.algorithm.PropertyAlgorithm extends BasicClass {
    48  	
    49  	/** The temporary result. */
    50  	private var r:Array;
    51  	
    52  	/** Already found getters. */
    53  	private var g:Object;
    54  	
    55  	/** Already found setters. */
    56  	private var s:Object;
    57  	
    58  	/** The class to search through. */
    59  	private var c:ClassInfo;
    60  	
    61  	/** Determines whether the property is static. */
    62  	private var a:Boolean;
    63  	
    64  	/**
    65  	 * Constructs a new {@code PropertyAlgorithm} instance.
    66  	 */
    67  	public function PropertyAlgorithm(Void) {
    68  	}
    69  	
    70  	/**
    71  	 * Searches for all properties of the passed-in class {@code c}.
    72  	 * 
    73  	 * <p>The resulting array contains {@link PropertyInfo} instances.
    74  	 * 
    75  	 * <p>{@code null} will be returned if:
    76  	 * <ul>
    77  	 *   <li>The passed-in class {@code c} is {@code null} or {@code undefined}.</li>
    78  	 *   <li>The {@code getType} method of the passed-in class returns {@code null}.</li>
    79  	 * </ul>
    80  	 * 
    81  	 * <p>Only the passed in class will be searched through, no super classes.
    82  	 *
    83  	 * <p>The found properties are stored in the resulting array by index as well as by
    84  	 * name. This means you can obtain {@code PropertyInfo} instances either by index:
    85  	 * <code>var myProperty:PropertyInfo = myProperties[0];</code>
    86  	 * 
    87  	 * <p>Or by name:
    88  	 * <code>var myProperty:PropertyInfo = myProperties["myPropertyName"];</code>
    89  	 * 
    90  	 * @param c the class info instance representing the class to search through
    91  	 * @return the found properties, an empty array or {@code null}
    92  	 */
    93  	public function execute(c:ClassInfo):Array {
    94  		if (c == null) return null;
    95  		var b:Function = c.getType();
    96  		if (!b) return null;
    97  		this.c = c;
    98  		this.r = new Array();
    99  		this.g = new Object();
   100  		this.s = new Object();
   101  		
   102  		this.a = true;
   103  		_global.ASSetPropFlags(b, null, 0, true);
   104  		_global.ASSetPropFlags(b, ["__proto__", "constructor", "prototype"], 1, true);
   105  		search(b);
   106  		
   107  		this.a = false;
   108  		var d:Object = b.prototype;
   109  		_global.ASSetPropFlags(d, null, 0, true);
   110  		_global.ASSetPropFlags(d, ["__proto__", "constructor", "__constructor__"], 1, true);
   111  		search(d);
   112  		
   113  		// ASSetPropFlags must be restored because unexpected behaviours get caused otherwise
   114  		_global.ASSetPropFlags(b, null, 1, true);
   115  		_global.ASSetPropFlags(d, null, 1, true);
   116  		
   117  		return r;
   118  	}
   119  	
   120  	private function search(t):Void {
   121  		var i:String;
   122  		for (i in t) {
   123  			if (typeof(t[i]) == "function") {
   124  				var n:String = i.substring(7);
   125  				if (i.indexOf("__get__") == 0) {
   126  					g[n] = true;
   127  					if (!s[n]) {
   128  						r[r.length] = new PropertyInfo(n, c, a, t["__set__" + n], t[i]);
   129  						r[n] = r[r.length-1];
   130  					}
   131  				} else if (i.indexOf("__set__") == 0) {
   132  					s[n] = true;
   133  					if (!g[n]) {
   134  						r[r.length] = new PropertyInfo(n, c, a, t[i], t["__get__" + n]);
   135  						r[n] = r[r.length-1];
   136  					}
   137  				}
   138  			}
   139  		}
   140  	}
   141  	
   142  }