//////////////////////////////////////////////////////////////////////////////// // // 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.utils.ByteArray; import flash.utils.Dictionary; import flash.utils.getQualifiedClassName; import flash.utils.describeType; import flash.xml.XMLNode; import mx.collections.IList; /** * The RPCObjectUtil class is a subset of ObjectUtil, removing methods * that create dependency issues when RPC messages are in a bootstrap loader. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class RPCObjectUtil { 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 // //-------------------------------------------------------------------------- /** * 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: * *
* (mx.messaging.messages::AsyncMessage)#2.* *
This id is used to indicate when a circular reference occurs.
* Properties of an object that are of the Class
type will
* appear only as the assigned type.
* For example a custom definition like the following:
* public class MyCustomClass { * public var clazz:Class; * }* *
With the clazz
property assigned to Date
* will display as shown below:
* (somepackage::MyCustomClass)#0 * clazz = (Date)* * @param obj 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 *
* // 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 ** * * @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:Dictionary= null, namespaceURIs:Array = null, exclude:Array = null):String { var str:String; var type:String = value == null ? "null" : typeof(value); switch (type) { case "boolean": case "number": { return value.toString(); } case "string": { return "\"" + value.toString() + "\""; } case "object": { if (value is Date) { return value.toString(); } else if (value is XMLNode) { 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 Dictionary(true); // Check to be sure we haven't processed this object before var id:Object = refs[value]; if (id != null) { str += "#" + int(id); return str; } if (value != null) { str += "#" + refCount.toString(); refs[value] = refCount; refCount++; } var isArray:Boolean = value is Array; 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 += "["; str += prop.toString(); if (isArray) str += "] "; else str += " = "; try { 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.toString(); } default: { return "(" + type + ")"; } } 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; } /** * Returns information about the class, and properties of the class, for * the specified Object. * * @param obj The Object to inspect. * * @param exclude Array of Strings specifying the property names that should be * excluded from the returned result. For example, you could specify *
["currentTarget", "target"]
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:
*
* includeReadOnly
: If false
,
* exclude Object properties that are read-only.
* The default value is true
.includeTransient
: If false
,
* exclude Object properties and variables that have [Transient]
metadata.
* The default value is true
.uris
: 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 ["mx_internal", "mx_object"]
* or ["~~"]
.name
: String containing the name of the class;properties
: Sorted list of the property names of the specified object.