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  
    19  /**
    20   * {@code AccessPermission} adjusts the access permissions of members like methods
    21   * and properties in a specific context.
    22   * 
    23   * <p>You can hide methods from for..in loops and protect them from deletion and
    24   * from being overwritten.
    25   *
    26   * <p>Note that no matter what access permissions you set they can be overwritten.
    27   * 
    28   * <p>Also note that the access permissions are not applied to the object but to
    29   * the reference to the object. That means that the object can for example be
    30   * enumerable in one reference but not in another.
    31   * 
    32   * <p>Example:
    33   * <code>
    34   *   var object:Object = new Object();
    35   *   object.myProperty = new Object();
    36   *   object.mySecondReference = object.myProperty;
    37   *   trace("myProperty:          Value: " + object.myProperty);
    38   *   trace("mySecondReference:   Value: " + object.mySecondReference);
    39   *   AccessPermission.set(object, ["myProperty"], AccessPermission.PROTECT_DELETE);
    40   *   trace("myProperty:          Permission: " + AccessPermission.get(object, "myProperty"));
    41   *   trace("mySecondReference:   Permission: " + AccessPermission.get(object, "mySecondReference"));
    42   *   delete object.myProperty;
    43   *   delete object.mySecondReference;
    44   *   trace("myProperty:          Value: " + object.myProperty);
    45   *   trace("mySecondReference:   Value: " + object.mySecondReference);
    46   * </code>
    47   *
    48   * <p>Output:
    49   * <pre>
    50   *   myProperty:          Value: [object Object]
    51   *   mySecondReference:   Value: [object Object]
    52   *   myProperty:          Permission: 2
    53   *   mySecondReference:   Permission: 0
    54   *   myProperty:          Value: [object Object]
    55   *   mySecondReference:   Value: undefined
    56   * </pre>
    57   *
    58   * <p>As you can see, the above statement holds true. We have two references that
    59   * reference the same object. We set the access permission of one reference. We can
    60   * then not delete the reference the access permission was applied to, but the other
    61   * reference.
    62   * 
    63   * <p>Following is another example with a property in its normal state and another
    64   * protected property we applied the {@link #ALLOW_NOTHING} access permission to.
    65   *
    66   * <p>Example:
    67   * <code>
    68   *   var object:Object = new Object();
    69   *   object.myNormalProperty = "myNormalPropertyValue";
    70   *   object.myProtectedProperty = "myProtectedPropertyValue";
    71   *   trace("myNormalProperty:      Default Permission: " + AccessPermission.get(object, "myNormalProperty"));
    72   *   trace("myProtectedProperty:   Default Permission: " + AccessPermission.get(object, "myProtectedProperty"));
    73   *   AccessPermission.set(object, ["myProtectedProperty"], AccessPermission.ALLOW_NOTHING);
    74   *   trace("myProtectedProperty:   New Permission: " + AccessPermission.get(object, "myProtectedProperty"));
    75   *   object.myNormalProperty = "newMyNormalPropertyValue";
    76   *   object.myProtectedProperty = "newMyProtectedPropertyValue";
    77   *   trace("myNormalProperty:      Value After Overwriting: " + object.myNormalProperty);
    78   *   trace("myProtectedProperty:   Value After Overwriting: " + object.myProtectedProperty);
    79   *   for (var i:String in object) {
    80   *     trace(i + ":      Found In For..In Loop, Value: " + object[i]);
    81   *   }
    82   *   delete object.myNormalProperty;
    83   *   delete object.myProtectedProperty;
    84   *   trace("myNormalProperty:      Value After Deletion: " + object.myNormalProperty);
    85   *   trace("myProtectedProperty:   Value After Deletion: " + object.myProtectedProperty);
    86   * </code>
    87   *
    88   * <p>Output:
    89   * <pre>
    90   *   myNormalProperty:      Default Permission: 0
    91   *   myProtectedProperty:   Default Permission: 0
    92   *   myProtectedProperty:   New Permission: 7
    93   *   myNormalProperty:      Value After Overwriting: newMyNormalPropertyValue
    94   *   myProtectedProperty:   Value After Overwriting: myProtectedPropertyValue
    95   *   myNormalProperty:      Found In For..In Loop, Value: newMyNormalPropertyValue
    96   *   myNormalProperty:      Value After Deletion: undefined
    97   *   myProtectedProperty:   Value After Deletion: myProtectedPropertyValue
    98   * </pre>
    99   *
   100   * <p>As you can see the protected property cannot be deleted, overwritten and is
   101   * hidden from for..in loops, while the non-protected property can be deleted, can
   102   * be overwritten and can be enumerated.
   103   * 
   104   * <p>Besides the {@link #get} method you can check up on properties for specific
   105   * access permissions using the {@link #isEnumerable}, {@link #isDeletable} and
   106   * {@link #isOverwritable} methods.
   107   *
   108   * @author Simon Wacker
   109   */
   110  class org.as2lib.util.AccessPermission extends BasicClass {
   111  	
   112  	/**
   113  	 * Allow everything to be done with the object.
   114       */
   115  	public static var ALLOW_ALL:Number = 0;
   116  	
   117  	/**
   118  	 * Hide an object from for..in loops.
   119       */
   120  	public static var HIDE:Number = 1;
   121  	
   122  	/**
   123  	 * Protect an object from deletion.
   124       */
   125  	public static var PROTECT_DELETE:Number = 2;
   126  	
   127  	/**
   128  	 * Protect an object from overwriting.
   129       */
   130  	public static var PROTECT_OVERWRITE:Number = 4;
   131  	
   132  	/**
   133  	 * Allow nothing to be done with the object.
   134       */
   135  	public static var ALLOW_NOTHING:Number = 7;
   136  	
   137  	/**
   138  	 * Sets the access permission of a reference by an access code.
   139  	 * 
   140  	 * <p>The following access codes are applicable:
   141  	 * <table>
   142  	 *   <tr>
   143  	 *     <th>{@link #HIDE}</th>
   144  	 *     <td>Hides the reference from for-in loops.</td>
   145  	 *   </tr>
   146  	 *   <tr>
   147  	 *     <th>{@link #PROTECT_DELETE}</th>
   148  	 *     <td>Protects the reference from deletion</td>
   149  	 *   </tr>
   150  	 *   <tr>
   151  	 *     <th>{@link #PROTECT_OVERWRITE}</th>
   152  	 *     <td>Protects the reference from overwriting</td>
   153  	 *   </tr>
   154  	 *   <tr>
   155  	 *     <th>{@link #ALLOW_ALL}</th>
   156  	 *     <td>Allows everything to be done with the reference.</td>
   157  	 *   </tr>
   158  	 *   <tr>
   159  	 *     <th>{@link #ALLOW_NOTHING}</th>
   160  	 *     <td>Allows nothing to be done with the reference.</td>
   161  	 *   </tr>
   162  	 * </table>
   163  	 * 
   164  	 * <p>These access codes can be combined as follows to apply multiple access
   165  	 * permissions.
   166  	 * <code>
   167  	 *   AccessPermission.PROTECT_DELETE | AccessPermission.PROTECT_OVERWRITE
   168  	 * </code>
   169  	 *
   170  	 * <p>Note that every new invocation of this method simply overwrites the old access
   171  	 * permissions of the reference.
   172  	 * 
   173  	 * @param target the object that holds references to the objects the access permissions
   174  	 * shall be applied to
   175  	 * @param referenceNames the names of the references to apply the access permission to
   176  	 * @param access the access permissions to apply
   177  	 */
   178  	public static function set(target, referenceNames:Array, access:Number):Void {
   179  		_global.ASSetPropFlags(target, referenceNames, access, true);
   180  	}
   181  	
   182  	/**
   183  	 * Returns the current access permission of the reference.
   184  	 *
   185  	 * <p>The permission is represented by a {@code Number}. This number is a bitwise
   186  	 * combination of the three access specifier {@link #HIDE}, {@link #PROTECT_DELETE}
   187  	 * and {@link #PROTECT_OVERWRITE}. You can find out what the returned access
   188  	 * permission number means using these constants.
   189  	 * 
   190  	 * @param target the target object that holds the reference
   191  	 * @param referenceName the name of the reference to return the access permission for
   192  	 * @return a number representing the access permission of the reference
   193  	 */
   194  	public static function get(target, referenceName:String):Number {
   195  		var result:Number = 0;
   196  		if (!isEnumerable(target, referenceName)) result |= HIDE;
   197  		if (!isOverwritable(target, referenceName)) result |= PROTECT_OVERWRITE;
   198  		if (!isDeletable(target, referenceName)) result |= PROTECT_DELETE;
   199  		return result;
   200  	}
   201  	
   202  	/**
   203  	 * Returns whether the reference is enumerable.
   204  	 * 
   205  	 * @param target the target object that holds the reference
   206  	 * @param referenceName the name of the reference to return whether it is enumerable
   207  	 * @return {@code true} if the reference is enumerable else {@code false}
   208  	 * @link http://chattyfig.figleaf.com/flashcoders-wiki/index.php?ASSetPropFlags
   209  	 */
   210  	public static function isEnumerable(target, referenceName:String):Boolean {
   211  		// Why not use target.isPropertyEnumerable(referenceName)?
   212  		for (var i:String in target){
   213  			if (i == referenceName) return true;
   214  		}
   215  		return false;
   216  	}
   217  	
   218  	/**
   219  	 * Returns whether the reference is overwritable.
   220  	 * 
   221  	 * @param target the target object that holds the reference
   222  	 * @param referenceName the name of the reference to return whether it is overwritable
   223  	 * @return {@code true} if the reference is overwritable else {@code false}
   224  	 * @link http://chattyfig.figleaf.com/flashcoders-wiki/index.php?ASSetPropFlags
   225  	 */
   226  	public static function isOverwritable(target, referenceName:String):Boolean {
   227  		var tmp = target[referenceName];
   228  		var newVal = (tmp == 0) ? 1 : 0;
   229  		target[referenceName] = newVal;
   230  		if(target[referenceName] == newVal){
   231  			target[referenceName] = tmp;
   232  			return true;
   233  		}else{
   234  			return false;
   235  		}
   236  	}
   237  	
   238  	/**
   239  	 * Returns whether the reference is deletable.
   240  	 * 
   241  	 * @param target the target object that holds the reference
   242  	 * @param referenceName the name of the reference to return whether it is deletable
   243  	 * @return {@code true} if the reference is deletable else {@code false}
   244  	 * @link http://chattyfig.figleaf.com/flashcoders-wiki/index.php?ASSetPropFlags
   245  	 */
   246  	public static function isDeletable(target, referenceName:String):Boolean {
   247  		var tmp = target[referenceName];
   248  		if (tmp === undefined) return false;
   249  		var enumerable:Boolean = isEnumerable(target, referenceName);
   250  		delete target[referenceName];
   251  		if(target[referenceName] === undefined){
   252  			target[referenceName] = tmp;
   253  			_global.ASSetPropFlags(target, referenceName, !enumerable, 1);
   254  			return true;
   255  		}
   256  		return false;
   257  	}
   258  	
   259  	/**
   260  	 * Private constructor.
   261  	 */
   262  	private function AccessPermission(Void) {
   263  	}
   264  	
   265  }