PageRenderTime 80ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/AS3/nl/demonsters/debugger/MonsterDebugger.as

http://multiplayer-api.googlecode.com/
ActionScript | 1819 lines | 1195 code | 232 blank | 392 comment | 286 complexity | cfeb88e38e42d7fec8d547c13a435af8 MD5 | raw file
  1. /**
  2. *
  3. * This is the client code that needs to be implemented into a
  4. * Flash, AIR or FLEX application to collect debug information
  5. * in De MonsterDebugger.
  6. *
  7. * Be aware that any traces made to De MonsterDebugger may
  8. * be viewed by others. De Monster Debugger is intended to be
  9. * used to debug unpublished Flash, AIR of FLEX applications in
  10. * the environment that they will be used in as a final product.
  11. * Please make sure that you do not send any debug material to
  12. * the debugger from a live running application.
  13. *
  14. * Use at your own risk.
  15. *
  16. * @author Ferdi Koomen
  17. * @company De Monsters
  18. * @link http://www.deMonsterDebugger.com
  19. * @version 2.04
  20. *
  21. *
  22. * Special thanks to Arjan van Wijk from MediaMonks.nl
  23. *
  24. *
  25. * Copyright 2009, De Monsters
  26. *
  27. * This program is free software: you can redistribute it and/or modify
  28. * it under the terms of the GNU General Public License as published by
  29. * the Free Software Foundation, either version 3 of the License, or
  30. * (at your option) any later version.
  31. *
  32. * This program is distributed in the hope that it will be useful,
  33. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. * GNU Lesser General Public License for more details.
  36. *
  37. * You should have received a copy of the GNU Lesser General Public License
  38. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  39. *
  40. */
  41. package nl.demonsters.debugger
  42. {
  43. import flash.display.Bitmap;
  44. import flash.display.BitmapData;
  45. import flash.display.DisplayObject;
  46. import flash.display.DisplayObjectContainer;
  47. import flash.display.Sprite;
  48. import flash.events.AsyncErrorEvent;
  49. import flash.events.SecurityErrorEvent;
  50. import flash.events.StatusEvent;
  51. import flash.events.TimerEvent;
  52. import flash.events.Event;
  53. import flash.net.LocalConnection;
  54. import flash.geom.Rectangle;
  55. import flash.utils.ByteArray;
  56. import flash.utils.Timer;
  57. import flash.utils.getTimer;
  58. import flash.utils.describeType;
  59. import flash.system.System;
  60. public class MonsterDebugger
  61. {
  62. // Singleton instance
  63. private static var instance:MonsterDebugger = null;
  64. // Connections
  65. private var lineOut:LocalConnection;
  66. private var lineIn:LocalConnection;
  67. // Connection names
  68. private const LINE_OUT :String = "_debuggerRed";
  69. private const LINE_IN :String = "_debuggerBlue";
  70. // The allow domain for the local connection
  71. // * = Allow communication with all domains
  72. private const ALLOWED_DOMAIN :String = "*";
  73. // Error colors
  74. public static const COLOR_NORMAL :uint = 0x111111;
  75. public static const COLOR_ERROR :uint = 0xFF0000;
  76. public static const COLOR_WARNING :uint = 0xFF3300;
  77. // Commands
  78. private const COMMAND_HELLO :String = "HELLO";
  79. private const COMMAND_ROOT :String = "ROOT";
  80. private const COMMAND_BASE :String = "BASE";
  81. private const COMMAND_TRACE :String = "TRACE";
  82. private const COMMAND_INSPECT :String = "INSPECT";
  83. private const COMMAND_GET_OBJECT :String = "GET_OBJECT";
  84. private const COMMAND_GET_DISPLAYOBJECT :String = "GET_DISPLAYOBJECT";
  85. private const COMMAND_GET_PROPERTIES :String = "GET_PROPERTIES";
  86. private const COMMAND_GET_FUNCTIONS :String = "GET_FUNCTIONS";
  87. private const COMMAND_SET_PROPERTY :String = "SET_PROPERTY";
  88. private const COMMAND_CALL_METHOD :String = "CALL_METHOD";
  89. private const COMMAND_SHOW_HIGHLIGHT :String = "SHOW_HIGHLIGHT";
  90. private const COMMAND_HIDE_HIGHLIGHT :String = "HIDE_HIGHLIGHT";
  91. private const COMMAND_CLEAR_TRACES :String = "CLEAR_TRACES";
  92. private const COMMAND_MONITOR :String = "MONITOR";
  93. private const COMMAND_SNAPSHOT :String = "SNAPSHOT";
  94. private const COMMAND_NOTFOUND :String = "NOTFOUND";
  95. // Types
  96. private const TYPE_VOID :String = "void";
  97. private const TYPE_ARRAY :String = "Array";
  98. private const TYPE_BOOLEAN :String = "Boolean";
  99. private const TYPE_NUMBER :String = "Number";
  100. private const TYPE_OBJECT :String = "Object";
  101. private const TYPE_VECTOR :String = "Vector";
  102. private const TYPE_STRING :String = "String";
  103. private const TYPE_BITMAP :String = "Bitmap";
  104. private const TYPE_BITMAPDATA :String = "BitmapData";
  105. private const TYPE_INT :String = "int";
  106. private const TYPE_UINT :String = "uint";
  107. private const TYPE_XML :String = "XML";
  108. private const TYPE_XMLLIST :String = "XMLList";
  109. private const TYPE_XMLNODE :String = "XMLNode";
  110. private const TYPE_XMLVALUE :String = "XMLValue";
  111. private const TYPE_XMLATTRIBUTE :String = "XMLAttribute";
  112. private const TYPE_METHOD :String = "MethodClosure";
  113. private const TYPE_FUNCTION :String = "Function";
  114. private const TYPE_BYTEARRAY :String = "ByteArray";
  115. private const TYPE_WARNING :String = "Warning";
  116. private const TYPE_DISPLAYOBJECT :String = "DisplayObject";
  117. // Access types
  118. private const ACCESS_VARIABLE :String = "variable";
  119. private const ACCESS_CONSTANT :String = "constant";
  120. private const ACCESS_ACCESSOR :String = "accessor";
  121. private const ACCESS_METHOD :String = "method";
  122. // Permission types
  123. private const PERMISSION_READWRITE :String = "readwrite";
  124. private const PERMISSION_READONLY :String = "readonly";
  125. private const PERMISSION_WRITEONLY :String = "writeonly";
  126. // Icon types
  127. private const ICON_DEFAULT :String = "iconDefault";
  128. private const ICON_ROOT :String = "iconRoot";
  129. private const ICON_WARNING :String = "iconWarning";
  130. private const ICON_VARIABLE :String = "iconVariable";
  131. private const ICON_VARIABLE_READONLY :String = "iconVariableReadonly";
  132. private const ICON_VARIABLE_WRITEONLY :String = "iconVariableWriteonly";
  133. private const ICON_XMLNODE :String = "iconXMLNode";
  134. private const ICON_XMLVALUE :String = "iconXMLValue";
  135. private const ICON_XMLATTRIBUTE :String = "iconXMLAttribute";
  136. private const ICON_FUNCTION :String = "iconFunction";
  137. // Highlight color and border thickness
  138. private const HIGHLIGHT_COLOR :uint = 0xFFFF00;
  139. private const HIGHLIGHT_BORDER :int = 4;
  140. // Max local connection package size
  141. private const MAX_PACKAGE_BYTES :int = 40000;
  142. // Version
  143. private const VERSION:Number = 2.04;
  144. // The root of the application
  145. private var root:Object = null;
  146. // Highlight display object
  147. private var highlight:Sprite = null;
  148. // Timer for the monitor
  149. private var monitor:Timer;
  150. private var monitorTime:uint;
  151. private var monitorFrames:uint;
  152. private var monitorSprite:Sprite;
  153. // Enabled / disabled
  154. public var _enabled:Boolean = true;
  155. /**
  156. * Constructor
  157. * The target can also be a static function
  158. * @param target: The root of the application
  159. */
  160. public function MonsterDebugger(target:Object = null)
  161. {
  162. // Check if the debugger has already been initialized
  163. if (instance == null)
  164. {
  165. // Save the instance
  166. instance = this;
  167. // Setup line out
  168. lineOut = new LocalConnection();
  169. lineOut.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
  170. lineOut.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
  171. lineOut.addEventListener(StatusEvent.STATUS, statusHandler);
  172. // Setup line in
  173. lineIn = new LocalConnection();
  174. lineIn.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
  175. lineIn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
  176. lineIn.addEventListener(StatusEvent.STATUS, statusHandler);
  177. lineIn.allowDomain(ALLOWED_DOMAIN);
  178. lineIn.client = this;
  179. // Setup the fps and memory listeners
  180. monitorTime = getTimer();
  181. monitorFrames = 0;
  182. monitorSprite = new Sprite();
  183. monitorSprite.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
  184. monitor = new Timer(500);
  185. monitor.addEventListener(TimerEvent.TIMER, monitorHandler);
  186. monitor.start();
  187. try {
  188. lineIn.connect(LINE_IN);
  189. } catch(error:ArgumentError) {
  190. // Do nothing
  191. }
  192. }
  193. // Save the root
  194. // Send the first message
  195. if (target != MonsterDebugger.singletonCheck) {
  196. instance.root = target;
  197. instance.send({text:COMMAND_HELLO, version:VERSION});
  198. }
  199. }
  200. /**
  201. * This function is called from the AIR application
  202. * @param data: A compressed object containing the commands
  203. */
  204. public function onReceivedData(data:ByteArray):void
  205. {
  206. if (enabled)
  207. {
  208. //Variables for the commands
  209. var object:*;
  210. var method:Function;
  211. var xml:XML;
  212. // Uncompress the item data
  213. data.uncompress();
  214. // Read the command from the data
  215. var command:Object = data.readObject();
  216. // Do the actions
  217. switch(command["text"])
  218. {
  219. // Save the domain
  220. case COMMAND_HELLO:
  221. send({text:COMMAND_HELLO, version:VERSION});
  222. break;
  223. // Get the base of the application
  224. case COMMAND_ROOT:
  225. object = getObject("", 0);
  226. if (object != null) {
  227. xml = XML(parseObject(object, "", command["functions"], 1, 2));
  228. send({text:COMMAND_ROOT, xml:xml});
  229. if (isDisplayObject(object)) {
  230. xml = XML(parseDisplayObject(object, "", command["functions"], 1, 2));
  231. send({text:COMMAND_BASE, xml:xml});
  232. }
  233. }
  234. break;
  235. // Return the parsed object
  236. case COMMAND_GET_OBJECT:
  237. object = getObject(command["target"], 0);
  238. if (object != null) {
  239. xml = XML(parseObject(object, command["target"], command["functions"], 1, 2));
  240. send({text:COMMAND_GET_OBJECT, xml:xml});
  241. }
  242. break;
  243. // Return the parsed object
  244. case COMMAND_GET_DISPLAYOBJECT:
  245. object = getObject(command["target"], 0);
  246. if (object != null) {
  247. if (isDisplayObject(object)) {
  248. xml = XML(parseDisplayObject(object, command["target"], command["functions"], 1, 2));
  249. send({text:COMMAND_GET_DISPLAYOBJECT, xml:xml});
  250. }
  251. }
  252. break;
  253. // Return a list of functions
  254. case COMMAND_GET_PROPERTIES:
  255. object = getObject(command["target"], 0);
  256. if (object != null) {
  257. xml = XML(parseObject(object, command["target"], false, 1, 1));
  258. send({text:COMMAND_GET_PROPERTIES, xml:xml});
  259. }
  260. break;
  261. // Return a list of functions
  262. case COMMAND_GET_FUNCTIONS:
  263. object = getObject(command["target"], 0);
  264. if (object != null) {
  265. xml = XML(getFunctions(object, command["target"]));
  266. send({text:COMMAND_GET_FUNCTIONS, xml:xml});
  267. }
  268. break;
  269. // Adjust a property and return the value
  270. case COMMAND_SET_PROPERTY:
  271. object = getObject(command["target"], 1);
  272. if (object != null) {
  273. try {
  274. object[command["name"]] = command["value"];
  275. send({text:COMMAND_SET_PROPERTY, value:object[command["name"]]});
  276. } catch (error1:Error) {
  277. send({text:COMMAND_NOTFOUND, target:command["target"]});
  278. break;
  279. }
  280. }
  281. break;
  282. // Call a method and return the answer
  283. case COMMAND_CALL_METHOD:
  284. method = getObject(command["target"], 0);
  285. if (method != null) {
  286. if (command["returnType"] == TYPE_VOID) {
  287. method.apply(this, command["arguments"]);
  288. } else {
  289. object = method.apply(this, command["arguments"]);
  290. xml = XML(parseObject(object, "", false, 1, 4));
  291. send({text:COMMAND_CALL_METHOD, id:command["id"], xml:xml});
  292. }
  293. }
  294. break;
  295. // Remove and add a new highlight if posible
  296. case COMMAND_SHOW_HIGHLIGHT:
  297. if (highlight != null) {
  298. try {
  299. highlight.parent.removeChild(highlight);
  300. highlight = null;
  301. } catch(error2:Error) {
  302. //
  303. }
  304. }
  305. object = getObject(command["target"], 0);
  306. if (isDisplayObject(object) && isDisplayObject(object["parent"])) {
  307. var bounds:Rectangle = object.getBounds(object["parent"]);
  308. highlight = new Sprite();
  309. highlight.x = 0;
  310. highlight.y = 0;
  311. highlight.graphics.beginFill(0, 0);
  312. highlight.graphics.lineStyle(HIGHLIGHT_BORDER, HIGHLIGHT_COLOR);
  313. highlight.graphics.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
  314. highlight.graphics.endFill();
  315. highlight.mouseChildren = false;
  316. highlight.mouseEnabled = false;
  317. try {
  318. object["parent"].addChild(highlight);
  319. } catch(error3:Error) {
  320. highlight = null;
  321. }
  322. }
  323. break;
  324. // Remove the highlight
  325. case COMMAND_HIDE_HIGHLIGHT:
  326. if (highlight != null) {
  327. try {
  328. highlight.parent.removeChild(highlight);
  329. highlight = null;
  330. } catch(error4:Error) {
  331. //
  332. }
  333. }
  334. break;
  335. }
  336. }
  337. }
  338. /**
  339. * The actual send function
  340. * @param data: The raw uncompressed data to send
  341. */
  342. private function send(data:Object):void
  343. {
  344. if (enabled)
  345. {
  346. // Compress the data
  347. var item:ByteArray = new ByteArray();
  348. item.writeObject(data);
  349. item.compress();
  350. // Array to hold the data packages
  351. var dataPackages:Array = new Array();
  352. // Counter for the loops
  353. var i:int = 0;
  354. // Check if the data should be splitted
  355. // The max size for localconnection = 40kb = 40960b
  356. // We use 960b for the package definition
  357. if (item.length > MAX_PACKAGE_BYTES)
  358. {
  359. // Save the length
  360. var bytesAvailable:int = item.length;
  361. var offset:int = 0;
  362. // Calculate the total package count
  363. var total:int = Math.ceil(item.length / MAX_PACKAGE_BYTES);
  364. // Loop through the bytes / chunks
  365. for (i = 0; i < total; i++)
  366. {
  367. // Set the length to read
  368. var length:int = bytesAvailable;
  369. if (length > MAX_PACKAGE_BYTES) {
  370. length = MAX_PACKAGE_BYTES;
  371. }
  372. // Read a chunk of data
  373. var tmp:ByteArray = new ByteArray();
  374. tmp.writeBytes(item, offset, length);
  375. // Create a data package
  376. dataPackages.push({total:total, nr:(i + 1), bytes:tmp});
  377. // Update the bytes available and offset
  378. bytesAvailable -= length;
  379. offset += length;
  380. }
  381. }
  382. else
  383. {
  384. // The data size is under 40kb, so just send one package
  385. dataPackages.push({total:1, nr:1, bytes:item});
  386. }
  387. // send the data packages through the line out
  388. for (i = 0; i < dataPackages.length; i++) {
  389. try {
  390. lineOut.send(LINE_OUT, "onReceivedData", dataPackages[i]);
  391. } catch (error:Error) {
  392. break;
  393. }
  394. }
  395. }
  396. }
  397. /**
  398. * Set another target in the or window
  399. * @param target: The target to display in the or
  400. */
  401. public static function inspect(target:Object):void
  402. {
  403. // Check if the debugger has been enabled
  404. if (instance != null && target != null)
  405. {
  406. // Set the new root
  407. instance.root = target;
  408. // Parse the new target
  409. // Send the new XML
  410. var object:* = instance.getObject("", 0);
  411. if (object != null) {
  412. var xml:XML = XML(instance.parseObject(object, "", false, 1, 2));
  413. instance.send({text:instance.COMMAND_INSPECT, xml:xml});
  414. if (instance.isDisplayObject(object)) {
  415. xml = XML(instance.parseDisplayObject(object, "", false, 1, 2));
  416. instance.send({text:instance.COMMAND_BASE, xml:xml});
  417. }
  418. }
  419. }
  420. }
  421. /**
  422. * Static snapshot function
  423. * @param target: The target from where the snapshot is called
  424. * @param object: The object to snapshot
  425. * @param color: The color in the interface
  426. */
  427. public static function snapshot(target:DisplayObject, color:uint = 0x111111):void
  428. {
  429. if (instance == null) instance = new MonsterDebugger(MonsterDebugger.singletonCheck);
  430. if (MonsterDebugger.enabled) instance.snapshotInternal(target, color);
  431. }
  432. /**
  433. * Private snapshot function
  434. * @param target: The target from where the snapshot is called
  435. * @param object: The object to snapshot
  436. * @param color: The color in the interface
  437. */
  438. private function snapshotInternal(target:DisplayObject, color:uint = 0x111111):void
  439. {
  440. if (enabled)
  441. {
  442. // Create the bitmapdata
  443. var bitmapData:BitmapData = new BitmapData(target.width, target.height);
  444. bitmapData.draw(target);
  445. // Write the bitmap in the bytearray
  446. var bytes:ByteArray = bitmapData.getPixels(new Rectangle(0, 0, target.width, target.height));
  447. //Create a send object
  448. send({text:COMMAND_SNAPSHOT, date:new Date(), target:String(target), bytes:bytes, width:target.width, height:target.height, color:color});
  449. // Clear the data
  450. bitmapData.dispose();
  451. bytes = null;
  452. }
  453. }
  454. /**
  455. * Static trace function
  456. * @param target: The target from where the trace is called
  457. * @param object: The object to trace
  458. * @param color: The color of the trace in the interface
  459. * @parem functions: Include or exclude the functions
  460. * @param depth: The maximum depth of the trace
  461. */
  462. public static function trace(target:Object, object:*, color:uint = 0x111111, functions:Boolean = false, depth:int = 4):void
  463. {
  464. if (instance == null) instance = new MonsterDebugger(MonsterDebugger.singletonCheck);
  465. if (MonsterDebugger.enabled) instance.traceInternal(target, object, color, functions, depth);
  466. }
  467. /**
  468. * Private trace function
  469. * @param target: The target from where the trace is called
  470. * @param object: The object to trace
  471. * @param color: The color of the trace in the interface
  472. * @parem functions: Include or exclude the functions
  473. * @param depth: The maximum depth of the trace
  474. */
  475. private function traceInternal(target:Object, object:*, color:uint = 0x111111, functions:Boolean = false, depth:int = 4):void
  476. {
  477. if (enabled)
  478. {
  479. // Get the object information
  480. var xml:XML = XML(parseObject(object, "", functions, 1, depth));
  481. //Create a send object
  482. send({text:COMMAND_TRACE, date:new Date(), target:String(target), xml:xml, color:color});
  483. }
  484. }
  485. /**
  486. * Static clear traces function
  487. * This clears the traces in the application
  488. */
  489. public static function clearTraces():void
  490. {
  491. if (instance == null) instance = new MonsterDebugger(MonsterDebugger.singletonCheck);
  492. if (MonsterDebugger.enabled) instance.clearTracesInternal();
  493. }
  494. /**
  495. * Private clear traces function
  496. * This clears the traces in the application
  497. */
  498. private function clearTracesInternal():void
  499. {
  500. if (enabled)
  501. {
  502. //Create a send object
  503. send({text:COMMAND_CLEAR_TRACES});
  504. }
  505. }
  506. /**
  507. * Check if an object is drawable displayobject
  508. * @param object: The object to check
  509. */
  510. private function isDisplayObject(object:*):Boolean
  511. {
  512. return (object is DisplayObject || object is DisplayObjectContainer);
  513. }
  514. /**
  515. * Return an object
  516. * @param target: A point seperated path to the object
  517. * @param parent: Number of parents
  518. */
  519. private function getObject(target:String = "", parent:int = 0):*
  520. {
  521. // Object to return
  522. var object:* = instance.root;
  523. // Check if the path is not empty
  524. if (target != "")
  525. {
  526. // Split the path
  527. var splitted:Array = target.split(".");
  528. // Loop through the array
  529. for (var i:int = 0; i < splitted.length - parent; i++)
  530. {
  531. // Check if the string isn't empty
  532. if (splitted[i] != "")
  533. {
  534. try
  535. {
  536. // Check if we should call the XML children function()
  537. // Or the getChildAt function
  538. // If not: Just update the path to the object
  539. if (splitted[i] == "children()")
  540. {
  541. object = object.children();
  542. }
  543. else if (splitted[i].indexOf("getChildAt(") == 0)
  544. {
  545. var index:Number = splitted[i].substring(11, splitted[i].indexOf(")", 11));
  546. object = DisplayObjectContainer(object).getChildAt(index);
  547. }
  548. else
  549. {
  550. object = object[splitted[i]];
  551. }
  552. }
  553. catch (error:ReferenceError)
  554. {
  555. // The object is not found
  556. send({text:COMMAND_NOTFOUND, target:target});
  557. break;
  558. }
  559. }
  560. }
  561. }
  562. // Return the object
  563. return object;
  564. }
  565. /**
  566. * Get the functions of an object
  567. * @param object: The object to parse
  568. * @param target: A point seperated path to the object
  569. */
  570. private function getFunctions(object:*, target:String = ""):String
  571. {
  572. // The return string
  573. var xml:String = "";
  574. // Create the opening node
  575. xml += createNode("root");
  576. try
  577. {
  578. // Get the descriptor
  579. var description:XML = describeType(object);
  580. var type:String = parseType(description.@name);
  581. var childType:String = "";
  582. var childName:String = "";
  583. var childTarget:String = "";
  584. var methods:XMLList = description..method;
  585. var methodsArr:Array = new Array();
  586. var returnType:String;
  587. var parameters:XMLList;
  588. var args:Array;
  589. var argsString:String;
  590. var optional:Boolean = false;
  591. var double:Boolean = false;
  592. var i:int = 0;
  593. var n:int = 0;
  594. // Create the head node
  595. xml += createNode("node", {icon:ICON_DEFAULT, label:"(" + type + ")", target:target});
  596. // Save the methods
  597. // Filter out doubles (this should not happen though)
  598. for (i = 0; i < methods.length(); i++) {
  599. for (n = 0; n < methodsArr.length; n++) {
  600. if (methodsArr[n].name == methods[i].@name) {
  601. double = true;
  602. break;
  603. }
  604. }
  605. if (!double) {
  606. methodsArr.push({name:methods[i].@name, xml:methods[i], access:ACCESS_METHOD});
  607. }
  608. }
  609. // Sort the nodes
  610. methodsArr.sortOn("name");
  611. // Loop through the methods
  612. for (i = 0; i < methodsArr.length; i++)
  613. {
  614. // Save the type
  615. childType = TYPE_FUNCTION;
  616. childName = methodsArr[i].xml.@name;
  617. childTarget = target + "." + childName;
  618. // Save the function info
  619. // Parameters, arguments, return type, etc
  620. returnType = parseType(methodsArr[i].xml.@returnType);
  621. parameters = methodsArr[i].xml..parameter;
  622. args = new Array();
  623. argsString = "";
  624. optional = false;
  625. // Create the parameters
  626. for (n = 0; n < parameters.length(); n++)
  627. {
  628. // Optional parameters should start with a bracket
  629. if (parameters[n].@optional == "true" && !optional){
  630. optional = true;
  631. args.push("[");
  632. }
  633. // Push the parameter
  634. args.push(parseType(parameters[n].@type));
  635. }
  636. // The optional bracket is needed
  637. if (optional) {
  638. args.push("]");
  639. }
  640. // Create the arguments string
  641. argsString = args.join(", ");
  642. argsString = argsString.replace("[, ", "[");
  643. argsString = argsString.replace(", ]", "]");
  644. // Create the node
  645. xml += createNode("node", {
  646. icon: ICON_FUNCTION,
  647. label: childName + "(" + argsString + "):" + returnType,
  648. args: argsString,
  649. name: childName,
  650. type: TYPE_FUNCTION,
  651. access: ACCESS_METHOD,
  652. returnType: returnType,
  653. target: childTarget
  654. });
  655. // Loop through the parameters
  656. for (n = 0; n < parameters.length(); n++)
  657. {
  658. // Create the node
  659. xml += createNode("parameter", {
  660. type: parseType(parameters[n].@type),
  661. index: parameters[n].@index,
  662. optional: parameters[n].@optional
  663. }, true);
  664. }
  665. // Close the function node
  666. xml += createNode("/node");
  667. }
  668. // Create the head node
  669. xml += createNode("/node");
  670. }
  671. catch (error:Error)
  672. {
  673. // The object is not found
  674. var msg:String = "";
  675. msg += createNode("root");
  676. msg += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Not found", name:"Not found"}, true);
  677. msg += createNode("/root");
  678. send({text:COMMAND_NOTFOUND, target:target, xml:XML(msg)});
  679. }
  680. // Create a closing node
  681. xml += createNode("/root");
  682. // Return the xml
  683. return xml;
  684. }
  685. /**
  686. * Parse an object
  687. * @param object: The object to parse
  688. * @param target: A point seperated path to the object
  689. * @param functions: Include or exclude functions
  690. * @param currentDepth: The current trace depth
  691. * @param maxDepth:: The maximum trace depth
  692. */
  693. private function parseObject(object:*, target:String = "", functions:Boolean = false, currentDepth:int = 1, maxDepth:int = 4):String
  694. {
  695. // Variables needed in the loops
  696. var xml:String = "";
  697. var childType:String = "";
  698. var childName:String = "";
  699. var childTarget:String = "";
  700. var description:XML = new XML();
  701. var type:String = "";
  702. var base:String = "";
  703. var isXML:Boolean = false;
  704. var isXMLString:XML;
  705. var i:int = 0;
  706. var n:int = 0;
  707. // Check if the max trace depth is reached
  708. if (maxDepth == -1 || currentDepth <= maxDepth)
  709. {
  710. // Create the opening node if needed
  711. if (currentDepth == 1) xml += createNode("root");
  712. try
  713. {
  714. // Get the descriptor
  715. description = describeType(object);
  716. type = parseType(description.@name);
  717. base = parseType(description.@base);
  718. if (functions && base == TYPE_FUNCTION)
  719. {
  720. // Trace an empty function
  721. xml += createNode("node", {
  722. icon: ICON_FUNCTION,
  723. label: "(Function)",
  724. name: "",
  725. type: TYPE_FUNCTION,
  726. value: "",
  727. target: target,
  728. access: ACCESS_VARIABLE,
  729. permission: PERMISSION_READWRITE
  730. }, true);
  731. }
  732. else if (type == TYPE_ARRAY || type == TYPE_VECTOR)
  733. {
  734. // Add data description if needed
  735. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  736. // Create the length property
  737. // The only property of the array
  738. xml += createNode("node", {
  739. icon: ICON_VARIABLE,
  740. label: "length" + " (" + TYPE_UINT + ") = " + object["length"],
  741. name: "length",
  742. type: TYPE_UINT,
  743. value: object["length"],
  744. target: target + "." + "length",
  745. access: ACCESS_VARIABLE,
  746. permission: PERMISSION_READONLY
  747. }, true);
  748. // Get and sort the properties
  749. var keys:Array = new Array();
  750. for (var key:* in object) {
  751. keys.push(key);
  752. }
  753. keys.sort();
  754. // Loop through the array
  755. for (i = 0; i < keys.length; i++)
  756. {
  757. // Save the type
  758. childType = parseType(describeType(object[keys[i]]).@name);
  759. childTarget = target + "." + String(keys[i]);
  760. // Check if we can create a single string or a new node
  761. if (childType == TYPE_STRING || childType == TYPE_BOOLEAN || childType == TYPE_NUMBER || childType == TYPE_INT || childType == TYPE_UINT || childType == TYPE_FUNCTION)
  762. {
  763. isXML = false;
  764. isXMLString = new XML();
  765. // Check if the string is a XML string
  766. if (childType == TYPE_STRING) {
  767. try {
  768. isXMLString = new XML(object[keys[i]]);
  769. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  770. } catch(error1:TypeError) {}
  771. }
  772. try {
  773. if (!isXML) {
  774. xml += createNode("node", {
  775. icon: ICON_VARIABLE,
  776. label: "[" + keys[i] + "] (" + childType + ") = " + printObject(object[keys[i]], childType),
  777. name: "[" + keys[i] + "]",
  778. type: childType,
  779. value: printObject(object[keys[i]], childType),
  780. target: childTarget,
  781. access: ACCESS_VARIABLE,
  782. permission: PERMISSION_READWRITE
  783. }, true);
  784. } else {
  785. xml += createNode("node", {
  786. icon: ICON_VARIABLE,
  787. label: "[" + keys[i] + "] (" + childType + ")",
  788. name: "[" + keys[i] + "]",
  789. type: childType,
  790. value: "",
  791. target: childTarget,
  792. access: ACCESS_VARIABLE,
  793. permission: PERMISSION_READWRITE
  794. }, false);
  795. xml += parseXML(isXMLString, childTarget + "." + "cildren()", currentDepth, maxDepth);
  796. xml += createNode("/node");
  797. }
  798. } catch(error2:Error) {}
  799. }
  800. else
  801. {
  802. xml += createNode("node", {
  803. icon: ICON_VARIABLE,
  804. label: "[" + keys[i] + "] (" + childType + ")",
  805. name: "[" + keys[i] + "]",
  806. type: childType,
  807. value: "",
  808. target: childTarget,
  809. access: ACCESS_VARIABLE,
  810. permission: PERMISSION_READWRITE
  811. });
  812. try
  813. {
  814. // Try to parse the object
  815. xml += parseObject(object[keys[i]], childTarget, functions, currentDepth + 1, maxDepth);
  816. }
  817. catch(error3:Error)
  818. {
  819. // If this fails add a warning message for the user
  820. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  821. }
  822. xml += createNode("/node");
  823. }
  824. }
  825. // Close data description if needed
  826. if (currentDepth == 1) xml += createNode("/node");
  827. }
  828. else if (type == TYPE_OBJECT)
  829. {
  830. // Add data description if needed
  831. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  832. // Get and sort the properties
  833. var properties:Array = new Array();
  834. for (var prop:* in object) {
  835. properties.push(prop);
  836. }
  837. properties.sort();
  838. // Loop through the array
  839. for (i = 0; i < properties.length; i++)
  840. {
  841. // Save the type
  842. childType = parseType(describeType(object[properties[i]]).@name);
  843. childTarget = target + "." + properties[i];
  844. // Check if we can create a single string or a new node
  845. if (childType == TYPE_STRING || childType == TYPE_BOOLEAN || childType == TYPE_NUMBER || childType == TYPE_INT || childType == TYPE_UINT || childType == TYPE_FUNCTION)
  846. {
  847. isXML = false;
  848. isXMLString = new XML();
  849. // Check if the string is a XML string
  850. if (childType == TYPE_STRING) {
  851. try {
  852. isXMLString = new XML(object[properties[i]]);
  853. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  854. } catch(error4:TypeError) {}
  855. }
  856. try {
  857. if (!isXML) {
  858. xml += createNode("node", {
  859. icon: ICON_VARIABLE,
  860. label: properties[i] + " (" + childType + ") = " + printObject(object[properties[i]], childType),
  861. name: properties[i],
  862. type: childType,
  863. value: printObject(object[properties[i]], childType),
  864. target: childTarget,
  865. access: ACCESS_VARIABLE,
  866. permission: PERMISSION_READWRITE
  867. }, true);
  868. } else {
  869. xml += createNode("node", {
  870. icon: ICON_VARIABLE,
  871. label: properties[i] + " (" + childType + ")",
  872. name: properties[i],
  873. type: childType,
  874. value: "",
  875. target: childTarget,
  876. access: ACCESS_VARIABLE,
  877. permission: PERMISSION_READWRITE
  878. }, false);
  879. xml += parseXML(isXMLString, childTarget + "." + "cildren()", currentDepth, maxDepth);
  880. xml += createNode("/node");
  881. }
  882. } catch(error5:Error) {}
  883. }
  884. else
  885. {
  886. xml += createNode("node", {
  887. icon: ICON_VARIABLE,
  888. label: properties[i] + " (" + childType + ")",
  889. name: properties[i],
  890. type: childType,
  891. value: "",
  892. target: childTarget,
  893. access: ACCESS_VARIABLE,
  894. permission: PERMISSION_READWRITE
  895. });
  896. try
  897. {
  898. // Try to parse the object
  899. xml += parseObject(object[properties[i]], childTarget, functions, currentDepth + 1, maxDepth);
  900. }
  901. catch(error6:Error)
  902. {
  903. // If this fails add a warning message for the user
  904. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  905. }
  906. xml += createNode("/node");
  907. }
  908. }
  909. // Close data description if needed
  910. if (currentDepth == 1) xml += createNode("/node");
  911. }
  912. else if (type == TYPE_XML)
  913. {
  914. // Add data description if needed
  915. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  916. // Parse the XML
  917. xml += parseXML(object, target + "." + "cildren()", currentDepth, maxDepth);
  918. // Close data description if needed
  919. if (currentDepth == 1) xml += createNode("/node");
  920. }
  921. else if (type == TYPE_XMLLIST)
  922. {
  923. // Add data description if needed
  924. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  925. // Create the length property
  926. // The only property of the array
  927. xml += createNode("node", {
  928. icon: ICON_VARIABLE,
  929. label: "length" + " (" + TYPE_UINT + ") = " + object.length(),
  930. name: "length",
  931. type: TYPE_UINT,
  932. value: object.length(),
  933. target: target + "." + "length",
  934. access: ACCESS_VARIABLE,
  935. permission: PERMISSION_READONLY
  936. }, true);
  937. // Loop through the xml nodes
  938. for (i = 0; i < object.length(); i++)
  939. {
  940. xml += parseXML(object[i], target + "." + String(i) + ".children()", currentDepth, maxDepth);
  941. }
  942. // Close data description if needed
  943. if (currentDepth == 1) xml += createNode("/node");
  944. }
  945. else if (type == TYPE_STRING || type == TYPE_BOOLEAN || type == TYPE_NUMBER || type == TYPE_INT || type == TYPE_UINT)
  946. {
  947. isXML = false;
  948. isXMLString = new XML();
  949. // Check if the string is a XML string
  950. if (type == TYPE_STRING) {
  951. try {
  952. isXMLString = new XML(object);
  953. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  954. } catch(error7:TypeError) {}
  955. }
  956. try {
  957. if (!isXML) {
  958. xml += createNode("node", {
  959. icon: ICON_VARIABLE,
  960. label: "(" + type + ") = " + printObject(object, type),
  961. name: "",
  962. type: type,
  963. value: printObject(object, type),
  964. target: target,
  965. access: ACCESS_VARIABLE,
  966. permission: PERMISSION_READWRITE
  967. }, true);
  968. } else {
  969. xml += createNode("node", {
  970. icon: ICON_VARIABLE,
  971. label: "(" + type + ")",
  972. name: "",
  973. type: type,
  974. value: "",
  975. target: target,
  976. access: ACCESS_VARIABLE,
  977. permission: PERMISSION_READWRITE
  978. }, false);
  979. xml += parseXML(isXMLString, target + "." + "cildren()", currentDepth, maxDepth);
  980. xml += createNode("/node");
  981. }
  982. } catch(error8:Error) {}
  983. }
  984. else
  985. {
  986. // Add data description if needed
  987. if (currentDepth == 1) xml += createNode("node", {icon:ICON_ROOT, label:"(" + type + ")", target:target});
  988. // Get the data
  989. var variables:XMLList = description..variable;
  990. var accessors:XMLList = description..accessor;
  991. var constants:XMLList = description..constant;
  992. var methods:XMLList = description..method;
  993. var variablesArr:Array = new Array();
  994. var methodsArr:Array = new Array();
  995. var double:Boolean = false;
  996. var permission:String = "";
  997. var icon:String = "";
  998. // Save the variables
  999. double = false;
  1000. for (i = 0; i < variables.length(); i++) {
  1001. for (n = 0; n < variablesArr.length; n++) {
  1002. if (variablesArr[n].name == variables[i].@name) {
  1003. double = true;
  1004. break;
  1005. }
  1006. }
  1007. if (!double) {
  1008. variablesArr.push({name:variables[i].@name, xml:variables[i], access:ACCESS_VARIABLE});
  1009. }
  1010. }
  1011. // Save the accessors
  1012. double = false;
  1013. for (i = 0; i < accessors.length(); i++) {
  1014. for (n = 0; n < variablesArr.length; n++) {
  1015. if (variablesArr[n].name == accessors[i].@name) {
  1016. double = true;
  1017. break;
  1018. }
  1019. }
  1020. if (!double) {
  1021. variablesArr.push({name:accessors[i].@name, xml:accessors[i], access:ACCESS_ACCESSOR});
  1022. }
  1023. }
  1024. // Save the constants
  1025. double = false;
  1026. for (i = 0; i < constants.length(); i++) {
  1027. for (n = 0; n < variablesArr.length; n++) {
  1028. if (variablesArr[n].name == constants[i].@name) {
  1029. double = true;
  1030. break;
  1031. }
  1032. }
  1033. if (!double) {
  1034. variablesArr.push({name:constants[i].@name, xml:constants[i], access:ACCESS_CONSTANT});
  1035. }
  1036. }
  1037. // Save the methods
  1038. double = false;
  1039. for (i = 0; i < methods.length(); i++) {
  1040. for (n = 0; n < methodsArr.length; n++) {
  1041. if (methodsArr[n].name == methods[i].@name) {
  1042. double = true;
  1043. break;
  1044. }
  1045. }
  1046. if (!double) {
  1047. methodsArr.push({name:methods[i].@name, xml:methods[i], access:ACCESS_METHOD});
  1048. }
  1049. }
  1050. // Sort the nodes
  1051. variablesArr.sortOn("name");
  1052. methodsArr.sortOn("name");
  1053. // VARIABLES
  1054. for (i = 0; i < variablesArr.length; i++)
  1055. {
  1056. // Save the type
  1057. childType = parseType(variablesArr[i].xml.@type);
  1058. childName = variablesArr[i].xml.@name;
  1059. childTarget = target + "." + childName;
  1060. // Save the permission and icon
  1061. permission = PERMISSION_READWRITE;
  1062. icon = ICON_VARIABLE;
  1063. // Check for read / write permissions
  1064. if (variablesArr[i].access == ACCESS_CONSTANT) {
  1065. // Constant
  1066. permission = PERMISSION_READONLY;
  1067. icon = ICON_VARIABLE_READONLY;
  1068. }
  1069. if (variablesArr[i].xml.@access == PERMISSION_READONLY) {
  1070. // Only a getter
  1071. permission = PERMISSION_READONLY;
  1072. icon = ICON_VARIABLE_READONLY;
  1073. }
  1074. if (variablesArr[i].xml.@access == PERMISSION_WRITEONLY) {
  1075. // Only a setter
  1076. permission = PERMISSION_WRITEONLY;
  1077. icon = ICON_VARIABLE_WRITEONLY;
  1078. }
  1079. // Don't include write only accessor
  1080. if (permission != PERMISSION_WRITEONLY)
  1081. {
  1082. // Check if we can create a single string or a new node
  1083. if (childType == TYPE_STRING || childType == TYPE_BOOLEAN || childType == TYPE_NUMBER || childType == TYPE_INT || childType == TYPE_UINT || childType == TYPE_FUNCTION)
  1084. {
  1085. isXML = false;
  1086. isXMLString = new XML();
  1087. // Check if the string is a XML string
  1088. if (childType == TYPE_STRING) {
  1089. try {
  1090. isXMLString = new XML(object[childName]);
  1091. if (!isXMLString.hasSimpleContent() && isXMLString.children().length() > 0) isXML = true;
  1092. } catch(error9:TypeError) {}
  1093. }
  1094. try {
  1095. if (!isXML) {
  1096. xml += createNode("node", {
  1097. icon: icon,
  1098. label: childName + " (" + childType + ") = " + printObject(object[childName], childType),
  1099. name: childName,
  1100. type: childType,
  1101. value: printObject(object[childName], childType),
  1102. target: childTarget,
  1103. access: variablesArr[i].access,
  1104. permission: permission
  1105. }, true);
  1106. } else {
  1107. xml += createNode("node", {
  1108. icon: icon,
  1109. label: childName + " (" + childType + ")",
  1110. name: childName,
  1111. type: childType,
  1112. value: "",
  1113. target: childTarget,
  1114. access: variablesArr[i].access,
  1115. permission: permission
  1116. }, false);
  1117. xml += parseXML(isXMLString, childTarget + "." + "cildren()", currentDepth, maxDepth);
  1118. xml += createNode("/node");
  1119. }
  1120. } catch(error10:Error) {}
  1121. }
  1122. else
  1123. {
  1124. xml += createNode("node", {
  1125. icon: icon,
  1126. label: childName + " (" + childType + ")",
  1127. name: childName,
  1128. type: childType,
  1129. target: childTarget,
  1130. access: variablesArr[i].access,
  1131. permission: permission
  1132. });
  1133. try
  1134. {
  1135. // Try to parse the object
  1136. xml += parseObject(object[childName], childTarget, functions, currentDepth + 1, maxDepth);
  1137. }
  1138. catch(error11:Error)
  1139. {
  1140. // If this fails add a warning message for the user
  1141. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  1142. }
  1143. xml += createNode("/node");
  1144. }
  1145. }
  1146. }
  1147. // METHODS
  1148. if (functions)
  1149. {
  1150. for (i = 0; i < methodsArr.length; i++)
  1151. {
  1152. // Save the type
  1153. childType = TYPE_FUNCTION;
  1154. childName = methodsArr[i].xml.@name;
  1155. childTarget = target + "." + childName;
  1156. // Save the parameters
  1157. var returnType:String = parseType(methodsArr[i].xml.@returnType);
  1158. var parameters:XMLList = methodsArr[i].xml..parameter;
  1159. var args:Array = new Array();
  1160. // Create the parameters
  1161. for (n = 0; n < parameters.length(); n++) {
  1162. args.push(parseType(parameters[n].@type));
  1163. }
  1164. // Create the node
  1165. xml += createNode("node", {
  1166. icon: ICON_FUNCTION,
  1167. label: childName + "(" + args.join(", ") + "):" + returnType,
  1168. args: args.join(", "),
  1169. name: childName,
  1170. type: TYPE_FUNCTION,
  1171. access: variablesArr[i].access,
  1172. returnType: returnType,
  1173. target: childTarget
  1174. }, true);
  1175. }
  1176. }
  1177. // Close data description if needed
  1178. if (currentDepth == 1) xml += createNode("/node");
  1179. }
  1180. }
  1181. catch (error12:Error)
  1182. {
  1183. // The object is not found
  1184. var msg:String = "";
  1185. msg += createNode("root");
  1186. msg += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Not found", name:"Not found"}, true);
  1187. msg += createNode("/root");
  1188. send({text:COMMAND_NOTFOUND, target:target, xml:XML(msg)});
  1189. }
  1190. // Create a closing node if needed
  1191. if (currentDepth == 1) xml += createNode("/root");
  1192. }
  1193. //Return the xml
  1194. return xml;
  1195. }
  1196. /**
  1197. * Parse a display object
  1198. * @param object: The object to parse
  1199. * @param target: A point seperated path to the object
  1200. * @param functions: Include or exclude functions
  1201. * @param currentDepth: The current trace depth
  1202. * @param maxDepth:: The maximum trace depth
  1203. */
  1204. private function parseDisplayObject(object:*, target:String = "", functions:Boolean = false, currentDepth:int = 1, maxDepth:int = 4):String
  1205. {
  1206. // Variables needed in the loops
  1207. var xml:String = "";
  1208. var childs:Array;
  1209. var child:DisplayObject;
  1210. var childType:String = "";
  1211. var childIcon:String = "";
  1212. var childName:String = "";
  1213. var childTarget:String = "";
  1214. var childChildren:String = "";
  1215. var i:int = 0;
  1216. var n:int = 0;
  1217. // Check if the max trace depth is reached
  1218. if (maxDepth == -1 || currentDepth <= maxDepth)
  1219. {
  1220. // Create the opening node if needed
  1221. if (currentDepth == 1) xml += createNode("root");
  1222. try
  1223. {
  1224. // Add data description if needed
  1225. if (currentDepth == 1) {
  1226. var ojectName:String = DisplayObject(object).name;
  1227. if (ojectName == null || ojectName == "null") {
  1228. ojectName = "DisplayObject";
  1229. }
  1230. xml += createNode("node", {icon:ICON_ROOT, label:"(" + ojectName + ")", target:target});
  1231. }
  1232. // Get the childs
  1233. childs = new Array();
  1234. for (i = 0; i < DisplayObjectContainer(object).numChildren; i++) {
  1235. childs.push(DisplayObjectContainer(object).getChildAt(i));
  1236. }
  1237. // Loop through the array
  1238. for (i = 0; i < childs.length; i++)
  1239. {
  1240. // Save the child properties
  1241. child = childs[i];
  1242. childName = describeType(child).@name;
  1243. childType = parseType(childName);
  1244. childTarget = target + "." + "getChildAt(" + i + ")";
  1245. childIcon = child is DisplayObjectContainer ? ICON_ROOT : ICON_VARIABLE;
  1246. childChildren = child is DisplayObjectContainer ? String(DisplayObjectContainer(child).numChildren) : ""
  1247. // Create the node
  1248. xml += createNode("node", {
  1249. icon: childIcon,
  1250. label: child.name + " (" + childType + ") " + childChildren,
  1251. name: child.name,
  1252. type: childType,
  1253. value: printObject(child, childType),
  1254. target: childTarget,
  1255. access: ACCESS_VARIABLE,
  1256. permission: PERMISSION_READWRITE
  1257. });
  1258. try
  1259. {
  1260. // Try to parse the object
  1261. xml += parseDisplayObject(child, childTarget, functions, currentDepth + 1, maxDepth);
  1262. }
  1263. catch(error13:Error)
  1264. {
  1265. // If this fails add a warning message for the user
  1266. xml += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Unreadable", name:"Unreadable"}, true);
  1267. }
  1268. xml += createNode("/node");
  1269. }
  1270. // Create a closing node if needed
  1271. if (currentDepth == 1) xml += createNode("/node");
  1272. }
  1273. catch (error14:Error)
  1274. {
  1275. // The object is not found
  1276. var msg:String = "";
  1277. msg += createNode("root");
  1278. msg += createNode("node", {icon:ICON_WARNING, type:TYPE_WARNING, label:"Not found", name:"Not found"}, true);
  1279. msg += createNode("/root");
  1280. send({text:COMMAND_NOTFOUND, target:target, xml:XML(msg)});
  1281. }
  1282. // Create a closing node if needed
  1283. if (currentDepth == 1) xml += createNode("/root");
  1284. }
  1285. //Return the xml
  1286. return xml;
  1287. }
  1288. /**
  1289. * Parse a XML node
  1290. * @param node: The xml to parse
  1291. * @param target: A point seperated path to the object
  1292. * @param currentDepth: The current trace depth
  1293. * @param maxDepth:: The maximum trace depth
  1294. */
  1295. private function parseXML(node:*, target:String = "", currentDepth:int = 1, maxDepth:int = -1):String
  1296. {
  1297. // Create a return string
  1298. var xml:String = "";
  1299. var i:int = 0;
  1300. // Check if the maximum trace depth is reached
  1301. if (maxDepth == -1 || currentDepth <= maxDepth)
  1302. {
  1303. // Check if the user traced an attribute
  1304. if (target.indexOf("@") != -1)
  1305. {
  1306. // Display a single attribute
  1307. xml += createNode("node", {
  1308. icon: ICON_XMLATTRIBUTE,
  1309. label: node,
  1310. name: "",
  1311. type: TYPE_XMLATTRIBUTE,
  1312. value: node,
  1313. target: target,
  1314. access: ACCESS_VARIABLE,
  1315. permission: PERMISSION_READWRITE
  1316. }, true);
  1317. }
  1318. else if (node.name() == null)
  1319. {
  1320. // Only a text value
  1321. xml += createNode("node", {
  1322. icon: ICON_XMLVALUE,
  1323. label: "(" + TYPE_XMLVALUE + ") = " + printObject(node, TYPE_XMLVALUE),
  1324. name: "",
  1325. type: TYPE_XMLVALUE,
  1326. value: printObject(node, TYPE_XMLVALUE),
  1327. target: target,
  1328. access: ACCESS_VARIABLE,
  1329. permission: PERMISSION_READWRITE
  1330. }, true);
  1331. }
  1332. else if (node.hasSimpleContent())
  1333. {
  1334. // Node with one text value and possible attributes
  1335. xml += createNode("node", {
  1336. icon: ICON_XMLNODE,
  1337. label: node.name() + " (" + TYPE_XMLNODE + ")",
  1338. name: node.name(),
  1339. type: TYPE_XMLNODE,
  1340. value: "",
  1341. target: target,
  1342. access: ACCESS_VARIABLE,
  1343. permission: PERMISSION_READWRITE
  1344. });
  1345. // Only a text value
  1346. if (node != "") {
  1347. xml += createNode("node", {
  1348. icon: ICON_XMLVALUE,
  1349. label: "(" + TYPE_XMLVALUE + ") = " + printObject(node, TYPE_XMLVALUE),
  1350. name: "",
  1351. type: TYPE_XMLVALUE,
  1352. value: printObject(node, TYPE_XMLVALUE),
  1353. target: target,
  1354. access: ACCESS_VARIABLE,
  1355. permission: PERMISSION_READWRITE
  1356. }, true);
  1357. }
  1358. // Loop through the arrributes
  1359. for (i = 0; i < node.attributes().length(); i++)
  1360. {
  1361. xml += createNode("node", {
  1362. icon: ICON_XMLATTRIBUTE,
  1363. label: "@" + node.attributes()[i].name() + " (" + TYPE_XMLATTRIBUTE + ") = " + node.attributes()[i],
  1364. name: "",
  1365. type: TYPE_XMLATTRIBUTE,
  1366. value: node.attributes()[i],
  1367. target: target + "." + "@" + node.attributes()[i].name(),
  1368. access: ACCESS_VARIABLE,
  1369. permission: PERMISSION_READWRITE
  1370. }, true);
  1371. }
  1372. // Close the node
  1373. xml += createNode("/node");
  1374. }
  1375. else
  1376. {
  1377. // Node with children and attributes
  1378. // This node has no value due to the children
  1379. xml += createNode("node", {
  1380. icon: ICON_XMLNODE,
  1381. label: node.name() + " (" + TYPE_XMLNODE + ")",
  1382. name: node.name(),
  1383. type: TYPE_XMLNODE,
  1384. value: "",
  1385. target: target,
  1386. access: ACCESS_VARIABLE,
  1387. permission: PERMISSION_READWRITE
  1388. });
  1389. // Loop through the arrributes
  1390. for (i = 0; i < node.attributes().length(); i++)
  1391. {
  1392. xml += createNode("node", {
  1393. icon: ICON_XMLATTRIBUTE,
  1394. label: "@" + node.attributes()[i].name() + " (" + TYPE_XMLATTRIBUTE + ") = " + node.attributes()[i],
  1395. name: "",
  1396. type: TYPE_XMLATTRIBUTE,
  1397. value: node.attributes()[i],
  1398. target: target + "." + "@" + node.attributes()[i].name(),
  1399. access: ACCESS_VARIABLE,
  1400. permission: PERMISSION_READWRITE
  1401. }, true);
  1402. }
  1403. // Loop through children
  1404. for (i = 0; i < node.children().length(); i++)
  1405. {
  1406. var childTarget:String = target + "." + "children()" + "." + i;
  1407. xml += parseXML(node.children()[i], childTarget, currentDepth + 1, maxDepth);
  1408. }
  1409. // Close the node
  1410. xml += createNode("/node");
  1411. }
  1412. }
  1413. // Return the formatted XML
  1414. return xml;
  1415. }
  1416. /**
  1417. * Converts package names to type
  1418. * Example: "nl.demonsters.debugger::MonsterDebugger" becomes "MonsterDebugger"
  1419. * We could also use getDefinitionByName() but that can't parse "builtin.as$0::MethodClosure"
  1420. * @param type: The string to parse
  1421. */
  1422. private function parseType(type:String):String
  1423. {
  1424. // The return string
  1425. var s:String = type;
  1426. // Remove the package information if needed
  1427. if (type.lastIndexOf("::") != -1) {
  1428. s = type.substring(type.lastIndexOf("::") + 2, type.length);
  1429. }
  1430. // Remove the items after the .
  1431. // Vector.<String> becomes Vector
  1432. if (s.lastIndexOf(".") != -1) {
  1433. s = s.substring(0, s.lastIndexOf("."));
  1434. }
  1435. // Change "MethodClosure" to "Function"
  1436. // This is better for the user interface
  1437. if (s == TYPE_METHOD) {
  1438. s = TYPE_FUNCTION;
  1439. }
  1440. // Return the value
  1441. return htmlEscape(s);
  1442. }
  1443. /**
  1444. * Create an XML node
  1445. * @param title: The title of the node
  1446. * @param object: The values of the node
  1447. * @param close: Create a closing tag
  1448. */
  1449. private function createNode(title:String, object:Object = null, close:Boolean = false):String
  1450. {
  1451. // The string to store the node
  1452. var xml:String = "";
  1453. // Open the node
  1454. xml += "<" + title;
  1455. //Loop through the values
  1456. if (object) {
  1457. for (var prop:* in object) {
  1458. xml += " " + prop + "='" + object[prop] + "'";
  1459. }
  1460. }
  1461. // Close the node
  1462. if (close) {
  1463. xml += "></" + title + ">";
  1464. } else {
  1465. xml += ">";
  1466. }
  1467. // Return the node
  1468. return xml;
  1469. }
  1470. /**
  1471. * Print a single object
  1472. * @param object: The object to parse
  1473. * @param type: The object type
  1474. */
  1475. private function printObject(object:*, type:String):String
  1476. {
  1477. // Create a return string
  1478. var s:String = "";
  1479. // Check if the object can be converted
  1480. // Here we can handle the exceptions
  1481. if (type == TYPE_BYTEARRAY)
  1482. {
  1483. // We dont want to send the complete byte array
  1484. // Only display the number of bytes
  1485. s = object["length"] + " bytes";
  1486. }
  1487. else
  1488. {
  1489. // Display the object
  1490. s = htmlEscape(String(object));
  1491. }
  1492. // Return the printed object
  1493. return s;
  1494. }
  1495. /**
  1496. * Send the current FPS
  1497. * @param event: Basic event
  1498. */
  1499. private function enterFrameHandler(event:Event):void
  1500. {
  1501. if (enabled) {
  1502. monitorFrames++;
  1503. }
  1504. }
  1505. /**
  1506. * Send the current memory consumption
  1507. * @param event: Basic timer event
  1508. */
  1509. private function monitorHandler(event:TimerEvent):void
  1510. {
  1511. if (enabled)
  1512. {
  1513. // Get the total Flash Player memory consumption
  1514. // Note: This includes all Flash Player instances
  1515. var memory:uint = System.totalMemory;
  1516. // Calculate the frames pro second
  1517. var now:uint = getTimer();
  1518. var delta:uint = now - monitorTime;
  1519. var fps:uint = monitorFrames / delta * 1000;
  1520. monitorFrames = 0;
  1521. monitorTime = now;
  1522. // Send the data
  1523. send({text:COMMAND_MONITOR, memory:memory, fps:fps, time:now, date:new Date()});
  1524. }
  1525. }
  1526. /**
  1527. * Converts regular characters to HTML characters
  1528. * @param s: The string to convert
  1529. */
  1530. private function htmlEscape(s:String):String
  1531. {
  1532. if (s) {
  1533. // Remove single quotes
  1534. while(s.indexOf("\'") != -1) {
  1535. s = s.replace("\'", "&apos;");
  1536. }
  1537. // Remove double quotes
  1538. while(s.indexOf("\"") != -1) {
  1539. s = s.replace("\"", "&quot;");
  1540. }
  1541. var xml:XML = <a>{s}</a>;
  1542. return xml.toXMLString().replace(/(^<a>)|(<\/a>$)|(^<a\/>$)/g, "");
  1543. } else {
  1544. return "";
  1545. }
  1546. }
  1547. /**
  1548. * Converts HTML characters to regular characters
  1549. * @param s: The string to convert
  1550. */
  1551. private function htmlUnescape(s:String):String
  1552. {
  1553. if (s) {
  1554. var xml:XML = <a/>;
  1555. xml.replace(0, s);
  1556. return String(xml);
  1557. } else {
  1558. return "";
  1559. }
  1560. }
  1561. /**
  1562. * Enable or disable the debugger
  1563. */
  1564. public static function get enabled():Boolean
  1565. {
  1566. if (instance == null) instance = new MonsterDebugger(null);
  1567. return instance._enabled;
  1568. }
  1569. public static function set enabled(value:Boolean):void
  1570. {
  1571. if (instance == null) instance = new MonsterDebugger(null);
  1572. instance._enabled = value;
  1573. }
  1574. /**
  1575. * This function is used for the singleton check in the constructor
  1576. * and is given as an argument in the trace and clearTrace function
  1577. */
  1578. private static function singletonCheck():void{}
  1579. /**
  1580. * Event handlers
  1581. * Can be used for debugging
  1582. */
  1583. private function asyncErrorHandler(event:AsyncErrorEvent):void {}
  1584. private function securityErrorHandler(event:SecurityErrorEvent):void {}
  1585. private function statusHandler(event:StatusEvent):void {}
  1586. }
  1587. }