//////////////////////////////////////////////////////////////////////////////// // // 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.