Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TList, TListIterator classes
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TList.php 2541 2008-10-21 15:05:13Z qiang.xue $
10
 * @package System.Collections
11
 */
12
 
13
/**
14
 * TList class
15
 *
16
 * TList implements an integer-indexed collection class.
17
 *
18
 * You can access, append, insert, remove an item by using
19
 * {@link itemAt}, {@link add}, {@link insert}, {@link remove}, and {@link removeAt}.
20
 * To get the number of the items in the list, use {@link getCount}.
21
 * TList can also be used like a regular array as follows,
22
 * <code>
23
 * $list[]=$item;  // append at the end
24
 * $list[$index]=$item; // $index must be between 0 and $list->Count
25
 * unset($list[$index]); // remove the item at $index
26
 * if(isset($list[$index])) // if the list has an item at $index
27
 * foreach($list as $index=>$item) // traverse each item in the list
28
 * $n=count($list); // returns the number of items in the list
29
 * </code>
30
 *
31
 * To extend TList by doing additional operations with each addition or removal
32
 * operation, override {@link insertAt()}, and {@link removeAt()}.
33
 *
34
 * @author Qiang Xue <qiang.xue@gmail.com>
35
 * @version $Id: TList.php 2541 2008-10-21 15:05:13Z qiang.xue $
36
 * @package System.Collections
37
 * @since 3.0
38
 */
