Qiang Xue
14 years ago
5 changed files with 609 additions and 132 deletions
@ -0,0 +1,25 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception represents a generic exception for all purposes. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @version $Id: CException.php 2799 2011-01-01 19:31:13Z qiang.xue $ |
||||||
|
* @package system.base |
||||||
|
* @since 1.0 |
||||||
|
*/ |
||||||
|
class Exception extends \Exception |
||||||
|
{ |
||||||
|
} |
||||||
|
|
@ -0,0 +1,345 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Vector class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* Vector implements an integer-indexed collection class. |
||||||
|
* |
||||||
|
* You can access, append, insert, remove an item from the vector |
||||||
|
* by calling methods such as [[itemAt]], [[add]], [[insertAt]], |
||||||
|
* [[remove]] and [[removeAt]]. |
||||||
|
* |
||||||
|
* To get the number of the items in the vector, use [[getCount]]. |
||||||
|
* |
||||||
|
* Because Vector implements a set of SPL interfaces, it can be used |
||||||
|
* like a regular PHP array as follows, |
||||||
|
* |
||||||
|
* ~~~php |
||||||
|
* $vector[] = $item; // append new item at the end |
||||||
|
* $vector[$index] = $item; // set new item at $index |
||||||
|
* unset($vector[$index]); // remove the item at $index |
||||||
|
* if (isset($vector[$index])) // if the vector has an item at $index |
||||||
|
* foreach ($vector as $index=>$item) // traverse each item in the vector |
||||||
|
* $n = count($vector); // count the number of items |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* Note that if you plan to extend Vector by performing additional operations |
||||||
|
* with each addition or removal of an item (e.g. performing type check), |
||||||
|
* please make sure you override [[insertAt]] and [[removeAt]]. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Vector extends Component implements \IteratorAggregate, \ArrayAccess, \Countable |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var boolean whether this vector is read-only or not. |
||||||
|
* If the vector is read-only, adding or moving items will throw an exception. |
||||||
|
*/ |
||||||
|
public $readOnly; |
||||||
|
/** |
||||||
|
* @var array internal data storage |
||||||
|
*/ |
||||||
|
private $_d = array(); |
||||||
|
/** |
||||||
|
* @var integer number of items |
||||||
|
*/ |
||||||
|
private $_c = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* Initializes the vector with an array or an iterable object. |
||||||
|
* @param mixed $data the initial data to be populated into the vector. |
||||||
|
* This can be an array or an iterable object. If null, the vector will be set as empty. |
||||||
|
* @param boolean $readOnly whether the vector should be marked as read-only. |
||||||
|
* @throws Exception if data is not well formed (neither an array nor an iterable object) |
||||||
|
*/ |
||||||
|
public function __construct($data = array(), $readOnly = false) |
||||||
|
{ |
||||||
|
if ($data !== array()) { |
||||||
|
$this->copyFrom($data); |
||||||
|
} |
||||||
|
$this->readOnly = $readOnly; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an iterator for traversing the items in the vector. |
||||||
|
* This method is required by the SPL interface `IteratorAggregate`. |
||||||
|
* It will be implicitly called when you use `foreach` to traverse the vector. |
||||||
|
* @return Iterator an iterator for traversing the items in the vector. |
||||||
|
*/ |
||||||
|
public function getIterator() |
||||||
|
{ |
||||||
|
return new VectorIterator($this->_d); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of items in the vector. |
||||||
|
* This method is required by the SPL `Countable` interface. |
||||||
|
* It will be implicitly called when you use `count($vector)`. |
||||||
|
* @return integer number of items in the vector. |
||||||
|
*/ |
||||||
|
public function count() |
||||||
|
{ |
||||||
|
return $this->getCount(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of items in the vector. |
||||||
|
* @return integer the number of items in the vector |
||||||
|
*/ |
||||||
|
public function getCount() |
||||||
|
{ |
||||||
|
return $this->_c; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the item at the specified index. |
||||||
|
* @param integer $index the index of the item |
||||||
|
* @return mixed the item at the index |
||||||
|
* @throws Exception if the index is out of range |
||||||
|
*/ |
||||||
|
public function itemAt($index) |
||||||
|
{ |
||||||
|
if (isset($this->_d[$index])) { |
||||||
|
return $this->_d[$index]; |
||||||
|
} |
||||||
|
elseif ($index >= 0 && $index < $this->_c) { // in case the value is null |
||||||
|
return $this->_d[$index]; |
||||||
|
} |
||||||
|
|
||||||
|
throw new Exception('Index out of range: ' . $index); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Appends an item at the end of the vector. |
||||||
|
* @param mixed $item new item |
||||||
|
* @return integer the zero-based index at which the item is added |
||||||
|
* @throws Exception if the vector is read-only. |
||||||
|
*/ |
||||||
|
public function add($item) |
||||||
|
{ |
||||||
|
$this->insertAt($this->_c, $item); |
||||||
|
return $this->_c-1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Inserts an item at the specified position. |
||||||
|
* Original item at the position and the following items will be moved |
||||||
|
* one step towards the end. |
||||||
|
* @param integer $index the specified position. |
||||||
|
* @param mixed $item new item to be inserted into the vector |
||||||
|
* @throws Exception if the index specified is out of range, or the vector is read-only. |
||||||
|
*/ |
||||||
|
public function insertAt($index, $item) |
||||||
|
{ |
||||||
|
if (!$this->readOnly) { |
||||||
|
if ($index === $this->_c) { |
||||||
|
$this->_d[$this->_c++] = $item; |
||||||
|
} |
||||||
|
elseif ($index >= 0 && $index < $this->_c) { |
||||||
|
array_splice($this->_d, $index, 0, array($item)); |
||||||
|
$this->_c++; |
||||||
|
} |
||||||
|
throw new Exception('Index out of range: ' . $index); |
||||||
|
} |
||||||
|
throw new Exception('Vector is read only.'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes an item from the vector. |
||||||
|
* The vector will search for the item, and the first item found |
||||||
|
* will be removed from the vector. |
||||||
|
* @param mixed $item the item to be removed. |
||||||
|
* @return mixed the index at which the item is being removed, or false |
||||||
|
* if the item cannot be found in the vector. |
||||||
|
* @throws Exception if the vector is read only. |
||||||
|
*/ |
||||||
|
public function remove($item) |
||||||
|
{ |
||||||
|
if (($index = $this->indexOf($item)) >= 0) |
||||||
|
{ |
||||||
|
$this->removeAt($index); |
||||||
|
return $index; |
||||||
|
} |
||||||
|
else |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes an item at the specified position. |
||||||
|
* @param integer $index the index of the item to be removed. |
||||||
|
* @return mixed the removed item. |
||||||
|
* @throws Exception if the index is out of range, or the vector is read only. |
||||||
|
*/ |
||||||
|
public function removeAt($index) |
||||||
|
{ |
||||||
|
if (!$this->readOnly) { |
||||||
|
if ($index >= 0 && $index < $this->_c) { |
||||||
|
$this->_c--; |
||||||
|
if ($index === $this->_c) { |
||||||
|
return array_pop($this->_d); |
||||||
|
} |
||||||
|
else { |
||||||
|
$item = $this->_d[$index]; |
||||||
|
array_splice($this->_d, $index, 1); |
||||||
|
return $item; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new Exception('Index out of range: ' . $index); |
||||||
|
} |
||||||
|
throw new Exception('Vector is read only.'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all items from the vector. |
||||||
|
*/ |
||||||
|
public function clear() |
||||||
|
{ |
||||||
|
for ($i = $this->_c-1;$i >= 0;--$i) { |
||||||
|
$this->removeAt($i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a value indicating whether the vector contains the specified item. |
||||||
|
* Note that the search is based on strict PHP comparison. |
||||||
|
* @param mixed $item the item |
||||||
|
* @return boolean whether the vector contains the item |
||||||
|
*/ |
||||||
|
public function contains($item) |
||||||
|
{ |
||||||
|
return $this->indexOf($item) >= 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the index of the specified item in the vector. |
||||||
|
* The index is zero-based. If the item is not found in the vector, -1 will be returned. |
||||||
|
* Note that the search is based on strict PHP comparison. |
||||||
|
* @param mixed $item the item |
||||||
|
* @return integer the index of the item in the vector (0 based), -1 if not found. |
||||||
|
*/ |
||||||
|
public function indexOf($item) |
||||||
|
{ |
||||||
|
$index = array_search($item, $this->_d, true); |
||||||
|
return $index === false ? -1 : $index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the vector as a PHP array. |
||||||
|
* @return array the items in the vector. |
||||||
|
*/ |
||||||
|
public function toArray() |
||||||
|
{ |
||||||
|
return $this->_d; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Copies iterable data into the vector. |
||||||
|
* Note, existing data in the vector will be cleared first. |
||||||
|
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable` |
||||||
|
* @throws Exception if data is neither an array nor an object implementing `Traversable`. |
||||||
|
*/ |
||||||
|
public function copyFrom($data) |
||||||
|
{ |
||||||
|
if (is_array($data) || ($data instanceof Traversable)) { |
||||||
|
if ($this->_c > 0) { |
||||||
|
$this->clear(); |
||||||
|
} |
||||||
|
if ($data instanceof self) { |
||||||
|
$data = $data->_d; |
||||||
|
} |
||||||
|
foreach ($data as $item) { |
||||||
|
$this->add($item); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new Exception('Data must be either an array or an object implementing Traversable.'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Merges iterable data into the vector. |
||||||
|
* New items will be appended to the end of the existing items. |
||||||
|
* @param mixed $data the data to be merged with, must be an array or an object implementing `Traversable` |
||||||
|
* @throws Exception if data is neither an array nor an object implementing `Traversable`. |
||||||
|
*/ |
||||||
|
public function mergeWith($data) |
||||||
|
{ |
||||||
|
if (is_array($data) || ($data instanceof Traversable)) { |
||||||
|
if ($data instanceof Vector) { |
||||||
|
$data = $data->_d; |
||||||
|
} |
||||||
|
foreach ($data as $item) { |
||||||
|
$this->add($item); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new Exception('Data must be either an array or an object implementing Traversable.'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a value indicating whether there is an item at the specified offset. |
||||||
|
* This method is required by the SPL interface `ArrayAccess`. |
||||||
|
* It is implicitly called when you use something like `isset($vector[$index])`. |
||||||
|
* @param integer $offset the offset to be checked |
||||||
|
* @return boolean whether there is an item at the specified offset. |
||||||
|
*/ |
||||||
|
public function offsetExists($offset) |
||||||
|
{ |
||||||
|
return $offset >= 0 && $offset < $this->_c; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the item at the specified offset. |
||||||
|
* This method is required by the SPL interface `ArrayAccess`. |
||||||
|
* It is implicitly called when you use something like `$value = $vector[$index];`. |
||||||
|
* @param integer $offset the offset to retrieve item. |
||||||
|
* @return mixed the item at the offset |
||||||
|
* @throws Exception if the offset is out of range |
||||||
|
*/ |
||||||
|
public function offsetGet($offset) |
||||||
|
{ |
||||||
|
return $this->itemAt($offset); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the item at the specified offset. |
||||||
|
* This method is required by the SPL interface `ArrayAccess`. |
||||||
|
* It is implicitly called when you use something like `$vector[$index] = $value;`. |
||||||
|
* If the offset is null or equal to the number of the existing items, |
||||||
|
* the new item will be appended to the vector. |
||||||
|
* Otherwise, the existing item at the offset will be replaced with the new item. |
||||||
|
* @param integer $offset the offset to set item |
||||||
|
* @param mixed $item the item value |
||||||
|
* @throws Exception if the offset is out of range, or the vector is read only. |
||||||
|
*/ |
||||||
|
public function offsetSet($offset, $item) |
||||||
|
{ |
||||||
|
if ($offset === null || $offset === $this->_c) { |
||||||
|
$this->insertAt($this->_c, $item); |
||||||
|
} |
||||||
|
else { |
||||||
|
$this->removeAt($offset); |
||||||
|
$this->insertAt($offset, $item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Unsets the item at the specified offset. |
||||||
|
* This method is required by the SPL interface `ArrayAccess`. |
||||||
|
* It is implicitly called when you use something like `unset($vector[$index])`. |
||||||
|
* This is equivalent to [[removeAt]]. |
||||||
|
* @param integer $offset the offset to unset item |
||||||
|
* @throws Exception if the offset is out of range, or the vector is read only. |
||||||
|
*/ |
||||||
|
public function offsetUnset($offset) |
||||||
|
{ |
||||||
|
$this->removeAt($offset); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* VectorIterator class file. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* VectorIterator implements the SPL `Iterator` interface for [[Vector]]. |
||||||
|
* |
||||||
|
* It allows [[Vector]] to return a new iterator for data traversing purpose. |
||||||
|
* You normally do not use this class directly. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class VectorIterator implements \Iterator |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var array the data to be iterated through |
||||||
|
*/ |
||||||
|
private $_d; |
||||||
|
/** |
||||||
|
* @var integer index of the current item |
||||||
|
*/ |
||||||
|
private $_i; |
||||||
|
/** |
||||||
|
* @var integer count of the data items |
||||||
|
*/ |
||||||
|
private $_c; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param array $data the data to be iterated through |
||||||
|
*/ |
||||||
|
public function __construct(&$data) |
||||||
|
{ |
||||||
|
$this->_d = &$data; |
||||||
|
$this->_i = 0; |
||||||
|
$this->_c = count($this->_d); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Rewinds the index of the current item. |
||||||
|
* This method is required by the SPL interface `Iterator`. |
||||||
|
*/ |
||||||
|
public function rewind() |
||||||
|
{ |
||||||
|
$this->_i = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the key of the current item. |
||||||
|
* This method is required by the SPL interface `Iterator`. |
||||||
|
* @return integer the key of the current item |
||||||
|
*/ |
||||||
|
public function key() |
||||||
|
{ |
||||||
|
return $this->_i; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current item. |
||||||
|
* This method is required by the SPL interface `Iterator`. |
||||||
|
* @return mixed the current item |
||||||
|
*/ |
||||||
|
public function current() |
||||||
|
{ |
||||||
|
return $this->_d[$this->_i]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the internal pointer to the next item. |
||||||
|
* This method is required by the SPL interface `Iterator`. |
||||||
|
*/ |
||||||
|
public function next() |
||||||
|
{ |
||||||
|
$this->_i++; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a value indicating whether there is an item at current position. |
||||||
|
* This method is required by the SPL interface `Iterator`. |
||||||
|
* @return boolean whether there is an item at current position. |
||||||
|
*/ |
||||||
|
public function valid() |
||||||
|
{ |
||||||
|
return $this->_i < $this->_c; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue