http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/77148f4a/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as ---------------------------------------------------------------------- diff --cc frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as index b38d635,0000000..4b65a97 mode 100644,000000..100644 --- a/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as +++ b/frameworks/projects/MX/src/main/flex/mx/utils/ObjectUtil.as @@@ -1,1700 -1,0 +1,1700 @@@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 ++COMPILE::SWF +{ + import flash.utils.ByteArray; + import flash.utils.Dictionary; + import flash.xml.XMLNode; +} +import mx.collections.IList; + +import org.apache.flex.reflection.DefinitionWithMetaData; +import org.apache.flex.reflection.MetaDataArgDefinition; +import org.apache.flex.reflection.MetaDataDefinition; +import org.apache.flex.reflection.MethodDefinition; +import org.apache.flex.reflection.TypeDefinition; +import org.apache.flex.reflection.getQualifiedClassName; + +/** + * 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 ++ COMPILE::SWF + { + 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 ++ COMPILE::SWF + { + 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; + + COMPILE::LATER + { + 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 ++ COMPILE::SWF + { + 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 - COMPILE::AS3 ++ COMPILE::SWF + { + var propName:QName; + } + COMPILE::JS + { + var propName:String; + } + 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 ++ COMPILE::SWF + { + return (value is XMLNode); + } + COMPILE::JS + { + return false; + } + } + + private static function areBothByteArrays(a:Object, b:Object):Boolean + { - COMPILE::AS3 ++ COMPILE::SWF + { + 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; + + COMPILE::LATER + { + 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; + COMPILE::LATER + { + var properties:XMLList; + var prop:XML; + } + var propertyList:Array; + var isDynamic:Boolean = false; + var metadataInfo:Object; + + if (typeof(obj) == "xml") + { + COMPILE::LATER + { + className = "XML"; + properties = obj.text(); + if (properties.length()) + propertyNames.push("*"); + properties = obj.attributes(); + } + } + else + { + var classInfo:TypeDefinition = DescribeTypeCache.describeType(obj).typeDescription; + className = classInfo.name; + COMPILE::LATER + { + classAlias = [email protected](); + } + isDynamic = classInfo.dynamic; + + propertyList.concat(classInfo.accessors); + propertyList.concat(classInfo.variables); + + COMPILE::LATER + { + 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(propertyList); + + var excludeObject:Object = {}; + if (excludes) + { + n = excludes.length; + for (i = 0; i < n; i++) + { + excludeObject[excludes[i]] = 1; + } + } + + var isArray:Boolean = (obj is Array); - COMPILE::AS3 ++ COMPILE::SWF + { + var isDict:Boolean = (obj is Dictionary); + } + COMPILE::JS + { + var isDict:Boolean = false; + } + + 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)); + } + - COMPILE::AS3 ++ COMPILE::SWF + { + var allDone:Boolean = isArray || isDict || className == "Object"; + } + COMPILE::JS + { + var allDone:Boolean = isArray || className == "Object"; + } + + if (allDone) + { + // Do nothing since we've already got the dynamic members + } + else if (className == "XML") + { + COMPILE::LATER + { + n = properties.length(); + for (i = 0; i < n; i++) + { + p = properties[i].name(); + if (excludeObject[p] != 1) + propertyNames.push(new QName("", "@" + p)); + } + } + } + else + { + n = propertyList.length; + var uris:Array = options.uris; + var uri:String; + var qName:QName; + for (i = 0; i < n; i++) + { + var member:DefinitionWithMetaData = propertyList[i] as DefinitionWithMetaData; + p = member.name; + COMPILE::LATER + { + uri = [email protected](); + } + uri = ""; + + if (excludeObject[p] == 1) + continue; + + if (!options.includeTransient && internalHasMetadata(metadataInfo, p, "Transient")) + continue; + + if (uris != null) + { + COMPILE::LATER + { + 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 ([email protected]() == 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:Array):Object + { + var result:Object = null; + + try + { + for each (var prop:DefinitionWithMetaData in properties) + { + var propName:String = prop.name; + var metadataList:Array = prop.metadata; + + if (metadataList.length > 0) + { + if (result == null) + result = {}; + + var metadata:Object = {}; + result[propName] = metadata; + + for each (var md:MetaDataDefinition in metadataList) + { + var mdName:String = md.name; + + var argsList:Array = md.args; + var value:Object = {}; + + for each (var arg:MetaDataArgDefinition in argsList) + { + var argKey:String = arg.name; + if (argKey != null) + { + var argValue:String = arg.value; + 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 ++ COMPILE::SWF + 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 ++COMPILE::SWF +{ + import flash.utils.Dictionary; +} + +class CircularReferenceManager +{ - COMPILE::AS3 ++ COMPILE::SWF + private var dict:Dictionary = new Dictionary(true); + + COMPILE::JS + private var array:Array = []; + + public function CircularReferenceManager() + { + } + + public function put(obj:Object):void + { - COMPILE::AS3 ++ COMPILE::SWF + { + dict[obj] = 1; + } + COMPILE::JS + { + array.push(obj); + } + } + + public function contains(obj:Object):Boolean + { - COMPILE::AS3 ++ COMPILE::SWF + { + return (dict[obj] == 1); + } + COMPILE::JS + { + return array.indexOf(obj) != -1; + } + } + - } ++}
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/77148f4a/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as ---------------------------------------------------------------------- diff --cc frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as index 013d4bd,0000000..57b3498 mode 100644,000000..100644 --- a/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as +++ b/frameworks/projects/MX/src/main/flex/mx/utils/OnDemandEventDispatcher.as @@@ -1,169 -1,0 +1,169 @@@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 ++COMPILE::SWF +{ + 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 ++ COMPILE::SWF + 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, handlerScope:Object = null):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 + */ - COMPILE::AS3 ++ COMPILE::SWF + public function dispatchEvent(event:Event):Boolean + { + if (_dispatcher != null) + return _dispatcher.dispatchEvent(event); + return true; + } + COMPILE::JS + public function dispatchEvent(event:Object):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 + */ - COMPILE::AS3 ++ COMPILE::SWF + public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void + { + if (_dispatcher != null) + _dispatcher.removeEventListener(type,listener,useCapture); + } + COMPILE::JS + public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false, handlerScope:Object = null):void + { + if (_dispatcher != null) + _dispatcher.removeEventListener(type,listener,useCapture); + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + public function willTrigger(type:String):Boolean + { + if (_dispatcher != null) + return _dispatcher.willTrigger(type); + return false; + } + + } - } ++} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/77148f4a/frameworks/projects/MX/src/main/flex/mx/utils/OrderedObject.as ---------------------------------------------------------------------- diff --cc frameworks/projects/MX/src/main/flex/mx/utils/OrderedObject.as index f1e05cb,0000000..b20c30e mode 100644,000000..100644 --- a/frameworks/projects/MX/src/main/flex/mx/utils/OrderedObject.as +++ b/frameworks/projects/MX/src/main/flex/mx/utils/OrderedObject.as @@@ -1,355 -1,0 +1,355 @@@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 org.apache.flex.utils.Proxy; - COMPILE::AS3 ++COMPILE::SWF +{ +import flash.utils.flash_proxy; +import mx.utils.object_proxy; + +use namespace flash_proxy; +use namespace object_proxy; +} + +/** + * OrderedObject acts as a wrapper to Object to preserve the ordering of the + * properties as they are added. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public dynamic class OrderedObject extends Proxy +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param item An Object containing name/value pairs. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function OrderedObject(item:Object=null) + { + super(); + + propertyList = []; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Contains a list of all of the property names for the proxied object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + object_proxy var propertyList:Array; + COMPILE::JS + protected var propertyList:Array; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * Work around for the Flash Player bug #232854. The Proxy bug occurs when + * the Proxy class is used in a sibling ApplicationDomain of the main + * application's ApplicationDomain. When the Proxy class is used in a + * sibling ApplicationDomain the RTE looks like this: + * + * ArgumentError: Error #1063: Argument count mismatch on + * Object/http://adobe.com/AS3/2006/builtin::hasOwnProperty(). + * Expected 0, got 2. + * + * Returns the specified property value of the proxied object. + * + * @param name Typically a string containing the name of the property, or + * possibly a QName where the property name is found by inspecting the + * <code>localName</code> property. + * + * @return The value of the property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + object_proxy function getObjectProperty(name:*):* + { + return getProperty(name); + } + + /** + * @private + * + * Work around for the Flash Player bug #232854. See the comments in + * getObjectProperty() for more details. + * + * Call this method to set a property value instead of hashing into an + * OrderObject which would end up calling setProperty(). + * + * Updates the specified property on the proxied object. + * + * @param name Object containing the name of the property that should be + * updated on the proxied object. + * + * @param value Value that should be set on the proxied object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + object_proxy function setObjectProperty(name:*, value:*):void + { + setProperty(name, value); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * Deletes the specified property on the proxied object. + * + * @param name Typically a string containing the name of the property, + * or possibly a QName where the property name is found by + * inspecting the <code>localName</code> property. + * + * @return A Boolean indicating if the property was deleted. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + override flash_proxy function deleteProperty(name:*):Boolean + { + var deleted:Boolean = delete valueMap[name]; + + var deleteIndex:int = -1; + for (var i:int = 0; i < propertyList.length; i++) + { + if (propertyList[i] == name) + { + deleteIndex = i; + break; + } + } + if (deleteIndex > -1) + { + propertyList.splice(deleteIndex, 1); + } + + return deleted; + } + + /** + * Deletes the specified property on the proxied object. + * + * @param name Typically a string containing the name of the property, + * or possibly a QName where the property name is found by + * inspecting the <code>localName</code> property. + * + * @return A Boolean indicating if the property was deleted. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + COMPILE::JS + override public function deleteProperty(name:String):Boolean + { + var deleted:Boolean = delete valueMap[name]; + + var deleteIndex:int = -1; + for (var i:int = 0; i < propertyList.length; i++) + { + if (propertyList[i] == name) + { + deleteIndex = i; + break; + } + } + if (deleteIndex > -1) + { + propertyList.splice(deleteIndex, 1); + } + + return deleted; + } + + /** + * This is an internal function that must be implemented by a subclass of + * flash.utils.Proxy. + * + * @param name The property name that should be tested for existence. + * + * @return If the property exists, <code>true</code>; otherwise + * <code>false</code>. + * + * @see flash.utils.Proxy#hasProperty() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + override flash_proxy function hasProperty(name:*):Boolean + { + return(name in valueMap); + } + COMPILE::JS + override public function hasProperty(name:String):Boolean + { + return(name in valueMap); + } + + /** + * This is an internal function that must be implemented by a subclass of + * flash.utils.Proxy. + * + * @param index The zero-based index value of the object's property. + * + * @return The property's name. + * + * @see flash.utils.Proxy#nextName() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + override flash_proxy function nextName(index:int):String + { + return propertyList[index -1]; + } + COMPILE::JS + override public function elementNames():Array + { + return propertyList.slice(); + } + + /** + * This is an internal function that must be implemented by a subclass of + * flash.utils.Proxy. + * + * @param index The zero-based index value of the object's property. + * + * @return The property's value. + * + * @see flash.utils.Proxy#nextValue() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + override flash_proxy function nextValue(index:int):* + { + return valueMap[propertyList[index -1]]; + } + + /** + * Updates the specified property on the proxied object. + * + * @param name Object containing the name of the property that should be + * updated on the proxied object. + * + * @param value Value that should be set on the proxied object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ - COMPILE::AS3 ++ COMPILE::SWF + override flash_proxy function setProperty(name:*, value:*):void + { + var oldVal:* = valueMap[name]; + if (oldVal !== value) + { + // Update item. + valueMap[name] = value; + + for (var i:int = 0; i < propertyList.length; i++) + { + if (propertyList[i] == name) + { + return; + } + } + propertyList.push(name); + } + } + COMPILE::JS + override public function setProperty(name:String, value:*):void + { + var oldVal:* = valueMap[name]; + if (oldVal !== value) + { + // Update item. + valueMap[name] = value; + + for (var i:int = 0; i < propertyList.length; i++) + { + if (propertyList[i] == name) + { + return; + } + } + propertyList.push(name); + } + } + +} + - } ++} http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/77148f4a/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as ---------------------------------------------------------------------- diff --cc frameworks/projects/MX/src/main/flex/mx/utils/Platform.as index e954380,0000000..2fb50eb mode 100644,000000..100644 --- a/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as +++ b/frameworks/projects/MX/src/main/flex/mx/utils/Platform.as @@@ -1,344 -1,0 +1,344 @@@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 ++COMPILE::SWF +{ + import flash.system.Capabilities; +} +import org.apache.flex.utils.Platform; +import org.apache.flex.reflection.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"; + + 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 p:String = org.apache.flex.utils.Platform.platform; + + _isAndroid = p == org.apache.flex.utils.Platform.ANDROID; + _isIOS = p == org.apache.flex.utils.Platform.IOS; + _isBlackBerry = p == org.apache.flex.utils.Platform.BLACKBERRY; + _isMobile = _isAndroid || _isIOS || _isBlackBerry; + + _isMac = p == org.apache.flex.utils.Platform.MAC; + _isWindows = p == org.apache.flex.utils.Platform.WINDOWS; + _isLinux = p == org.apache.flex.utils.Platform.LINUX; // note that Android is also Linux + _isIPad = org.apache.flex.utils.Platform.isIPad; + _isDesktop = !_isMobile; + + _isAir = org.apache.flex.utils.Platform.isAir; + _isBrowser = org.apache.flex.utils.Platform.isBrowser; + + _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 + { - COMPILE::AS3 ++ COMPILE::SWF + { + 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; + } + COMPILE::JS + { + // TODO (aharui): Do something better someday? + return "0"; + } + } + +} +}
