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.Stringifier;
    19  import org.as2lib.env.reflect.TypeInfo;
    20  import org.as2lib.env.reflect.MethodInfo;
    21  import org.as2lib.env.reflect.TypeMemberInfo;
    22  import org.as2lib.env.reflect.stringifier.PropertyInfoStringifier;
    23  
    24  /**
    25   * {@code PropertyInfo} represents a property.
    26   * 
    27   * <p>The term property means only properties added via {@code Object.addProperty}
    28   * or the ones added with the {@code get} and {@code set} keywords, that are implicit
    29   * getters and setters, not variables.
    30   * 
    31   * <p>{@code PropertyInfo} instances for specific properties can be obtained using
    32   * the methods {@link ClassInfo#getProperties} or {@link ClassInfo#getProperty}.
    33   * That means you first have to get a class info for the class that declares or
    34   * inherits the property. You can therefor use the {@link ClassInfo#forObject},
    35   * {@link ClassInfo#forClass}, {@link ClassInfo#forInstance} and {@link ClassInfo#forName}
    36   * methods.
    37   * 
    38   * <p>When you have obtained the property info you can use it to get information
    39   * about the property.
    40   *
    41   * <code>
    42   *   trace("Property name: " + propertyInfo.getName());
    43   *   trace("Declaring type: " + propertyInfo.getDeclaringType().getFullName());
    44   *   trace("Is Static?: " + propertyInfo.isStatic());
    45   *   trace("Is Writable?: " + propertyInfo.isWritable());
    46   *   trace("Is Readable?: " + propertyInfo.isReadable());
    47   * </code>
    48   *
    49   * @author Simon Wacker
    50   */
    51  class org.as2lib.env.reflect.PropertyInfo extends BasicClass implements TypeMemberInfo {
    52  	
    53  	/** The property info stringifier. */
    54  	private static var stringifier:Stringifier;
    55  	
    56  	/**
    57  	 * Returns the stringifier used to stringify property infos.
    58  	 *
    59  	 * <p>If no custom stringifier has been set via the {@link #setStringifier} method,
    60  	 * an instance of the default {@link PropertyInfoStringifier} class is returned.
    61  	 * 
    62  	 * @return the stringifier that stringifies property infos
    63  	 */
    64  	public static function getStringifier(Void):Stringifier {
    65  		if (!stringifier) stringifier = new PropertyInfoStringifier();
    66  		return stringifier;
    67  	}
    68  	
    69  	/**
    70  	 * Sets the stringifier used to stringify property infos.
    71  	 *
    72  	 * <p>If {@code propertyInfoStringifier} is {@code null} or {@code undefined} the
    73  	 * {@link #getStringifier} method will return the default stringifier.
    74  	 * 
    75  	 * @param propertyInfoStringifier the stringifier that stringifies property infos
    76  	 */
    77  	public static function setStringifier(propertyInfoStringifier:PropertyInfoStringifier):Void {
    78  		stringifier = propertyInfoStringifier;
    79  	}
    80  	
    81  	/** The name of this property. */
    82  	private var name:String;
    83  	
    84  	/** The setter method of this property. */
    85  	private var setter:MethodInfo;
    86  	
    87  	/** The getter method of this property. */
    88  	private var getter:MethodInfo;
    89  	
    90  	/** The type that declares this property. */
    91  	private var declaringType:TypeInfo;
    92  	
    93  	/** A flag representing whether this property is static. */
    94  	private var staticFlag:Boolean;
    95  	
    96  	/**
    97  	 * Constructs a new {@code PropertyInfo} instance.
    98  	 *
    99  	 * <p>All arguments are allowed to be {@code null}. But keep in mind that not all
   100  	 * methods will function properly if one is.
   101  	 * 
   102  	 * <p>If arguments {@code setter} or {@code getter} are not specified they will be
   103  	 * resolved at run-time everytime asked for. Making use of this functionality you
   104  	 * will always get the up-to-date setter or getter.
   105  	 * 
   106  	 * @param name the name of the property
   107  	 * @param declaringType the type declaring the property
   108  	 * @param staticFlag determines whether the property is static
   109  	 * @param setter (optional) the setter method of the property
   110  	 * @param getter (optional) the getter method of the property
   111  	 */
   112  	public function PropertyInfo(name:String,
   113  								 declaringType:TypeInfo,
   114  								 staticFlag:Boolean,
   115  								 setter:Function,
   116  								 getter:Function) {
   117  		this.name = name;
   118  		this.declaringType = declaringType;
   119  		this.staticFlag = staticFlag;
   120  		this.setter = new MethodInfo("__set__" + name, declaringType, staticFlag, setter);
   121  		this.getter = new MethodInfo("__get__" + name, declaringType, staticFlag, getter);
   122  	}
   123  	
   124  	/**
   125  	 * Returns the name of this property.
   126  	 *
   127  	 * <p>If you want the getter or setter methods' name you must use the {@code getName}
   128  	 * method of the {@code getGetter} or {@code getSetter} method respectively. The
   129  	 * name of this getter or setter method is the prefix '__get__' or '__set__' plus
   130  	 * the name of this property.
   131  	 * 
   132  	 * @return the name of this property
   133  	 */
   134  	public function getName(Void):String {
   135  		return name;
   136  	}
   137  	
   138  	/**
   139  	 * Returns the full name of this property.
   140  	 * 
   141  	 * <p>The full name is the fully qualified name of the declaring type plus the name
   142  	 * of this property.
   143  	 *
   144  	 * @return the full name of this property
   145  	 */
   146  	public function getFullName(Void):String {
   147  		if (declaringType.getFullName()) {
   148  			return declaringType.getFullName() + "." + name;
   149  		}
   150  		return name;
   151  	}
   152  	
   153  	/**
   154  	 * Returns the setter method of this property.
   155  	 * 
   156  	 * <p>The setter method of a property takes one argument, that is the new value that
   157  	 * shall be assigned to the property. You can invoke it the same as every other method.
   158  	 *
   159  	 * <p>The name of this setter method is the prefix '__set__' plus the name of this
   160  	 * property.
   161  	 *
   162  	 * <p>Property setter methods are also known under the name implicit setters.
   163  	 * 
   164  	 * @return the setter method of this property
   165  	 */
   166  	public function getSetter(Void):MethodInfo {
   167  		if (setter.getMethod()) {
   168  			return setter;
   169  		}
   170  		return null;
   171  	}
   172  	
   173  	/**
   174  	 * Returns the getter method of this property.
   175  	 * 
   176  	 * <p>The getter method of a property takes no arguments, but returns the value of
   177  	 * the property. You can invoke it the same as every other method.
   178  	 * 
   179  	 * <p>The name of this getter method is the prefix '__get__' plus the name of this
   180  	 * property.
   181  	 *
   182  	 * <p>Property getter methods are also known under the name implicit getters.
   183  	 *
   184  	 * @return the getter method of the property
   185  	 */
   186  	public function getGetter(Void):MethodInfo {
   187  		if (getter.getMethod()) {
   188  			return getter;
   189  		}
   190  		return null;
   191  	}
   192  	
   193  	/**
   194  	 * Returns the type that declares this property.
   195  	 *
   196  	 * <p>At this time interfaces are not allowed to declare properties. The declaring
   197  	 * type is thus allways an instance of type {@link ClassInfo}, a class.
   198  	 * 
   199  	 * @return the type that declares this property
   200  	 */
   201  	public function getDeclaringType(Void):TypeInfo {
   202  		return declaringType;
   203  	}
   204  	
   205  	/**
   206  	 * Returns whether this property is writable.
   207  	 *
   208  	 * <p>This property is writable when its setter is not {@code null}.
   209  	 * 
   210  	 * @return {@code true} if this property is writable else {@code false}
   211  	 */
   212  	public function isWritable(Void):Boolean {
   213  		return (getSetter() != null);
   214  	}
   215  	
   216  	/**
   217  	 * Returns whether this property is readable.
   218  	 *
   219  	 * <p>This property is readable when its getter is not {@code null}.
   220  	 *
   221  	 * @return {@code true} when this property is readable else {@code false}
   222  	 */
   223  	public function isReadable(Void):Boolean {
   224  		return (getGetter() != null);
   225  	}
   226  	
   227  	/**
   228  	 * Returns whether this property is static or not.
   229  	 *
   230  	 * <p>Static properties are properties per type.
   231  	 *
   232  	 * <p>Non-Static properties are properties per instance.
   233  	 *
   234  	 * @return {@code true} if this property is static else {@code false}
   235  	 */
   236  	public function isStatic(Void):Boolean {
   237  		return staticFlag;
   238  	}
   239  	
   240  	/**
   241  	 * Returns a property info that reflects the current state of this property info.
   242  	 * 
   243  	 * @return a snapshot of this property info
   244  	 */
   245  	public function snapshot(Void):PropertyInfo {
   246  		var setter:Function = null;
   247  		if (getSetter()) setter = getSetter().getMethod();
   248  		var getter:Function = null;
   249  		if (getGetter()) getter = getGetter().getMethod();
   250  		return new PropertyInfo(name, declaringType, staticFlag, setter, getter);
   251  	}
   252  	
   253  	/**
   254  	 * Returns the string representation of this property.
   255  	 *
   256  	 * <p>The string representation is obtained via the stringifier returned by the
   257  	 * static {@link #getStringifier} method.
   258  	 * 
   259  	 * @return the string representation of this property
   260  	 */
   261  	public function toString():String {
   262  		return getStringifier().execute(this);
   263  	}
   264  	
   265  }