package flare.util { /** * Utility class for accessing arbitrary property chains, allowing * nested property expressions (e.g., x.a.b.c or * x.a[1]). To reduce initialization times, this class also * maintains a static cache of all Property instances created using the * static $() method. */ public class Property implements IEvaluable, IPredicate { private static const DELIMITER:* = /[\.|\[(.*)\]]/; private static var __cache:Object = {}; private static var __stack:Vector. = new Vector.(); private static var __proxy:IValueProxy; /** * Requests a Property instance for the given property name. This is a * factory method that caches and reuses property instances, saving * memory and construction time. This method is the preferred way of * getting a property and should be used instead of the constructor. * @param name the name of the property * @return the requested property instance */ public static function $(name:String):Property { if (name == null) return null; var p:Property = __cache[name]; if (p == null) { p = new Property(name); __cache[name] = p; } return p; } /** * Clears the cache of created Property instances */ public static function clearCache():void { __cache = {}; } /** A minimal IValueProxy instance that gets and sets * property values through Property instances. */ public static function get proxy():IValueProxy { if (__proxy == null) __proxy = new PropertyProxy(); return __proxy; } // -------------------------------------------------------------------- private var _field:String; private var _chain:Vector. = new Vector.(); /** The property name string. */ public function get name():String { return _field; } // -------------------------------------------------------------------- /** * Creates a new Property, in most cases the static $ * method should be used instead of this constructor. * @param name the property name string */ public function Property(name:String) { if (name == null) { throw new ArgumentError("Not a valid property name: "+name); } _field = name; _chain = null; if (_field != null) { var parts:Array = _field.split(DELIMITER); if (parts.length > 1) { _chain = new Vector.(); // Clear the chain for (var i:int=0; i 0) _chain.push(parts[i]); } } } } /** * Gets the value of this property for the input object. * @param x the object to retrieve the property value for * @return the property value */ public function getValue(x:Object):* { if (x == null) { return null; } else if (_chain == null) { return x[_field]; } else { for (var i:uint=0; i<_chain.length; ++i) { x = x[_chain[i]]; } return x; } } /** * Gets the value of this property for the input object; this * is the same as getValue, but provided in order to * implement the IEvaluable interface. * @param x the object to retrieve the property value for * @return the property value */ public function eval(x:Object=null):* { if (x == null) { return null; } else if (_chain == null) { return x[_field]; } else { for (var i:uint=0; i<_chain.length; ++i) { x = x[_chain[i]]; } return x; } } /** * Gets the value of this property and casts the result to a * Boolean value. * @param x the object to retrieve the property value for * @return the property value as a Boolean */ public function predicate(x:Object):Boolean { return Boolean(eval(x)); } /** * Sets the value of this property for the input object. If the reset * flag is true, all properties along a property chain will be updated. * Otherwise, only the last property in the chain is updated. * @param x the object to set the property value for * @param val the value to set */ public function setValue(x:Object, val:*):void { if (_chain == null) { x[_field] = val; } else { __stack.push(x); for (var i:uint=0; i<_chain.length-1; ++i) { __stack.push(x = x[_chain[i]]); } var p:Object = __stack.pop(); p[_chain[i]] = val; for (i=_chain.length-1; --i >= 0; ) { x = p; p = __stack.pop(); try { p[_chain[i]] = x; } catch (err:Error) {} } } } /** * Deletes a dynamically-bound property from an object. * @param x the object from which to delete the property */ public function deleteValue(x:Object):void { if (_chain == null) { delete x[_field]; } else { for (var i:uint=0; i<_chain.length-1; ++i) { x = x[_chain[i]]; } delete x[_chain[i]]; } } } // end of class Property } import flare.util.IValueProxy; import flare.util.Property; /** A simple value proxy that uses Property instances to set and * get values for input objects. */ class PropertyProxy implements IValueProxy { public function setValue(o:Object, name:String, value:*):void { Property.$(name).setValue(o, value); } public function getValue(o:Object, name:String):* { return Property.$(name).getValue(o); } public function $(o:Object):Object { return o; } }