<?php

require_once 'common/collection/Collection.php';
require_once 'common/collection/BaseIterator.php';

/**
 * Class that encapsulates a HashMap array.
 * 
 * This class provides a HashMap implementation.
 * Also known as Map, Set, Dictionary.
 * <br />
 * <strong>Note:</strong> these class has a couple of duplicated methods, 
 * ypu can choose the signature you prefer: <br />
 * <ul>
 * <li>keys() / keySet()</li>
 * <li>set() / put()</li>
 * <li>setAll()/putAll()</li>
 * </ul>
 * This division is useful when you want to extend functionality, because you 
 * can re-define set() - that is the common method to use -, whitout changing 
 * the real behavoir carried out by put().
 * <br />
 * <strong>Usage example:</strong>
 * <code>
 * // create a HashMap and populate it
 * $jL = new HashMap();
 * $jL->set('name', 'John Winston Lennon');
 * $jL->set('born', 1940);
 * // get some specific data by key
 * $jL->get('name');
 * // traverse all data (note that next() must be called before key()!
 * $it = $jL->getIterator();
 * while ($it->hasMore()) {
 *     $it->next();
 *     echo $it->key . ": " . $it->current() . "<br />";
 * }
 * </code>
 * If you want to set some initial values, you have a couple of options to do 
 * it:<dl>
 * <dt>Extending this class</dt>
 * <dd>In the new constructor: <code>
 * function __construct($arr) {
 *   parent::__construct(array(...));
 *   this->setAll($arr);
 * }</code></dd>
 * <dt>When instantiating it</dt>
 * <dd><code>%hM = new HashMap(array(...));</code></dd>
 * <dt>In other method</dt>
 * <dd>And then you call it from constructor.</dd>
 * </dl>
 * It's important to set default values before custom ones, to avoid that 
 * default values replace custom ones.
 * 
 * @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 HashMap extends Collection {
    
    /**
     * 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>HashMap</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 = 'HashMap') {
        return Collection::castCollection($collection, $typeToCast);
    }
    
    /**
     * Returns true if this map contains the specified key.
     *
     * @access    public
     * @param     mixed     $key         Value to look for
     * @return    boolean
     */
    public function containsKey($key) {
        return array_key_exists($key, $this->elements);
    }
    
    /**
     * Returns the element to which the specified key is mapped in this 
     * identity hash map, or null if the map contains no mapping for this key.
     *
     * @access    public
     * @param     mixed     $key         Value to look for and return
     * @return    mixed
     */
    public function get($key) {
        if (isset($this->elements[$key])) {
            return $this->elements[$key];
        }
    }
    
    /**
     * Retrieves the key for this element from this map if present.
     *
     * @access    public
     * @param     mixed    $element
     * @return    mixed
     */
    public function key($element) {
        if ($this->contains($element)) {
            $it = $this->getIterator();
            while ($it->hasMore()) {
                if ($it->next() === $element) {
                    return $it->key();
                }
            }
        }
    }
    
    /**
     * Returns an array of the keys contained in this map.
     *
     * @access    public
     * @return    array
     */
    public function keySet() {
        return array_keys($this->elements);
    }
    
    /**
     * Returns an array of the keys contained in this map.
     * This method is an alias for <em>keySet()</em>.
     *
     * @access    public
     * @return    array
     */
    public function keys() {
        return $this->keySet();
    }
    
    /**
     * Associates the specified element with the specified key in this map.
     * If this key was used, it returns the previous element.
     *
     * @access    public
     * @param     mixed    $key
     * @param     mixed    $element
     * @return    mixed
     */
    public function put($key, $element) {
        $previous = $this->get($key);
        $this->elements[$key] = $element;
        return $previous;
    }
    
    /**
     * Associates the specified element with the specified key in this map.
     * If this key was used, it returns the previous element.
     * This method is an alias for <em>put()</em>.
     *
     * @access    public
     * @param     mixed    $key
     * @param     mixed    $element
     * @return    mixed
     */
    public function set($key, $element) {
        return $this->put($key, $element);
    }
    
    /**
     * Copies all of the mappings from the specified ICollection to this map. 
     * This mappings will replace any mappings that this map had for any of 
     * the keys currently in the specified map.
     *
     * @access    public
     * @param     ICollection    $elements
     */
    public function putAll($elements) {
        $it = BaseIterator::castCollection($elements);
        while ($it->hasMore()) {
            $it->next();
            $this->set($it->key(), $it->current());
        }
    }
    
    /**
     * Copies all of the mappings from the specified ICollection to this map. 
     * This mappings will replace any mappings that this map had for any of 
     * the keys currently in the specified map.
     * This method is an alias for <em>putAll()</em>.
     *
     * @access    public
     * @param     ICollection    $elements
     */
    public function setAll($elements) {
        $this->putAll($elements);
    }
    
    /**
     * Removes the mapping for this key from this map if present. If an 
     * item is actually removed, its value is returned.
     *
     * @access    public
     * @param     mixed    $key
     * @return    mixed
     */
    public function remove($key) {
        if ($this->containsKey($key)) {
            $element = $this->get($key);
            unset($this->elements[$key]);
            return $element;
        }
    }
    
    /**
     * Removes the mapping for this element from this map if present. If an 
     * item is actually removed, its key is returned.
     *
     * @access    public
     * @param     mixed    $element
     * @return    mixed
     */
    public function removeElement($element) {
        if ($this->contains($element)) {
            $oldKey = $this->key($element);
            $this->remove($oldKey);
            return $oldKey;
        }
    }
    
    /**
     * Returns a XML string representation of this object.
     * Implementation is based on HashMap's content.
     * 
     * @access    public
     * @return    string
     */
    public function toXMLString() {
        $sp = "\t";
        $elements = '';
        foreach ($this->toArray() as $k => $v) {
            $elements .= $sp . $sp . '<element name="' . $k . '" type="' 
                . gettype($v) . '">' . $v . '</element>' . PHP_EOL;
        }
        return '<class name="' . ObjectInspector::getClass($this) 
            . '" type="HashMap">' . PHP_EOL . ($elements?$sp . '<elements>' 
            . PHP_EOL . $elements . $sp . '</elements>' . PHP_EOL:'') 
            . '</class>' . PHP_EOL;
    }
    
}

?>
