<?php

require_once 'common/collection/Collection.php';

/**
 * Class that encapsulates an ArrayList array.
 * 
 * This class provides an ArrayList implementation. Also known as Vector, or 
 * List. Note that this class can have non contiguous indexes, since you can, 
 * for example, remove elements. So, if you depend on it, you should use 
 * renum(), or extend this class with a new one that does an auto renum.
 * <br />
 * <em>Note:</em> NULL support for params is limited, because it doesn't work 
 * with some methods. As an example: get(int) will return NULL if it's not 
 * present, or if it's null.
 * <br />
 * <strong>Usage example:</strong>
 * <code>
 * // create an ArrayList and populate it
 * $beatles = new ArrayList();
 * $beatles->add('John Lennon');
 * $beatles->add('Paul McCartney');
 * $beatles->add('Ringo Starr');
 * $beatles->add('George Harrison');
 * // traverse all data
 * $it = $beatles->getIterator();
 * echo '<ul>';
 * while ($it->hasMore()) {
 *     echo '<li>' . $it->next() . '</li>';
 * }
 * echo '</ul>';
 * </code>
 *
 * @implements ICollection, IIterable
 * @author  $Author: pruna $
 * @package common/collection
 * @version $Revision: 1.1 $
 * @created 17-Dic-2003
 * @modified $Date: 2005/12/06 02:56:45 $
 */
class ArrayList extends Collection {
    
    /**
     * Appends the specified element to the end of this list. If it's used 
     * with two params, it's an alias for set(), added for compatibility with 
     * java implementation.
     * It doesn't work as expected if you pass null as second param. In that 
     * case, you should call set directly.
     *
     * @access    public
     * @param     mixed     $element   Element to add, or index if other param
     * @param     mixed     $other     Element to add, if used.
     * @return    boolean
     */
    public function add($element, $other = null) {
        if (is_int($element) && ($other !== null)) {
            return $this->set($element, $other);
        }
        return (array_push($this->elements, $element)) ? TRUE : FALSE;
    }
    
    /**
     * Appends all of the elements in the specified ICollection to the end of
     * this list, in the order that they are returned by the specified
     * ICollection's iterator.
     *
     * @access    public
     * @param     ICollection  $list
     * @return    int
    */
    public function addAll($list) {
        $sizeBefore = $this->size();
        $it = BaseIterator::castCollection($list);
        while ($it->hasMore()) {
            $this->add($it->next());
        }
        return ($this->size() - $sizeBefore);
    }
    
    /**
     * Gets an ICollection, and cast it to another (by type or name).
     * The behavoir is the same that in <em>Collection::castCollection()</em>, 
     * but here it defaults to <em>ArrayList</em> instead of array.
     * See <em>Collection::castCollection()</em>'s @docs for more info.
     * 
     * @param   mixed   $collection     ICollection or array to cast.
     * @param   mixed   $typeToCast     ICollection or string to cast to.
     * @access  public
     * @return  mixed
     */
    public function castCollection($collection, $typeToCast = 'ArrayList') {
        return Collection::castCollection($collection, $typeToCast);
    }
    
    /**
     * Returns the element at the specified position in this list, or null, 
     * if this position doesn't exists.
     *
     * @access    public
     * @param     integer    $index
     * @return    mixed
     */
    public function get($index) {
        if (isset($this->elements[$index])) {
            return $this->elements[$index];
        }
    }
    
    /**
     * Returns a string representation of array elements, whit $glue between 
     * them. It assumes that elements can be represented as strings.
     *
     * @access    public
     * @param     string    $glue
     * @return    string
     */
    public function implode($glue = ', ') {
        return implode($glue, $this->toArray());
    }
    
    /**
     * Searchs for the first occurence of the given argument.
     *
     * @access    public
     * @param     mixed    $element
     * @return    mixed
     */
    public function indexOf($element) {
        return array_search($element, $this->elements);
    }
    
    /**
     * Removes the element at the specified position in this list.
     * Returns the previous element's value for this index.
     * 
     * @access    public
     * @param     integer    $index
     * @return    mixed
     */
    public function remove($index) {
        $element = $this->get($index);
        if (isset($this->elements[$index])) {
            array_slice($this->elements, $index, 1);
        }
        return $element;
    }
    
    /**
     * Removes the specified element.
     * Returns the removed element's index.
     * 
     * @access    public
     * @param     mixed    $element
     * @return    mixed
     */
    public function removeElement($element) {
        if ($this->contains($element)) {
            $index = $this->indexOf($element);
            $this->remove($index);
            return $index;
        }
    }
    
    /**
     * Removes from this List all of the elements whose index is between start,
     * inclusive, and end, exclusive.
     * 
     * @access    public
     * @param     integer    $start
     * @param     integer    $end
     * @return    array
     */
    public function removeRange($start, $end) {
        return array_slice($this->elements, $start, $end);
    }
    
    /**
     * Renumerate the list, making index contiguous.
     * As this class doesn't respect index numbers when you, for example, 
     * remove an element, this method is provided for those who need 
     * continuos index numbers.
     * 
     * @access    public
     */
    public function renum() {
        $this->elements = array_values($this->elements);
    }
    
    /**
     * Replaces the element at the specified position in this list with the 
     * specified element.
     *
     * @access    public
     * @param     integer    $index
     * @param     mixed    $element
     * @return    mixed
     */
    public function set($index, $element) {
        $previous = $this->get($index);
        $this->elements[$index] = $element;
        return $previous;
    }
    
    /**
     * Returns a XML string representation of this object.
     * Implementation is based on ArrayList's content.
     * 
     * @access    public
     * @return    string
     */
    public function toXMLString() {
        $sp = "\t";
        return '<class name="' . ObjectInspector::getClass($this) 
            . '" type="ArrayList">' . PHP_EOL . ($this->size()?$sp 
            . '<element>' . @$this->implode('</element>' . PHP_EOL . $sp 
            . '<element>') . '</element>' . PHP_EOL:'') . '</class>' . PHP_EOL;
    }
    
}

?>