flex-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aha...@apache.org
Subject [09/37] git commit: [flex-asjs] [refs/heads/spark] - copy of many flex-sdk classes, modified to try to compile on top of the FlexJS base classes. A COMPILE::LATER flag is used to comment out features like multi-swf support and a few other features that
Date Sat, 13 Feb 2016 00:14:16 GMT
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/1f6418c4/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as b/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as
new file mode 100644
index 0000000..fba73e5
--- /dev/null
+++ b/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as
@@ -0,0 +1,1640 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package mx.utils
+{
+
+COMPILE::AS3
+{
+    import flash.utils.ByteArray;
+    import flash.utils.Dictionary;
+    import flash.utils.getQualifiedClassName;
+    import flash.xml.XMLNode;
+}
+
+import mx.collections.IList;
+
+/**
+ *  The ObjectUtil class is an all-static class with methods for
+ *  working with Objects within Flex.
+ *  You do not create instances of ObjectUtil;
+ *  instead you simply call static methods such as the 
+ *  <code>ObjectUtil.isSimple()</code> method.
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 9
+ *  @playerversion AIR 1.1
+ *  @productversion Flex 3
+ */
+public class ObjectUtil
+{
+    include "../core/Version.as";
+    
+    /**
+    *  Array of properties to exclude from debugging output.
+    *  
+    *  @langversion 3.0
+    *  @playerversion Flash 9
+    *  @playerversion AIR 1.1
+    *  @productversion Flex 3
+    */
+    private static var defaultToStringExcludes:Array = ["password", "credentials"];
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Compares the Objects and returns an integer value 
+     *  indicating if the first item is less than greater than or equal to
+     *  the second item.
+     *  This method will recursively compare properties on nested objects and
+     *  will return as soon as a non-zero result is found.
+     *  By default this method will recurse to the deepest level of any property.
+     *  To change the depth for comparison specify a non-negative value for
+     *  the <code>depth</code> parameter.
+     *
+     *  @param a Object.
+     *
+     *  @param b Object.
+     *
+     *  @param depth Indicates how many levels should be 
+     *  recursed when performing the comparison.
+     *  Set this value to 0 for a shallow comparison of only the primitive 
+     *  representation of each property.
+     *  For example:<pre>
+     *  var a:Object = {name:"Bob", info:[1,2,3]};
+     *  var b:Object = {name:"Alice", info:[5,6,7]};
+     *  var c:int = ObjectUtil.compare(a, b, 0);</pre>
+     *
+     *  <p>In the above example the complex properties of <code>a</code> and 
+     *  <code>b</code> will be flattened by a call to <code>toString()</code>
+     *  when doing the comparison.
+     *  In this case the <code>info</code> property will be turned into a string
+     *  when performing the comparison.</p>
+     *
+     *  @return Return 0 if a and b are null, NaN, or equal. 
+     *  Return 1 if a is null or greater than b. 
+     *  Return -1 if b is null or greater than a. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function compare(a:Object, b:Object, depth:int = -1):int
+    {
+        return internalCompare(a, b, 0, depth, new CircularReferenceManager());
+    }
+    
+    /**
+     *  Copies the specified Object and returns a reference to the copy.
+     *  The copy is made using a native serialization technique. 
+     *  This means that custom serialization will be respected during the copy.
+     *
+     *  <p>This method is designed for copying data objects, 
+     *  such as elements of a collection. It is not intended for copying 
+     *  a UIComponent object, such as a TextInput control. If you want to create copies 
+     *  of specific UIComponent objects, you can create a subclass of the component and implement 
+     *  a <code>clone()</code> method, or other method to perform the copy.</p>
+     * 
+     *  @param value Object that should be copied.
+     * 
+     *  @return Copy of the specified Object.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */ 
+    public static function copy(value:Object):Object
+    {
+        COMPILE::AS3
+        {
+            var buffer:ByteArray = new ByteArray();
+            buffer.writeObject(value);
+            buffer.position = 0;
+            var result:Object = buffer.readObject();
+            return result;
+        }
+        COMPILE::JS
+        {
+            return copyObject(value);
+        }
+    }
+    
+    COMPILE::JS
+    private static function copyObject(value:*):*
+    {
+        var t:String = typeof(value);
+        if (t === "undefined")
+            return undefined;
+        else if (t === "boolean")
+            return value;
+        else if (t === "string")
+            return value;
+        else if (t === "function")
+            return value; // need to copy?
+        else if (t === null)
+            return null;
+        
+        var newObj:Object;
+        if (value.constructor)
+            newObj = new value.constructor(); // what about required constuctor args?
+        else
+            newObj = {};
+        for (var p:String in value)
+        {
+            newObj[p] = copyObject(value[p]);
+        }
+        return newObj;
+    }
+
+    /**
+     *  Clones the specified Object and returns a reference to the clone.
+     *  The clone is made using a native serialization technique. 
+     *  This means that custom serialization will be respected during the
+     *  cloning.  clone() differs from copy() in that the uid property of
+     *  each object instance is retained.
+     *
+     *  <p>This method is designed for cloning data objects, 
+     *  such as elements of a collection. It is not intended for cloning 
+     *  a UIComponent object, such as a TextInput control. If you want to clone
+     *  specific UIComponent objects, you can create a subclass of the component
+     *  and implement a <code>clone()</code> method.</p>
+     * 
+     *  @param value Object that should be cloned.
+     * 
+     *  @return Clone of the specified Object.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 4
+     */ 
+    public static function clone(value:Object):Object
+    {
+        var result:Object = copy(value);
+        cloneInternal(result, value);
+        return result;
+    }
+
+    /**
+     *  Recursive helper used by the public clone method. 
+     *  @private
+     */    
+    private static function cloneInternal(result:Object, value:Object):void
+    {
+        if (value && value.hasOwnProperty("uid"))
+            result.uid = value.uid;
+
+        var classInfo:Object = getClassInfo(value);
+        var v:Object;
+        for each (var p:* in classInfo.properties) 
+        {
+            v = value[p];
+            if (v && v.hasOwnProperty("uid")) 
+                cloneInternal(result[p], v);
+        }
+    }
+   
+    /**
+     *  Returns <code>true</code> if the object reference specified
+     *  is a simple data type. The simple data types include the following:
+     *  <ul>
+     *    <li><code>String</code></li>
+     *    <li><code>Number</code></li>
+     *    <li><code>uint</code></li>
+     *    <li><code>int</code></li>
+     *    <li><code>Boolean</code></li>
+     *    <li><code>Date</code></li>
+     *    <li><code>Array</code></li>
+     *  </ul>
+     *
+     *  @param value Object inspected.
+     *
+     *  @return <code>true</code> if the object specified
+     *  is one of the types above; <code>false</code> otherwise.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function isSimple(value:Object):Boolean
+    {
+        var objectType:String = typeof(value);
+        switch (objectType)
+        {
+            case "number":
+            case "string":
+            case "boolean":
+            {
+                return true;
+            }
+
+            case "object":
+            {
+                return (value is Date) || (value is Array);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     *  Compares two numeric values.
+     * 
+     *  @param a First number.
+     * 
+     *  @param b Second number.
+     *
+     *  @return 0 is both numbers are NaN. 
+     *  1 if only <code>a</code> is a NaN.
+     *  -1 if only <code>b</code> is a NaN.
+     *  -1 if <code>a</code> is less than <code>b</code>.
+     *  1 if <code>a</code> is greater than <code>b</code>.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function numericCompare(a:Number, b:Number):int
+    {
+        if (isNaN(a) && isNaN(b))
+            return 0;
+
+        if (isNaN(a))
+            return 1;
+
+        if (isNaN(b))
+           return -1;
+
+        if (a < b)
+            return -1;
+
+        if (a > b)
+            return 1;
+
+        return 0;
+    }
+
+    /**
+     *  Compares two String values.
+     * 
+     *  @param a First String value.
+     * 
+     *  @param b Second String value.
+     *
+     *  @param caseInsensitive Specifies to perform a case insensitive compare, 
+     *  <code>true</code>, or not, <code>false</code>.
+     *
+     *  @return 0 is both Strings are null. 
+     *  1 if only <code>a</code> is null.
+     *  -1 if only <code>b</code> is null.
+     *  -1 if <code>a</code> precedes <code>b</code>.
+     *  1 if <code>b</code> precedes <code>a</code>.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function stringCompare(a:String, b:String,
+                                         caseInsensitive:Boolean = false):int
+    {
+        if (a == null && b == null)
+            return 0;
+
+        if (a == null)
+          return 1;
+
+        if (b == null)
+           return -1;
+
+        // Convert to lowercase if we are case insensitive.
+        if (caseInsensitive)
+        {
+            a = a.toLocaleLowerCase();
+            b = b.toLocaleLowerCase();
+        }
+
+        var result:int = a.localeCompare(b);
+        
+        if (result < -1)
+            result = -1;
+        else if (result > 1)
+            result = 1;
+
+        return result;
+    }
+
+    /**
+     *  Compares the two Date objects and returns an integer value 
+     *  indicating if the first Date object is before, equal to, 
+     *  or after the second item.
+     *
+     *  @param a Date object.
+     *
+     *  @param b Date object.
+     *
+     *  @return 0 if <code>a</code> and <code>b</code> are equal
+     *  (or both are <code>null</code>);
+     *  -1 if <code>a</code> is before <code>b</code>
+     *  (or <code>b</code> is <code>null</code>);
+     *  1 if <code>a</code> is after <code>b</code>
+     *  (or <code>a</code> is <code>null</code>);
+	 *  0 is both dates getTime's are NaN;
+     *  1 if only <code>a</code> getTime is a NaN;
+     *  -1 if only <code>b</code> getTime is a NaN.    
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function dateCompare(a:Date, b:Date):int
+    {
+        if (a == null && b == null)
+            return 0;
+
+        if (a == null)
+          return 1;
+
+        if (b == null)
+           return -1;
+
+        var na:Number = a.getTime();
+        var nb:Number = b.getTime();
+        
+        if (na < nb)
+            return -1;
+
+        if (na > nb)
+            return 1;
+		
+		if (isNaN(na) && isNaN(nb))
+			return 0;
+		
+		if (isNaN(na))
+			return 1;
+		
+		if (isNaN(nb))
+			return -1;
+
+        return 0;
+    }
+        
+    /**
+     *  Pretty-prints the specified Object into a String.
+     *  All properties will be in alpha ordering.
+     *  Each object will be assigned an id during printing;
+     *  this value will be displayed next to the object type token
+     *  preceded by a '#', for example:
+     *
+     *  <pre>
+     *  (mx.messaging.messages::AsyncMessage)#2.</pre>
+     *
+     *  <p>This id is used to indicate when a circular reference occurs.
+     *  Properties of an object that are of the <code>Class</code> type will
+     *  appear only as the assigned type.
+     *  For example a custom definition like the following:</p>
+     *
+     *  <pre>
+     *    public class MyCustomClass {
+     *      public var clazz:Class;
+     *    }</pre>
+     * 
+     *  <p>With the <code>clazz</code> property assigned to <code>Date</code>
+     *  will display as shown below:</p>
+     * 
+     *  <pre>
+     *   (somepackage::MyCustomClass)#0
+     *      clazz = (Date)</pre>
+     *
+     *  @param value Object to be pretty printed.
+     * 
+     *  @param namespaceURIs Array of namespace URIs for properties 
+     *  that should be included in the output.
+     *  By default only properties in the public namespace will be included in
+     *  the output.
+     *  To get all properties regardless of namespace pass an array with a 
+     *  single element of "*".
+     * 
+     *  @param exclude Array of the property names that should be 
+     *  excluded from the output.
+     *  Use this to remove data from the formatted string.
+     * 
+     *  @return String containing the formatted version
+     *  of the specified object.
+     *
+     *  @example
+     *  <pre>
+     *  // example 1
+     *  var obj:AsyncMessage = new AsyncMessage();
+     *  obj.body = [];
+     *  obj.body.push(new AsyncMessage());
+     *  obj.headers["1"] = { name: "myName", num: 15.3};
+     *  obj.headers["2"] = { name: "myName", num: 15.3};
+     *  obj.headers["10"] = { name: "myName", num: 15.3};
+     *  obj.headers["11"] = { name: "myName", num: 15.3};
+     *  trace(ObjectUtil.toString(obj));
+     *
+     *  // will output to flashlog.txt
+     *  (mx.messaging.messages::AsyncMessage)#0
+     *    body = (Array)#1
+     *      [0] (mx.messaging.messages::AsyncMessage)#2
+     *        body = (Object)#3
+     *        clientId = (Null)
+     *        correlationId = ""
+     *        destination = ""
+     *        headers = (Object)#4
+     *        messageId = "378CE96A-68DB-BC1B-BCF7FFFFFFFFB525"
+     *        sequenceId = (Null)
+     *        sequencePosition = 0
+     *        sequenceSize = 0
+     *        timeToLive = 0
+     *        timestamp = 0
+     *    clientId = (Null)
+     *    correlationId = ""
+     *    destination = ""
+     *    headers = (Object)#5
+     *      1 = (Object)#6
+     *        name = "myName"
+     *        num = 15.3
+     *      10 = (Object)#7
+     *        name = "myName"
+     *        num = 15.3
+     *      11 = (Object)#8
+     *        name = "myName"
+     *        num = 15.3
+     *      2 = (Object)#9
+     *        name = "myName"
+     *        num = 15.3
+     *    messageId = "1D3E6E96-AC2D-BD11-6A39FFFFFFFF517E"
+     *    sequenceId = (Null)
+     *    sequencePosition = 0
+     *    sequenceSize = 0
+     *    timeToLive = 0
+     *    timestamp = 0
+     *
+     *  // example 2 with circular references
+     *  obj = {};
+     *  obj.prop1 = new Date();
+     *  obj.prop2 = [];
+     *  obj.prop2.push(15.2);
+     *  obj.prop2.push("testing");
+     *  obj.prop2.push(true);
+     *  obj.prop3 = {};
+     *  obj.prop3.circular = obj;
+     *  obj.prop3.deeper = new ErrorMessage();
+     *  obj.prop3.deeper.rootCause = obj.prop3.deeper;
+     *  obj.prop3.deeper2 = {};
+     *  obj.prop3.deeper2.deeperStill = {};
+     *  obj.prop3.deeper2.deeperStill.yetDeeper = obj;
+     *  trace(ObjectUtil.toString(obj));
+     *
+     *  // will output to flashlog.txt
+     *  (Object)#0
+     *    prop1 = Tue Apr 26 13:59:17 GMT-0700 2005
+     *    prop2 = (Array)#1
+     *      [0] 15.2
+     *      [1] "testing"
+     *      [2] true
+     *    prop3 = (Object)#2
+     *      circular = (Object)#0
+     *      deeper = (mx.messaging.messages::ErrorMessage)#3
+     *        body = (Object)#4
+     *        clientId = (Null)
+     *        code = (Null)
+     *        correlationId = ""
+     *        destination = ""
+     *        details = (Null)
+     *        headers = (Object)#5
+     *        level = (Null)
+     *        message = (Null)
+     *        messageId = "14039376-2BBA-0D0E-22A3FFFFFFFF140A"
+     *        rootCause = (mx.messaging.messages::ErrorMessage)#3
+     *        sequenceId = (Null)
+     *        sequencePosition = 0
+     *        sequenceSize = 0
+     *        timeToLive = 0
+     *        timestamp = 0
+     *      deeper2 = (Object)#6
+     *        deeperStill = (Object)#7
+     *          yetDeeper = (Object)#0
+     * 
+     * // example 3 with Dictionary
+     * var point:Point = new Point(100, 100);
+     * var point2:Point = new Point(100, 100);
+     * var obj:Dictionary = new Dictionary();
+     * obj[point] = "point";
+     * obj[point2] = "point2";
+     * obj["1"] = { name: "one", num: 1};
+     * obj["two"] = { name: "2", num: 2};
+     * obj[3] = 3;
+     * trace(ObjectUtil.toString(obj));
+     * 
+     * // will output to flashlog.txt
+     * (flash.utils::Dictionary)#0
+     *   {(flash.geom::Point)#1
+     *     length = 141.4213562373095
+     *     x = 100
+     *     y = 100} = "point2"
+     *   {(flash.geom::Point)#2
+     *     length = 141.4213562373095
+     *     x = 100
+     *     y = 100} = "point"
+     *   {1} = (Object)#3
+     *     name = "one"
+     *     num = 1
+     *   {3} = 3
+     *   {"two"} = (Object)#4
+     *     name = "2"
+     *     num = 2
+     * 
+     * </pre>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function toString(value:Object, 
+                                    namespaceURIs:Array = null, 
+                                    exclude:Array = null):String
+    {
+        if (exclude == null)
+        {
+            exclude = defaultToStringExcludes;
+        }
+        
+        refCount = 0;
+        return internalToString(value, 0, null, namespaceURIs, exclude);
+    }
+    
+    /**
+     *  This method cleans up all of the additional parameters that show up in AsDoc
+     *  code hinting tools that developers shouldn't ever see.
+     *  @private
+     */
+    private static function internalToString(value:Object, 
+                                             indent:int = 0,
+                                             refs:CircularReferenceManager = null, 
+                                             namespaceURIs:Array = null, 
+                                             exclude:Array = null):String
+    {
+        var str:String;
+        var objectType:String = value == null ? "null" : typeof(value);
+        switch (objectType)
+        {
+            case "boolean":
+            case "number":
+            {
+                return value.toString();
+            }
+
+            case "string":
+            {
+                return "\"" + value.toString() + "\"";
+            }
+
+            case "object":
+            {
+                if (value is Date)
+                {
+                    return value.toString();
+                }
+                else if (isXMLNode(value))
+                {
+                    return value.toString();
+                }
+                else if (value is Class)
+                {
+                    return "(" + getQualifiedClassName(value) + ")";
+                }
+                else
+                {
+                    var classInfo:Object = getClassInfo(value, exclude,
+                        { includeReadOnly: true, uris: namespaceURIs });
+                        
+                    var properties:Array = classInfo.properties;
+                    
+                    str = "(" + classInfo.name + ")";
+                    
+                    // refs help us avoid circular reference infinite recursion.
+                    // Each time an object is encoumtered it is pushed onto the
+                    // refs stack so that we can determine if we have visited
+                    // this object already.
+                    if (refs == null)
+                        refs = new CircularReferenceManager();
+
+                    // Check to be sure we haven't processed this object before
+                    // Dictionary has some bugs, so we want to work around them as best we can
+                    try
+                    {
+                        var id:Object = refs[value];
+                        if (id != null)
+                        {
+                            str += "#" + int(id);
+                            return str;
+                        }
+                    }
+                    catch (e:Error)
+                    {
+                        //Since we can't test for infinite loop, we simply return toString.
+                        return String(value);
+                    }
+                    
+                    if (value != null)
+                    {
+                        str += "#" + refCount.toString();
+                        refs[value] = refCount;
+                        refCount++;
+                    }
+
+                    var isArray:Boolean = value is Array;
+                    var isDict:Boolean;
+                    COMPILE::AS3
+                    {
+                        isDict = value is Dictionary;
+                    }
+                    var prop:*;
+                    indent += 2;
+                    
+                    // Print all of the variable values.
+                    for (var j:int = 0; j < properties.length; j++)
+                    {
+                        str = newline(str, indent);
+                        prop = properties[j];
+                        
+                        if (isArray)
+                            str += "[";
+                        else if (isDict)
+                            str += "{";
+
+                    
+                        if (isDict)
+                        {
+                            // in dictionaries, recurse on the key, because it can be a complex object
+                            str += internalToString(prop, indent, refs,
+                                                    namespaceURIs, exclude);
+                        }
+                        else
+                        {
+                            str += prop.toString();
+                        }
+                        
+                        if (isArray)
+                            str += "] ";
+                        else if (isDict)
+                            str += "} = ";
+                        else
+                            str += " = ";
+                        
+                        try
+                        {
+                            // print the value
+                            str += internalToString(value[prop], indent, refs,
+                                                    namespaceURIs, exclude);
+                        }
+                        catch(e:Error)
+                        {
+                            // value[prop] can cause an RTE
+                            // for certain properties of certain objects.
+                            // For example, accessing the properties
+                            //   actionScriptVersion
+                            //   childAllowsParent
+                            //   frameRate
+                            //   height
+                            //   loader
+                            //   parentAllowsChild
+                            //   sameDomain
+                            //   swfVersion
+                            //   width
+                            // of a Stage's loaderInfo causes
+                            //   Error #2099: The loading object is not
+                            //   sufficiently loaded to provide this information
+                            // In this case, we simply output ? for the value.
+                            str += "?";
+                        }
+                    }
+                    indent -= 2;
+                    return str;
+                }
+                break;
+            }
+
+            case "xml":
+            {
+                return value.toXMLString();
+            }
+
+            default:
+            {
+                return "(" + objectType + ")";
+            }
+        }
+        
+        return "(unknown)";
+    }
+
+    /**
+     *  @private
+     *  This method will append a newline and the specified number of spaces
+     *  to the given string.
+     */
+    private static function newline(str:String, n:int = 0):String
+    {
+        var result:String = str;
+        result += "\n";
+        
+        for (var i:int = 0; i < n; i++)
+        {
+            result += " ";
+        }
+        return result;
+    }
+    
+    private static function internalCompare(a:Object, b:Object,
+                                            currentDepth:int, desiredDepth:int,
+                                            refs:CircularReferenceManager):int
+    {
+        if (a == null && b == null)
+            return 0;
+    
+        if (a == null)
+            return 1;
+    
+        if (b == null)
+            return -1;
+           
+        if (a is ObjectProxy)
+            a = ObjectProxy(a).object_proxy::object;
+            
+        if (b is ObjectProxy)
+            b = ObjectProxy(b).object_proxy::object;
+            
+        var typeOfA:String = typeof(a);
+        var typeOfB:String = typeof(b);
+        
+        var result:int = 0;
+        
+        if (typeOfA == typeOfB)
+        {
+            switch(typeOfA)
+            {
+                case "boolean":
+                {
+                    result = numericCompare(Number(a), Number(b));
+                    break;
+                }
+                
+                case "number":
+                {
+                    result = numericCompare(a as Number, b as Number);
+                    break;
+                }
+                
+                case "string":
+                {
+                    result = stringCompare(a as String, b as String);
+                    break;
+                }
+                
+                case "object":
+                {
+                    var newDepth:int = desiredDepth > 0 ? desiredDepth -1 : desiredDepth;
+                    
+                    // refs help us avoid circular reference infinite recursion.
+                    var aRef:Object = getRef(a,refs);
+                    var bRef:Object = getRef(b,refs);
+                    
+                    if (aRef == bRef)
+                        return 0;
+                    // the cool thing about our dictionary is that if 
+                    // we've seen objects and determined that they are unequal, then
+                    // we would've already exited out of this compare() call.  So the 
+                    // only info in the dictionary are sets of equal items
+                    
+                    // let's first define them as equal
+                    // this stops an "infinite loop" problem where A.i = B and B.i = A
+                    // if we later find that an object (one of the subobjects) is in fact unequal, 
+                    // then we will return false and quit out of everything.  These refs are thrown away
+                    // so it doesn't matter if it's correct.
+                    refs[bRef] = aRef;
+                    
+                    if (desiredDepth != -1 && (currentDepth > desiredDepth))
+                    {
+                        // once we try to go beyond the desired depth we should 
+                        // toString() our way out
+                        result = stringCompare(a.toString(), b.toString());
+                    }
+                    else if ((a is Array) && (b is Array))
+                    {
+                        result = arrayCompare(a as Array, b as Array, currentDepth, desiredDepth, refs);
+                    }
+                    else if ((a is Date) && (b is Date))
+                    {
+                        result = dateCompare(a as Date, b as Date);
+                    }
+                    else if ((a is IList) && (b is IList))
+                    {
+                        result = listCompare(a as IList, b as IList, currentDepth, desiredDepth, refs);
+                    }
+                    else if (areBothByteArrays(a, b))
+                    {
+                        COMPILE::AS3
+                        {
+                            result = byteArrayCompare(a as ByteArray, b as ByteArray);                                
+                        }
+                    }
+                    else if (getQualifiedClassName(a) == getQualifiedClassName(b))
+                    {
+                        var aProps:Array = getClassInfo(a).properties;
+                        var bProps:Array;
+                        
+                        // if the objects are dynamic they could have different 
+                        // # of properties and should be treated on that basis first
+                        var isObjectDynamic:Boolean = isDynamicObject(a);
+                        
+                        // if it's dynamic, check to see that they have all the same properties
+                        if (isObjectDynamic)
+                        {
+                            bProps = getClassInfo(b).properties;
+                            result = arrayCompare(aProps, bProps, currentDepth, newDepth, refs);
+                            if (result != 0)
+                                return result;
+                        }
+                        
+                        // now that we know we have the same properties, let's compare the values
+                        var propName:QName;
+                        var aProp:Object;
+                        var bProp:Object;
+                        for (var i:int = 0; i < aProps.length; i++)
+                        {
+                            propName = aProps[i];
+                            aProp = a[propName];
+                            bProp = b[propName];
+                            result = internalCompare(aProp, bProp, currentDepth+1, newDepth, refs);
+                            if (result != 0)
+                            {
+                                return result;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // We must be unequal, so return 1
+                        return 1;
+                    }
+                    break;
+                }
+            }
+        }
+        else // be consistent with the order we return here
+        {
+            return stringCompare(typeOfA, typeOfB);
+        }
+
+        return result;
+    }
+    
+    private static function isXMLNode(value:Object):Boolean
+    {
+        COMPILE::AS3
+        {
+            return (value is XMLNode);
+        }
+        COMPILE::JS
+        {
+            return false;
+        }
+    }
+    
+    private static function areBothByteArrays(a:Object, b:Object):Boolean
+    {
+        COMPILE::AS3
+        {
+            return (a is ByteArray) && (b is ByteArray)
+        }
+        COMPILE::JS
+        {
+            return false;
+        }
+    }
+    /**
+     *  Returns information about the class, and properties of the class, for
+     *  the specified Object.
+     *
+     *  @param obj The Object to inspect.
+     *
+     *  @param excludes Array of Strings specifying the property names that should be
+     *  excluded from the returned result. For example, you could specify 
+     *  <code>["currentTarget", "target"]</code> for an Event object since these properties 
+     *  can cause the returned result to become large.
+     *
+     *  @param options An Object containing one or more properties 
+     *  that control the information returned by this method. 
+     *  The properties include the following:
+     *
+     *  <ul>
+     *    <li><code>includeReadOnly</code>: If <code>false</code>, 
+     *      exclude Object properties that are read-only. 
+     *      The default value is <code>true</code>.</li>
+     *  <li><code>includeTransient</code>: If <code>false</code>, 
+     *      exclude Object properties and variables that have <code>[Transient]</code> metadata.
+     *      The default value is <code>true</code>.</li>
+     *  <li><code>uris</code>: Array of Strings of all namespaces that should be included in the output.
+     *      It does allow for a wildcard of "~~". 
+     *      By default, it is null, meaning no namespaces should be included. 
+     *      For example, you could specify <code>["mx_internal", "mx_object"]</code> 
+     *      or <code>["~~"]</code>.</li>
+     *  </ul>
+     * 
+     *  @return An Object containing the following properties:
+     *  <ul>
+     *    <li><code>name</code>: String containing the name of the class.</li>
+     *    <li><code>properties</code>: Sorted list of the property names of the specified object,
+     *    or references to the original key if the specified object is a Dictionary. The individual
+     *    array elements are QName instances, which contain both the local name of the property as well as the URI.</li>
+     *  </ul>
+    *  
+    *  @langversion 3.0
+    *  @playerversion Flash 9
+    *  @playerversion AIR 1.1
+    *  @productversion Flex 3
+    */
+    public static function getClassInfo(obj:Object,
+                                        excludes:Array = null,
+                                        options:Object = null):Object
+    {   
+        var n:int;
+        var i:int;
+
+        if (obj is ObjectProxy)
+            obj = ObjectProxy(obj).object_proxy::object;
+
+        if (options == null)
+            options = { includeReadOnly: true, uris: null, includeTransient: true };
+
+        var result:Object;
+        var propertyNames:Array = [];
+        var cacheKey:String;
+
+        var className:String;
+        var classAlias:String;
+        var properties:XMLList;
+        var prop:XML;
+        var isDynamic:Boolean = false;
+        var metadataInfo:Object;
+
+        if (typeof(obj) == "xml")
+        {
+            className = "XML";
+            properties = obj.text();
+            if (properties.length())
+                propertyNames.push("*");
+            properties = obj.attributes();
+        }
+        else
+        {
+            var classInfo:XML = DescribeTypeCache.describeType(obj).typeDescription;
+            className = classInfo.@name.toString();
+            classAlias = classInfo.@alias.toString();
+            isDynamic = classInfo.@isDynamic.toString() == "true";
+
+            if (options.includeReadOnly)
+                properties = classInfo..accessor.(@access != "writeonly") + classInfo..variable;
+            else
+                properties = classInfo..accessor.(@access == "readwrite") + classInfo..variable;
+
+            var numericIndex:Boolean = false;
+        }
+
+        // If type is not dynamic, check our cache for class info...
+        if (!isDynamic)
+        {
+            cacheKey = getCacheKey(obj, excludes, options);
+            result = CLASS_INFO_CACHE[cacheKey];
+            if (result != null)
+                return result;
+        }
+
+        result = {};
+        result["name"] = className;
+        result["alias"] = classAlias;
+        result["properties"] = propertyNames;
+        result["dynamic"] = isDynamic;
+        result["metadata"] = metadataInfo = recordMetadata(properties);
+        
+        var excludeObject:Object = {};
+        if (excludes)
+        {
+            n = excludes.length;
+            for (i = 0; i < n; i++)
+            {
+                excludeObject[excludes[i]] = 1;
+            }
+        }
+
+        var isArray:Boolean = (obj is Array);
+        var isDict:Boolean  = (obj is Dictionary);
+        
+        if (isDict)
+        {
+            // dictionaries can have multiple keys of the same type,
+            // (they can index by reference rather than QName, String, or number),
+            // which cannot be looked up by QName, so use references to the actual key
+            for (var key:* in obj)
+            {
+                propertyNames.push(key);
+            }
+        }
+        else if (isDynamic)
+        {
+            for (var p:String in obj)
+            {
+                if (excludeObject[p] != 1)
+                {
+                    if (isArray)
+                    {
+                         var pi:Number = parseInt(p);
+                         if (isNaN(pi))
+                            propertyNames.push(new QName("", p));
+                         else
+                            propertyNames.push(pi);
+                    }
+                    else
+                    {
+                        propertyNames.push(new QName("", p));
+                    }
+                }
+            }
+            numericIndex = isArray && !isNaN(Number(p));
+        }
+
+        if (isArray || isDict || className == "Object")
+        {
+            // Do nothing since we've already got the dynamic members
+        }
+        else if (className == "XML")
+        {
+            n = properties.length();
+            for (i = 0; i < n; i++)
+            {
+                p = properties[i].name();
+                if (excludeObject[p] != 1)
+                    propertyNames.push(new QName("", "@" + p));
+            }
+        }
+        else
+        {
+            n = properties.length();
+            var uris:Array = options.uris;
+            var uri:String;
+            var qName:QName;
+            for (i = 0; i < n; i++)
+            {
+                prop = properties[i];
+                p = prop.@name.toString();
+                uri = prop.@uri.toString();
+                
+                if (excludeObject[p] == 1)
+                    continue;
+                    
+                if (!options.includeTransient && internalHasMetadata(metadataInfo, p, "Transient"))
+                    continue;
+                
+                if (uris != null)
+                {
+                    if (uris.length == 1 && uris[0] == "*")
+                    {   
+                        qName = new QName(uri, p);
+                        try
+                        {
+                            obj[qName]; // access the property to ensure it is supported
+                            propertyNames.push();
+                        }
+                        catch(e:Error)
+                        {
+                            // don't keep property name 
+                        }
+                    }
+                    else
+                    {
+                        for (var j:int = 0; j < uris.length; j++)
+                        {
+                            uri = uris[j];
+                            if (prop.@uri.toString() == uri)
+                            {
+                                qName = new QName(uri, p);
+                                try
+                                {
+                                    obj[qName];
+                                    propertyNames.push(qName);
+                                }
+                                catch(e:Error)
+                                {
+                                    // don't keep property name 
+                                }
+                            }
+                        }
+                    }
+                }
+                else if (uri.length == 0)
+                {
+                    qName = new QName(uri, p);
+                    try
+                    {
+                        obj[qName];
+                        propertyNames.push(qName);
+                    }
+                    catch(e:Error)
+                    {
+                        // don't keep property name 
+                    }
+                }
+            }
+        }
+
+        propertyNames.sort(Array.CASEINSENSITIVE |
+                           (numericIndex ? Array.NUMERIC : 0));
+
+        // dictionary keys can be indexed by an object reference
+        // there's a possibility that two keys will have the same toString()
+        // so we don't want to remove dupes
+        if (!isDict)
+        {
+            // for Arrays, etc., on the other hand...
+            // remove any duplicates, i.e. any items that can't be distingushed by toString()
+            for (i = 0; i < propertyNames.length - 1; i++)
+            {
+                // the list is sorted so any duplicates should be adjacent
+                // two properties are only equal if both the uri and local name are identical
+                if (propertyNames[i].toString() == propertyNames[i + 1].toString())
+                {
+                    propertyNames.splice(i, 1);
+                    i--; // back up
+                }
+            }
+        }
+
+        // For normal, non-dynamic classes we cache the class info
+		// Don't cache XML as it can be dynamic
+        if (!isDynamic && className != "XML")
+        {
+            cacheKey = getCacheKey(obj, excludes, options);
+            CLASS_INFO_CACHE[cacheKey] = result;
+        }
+
+        return result;
+    }
+
+    /**
+     * Uses <code>getClassInfo</code> and examines the metadata information to
+     * determine whether a property on a given object has the specified 
+     * metadata.
+     * 
+     * @param obj The object holding the property.
+     * @param propName The property to check for metadata.
+     * @param metadataName The name of the metadata to check on the property.
+     * @param excludes If any properties need to be excluded when generating class info. (Optional)
+     * @param options If any options flags need to changed when generating class info. (Optional)
+     * @return true if the property has the specified metadata.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function hasMetadata(obj:Object, 
+                propName:String, 
+                metadataName:String, 
+                excludes:Array = null,
+                options:Object = null):Boolean
+    {
+        var classInfo:Object = getClassInfo(obj, excludes, options);
+        var metadataInfo:Object = classInfo["metadata"];
+        return internalHasMetadata(metadataInfo, propName, metadataName);
+    }
+
+    /**
+     *  Returns <code>true</code> if the object is an instance of a dynamic class.
+     *
+     *  @param obj The object.
+     *
+     *  @return <code>true</code> if the object is an instance of a dynamic class.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function isDynamicObject(obj:Object):Boolean
+    {
+        try
+        {
+            // this test for checking whether an object is dynamic or not is 
+            // pretty hacky, but it assumes that no-one actually has a 
+            // property defined called "wootHackwoot"
+            obj["wootHackwoot"];
+        }
+        catch (e:Error)
+        {
+            // our object isn't from a dynamic class
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     *  Returns the value at the end of the property chain <code>path</code>.
+     *  If the value cannot be reached due to null links on the chain,
+     *  <code>undefined</code> is returned.
+     *
+     *  @param obj The object at the beginning of the property chain
+     *  @param path The path to inspect (e.g. "address.street")
+     *
+     *  @return the value at the end of the property chain, <code>undefined</code>
+     *  if it cannot be reached, or the object itself when <code>path</code> is empty.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function getValue(obj:Object, path:Array):*
+    {
+        if(!obj)
+            return undefined;
+
+        if(!path || !path.length)
+            return obj;
+
+        var result:* = obj;
+        var i:int = -1;
+        while(++i < path.length && result)
+            result = result.hasOwnProperty(path[i]) ? result[path[i]] : undefined;
+
+        return result;
+    }
+
+
+    /**
+     *  Sets a new value at the end of the property chain <code>path</code>.
+     *  If the value cannot be reached due to null links on the chain,
+     *  <code>false</code> is returned.
+     *
+     *  @param obj The object at the beginning of the property chain
+     *  @param path The path to traverse (e.g. "address.street")
+     *  @param newValue The value to set (e.g. "Fleet Street")
+     *
+     *  @return <code>true</code> if the value is successfully set,
+     *  <code>false</code> otherwise. Note that the function does not
+     *  use a try/catch block. You can implement one in the calling
+     *  function if there's a risk of type mismatch or other errors during
+     *  the assignment.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function setValue(obj:Object, path:Array, newValue:*):Boolean
+    {
+        if(!obj || !path || !path.length)
+            return false;
+
+        var secondToLastLink:* = getValue(obj, path.slice(0, -1));
+        if(secondToLastLink && secondToLastLink.hasOwnProperty(path[path.length - 1]))
+        {
+            secondToLastLink[path[path.length - 1]] = newValue;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     *  @private
+     */
+    private static function internalHasMetadata(metadataInfo:Object, propName:String, metadataName:String):Boolean
+    {
+        if (metadataInfo != null)
+        {
+            var metadata:Object = metadataInfo[propName];
+            if (metadata != null)
+            {
+                if (metadata[metadataName] != null)
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     *  @private
+     */
+    private static function recordMetadata(properties:XMLList):Object
+    {
+        var result:Object = null;
+
+        try
+        {
+            for each (var prop:XML in properties)
+            {
+                var propName:String = prop.attribute("name").toString();
+                var metadataList:XMLList = prop.metadata;
+
+                if (metadataList.length() > 0)
+                {
+                    if (result == null)
+                        result = {};
+
+                    var metadata:Object = {};
+                    result[propName] = metadata;
+
+                    for each (var md:XML in metadataList)
+                    {
+                        var mdName:String = md.attribute("name").toString();
+                        
+                        var argsList:XMLList = md.arg;
+                        var value:Object = {};
+
+                        for each (var arg:XML in argsList)
+                        {
+                            var argKey:String = arg.attribute("key").toString();
+                            if (argKey != null)
+                            {
+                                var argValue:String = arg.attribute("value").toString();
+                                value[argKey] = argValue;
+                            }
+                        }
+
+                        var existing:Object = metadata[mdName];
+                        if (existing != null)
+                        {
+                            var existingArray:Array;
+                            if (existing is Array)
+                                existingArray = existing as Array;
+                            else
+                            {
+                                existingArray = [existing];
+                                delete metadata[mdName];
+                            }
+                            existingArray.push(value);
+                            existing = existingArray;
+                        }
+                        else
+                        {
+                            existing = value;
+                        }
+                        metadata[mdName] = existing;
+                    }
+                }
+            }
+        }
+        catch(e:Error)
+        {
+        }
+        
+        return result;
+    }
+
+
+    /**
+     *  @private
+     */
+    private static function getCacheKey(o:Object, excludes:Array = null, options:Object = null):String
+    {
+        var key:String = getQualifiedClassName(o);
+		
+        if (excludes != null)
+        {
+			var length:int = excludes.length;
+            for (var i:uint = 0; i < length; i++)
+            {
+                var excl:String = excludes[i] as String;
+                if (excl != null)
+                    key += excl;
+            }
+        }
+
+        if (options != null)
+        {
+            for (var flag:String in options)
+            {
+                key += flag;
+				var value:String = options[flag];
+				if (value != null)
+					key += value.toString();
+			}
+        }
+        return key;
+    }
+
+    /**
+     *  @private
+     */
+    private static function arrayCompare(a:Array, b:Array,
+                                         currentDepth:int, desiredDepth:int,
+                                         refs:CircularReferenceManager):int
+    {
+        var result:int = 0;
+
+        if (a.length != b.length)
+        {
+            if (a.length < b.length)
+                result = -1;
+            else
+                result = 1;
+        }
+        else
+        {
+            var key:Object;
+            for (key in a)
+            {
+                if (b.hasOwnProperty(key))
+                {
+                    result = internalCompare(a[key], b[key], currentDepth,
+                                         desiredDepth, refs);
+
+                    if (result != 0)
+                        return result;
+                }
+                else
+                {
+                    return -1;
+                }
+            }
+
+            for (key in b)
+            {
+                if (!a.hasOwnProperty(key))
+                {
+                    return 1;
+                }
+            }
+        }
+
+        return result;
+    }
+    
+    /**
+     * @private
+     */
+    COMPILE::AS3
+    private static function byteArrayCompare(a:ByteArray, b:ByteArray):int
+    {
+        var result:int = 0;
+        
+        if (a == b)
+            return result;
+            
+        if (a.length != b.length)
+        {
+            if (a.length < b.length)
+                result = -1;
+            else
+                result = 1;
+        }
+        else
+        {
+            for (var i:int = 0; i < a.length; i++)
+            {
+                result = numericCompare(a[i], b[i]);
+                if (result != 0)
+                {
+                    i = a.length;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     *  @private
+     */
+    private static function listCompare(a:IList, b:IList, currentDepth:int, 
+                                        desiredDepth:int, refs:CircularReferenceManager):int
+    {
+        var result:int = 0;
+
+        if (a.length != b.length)
+        {
+            if (a.length < b.length)
+                result = -1;
+            else
+                result = 1;
+        }
+        else
+        {
+            for (var i:int = 0; i < a.length; i++)
+            {
+                result = internalCompare(a.getItemAt(i), b.getItemAt(i), 
+                                         currentDepth+1, desiredDepth, refs);
+                if (result != 0)
+                {
+                    i = a.length;
+                }
+            }
+        }
+
+        return result;
+    }
+    
+    /**
+     * @private
+     * This is the "find" for our union-find algorithm when doing object searches.
+     * The dictionary keeps track of sets of equal objects
+     */
+    private static function getRef(o:Object, refs:CircularReferenceManager):Object
+    {
+        var oRef:Object = refs[o]; 
+        while (oRef && oRef != refs[oRef])
+        {
+            oRef = refs[oRef];
+        }
+        if (!oRef)
+            oRef = o;
+        if (oRef != refs[o])
+            refs[o] = oRef;
+        
+        return oRef;
+    }
+    
+    /**
+     * @private
+     */
+    private static var refCount:int = 0;
+
+    /**
+     * @private
+     */ 
+    private static var CLASS_INFO_CACHE:Object = {};
+}
+
+}
+
+COMPILE::AS3
+{
+    import flash.utils.Dictionary;
+}
+
+class CircularReferenceManager
+{
+    COMPILE::AS3
+    private var dict:Dictionary = new Dictionary(true);
+    
+    COMPILE::JS
+    private var array:Array = [];
+    
+    public function CircularReferenceManager()
+    {    
+    }
+    
+    public function put(obj:Object):void
+    {
+        COMPILE::AS3
+        {
+            dict[obj] = 1;
+        }
+        COMPILE::JS
+        {
+            array.push(obj);
+        }
+    }
+    
+    public function contains(obj:Object):Boolean
+    {
+        COMPILE::AS3
+        {
+            return (dict[obj] == 1);
+        }
+        COMPILE::JS
+        {
+            return array.indexOf(obj) != -1;
+        }
+    }
+        
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/1f6418c4/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as b/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as
new file mode 100644
index 0000000..c4a0d3b
--- /dev/null
+++ b/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as
@@ -0,0 +1,153 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package mx.utils
+{
+COMPILE::AS3
+{
+	import flash.events.Event;	
+}
+COMPILE::JS
+{
+	import org.apache.flex.events.Event;	
+}
+    import org.apache.flex.events.IEventDispatcher;
+    import org.apache.flex.events.EventDispatcher;
+
+    /**
+     * OnDemandEventDispatcher serves as a base class for classes that dispatch events but expect listeners
+     * to be infrequent.  When a class extends OnDemandEventDispatcher instead of the standard EventDispatcher,
+     * it is trading off a small overhead on every single instance for a slightly larger overhead on only the instances
+     * that actually have listeners attached to them.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public class OnDemandEventDispatcher implements IEventDispatcher
+    {
+        private var _dispatcher:EventDispatcher;
+    
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+        /**
+         * Constructor.
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 9
+         *  @playerversion AIR 1.1
+         *  @productversion Flex 3
+         */
+        public function OnDemandEventDispatcher()
+        {
+        }
+
+        /**
+         *  @inheritDoc
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 9
+         *  @playerversion AIR 1.1
+         *  @productversion Flex 3
+         */
+		COMPILE::AS3
+        public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
+        {
+            if (_dispatcher == null)
+            {
+                _dispatcher = new EventDispatcher(this);
+            }
+            _dispatcher.addEventListener(type,listener,useCapture,priority,useWeakReference); 
+        }
+		COMPILE::JS
+		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false):void
+		{
+			if (_dispatcher == null)
+			{
+				_dispatcher = new EventDispatcher(this);
+			}
+			_dispatcher.addEventListener(type,listener,useCapture); 
+		}
+        
+            
+        /**
+         *  @inheritDoc
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 9
+         *  @playerversion AIR 1.1
+         *  @productversion Flex 3
+         */
+        public function dispatchEvent(event:Event):Boolean
+        {
+            if (_dispatcher != null)
+                return _dispatcher.dispatchEvent(event);
+            return true; 
+        }
+    
+        /**
+         *  @inheritDoc
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 9
+         *  @playerversion AIR 1.1
+         *  @productversion Flex 3
+         */
+        public function hasEventListener(type:String):Boolean
+        {
+            if (_dispatcher != null)
+                return _dispatcher.hasEventListener(type);
+            return false; 
+        }
+            
+        /**
+         *  @inheritDoc
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 9
+         *  @playerversion AIR 1.1
+         *  @productversion Flex 3
+         */
+        public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
+        {
+            if (_dispatcher != null)
+                _dispatcher.removeEventListener(type,listener,useCapture);         
+        }
+    
+        /**
+         *  @inheritDoc
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 9
+         *  @playerversion AIR 1.1
+         *  @productversion Flex 3
+         */
+        public function willTrigger(type:String):Boolean
+        {
+            if (_dispatcher != null)
+                return _dispatcher.willTrigger(type);
+            return false; 
+        }
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/1f6418c4/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as b/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as
new file mode 100644
index 0000000..6a19b2e
--- /dev/null
+++ b/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as
@@ -0,0 +1,337 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package mx.utils
+{
+
+import flash.system.Capabilities;
+import flash.utils.getDefinitionByName;
+
+/**
+ *  The Platform utility class contains several static methods to check what
+ *  desktop or mobile platform the application is running on.
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 2.0
+ *  @productversion Flex 4.12
+ */
+public class Platform
+{
+    include "../core/Version.as";
+
+    private static var _instance: Platform;
+	
+	protected static var _initialized:Boolean;
+	protected static var _isAndroid:Boolean;
+	protected static var _isIOS:Boolean;
+	protected static var _isIPad:Boolean;
+	protected static var _isBlackBerry:Boolean;
+	protected static var _isMobile:Boolean;
+	protected static var _isMac:Boolean;
+	protected static var _isWindows:Boolean;
+	protected static var _isLinux:Boolean;
+	protected static var _isDesktop:Boolean;
+	protected static var _isBrowser:Boolean;
+	protected static var _isAir:Boolean;
+    private static var _osVersion: String = null;
+
+	/**
+	 * This value is set from AndroidPlatformVersionOverride
+	 * 
+	 */
+	mx_internal static var androidVersionOverride:String;
+
+	/**
+	 * This value is set from IOSPlatformVersionOverride
+	 * 
+	 */
+	mx_internal static var iosVersionOverride:String;
+	
+	/**
+	 *  Returns true if the application is running on IOS.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isIOS():Boolean
+	{
+		getPlatforms();
+		
+		return _isIOS;
+	}
+	
+	/**
+	 *  Returns true if the application is running on an iPad.
+	 *  Note this returns false in the AIR mobile device simulator.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isIPad():Boolean
+	{
+		getPlatforms();
+		
+		return _isIPad;
+	}
+	
+	/**
+	 *  Returns true if the application is running on a BlackBerry.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isBlackBerry():Boolean
+	{
+		getPlatforms();
+		
+		return _isBlackBerry;
+	}
+	
+	/**
+	 *  Returns true if the application is running on Android.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isAndroid():Boolean
+	{
+		getPlatforms();
+		
+		return _isAndroid;
+	}
+	
+	/**
+	 *  Returns true if the application is running on Windows.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isWindows():Boolean
+	{
+		getPlatforms();
+		
+		return _isWindows;
+	}
+	
+	/**
+	 *  Returns true if the application is running on a Mac.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isMac():Boolean
+	{
+		getPlatforms();
+		
+		return _isMac;
+	}
+	
+	/**
+	 *  Returns true if the application is running on Linux.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isLinux():Boolean
+	{
+		getPlatforms();
+		
+		return _isLinux;
+	}
+	
+	/**
+	 *  Returns true if the application is running on a Desktop OS.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isDesktop():Boolean
+	{
+		getPlatforms();
+		
+		return _isDesktop;
+	}
+	
+	/**
+	 *  Returns true if the application is running on a Mobile device.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isMobile():Boolean
+	{
+		getPlatforms();
+		
+		return _isMobile;
+	}
+	
+	/**
+	 *  Returns true if the application is running on a desktop AIR.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isAir():Boolean
+	{
+		getPlatforms();
+		
+		return _isAir;
+	}
+	
+	/**
+	 *  Returns true if the application is running in a browser.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10
+	 *  @playerversion AIR 2.0
+	 *  @productversion Flex 4.12
+	 */
+	public static function get isBrowser():Boolean
+	{
+		getPlatforms();
+		
+		return _isBrowser;
+	}
+
+    /**
+     *  Returns the version of the OS the application  is running on
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.0
+     *  @productversion Flex 4.13
+     */
+    public static function get osVersion(): String
+    {
+        //We needed to compute _osVersion later than getPlatforms, because it relies on resources that  ready later
+        if (_osVersion == null){
+			if(mx_internal::androidVersionOverride == null && mx_internal::iosVersionOverride == null)
+			{
+				_osVersion = computeOSVersionString();	
+			}
+			else if(mx_internal::androidVersionOverride != null)
+			{
+				_osVersion = mx_internal::androidVersionOverride; 
+			}
+			else if(mx_internal::iosVersionOverride != null)
+			{
+				_osVersion = mx_internal::iosVersionOverride; 
+			}
+        }
+        return _osVersion;
+    }
+
+    /* Notes on Capabilities.os for mobile apps:
+        - on ADL => returns the OS where the ADL is running ( eg. Windows 7, or Mac OS )
+         - on device => returns the OS of the device (eg.  iPhone OS ...  for iOS devices  )
+    * */
+	protected static function getPlatforms():void {
+		if (!_initialized)
+		{
+            var cap: Class = Capabilities;
+            var version:  String = Capabilities.version;
+            var os: String = Capabilities.os;
+            var playerType: String = Capabilities.playerType;
+
+			_isAndroid = version.indexOf("AND") > -1;
+			_isIOS = version.indexOf('IOS') > -1;
+			_isBlackBerry = version.indexOf('QNX') > -1;
+			_isMobile = _isAndroid || _isIOS || _isBlackBerry;
+			
+			_isMac = os.indexOf("Mac OS") != -1;
+			_isWindows = os.indexOf("Windows") != -1;
+			_isLinux = os.indexOf("Linux") != -1; // note that Android is also Linux
+			_isIPad = os.indexOf('iPad') > -1;
+			_isDesktop = !_isMobile;
+			
+			_isAir = playerType == "Desktop";
+			_isBrowser = (playerType == "PlugIn" || playerType == "ActiveX");
+			
+			_initialized = true;
+		}
+	}
+
+    /** @private
+     * extract OS version information from Capabilities.os
+     * os is typically a non-numeric string (such as Windows,  iPhone OS, Android, etc...)  followed by a number sequence.
+     * if no number is found, OS version is set to 0.
+     * os on ADL will return the host OS and not the device OS.
+     *
+     * That's why we need to check for a specific sequence for iOS and Android.
+     * On Android, os  is the Linux kernel version (such as Linux 3.4.34-1790463).
+     * So the version information must be  retrieved from an internal file.
+     * Since reading files API is only available on AIR, it's delegated to PlatformMobileHelper  in mobilecomponents.swc
+     * @see   spark.utils.PlatformMobileHelper
+     *
+     * @return version number string, or empty string if could not retrieve the version.
+     * */
+    private static function computeOSVersionString(): String
+    {
+        var os: String = Capabilities.os;
+        var osVersionMatch: Array;
+        var version: String = "";
+
+        if (isIOS) {
+            osVersionMatch = os.match(/iPhone OS\s([\d\.]+)/);
+            if (osVersionMatch && osVersionMatch.length == 2)
+                version = osVersionMatch[1];
+        }
+        else if (isAndroid) {
+            try {
+                var mobileHelperClass: Class = Class(getDefinitionByName("spark.utils::PlatformMobileHelper"));
+                if (mobileHelperClass != null) {
+                    version = mobileHelperClass["computeOSVersionForAndroid"]();
+                }
+            }
+            catch (e: Error) {
+                trace("Error: " + e.message);
+            }
+        }
+        else {
+            //on  other OS, extract version
+            osVersionMatch = os.match(/[A-Za-z\s]+([\d\.]+)/);
+            if (osVersionMatch && osVersionMatch.length == 2)
+                version = osVersionMatch[1];
+        }
+        return version;
+    }
+
+}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/1f6418c4/frameworks/projects/MX/src/main/flex/mx/utils/StringUtil.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/MX/src/main/flex/mx/utils/StringUtil.as b/frameworks/projects/MX/src/main/flex/mx/utils/StringUtil.as
new file mode 100644
index 0000000..d8ff8a8
--- /dev/null
+++ b/frameworks/projects/MX/src/main/flex/mx/utils/StringUtil.as
@@ -0,0 +1,365 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package mx.utils
+{
+
+/**
+ *  The StringUtil utility class is an all-static class with methods for
+ *  working with String objects within Flex.
+ *  You do not create instances of StringUtil;
+ *  instead you call methods such as 
+ *  the <code>StringUtil.substitute()</code> method.  
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 9
+ *  @playerversion AIR 1.1
+ *  @productversion Flex 3
+ */
+public class StringUtil
+{
+    include "../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Removes all whitespace characters from the beginning and end
+     *  of the specified string.
+     *
+     *  @param str The String whose whitespace should be trimmed. 
+     *
+     *  @return Updated String where whitespace was removed from the 
+     *  beginning and end. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function trim(str:String):String
+    {
+        if (str == null) return '';
+        
+        var startIndex:int = 0;
+        while (isWhitespace(str.charAt(startIndex)))
+            ++startIndex;
+
+        var endIndex:int = str.length - 1;
+        while (isWhitespace(str.charAt(endIndex)))
+            --endIndex;
+
+        if (endIndex >= startIndex)
+            return str.slice(startIndex, endIndex + 1);
+        else
+            return "";
+    }
+    
+    /**
+     *  Removes all whitespace characters from the beginning and end
+     *  of each element in an Array, where the Array is stored as a String. 
+     *
+     *  @param value The String whose whitespace should be trimmed. 
+     *
+     *  @param separator The String that delimits each Array element in the string.
+     *
+     *  @return Updated String where whitespace was removed from the 
+     *  beginning and end of each element. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function trimArrayElements(value:String, delimiter:String):String
+    {
+        if (value != "" && value != null)
+        {
+            var items:Array = value.split(delimiter);
+            
+            var len:int = items.length;
+            for (var i:int = 0; i < len; i++)
+            {
+                items[i] = StringUtil.trim(items[i]);
+            }
+            
+            if (len > 0)
+            {
+                value = items.join(delimiter);
+            }
+        }
+        
+        return value;
+    }
+
+    /**
+     *  Returns <code>true</code> if the specified string is
+     *  a single space, tab, carriage return, newline, or formfeed character.
+     *
+     *  @param str The String that is is being queried. 
+     *
+     *  @return <code>true</code> if the specified string is
+     *  a single space, tab, carriage return, newline, or formfeed character.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function isWhitespace(character:String):Boolean
+    {
+        switch (character)
+        {
+            case " ":
+            case "\t":
+            case "\r":
+            case "\n":
+            case "\f":
+			// non breaking space
+			case "\u00A0":
+			// line seperator
+			case "\u2028":
+			// paragraph seperator
+			case "\u2029":
+			// ideographic space
+			case "\u3000":
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     *  Substitutes "{n}" tokens within the specified string
+     *  with the respective arguments passed in.
+	 * 
+	 *  Note that this uses String.replace and "$" can have special
+	 *  meaning in the argument strings escape by using "$$".
+     *
+     *  @param str The string to make substitutions in.
+     *  This string can contain special tokens of the form
+     *  <code>{n}</code>, where <code>n</code> is a zero based index,
+     *  that will be replaced with the additional parameters
+     *  found at that index if specified.
+     *
+     *  @param rest Additional parameters that can be substituted
+     *  in the <code>str</code> parameter at each <code>{n}</code>
+     *  location, where <code>n</code> is an integer (zero based)
+     *  index value into the array of values specified.
+     *  If the first parameter is an array this array will be used as
+     *  a parameter list.
+     *  This allows reuse of this routine in other methods that want to
+     *  use the ... rest signature.
+     *  For example <pre>
+     *     public function myTracer(str:String, ... rest):void
+     *     { 
+     *         label.text += StringUtil.substitute(str, rest) + "\n";
+     *     } </pre>
+     *
+     *  @return New string with all of the <code>{n}</code> tokens
+     *  replaced with the respective arguments specified.
+     *
+     *  @example
+     *
+     *  var str:String = "here is some info '{0}' and {1}";
+     *  trace(StringUtil.substitute(str, 15.4, true));
+     *
+     *  // this will output the following string:
+     *  // "here is some info '15.4' and true"
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public static function substitute(str:String, ... rest):String
+    {
+        if (str == null) return '';
+        
+        // Replace all of the parameters in the msg string.
+        var len:uint = rest.length;
+        var args:Array;
+        if (len == 1 && rest[0] is Array)
+        {
+            args = rest[0] as Array;
+            len = args.length;
+        }
+        else
+        {
+            args = rest;
+        }
+        
+        for (var i:int = 0; i < len; i++)
+        {
+            str = str.replace(new RegExp("\\{"+i+"\\}", "g"), args[i]);
+        }
+
+        return str;
+    }
+
+    /**
+     *  Returns a string consisting of a specified string
+     *  concatenated with itself a specified number of times.
+     *
+     *  @param str The string to be repeated.
+     *
+     *  @param n The repeat count.
+     *
+     *  @return The repeated string.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4.1
+     */
+    public static function repeat(str:String, n:int):String
+    {
+        if (n == 0)
+            return "";
+
+        var s:String = str;
+        for (var i:int = 1; i < n; i++)
+        {
+            s += str;
+        }
+        return s;
+    }
+
+    /**
+     *  Removes "unallowed" characters from a string.
+     *  A "restriction string" such as <code>"A-Z0-9"</code>
+     *  is used to specify which characters are allowed.
+     *  This method uses the same logic as the <code>restrict</code>
+     *  property of TextField.
+     *
+     *  @param str The input string.
+     *
+     *  @param restrict The restriction string.
+     *
+     *  @return The input string, minus any characters
+     *  that are not allowed by the restriction string.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4.1
+     */
+    public static function restrict(str:String, restrict:String):String
+    {
+        // A null 'restrict' string means all characters are allowed.
+        if (restrict == null)
+            return str;
+            
+        // An empty 'restrict' string means no characters are allowed.
+        if (restrict == "")
+            return "";
+            
+        // Otherwise, we need to test each character in 'str'
+        // to determine whether the 'restrict' string allows it.
+        var charCodes:Array = [];
+        
+        var n:int = str.length;
+        for (var i:int = 0; i < n; i++)
+        {
+            var charCode:uint = str.charCodeAt(i);
+            if (testCharacter(charCode, restrict))
+                charCodes.push(charCode);
+        }
+        
+        return String.fromCharCode.apply(null, charCodes);
+    }
+                            
+    /**
+     *  @private
+     *  Helper method used by restrict() to test each character
+     *  in the input string against the restriction string.
+     *  The logic in this method implements the same algorithm
+     *  as in TextField's 'restrict' property (which is quirky,
+     *  such as how it handles a '-' at the beginning of the
+     *  restriction string).
+     */
+    private static function testCharacter(charCode:uint,
+                                          restrict:String):Boolean
+    {
+        var allowIt:Boolean = false;
+        
+        var inBackSlash:Boolean = false;
+        var inRange:Boolean = false;
+        var setFlag:Boolean = true;
+        var lastCode:uint = 0;
+                        
+        var n:int = restrict.length;
+        var code:uint;
+        
+        if (n > 0)
+        {
+            code = restrict.charCodeAt(0);
+            if (code == 94) // caret
+                allowIt = true;
+        }
+        
+        for (var i:int = 0; i < n; i++)
+        {
+            code = restrict.charCodeAt(i)
+            
+            var acceptCode:Boolean = false;
+            if (!inBackSlash)
+            {
+                if (code == 45) // hyphen
+                    inRange = true;
+                else if (code == 94) // caret
+                    setFlag = !setFlag;
+                else if (code == 92) // backslash
+                    inBackSlash = true;
+                else
+                    acceptCode = true;
+            }
+            else
+            {
+                acceptCode = true;
+                inBackSlash = false;
+            }
+            
+            if (acceptCode)
+            {
+                if (inRange)
+                {
+                    if (lastCode <= charCode && charCode <= code)
+                        allowIt = setFlag;
+                    inRange = false;
+                    lastCode = 0;
+                }
+                else
+                {
+                    if (charCode == code)
+                        allowIt = setFlag;
+                    lastCode = code;
+                }
+            }
+        }
+        
+        return allowIt;
+    }
+}
+
+}


Mime
View raw message