PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Sample/mx/utils/UIDUtil.as

https://github.com/ingydotnet/yaml-oscon2009-talk
ActionScript | 385 lines | 214 code | 43 blank | 128 comment | 72 complexity | d3629e4028f988febdcb57530587eb7b MD5 | raw file
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // ADOBE SYSTEMS INCORPORATED
  4. // Copyright 2005-2007 Adobe Systems Incorporated
  5. // All Rights Reserved.
  6. //
  7. // NOTICE: Adobe permits you to use, modify, and distribute this file
  8. // in accordance with the terms of the license agreement accompanying it.
  9. //
  10. ////////////////////////////////////////////////////////////////////////////////
  11. package mx.utils
  12. {
  13. import flash.utils.ByteArray;
  14. import flash.utils.Dictionary;
  15. import mx.core.IPropertyChangeNotifier;
  16. import mx.core.IUIComponent;
  17. import mx.core.IUID;
  18. import mx.core.mx_internal;
  19. use namespace mx_internal;
  20. /**
  21. * The UIDUtil class is an all-static class
  22. * with methods for working with UIDs (unique identifiers) within Flex.
  23. * You do not create instances of UIDUtil;
  24. * instead you simply call static methods such as the
  25. * <code>UIDUtil.createUID()</code> method.
  26. *
  27. * <p><b>Note</b>: If you have a dynamic object that has no [Bindable] properties
  28. * (which force the object to implement the IUID interface), Flex adds an
  29. * <code>mx_internal_uid</code> property that contains a UID to the object.
  30. * To avoid having this field
  31. * in your dynamic object, make it [Bindable], implement the IUID interface
  32. * in the object class, or set a <coded>uid</coded> property with a value.</p>
  33. */
  34. public class UIDUtil
  35. {
  36. include "../core/Version.as";
  37. //--------------------------------------------------------------------------
  38. //
  39. // Class constants
  40. //
  41. //--------------------------------------------------------------------------
  42. /**
  43. * @private
  44. * Char codes for 0123456789ABCDEF
  45. */
  46. private static const ALPHA_CHAR_CODES:Array = [48, 49, 50, 51, 52, 53, 54,
  47. 55, 56, 57, 65, 66, 67, 68, 69, 70];
  48. //--------------------------------------------------------------------------
  49. //
  50. // Class variables
  51. //
  52. //--------------------------------------------------------------------------
  53. /**
  54. * This Dictionary records all generated uids for all existing items.
  55. */
  56. private static var uidDictionary:Dictionary = new Dictionary(true);
  57. //--------------------------------------------------------------------------
  58. //
  59. // Class methods
  60. //
  61. //--------------------------------------------------------------------------
  62. /**
  63. * Generates a UID (unique identifier) based on ActionScript's
  64. * pseudo-random number generator and the current time.
  65. *
  66. * <p>The UID has the form
  67. * <code>"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"</code>
  68. * where X is a hexadecimal digit (0-9, A-F).</p>
  69. *
  70. * <p>This UID will not be truly globally unique; but it is the best
  71. * we can do without player support for UID generation.</p>
  72. *
  73. * @return The newly-generated UID.
  74. */
  75. public static function createUID():String
  76. {
  77. var uid:Array = new Array(36);
  78. var index:int = 0;
  79. var i:int;
  80. var j:int;
  81. for (i = 0; i < 8; i++)
  82. {
  83. uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)];
  84. }
  85. for (i = 0; i < 3; i++)
  86. {
  87. uid[index++] = 45; // charCode for "-"
  88. for (j = 0; j < 4; j++)
  89. {
  90. uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)];
  91. }
  92. }
  93. uid[index++] = 45; // charCode for "-"
  94. var time:Number = new Date().getTime();
  95. // Note: time is the number of milliseconds since 1970,
  96. // which is currently more than one trillion.
  97. // We use the low 8 hex digits of this number in the UID.
  98. // Just in case the system clock has been reset to
  99. // Jan 1-4, 1970 (in which case this number could have only
  100. // 1-7 hex digits), we pad on the left with 7 zeros
  101. // before taking the low digits.
  102. var timeString:String = ("0000000" + time.toString(16).toUpperCase()).substr(-8);
  103. for (i = 0; i < 8; i++)
  104. {
  105. uid[index++] = timeString.charCodeAt(i);
  106. }
  107. for (i = 0; i < 4; i++)
  108. {
  109. uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)];
  110. }
  111. return String.fromCharCode.apply(null, uid);
  112. }
  113. /**
  114. * Converts a 128-bit UID encoded as a ByteArray to a String representation.
  115. * The format matches that generated by createUID. If a suitable ByteArray
  116. * is not provided, null is returned.
  117. *
  118. * @param ba ByteArray 16 bytes in length representing a 128-bit UID.
  119. *
  120. * @return String representation of the UID, or null if an invalid
  121. * ByteArray is provided.
  122. */
  123. public static function fromByteArray(ba:ByteArray):String
  124. {
  125. if (ba != null && ba.length >= 16 && ba.bytesAvailable >= 16)
  126. {
  127. var chars:Array = new Array(36);
  128. var index:uint = 0;
  129. for (var i:uint = 0; i < 16; i++)
  130. {
  131. if (i == 4 || i == 6 || i == 8 || i == 10)
  132. chars[index++] = 45; // Hyphen char code
  133. var b:int = ba.readByte();
  134. chars[index++] = ALPHA_CHAR_CODES[(b & 0xF0) >>> 4];
  135. chars[index++] = ALPHA_CHAR_CODES[(b & 0x0F)];
  136. }
  137. return String.fromCharCode.apply(null, chars);
  138. }
  139. return null;
  140. }
  141. /**
  142. * A utility method to check whether a String value represents a
  143. * correctly formatted UID value. UID values are expected to be
  144. * in the format generated by createUID(), implying that only
  145. * capitalized A-F characters in addition to 0-9 digits are
  146. * supported.
  147. *
  148. * @param uid The value to test whether it is formatted as a UID.
  149. *
  150. * @return Returns true if the value is formatted as a UID.
  151. */
  152. public static function isUID(uid:String):Boolean
  153. {
  154. if (uid != null && uid.length == 36)
  155. {
  156. for (var i:uint = 0; i < 36; i++)
  157. {
  158. var c:Number = uid.charCodeAt(i);
  159. // Check for correctly placed hyphens
  160. if (i == 8 || i == 13 || i == 18 || i == 23)
  161. {
  162. if (c != 45)
  163. {
  164. return false;
  165. }
  166. }
  167. // We allow capital alpha-numeric hex digits only
  168. else if (c < 48 || c > 70 || (c > 57 && c < 65))
  169. {
  170. return false;
  171. }
  172. }
  173. return true;
  174. }
  175. return false;
  176. }
  177. /**
  178. * Converts a UID formatted String to a ByteArray. The UID must be in the
  179. * format generated by createUID, otherwise null is returned.
  180. *
  181. * @param String representing a 128-bit UID
  182. *
  183. * @return ByteArray 16 bytes in length representing the 128-bits of the
  184. * UID or null if the uid could not be converted.
  185. */
  186. public static function toByteArray(uid:String):ByteArray
  187. {
  188. if (isUID(uid))
  189. {
  190. var result:ByteArray = new ByteArray();
  191. for (var i:uint = 0; i < uid.length; i++)
  192. {
  193. var c:String = uid.charAt(i);
  194. if (c == "-")
  195. continue;
  196. var h1:uint = getDigit(c);
  197. i++;
  198. var h2:uint = getDigit(uid.charAt(i));
  199. result.writeByte(((h1 << 4) | h2) & 0xFF);
  200. }
  201. result.position = 0;
  202. return result;
  203. }
  204. return null;
  205. }
  206. /**
  207. * Returns the UID (unique identifier) for the specified object.
  208. * If the specified object doesn't have an UID
  209. * then the method assigns one to it.
  210. * If a map is specified this method will use the map
  211. * to construct the UID.
  212. * As a special case, if the item passed in is null,
  213. * this method returns a null UID.
  214. *
  215. * @param item Object that we need to find the UID for.
  216. *
  217. * @return The UID that was either found or generated.
  218. */
  219. public static function getUID(item:Object):String
  220. {
  221. var result:String = null;
  222. if (item == null)
  223. return result;
  224. if (item is IUID)
  225. {
  226. result = IUID(item).uid;
  227. if (result == null || result.length == 0)
  228. {
  229. result = createUID();
  230. IUID(item).uid = result;
  231. }
  232. }
  233. else if ((item is IPropertyChangeNotifier) &&
  234. !(item is IUIComponent))
  235. {
  236. result = IPropertyChangeNotifier(item).uid;
  237. if (result == null || result.length == 0)
  238. {
  239. result = createUID();
  240. IPropertyChangeNotifier(item).uid = result;
  241. }
  242. }
  243. else if (item is String)
  244. {
  245. return item as String;
  246. }
  247. else
  248. {
  249. try
  250. {
  251. // We don't create uids for XMLLists, but if
  252. // there's only a single XML node, we'll extract it.
  253. if (item is XMLList && item.length == 1)
  254. item = item[0];
  255. if (item is XML)
  256. {
  257. // XML nodes carry their UID on the
  258. // function-that-is-a-hashtable they can carry around.
  259. // To decorate an XML node with a UID,
  260. // we need to first initialize it for notification.
  261. // There is a potential performance issue here,
  262. // since notification does have a cost,
  263. // but most use cases for needing a UID on an XML node also
  264. // require listening for change notifications on the node.
  265. var xitem:XML = XML(item);
  266. var nodeKind:String = xitem.nodeKind();
  267. if (nodeKind == "text" || nodeKind == "attribute")
  268. return xitem.toString();
  269. var notificationFunction:Function = xitem.notification();
  270. if (!(notificationFunction is Function))
  271. {
  272. // The xml node hasn't already been initialized
  273. // for notification, so do so now.
  274. notificationFunction =
  275. XMLNotifier.initializeXMLForNotification();
  276. xitem.setNotification(notificationFunction);
  277. }
  278. // Generate a new uid for the node if necessary.
  279. if (notificationFunction["uid"] == undefined)
  280. result = notificationFunction["uid"] = createUID();
  281. result = notificationFunction["uid"];
  282. }
  283. else
  284. {
  285. if ("mx_internal_uid" in item)
  286. return item.mx_internal_uid;
  287. if ("uid" in item)
  288. return item.uid;
  289. result = uidDictionary[item];
  290. if (!result)
  291. {
  292. result = createUID();
  293. try
  294. {
  295. item.mx_internal_uid = result;
  296. }
  297. catch(e:Error)
  298. {
  299. uidDictionary[item] = result;
  300. }
  301. }
  302. }
  303. }
  304. catch(e:Error)
  305. {
  306. result = item.toString();
  307. }
  308. }
  309. return result;
  310. }
  311. /**
  312. * Returns the decimal representation of a hex digit.
  313. * @private
  314. */
  315. private static function getDigit(hex:String):uint
  316. {
  317. switch (hex)
  318. {
  319. case "A":
  320. case "a":
  321. return 10;
  322. case "B":
  323. case "b":
  324. return 11;
  325. case "C":
  326. case "c":
  327. return 12;
  328. case "D":
  329. case "d":
  330. return 13;
  331. case "E":
  332. case "e":
  333. return 14;
  334. case "F":
  335. case "f":
  336. return 15;
  337. default:
  338. return new uint(hex);
  339. }
  340. }
  341. }
  342. }