root/trunk/framework/Collections/TList.php

Revision 2482, 10.6 kB (checked in by knut, 4 months ago)

updated copyright

  • Property svn:keywords set to Id
Line 
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$
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$
36 * @package System.Collections
37 * @since 3.0
38 */
39class 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$
352 * @package System.Collections
353 * @since 3.0
354 */
355class 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
430?>
Note: See TracBrowser for help on using the browser.