/flight/list/ArrayList.as

https://code.google.com/ · ActionScript · 402 lines · 313 code · 59 blank · 30 comment · 51 complexity · 4f76e130067bc70964f8fab3495ac1cc MD5 · raw file

  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2009 Tyler Wright, Robert Taylor, Jacob Wright
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. ////////////////////////////////////////////////////////////////////////////////
  24. package flight.list
  25. {
  26. import flight.events.Dispatcher;
  27. import flight.events.ListEvent;
  28. import flight.events.ListEventKind;
  29. import flight.vo.IValueObject;
  30. use namespace list_internal;
  31. [Event(name="listChange", type="flight.events.ListEvent")]
  32. public class ArrayList extends Dispatcher implements IList, IValueObject
  33. {
  34. public var idField:String = "id"; // TODO: replace with dataMap
  35. list_internal var _source:*; // internally available to XMLListAdapter
  36. private var adapter:*;
  37. private var _selection:ListSelection;
  38. private var _mxlist:MXList;
  39. public function ArrayList(source:* = null)
  40. {
  41. this.source = source;
  42. }
  43. [Bindable(event="lengthChange")]
  44. public function get length():int
  45. {
  46. return adapter.length;
  47. }
  48. [Bindable(event="mxlist")]
  49. public function get mxlist():MXList
  50. {
  51. if (_mxlist == null) {
  52. _mxlist = new MXList(this);
  53. }
  54. return _mxlist;
  55. }
  56. [Bindable(event="selectionChange")]
  57. public function get selection():ListSelection
  58. {
  59. if (_selection == null) {
  60. _selection = new ListSelection(this);
  61. }
  62. return _selection;
  63. }
  64. [Bindable(event="sourceChange")]
  65. public function get source():*
  66. {
  67. return _source;
  68. }
  69. public function set source(value:*):void
  70. {
  71. if (value == null) {
  72. value = [];
  73. } else if (_source == value) {
  74. return;
  75. }
  76. var oldValue:Object = _source;
  77. if (value is XMLList) {
  78. _source = value;
  79. adapter = new XMLListAdapter(this);
  80. } else {
  81. _source = ("splice" in value) ? value : [value];
  82. adapter = _source;
  83. }
  84. propertyChange("source", oldValue, _source);
  85. propertyChange("length", oldValue, adapter.length);
  86. dispatchEvent(new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.RESET));
  87. }
  88. public function addItem(item:Object):Object
  89. {
  90. var oldValue:int = adapter.length;
  91. adapter.push(item);
  92. propertyChange("length", oldValue, adapter.length);
  93. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.ADD,
  94. adapter.slice(oldValue, oldValue+1), oldValue) );
  95. return item;
  96. }
  97. public function addItemAt(item:Object, index:int):Object
  98. {
  99. var oldValue:int = adapter.length;
  100. if (index < 0) {
  101. index = Math.max(adapter.length + index, 0);
  102. }
  103. adapter.splice(index, 0, item);
  104. propertyChange("length", oldValue, adapter.length);
  105. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.ADD,
  106. adapter.slice(index, index+1), index) );
  107. return item;
  108. }
  109. public function addItems(items:*, index:int = 0x7FFFFFFF):*
  110. {
  111. // empty list
  112. if (items[0] === undefined) {
  113. return items;
  114. }
  115. var oldValue:int = adapter.length;
  116. if (index < 0) {
  117. index = Math.max(adapter.length + index, 0);
  118. } else if (index > oldValue) {
  119. index = oldValue;
  120. }
  121. adapter.splice.apply(adapter, [index, 0].concat(items));
  122. propertyChange("length", oldValue, adapter.length);
  123. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.ADD, items, index) );
  124. return items;
  125. }
  126. public function containsItem(item:Object):Boolean
  127. {
  128. return Boolean(adapter.indexOf(item) != -1);
  129. }
  130. public function getItemAt(index:int):Object
  131. {
  132. if (index < 0) {
  133. index = Math.max(adapter.length + index, 0);
  134. }
  135. return _source[index];
  136. }
  137. public function getItemById(id:String):Object
  138. {
  139. for each (var item:Object in _source) {
  140. if (idField in item && item[idField] == id) {
  141. return item;
  142. }
  143. }
  144. return null;
  145. }
  146. public function getItemIndex(item:Object):int
  147. {
  148. return adapter.indexOf(item);
  149. }
  150. public function getItems(index:int=0, length:int = 0x7FFFFFFF):*
  151. {
  152. if (index < 0) {
  153. index = Math.max(adapter.length + index, 0);
  154. }
  155. length = Math.max(length, 0);
  156. return adapter.slice(index, length+index);
  157. }
  158. public function removeItem(item:Object):Object
  159. {
  160. return removeItemAt(adapter.indexOf(item));
  161. }
  162. public function removeItemAt(index:int):Object
  163. {
  164. var oldValue:int = adapter.length;
  165. if (index < 0) {
  166. index = Math.max(adapter.length + index, 0);
  167. }
  168. var items:* = adapter.splice(index, 1);
  169. // empty list
  170. if (items[0] !== undefined) {
  171. propertyChange("length", oldValue, adapter.length);
  172. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.REMOVE, items, index) );
  173. }
  174. return items[0];
  175. }
  176. public function removeItems(index:int=0, length:int = 0x7FFFFFFF):*
  177. {
  178. var oldValue:int = adapter.length;
  179. if (index < 0) {
  180. index = Math.max(adapter.length + index, 0);
  181. }
  182. var items:* = adapter.splice(index, length);
  183. // empty list
  184. if (items[0] !== undefined) {
  185. propertyChange("length", oldValue, adapter.length);
  186. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.REMOVE, items, index) );
  187. }
  188. return items;
  189. }
  190. public function setItemAt(item:Object, index:int):Object
  191. {
  192. if (index < 0) {
  193. index = Math.max(adapter.length + index, 0);
  194. }
  195. adapter.splice(index, 0, item);
  196. var items:* = adapter.slice(index, index + 2);
  197. adapter.splice(index + 1, 1);
  198. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.REPLACE, items, index) );
  199. return item;
  200. }
  201. public function setItemIndex(item:Object, index:int):Object
  202. {
  203. var oldIndex:int = adapter.indexOf(item);
  204. if (oldIndex == -1) {
  205. return addItemAt(item, index);
  206. } else if (index < 0) {
  207. index = Math.max(adapter.length + index, 0);
  208. }
  209. var items:* = adapter.splice(oldIndex, 1);
  210. adapter.splice(index, 0, item);
  211. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.MOVE, items, index, oldIndex) );
  212. return item;
  213. }
  214. public function swapItems(item1:Object, item2:Object):void
  215. {
  216. var index1:int = adapter.indexOf(item1);
  217. var index2:int = adapter.indexOf(item2);
  218. swapItemsAt(index1, index2);
  219. }
  220. public function swapItemsAt(index1:int, index2:int):void
  221. {
  222. if (index1 > index2) {
  223. var temp:int = index1;
  224. index1 = index2;
  225. index2 = temp;
  226. }
  227. var item1:Object = _source[index1];
  228. var item2:Object = _source[index2];
  229. var items:* = adapter.splice(index2, 1);
  230. if (items is XMLList) {
  231. items += adapter.splice(index1, 1, item2);
  232. } else {
  233. items.push( adapter.splice(index1, 1, item2) );
  234. }
  235. adapter.splice(index2, 0, item1);
  236. dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.MOVE, items, index1, index2) );
  237. }
  238. public function equals(value:Object):Boolean
  239. {
  240. if ("source" in value) {
  241. value = value["source"];
  242. }
  243. for (var i:int = 0; i < adapter.length; i++) {
  244. if (_source[i] != value[i]) {
  245. return false;
  246. }
  247. }
  248. return true;
  249. }
  250. public function clone():Object
  251. {
  252. return new ArrayList( adapter.concat() );
  253. }
  254. }
  255. }
  256. import flight.events.Dispatcher;
  257. import flight.list.ArrayList;
  258. import flight.list.IList;
  259. import flight.list.ListSelection;
  260. namespace list_internal;
  261. class XMLListAdapter
  262. {
  263. use namespace list_internal;
  264. public var source:XMLList;
  265. public var list:ArrayList;
  266. public function get length():uint
  267. {
  268. return source.length();
  269. }
  270. public function XMLListAdapter(list:ArrayList)
  271. {
  272. this.list = list;
  273. source = list.source;
  274. }
  275. public function indexOf(searchElement:*, fromIndex:int = 0):int
  276. {
  277. for (var i:int = 0; i < source.length(); i++) {
  278. if (source[i] == searchElement) {
  279. return i;
  280. }
  281. }
  282. return -1;
  283. }
  284. public function concat(... args):XMLList
  285. {
  286. var items:XMLList = source.copy();
  287. for each (var xml:Object in args) {
  288. items += xml;
  289. }
  290. return items;
  291. }
  292. public function push(... args):uint
  293. {
  294. for each (var node:XML in args) {
  295. source += node;
  296. }
  297. list._source = source;
  298. return source.length();
  299. }
  300. public function slice(startIndex:int = 0, endIndex:int = 0x7FFFFFFF):XMLList
  301. {
  302. if (startIndex < 0) {
  303. startIndex = Math.max(source.length() + startIndex, 0);
  304. }
  305. if (endIndex < 0) {
  306. endIndex = Math.max(source.length() + endIndex, 0);
  307. }
  308. // remove trailing items
  309. var items:XMLList = source.copy();
  310. while (endIndex < items.length()) {
  311. delete items[endIndex];
  312. }
  313. // now remove from the front
  314. endIndex = items.length() - startIndex;
  315. while (endIndex < items.length()) {
  316. delete items[0];
  317. }
  318. return items;
  319. }
  320. public function splice(startIndex:int, deleteCount:uint, ... values):XMLList
  321. {
  322. startIndex = Math.min(startIndex, source.length());
  323. if (startIndex < 0) {
  324. startIndex = Math.max(source.length() + startIndex, 0);
  325. }
  326. // remove deleted items
  327. var deletedItems:XMLList = new XMLList();
  328. for (var i:int = 0; i < deleteCount; i++) {
  329. deletedItems += source[startIndex];
  330. delete source[startIndex];
  331. }
  332. // build values to insert
  333. var insertedItems:XMLList = new XMLList();
  334. for each (var item:Object in values) {
  335. insertedItems += item;
  336. }
  337. source[startIndex] = (startIndex < source.length()) ?
  338. insertedItems + source[startIndex] :
  339. insertedItems;
  340. list._source = source;
  341. return deletedItems;
  342. }
  343. }