PageRenderTime 62ms CodeModel.GetById 12ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 25package flight.list
 26{
 27	import flight.events.Dispatcher;
 28	import flight.events.ListEvent;
 29	import flight.events.ListEventKind;
 30	import flight.vo.IValueObject;
 31	
 32	use namespace list_internal;
 33	
 34	[Event(name="listChange", type="flight.events.ListEvent")]
 35	
 36	public class ArrayList extends Dispatcher implements IList, IValueObject
 37	{
 38		public var idField:String = "id";	// TODO: replace with dataMap
 39		
 40		list_internal var _source:*;	// internally available to XMLListAdapter
 41		
 42		private var adapter:*;
 43		private var _selection:ListSelection;
 44		private var _mxlist:MXList;
 45		
 46		public function ArrayList(source:* = null)
 47		{
 48			this.source = source;
 49		}
 50		
 51		[Bindable(event="lengthChange")]
 52		public function get length():int
 53		{
 54			return adapter.length;
 55		}
 56		
 57		[Bindable(event="mxlist")]
 58		public function get mxlist():MXList
 59		{
 60			if (_mxlist == null) {
 61				_mxlist = new MXList(this);
 62			}
 63			return _mxlist;
 64		}
 65		
 66		[Bindable(event="selectionChange")]
 67		public function get selection():ListSelection
 68		{
 69			if (_selection == null) {
 70				_selection = new ListSelection(this);
 71			}
 72			return _selection;
 73		}
 74		
 75		[Bindable(event="sourceChange")]
 76		public function get source():*
 77		{
 78			return _source;
 79		}
 80		public function set source(value:*):void
 81		{
 82			if (value == null) {
 83				value = [];
 84			} else if (_source == value) {
 85				return;
 86			}
 87			
 88			var oldValue:Object = _source;
 89			
 90			if (value is XMLList) {
 91				_source = value;
 92				adapter = new XMLListAdapter(this);
 93			} else {
 94				_source = ("splice" in value) ? value : [value];
 95				adapter = _source;
 96			}
 97			
 98			propertyChange("source", oldValue, _source);
 99			propertyChange("length", oldValue, adapter.length);
100			dispatchEvent(new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.RESET));
101		}
102		
103		public function addItem(item:Object):Object
104		{
105			var oldValue:int = adapter.length;
106			adapter.push(item);
107			
108			propertyChange("length", oldValue, adapter.length);
109			dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.ADD,
110										 adapter.slice(oldValue, oldValue+1), oldValue) );
111			return item;
112		}
113		
114		public function addItemAt(item:Object, index:int):Object
115		{
116			var oldValue:int = adapter.length;
117			if (index < 0) {
118				index = Math.max(adapter.length + index, 0);
119			}
120			adapter.splice(index, 0, item);
121			
122			propertyChange("length", oldValue, adapter.length);
123			dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.ADD,
124										 adapter.slice(index, index+1), index) );
125			return item;
126		}
127		
128		public function addItems(items:*, index:int = 0x7FFFFFFF):*
129		{
130			// empty list
131			if (items[0] === undefined) {
132				return items;
133			}
134			
135			var oldValue:int = adapter.length;
136			if (index < 0) {
137				index = Math.max(adapter.length + index, 0);
138			} else if (index > oldValue) {
139				index = oldValue;
140			}
141			adapter.splice.apply(adapter, [index, 0].concat(items));
142			
143			propertyChange("length", oldValue, adapter.length);
144			dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.ADD, items, index) );
145			return items;
146		}
147		
148		public function containsItem(item:Object):Boolean
149		{
150			return Boolean(adapter.indexOf(item) != -1);
151		}
152		
153		public function getItemAt(index:int):Object
154		{
155			if (index < 0) {
156				index = Math.max(adapter.length + index, 0);
157			}
158			return _source[index];
159		}
160		
161		public function getItemById(id:String):Object
162		{
163			for each (var item:Object in _source) {
164				if (idField in item && item[idField] == id) {
165					return item;
166				}
167			}
168			return null;
169		}
170		
171		public function getItemIndex(item:Object):int
172		{
173			return adapter.indexOf(item);
174		}
175		
176		public function getItems(index:int=0, length:int = 0x7FFFFFFF):*
177		{
178			if (index < 0) {
179				index = Math.max(adapter.length + index, 0);
180			}
181			length = Math.max(length, 0);
182			return adapter.slice(index, length+index);
183		}
184		
185		public function removeItem(item:Object):Object
186		{
187			return removeItemAt(adapter.indexOf(item));
188		}
189		
190		public function removeItemAt(index:int):Object
191		{
192			var oldValue:int = adapter.length;
193			if (index < 0) {
194				index = Math.max(adapter.length + index, 0);
195			}
196			var items:* = adapter.splice(index, 1);
197			// empty list
198			if (items[0] !== undefined) {
199				propertyChange("length", oldValue, adapter.length);
200				dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.REMOVE, items, index) );
201			}
202			return items[0];
203		}
204		
205		public function removeItems(index:int=0, length:int = 0x7FFFFFFF):*
206		{
207			var oldValue:int = adapter.length;
208			if (index < 0) {
209				index = Math.max(adapter.length + index, 0);
210			}
211			var items:* = adapter.splice(index, length);
212			// empty list
213			if (items[0] !== undefined) {
214				propertyChange("length", oldValue, adapter.length);
215				dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.REMOVE, items, index) );
216			}
217			return items;
218		}
219		
220		public function setItemAt(item:Object, index:int):Object
221		{
222			if (index < 0) {
223				index = Math.max(adapter.length + index, 0);
224			}
225			adapter.splice(index, 0, item);
226			var items:* = adapter.slice(index, index + 2);
227			adapter.splice(index + 1, 1);
228			
229			dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.REPLACE, items, index) );
230			return item;
231		}
232		
233		public function setItemIndex(item:Object, index:int):Object
234		{
235			var oldIndex:int = adapter.indexOf(item);
236			if (oldIndex == -1) {
237				return addItemAt(item, index);
238			} else if (index < 0) {
239				index = Math.max(adapter.length + index, 0);
240			}
241			
242			var items:* = adapter.splice(oldIndex, 1);
243			adapter.splice(index, 0, item);
244			dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.MOVE, items, index, oldIndex) );
245			return item;
246		}
247		
248		public function swapItems(item1:Object, item2:Object):void
249		{
250			var index1:int = adapter.indexOf(item1);
251			var index2:int = adapter.indexOf(item2);
252			swapItemsAt(index1, index2);
253		}
254		
255		public function swapItemsAt(index1:int, index2:int):void
256		{
257			if (index1 > index2) {
258				var temp:int = index1;
259				index1 = index2;
260				index2 = temp;
261			}
262			
263			var item1:Object = _source[index1];
264			var item2:Object = _source[index2];
265			
266			var items:* = adapter.splice(index2, 1);
267			if (items is XMLList) {
268				items += adapter.splice(index1, 1, item2);
269			} else {
270				items.push( adapter.splice(index1, 1, item2) );
271			}
272			adapter.splice(index2, 0, item1);
273			dispatchEvent( new ListEvent(ListEvent.LIST_CHANGE, ListEventKind.MOVE, items, index1, index2) );
274		}
275		
276		public function equals(value:Object):Boolean
277		{
278			if ("source" in value) {
279				value = value["source"];
280			}
281			
282			for (var i:int = 0; i < adapter.length; i++) {
283				if (_source[i] != value[i]) {
284					return false;
285				}
286			}
287			return true;
288		}
289		
290		public function clone():Object
291		{
292			return new ArrayList( adapter.concat() );
293		}
294		
295	}
296}
297
298import flight.events.Dispatcher;
299import flight.list.ArrayList;
300import flight.list.IList;
301import flight.list.ListSelection;
302
303namespace list_internal;
304
305class XMLListAdapter
306{
307	use namespace list_internal;
308	
309	public var source:XMLList;
310	public var list:ArrayList;
311	
312	public function get length():uint
313	{
314		return source.length();
315	}
316	
317	public function XMLListAdapter(list:ArrayList)
318	{
319		this.list = list;
320		source = list.source;
321	}
322	
323	public function indexOf(searchElement:*, fromIndex:int = 0):int
324	{
325		for (var i:int = 0; i < source.length(); i++) {
326			if (source[i] == searchElement) {
327				return i;
328			}
329		}
330		return -1;
331	}
332	
333	public function concat(... args):XMLList
334	{
335		var items:XMLList = source.copy();
336		for each (var xml:Object in args) {
337			items += xml;
338		}
339		return items;
340	}
341	
342	public function push(... args):uint
343	{
344		for each (var node:XML in args) {
345			source += node;
346		}
347		list._source = source;
348		return source.length();
349	}
350	
351	public function slice(startIndex:int = 0, endIndex:int = 0x7FFFFFFF):XMLList
352	{
353		if (startIndex < 0) {
354			startIndex = Math.max(source.length() + startIndex, 0);
355		}
356		if (endIndex < 0) {
357			endIndex = Math.max(source.length() + endIndex, 0);
358		}
359		
360		// remove trailing items
361		var items:XMLList = source.copy();
362		while (endIndex < items.length()) {
363			delete items[endIndex];
364		}
365		
366		// now remove from the front
367		endIndex = items.length() - startIndex;
368		while (endIndex < items.length()) {
369			delete items[0];
370		}
371		
372		return items;
373	}
374		
375	public function splice(startIndex:int, deleteCount:uint, ... values):XMLList
376	{
377		startIndex = Math.min(startIndex, source.length());
378		if (startIndex < 0) {
379			startIndex = Math.max(source.length() + startIndex, 0);
380		}
381		
382		// remove deleted items
383		var deletedItems:XMLList = new XMLList();
384		for (var i:int = 0; i < deleteCount; i++) {
385			deletedItems += source[startIndex];
386			delete source[startIndex];
387		}
388		
389		// build values to insert
390		var insertedItems:XMLList = new XMLList();
391		for each (var item:Object in values) {
392			insertedItems += item;
393		}
394		source[startIndex] = (startIndex < source.length()) ?
395							 insertedItems + source[startIndex] :
396							 insertedItems;
397		
398		list._source = source;
399		return deletedItems;
400	}
401	
402}