39
class TList extends TComponent implements IteratorAggregate,ArrayAccess,Countable
40
{
41
	/**
42
	 * internal data storage
43
	 * @var array
44
	 */
45
	private $_d=array();
46
	/**
47
	 * number of items
48
	 * @var integer
49
	 */
50
	private $_c=0;
51
	/**
52
	 * @var boolean whether this list is read-only
53
	 */
54
	private $_r=false;
55
 
56
	/**
57
	 * Constructor.
58
	 * Initializes the list with an array or an iterable object.
59
	 * @param array|Iterator the initial data. Default is null, meaning no initialization.
60
	 * @param boolean whether the list is read-only
61
	 * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator.
62
	 */
63
	public function __construct($data=null,$readOnly=false)
64
	{
65
		if($data!==null)
66
			$this->copyFrom($data);
67
		$this->setReadOnly($readOnly);
68
	}
69
 
70
	/**
71
	 * @return boolean whether this list is read-only or not. Defaults to false.
72
	 */
73
	public function getReadOnly()
74
	{
75
		return $this->_r;
76
	}
77
 
78
	/**
79
	 * @param boolean whether this list is read-only or not
80
	 */
81
	protected function setReadOnly($value)
82
	{
83
		$this->_r=TPropertyValue::ensureBoolean($value);
84
	}
85
 
86
	/**
87
	 * Returns an iterator for traversing the items in the list.
88
	 * This method is required by the interface IteratorAggregate.
89
	 * @return Iterator an iterator for traversing the items in the list.
90
	 */
91
	public function getIterator()
92
	{
93
		return new TListIterator($this->_d);
94
	}
95
 
96
	/**
97
	 * Returns the number of items in the list.
98
	 * This method is required by Countable interface.
99
	 * @return integer number of items in the list.
100
	 */
101
	public function count()
102
	{
103
		return $this->getCount();
104
	}
105
 
106
	/**
107
	 * @return integer the number of items in the list
108
	 */
109
	public function getCount()
110
	{
111
		return $this->_c;
112
	}
113
 
114
	/**
115
	 * Returns the item at the specified offset.
116
	 * This method is exactly the same as {@link offsetGet}.
117
	 * @param integer the index of the item
118
	 * @return mixed the item at the index
119
	 * @throws TInvalidDataValueException if the index is out of the range
120
	 */
121
	public function itemAt($index)
122
	{
123
		if($index>=0 && $index<$this->_c)
124
			return $this->_d[$index];
125
		else
126
			throw new TInvalidDataValueException('list_index_invalid',$index);
127
	}
128
 
129
	/**
130
	 * Appends an item at the end of the list.
131
	 * @param mixed new item
132
	 * @return integer the zero-based index at which the item is added
133
	 */
134
	public function add($item)
135
	{
136
		$this->insertAt($this->_c,$item);
137
		return $this->_c-1;
138
	}
139
 
140
	/**
141
	 * Inserts an item at the specified position.
142
	 * Original item at the position and the next items
143
	 * will be moved one step towards the end.
144
	 * @param integer the specified position.
145
	 * @param mixed new item
146
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
147
	 * @throws TInvalidOperationException if the list is read-only
148
	 */
149
	public function insertAt($index,$item)
150
	{
151
		if(!$this->_r)
152
		{
153
			if($index===$this->_c)
154
				$this->_d[$this->_c++]=$item;
155
			else if($index>=0 && $index<$this->_c)
156
			{
157
				array_splice($this->_d,$index,0,array($item));
158
				$this->_c++;
159
			}
160
			else
161
				throw new TInvalidDataValueException('list_index_invalid',$index);
162
		}
163
		else
164
			throw new TInvalidOperationException('list_readonly',get_class($this));
165
	}
166
 
167
	/**
168
	 * Removes an item from the list.
169
	 * The list will first search for the item.
170
	 * The first item found will be removed from the list.
171
	 * @param mixed the item to be removed.
172
	 * @return integer the index at which the item is being removed
173
	 * @throws TInvalidDataValueException If the item does not exist
174
	 */
175
	public function remove($item)
176
	{
177
		if(($index=$this->indexOf($item))>=0)
178
		{
179
			$this->removeAt($index);
180
			return $index;
181
		}
182
		else
183
			throw new TInvalidDataValueException('list_item_inexistent');
184
	}
185
 
186
	/**
187
	 * Removes an item at the specified position.
188
	 * @param integer the index of the item to be removed.
189
	 * @return mixed the removed item.
190
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
191
	 * @throws TInvalidOperationException if the list is read-only
192
	 */
193
	public function removeAt($index)
194
	{
195
		if(!$this->_r)
196
		{
197
			if($index>=0 && $index<$this->_c)
198
			{
199
				$this->_c--;
200
				if($index===$this->_c)
201
					return array_pop($this->_d);
202
				else
203
				{
204
					$item=$this->_d[$index];
205
					array_splice($this->_d,$index,1);
206
					return $item;
207
				}
208
			}
209
			else
210
				throw new TInvalidDataValueException('list_index_invalid',$index);
211
		}
212
		else
213
			throw new TInvalidOperationException('list_readonly',get_class($this));
214
	}
215
 
216
	/**
217
	 * Removes all items in the list.
218
	 */
219
	public function clear()
220
	{
221
		for($i=$this->_c-1;$i>=0;--$i)
222
			$this->removeAt($i);
223
	}
224
 
225
	/**
226
	 * @param mixed the item
227
	 * @return boolean whether the list contains the item
228
	 */
229
	public function contains($item)
230
	{
231
		return $this->indexOf($item)>=0;
232
	}
233
 
234
	/**
235
	 * @param mixed the item
236
	 * @return integer the index of the item in the list (0 based), -1 if not found.
237
	 */
238
	public function indexOf($item)
239
	{
240
		if(($index=array_search($item,$this->_d,true))===false)
241
			return -1;
242
		else
243
			return $index;
244
	}
245
 
246
	/**
247
	 * @return array the list of items in array
248
	 */
249
	public function toArray()
250
	{
251
		return $this->_d;
252
	}
253
 
254
	/**
255
	 * Copies iterable data into the list.
256
	 * Note, existing data in the list will be cleared first.
257
	 * @param mixed the data to be copied from, must be an array or object implementing Traversable
258
	 * @throws TInvalidDataTypeException If data is neither an array nor a Traversable.
259
	 */
260
	public function copyFrom($data)
261
	{
262
		if(is_array($data) || ($data instanceof Traversable))
263
		{
264
			if($this->_c>0)
265
				$this->clear();
266
			foreach($data as $item)
267
				$this->add($item);
268
		}
269
		else if($data!==null)
270
			throw new TInvalidDataTypeException('list_data_not_iterable');
271
	}
272
 
273
	/**
274
	 * Merges iterable data into the map.
275
	 * New data will be appended to the end of the existing data.
276
	 * @param mixed the data to be merged with, must be an array or object implementing Traversable
277
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
278
	 */
279
	public function mergeWith($data)
280
	{
281
		if(is_array($data) || ($data instanceof Traversable))
282
		{
283
			foreach($data as $item)
284
				$this->add($item);
285
		}
286
		else if($data!==null)
287
			throw new TInvalidDataTypeException('list_data_not_iterable');
288
	}
289
 
290
	/**
291
	 * Returns whether there is an item at the specified offset.
292
	 * This method is required by the interface ArrayAccess.
293
	 * @param integer the offset to check on
294
	 * @return boolean
295
	 */
296
	public function offsetExists($offset)
297
	{
298
		return ($offset>=0 && $offset<$this->_c);
299
	}
300
 
301
	/**
302
	 * Returns the item at the specified offset.
303
	 * This method is required by the interface ArrayAccess.
304
	 * @param integer the offset to retrieve item.
305
	 * @return mixed the item at the offset
306
	 * @throws TInvalidDataValueException if the offset is invalid
307
	 */
308
	public function offsetGet($offset)
309
	{
310
		return $this->itemAt($offset);
311
	}
312
 
313
	/**
314
	 * Sets the item at the specified offset.
315
	 * This method is required by the interface ArrayAccess.
316
	 * @param integer the offset to set item
317
	 * @param mixed the item value
318
	 */
319
	public function offsetSet($offset,$item)
320
	{
321
		if($offset===null || $offset===$this->_c)
322
			$this->insertAt($this->_c,$item);
323
		else
324
		{
325
			$this->removeAt($offset);
326
			$this->insertAt($offset,$item);
327
		}
328
	}
329
 
330
	/**
331
	 * Unsets the item at the specified offset.
332
	 * This method is required by the interface ArrayAccess.
333
	 * @param integer the offset to unset item
334
	 */
335
	public function offsetUnset($offset)
336
	{
337
		$this->removeAt($offset);
338
	}
339
}
340
 
341
 
342
/**
343
 * TListIterator class
344
 *
345
 * TListIterator implements Iterator interface.
346
 *
347
 * TListIterator is used by TList. It allows TList to return a new iterator
348
 * for traversing the items in the list.
349
 *
350
 * @author Qiang Xue <qiang.xue@gmail.com>
351
 * @version $Id: TList.php 2541 2008-10-21 15:05:13Z qiang.xue $
352
 * @package System.Collections
353
 * @since 3.0
354
 */
355
class TListIterator implements Iterator
356
{
357
	/**
358
	 * @var array the data to be iterated through
359
	 */
360
	private $_d;
361
	/**
362
	 * @var integer index of the current item
363
	 */
364
	private $_i;
365
	/**
366
	 * @var integer count of the data items
367
	 */
368
	private $_c;
369
 
370
	/**
371
	 * Constructor.
372
	 * @param array the data to be iterated through
373
	 */
374
	public function __construct(&$data)
375
	{
376
		$this->_d=&$data;
377
		$this->_i=0;
378
		$this->_c=count($this->_d);
379
	}
380
 
381
	/**
382
	 * Rewinds internal array pointer.
383
	 * This method is required by the interface Iterator.
384
	 */
385
	public function rewind()
386
	{
387
		$this->_i=0;
388
	}
389
 
390
	/**
391
	 * Returns the key of the current array item.
392
	 * This method is required by the interface Iterator.
393
	 * @return integer the key of the current array item
394
	 */
395
	public function key()
396
	{
397
		return $this->_i;
398
	}
399
 
400
	/**
401
	 * Returns the current array item.
402
	 * This method is required by the interface Iterator.
403
	 * @return mixed the current array item
404
	 */
405
	public function current()
406
	{
407
		return $this->_d[$this->_i];
408
	}
409
 
410
	/**
411
	 * Moves the internal pointer to the next array item.
412
	 * This method is required by the interface Iterator.
413
	 */
414
	public function next()
415
	{
416
		$this->_i++;
417
	}
418
 
419
	/**
420
	 * Returns whether there is an item at current position.
421
	 * This method is required by the interface Iterator.
422
	 * @return boolean
423
	 */
424
	public function valid()
425
	{
426
		return $this->_i<$this->_c;
427
	}
428
}
429