/***************************************************************************
    Copyright          : (C) 2002 by Neoworks Limited. All rights reserved
    URL                : http://www.neoworks.com
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

package com.neoworks.util;

import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.List;
import java.util.ArrayList;
import com.neoworks.util.AbstractMultiMap;
import java.lang.Object;
import com.neoworks.util.MultiMap;
import com.neoworks.util.MultiMap.Entry;
import java.util.AbstractList;
import java.util.AbstractCollection;
import java.util.Collection;


/**
 * This class implements the <code>MultiMap</code> interface backed with a <code>Vector</code>.
 *
 * @author Stuart Farnan <a href="mailto:stuart@neoworks.com">stuart@neoworks.com</a>
 * @version 0.1
 */
public class VectorMultiMap extends AbstractMultiMap
{
	/**
	 * Default constructor, creates an empty <code>VectorMultiMap</code>.
	 */
	public VectorMultiMap ()
	{
		entries = new Vector();
	}


	/**
	 * Constructor, creates a <code>VectorMultiMap</code> with the same mapping as the given multimap.
	 *
	 * @param multimap The multimap whose mappings we want to copy.
	 */
	public VectorMultiMap (MultiMap multimap)
	{
		entries = new Vector();
		putAll(multimap);
	}


	/**
	 * Associates the specified value with the specified key in this map (optional operation). If the multimap previously
	 * contained a mapping for this key, the old value is not lost.
	 *
	 * <p>
	 * This implementation always throws an <code>UnsupportedOperationException</code>.
	 * </p>
	 *
	 * @return <code>Set</code> of values associated with the specified key.
	 * @param key Key with which the specified value is to be associated.
	 * @param value Value to be associated with the specified key. */
	public Set put (Object key, Object value)
	{
		Entry e = new Entry(key, value);
		entrySet().add(e);
		return get(key);
	}

	/**
	 * Get the values for a key as a list
	 *
	 * @param key The key
	 * @return A List of the values
	 */
	public List getValueList (Object key)
	{
		List valueList = new ArrayList();
		Iterator i = entrySet().iterator();
		if (key == null)
		{
			while (i.hasNext())
			{
				Entry e = (Entry) i.next();
				if (e.getKey() == null)
				{
					valueList.add(e.getValue());
				}
			}
		}
		else
		{
			while (i.hasNext())
			{
				Entry e = (Entry) i.next();
				if (key.equals(e.getKey()))
				{
					valueList.add(e.getValue());
				}
			}
		}

		return (valueList.size() == 0 ? null : valueList);
	}	

	/**
	 * Cache of the entry Set
	 */
	private transient Set entryView;

	/**
	 * Returns a set view of the mappings contained in this multimap. Each element in this set is a MultiMap.Entry. The set is
	 * backed by the map, so changes to the multimap are reflected in the set, and vice-versa. (If the map is modified while
	 * an iteration over the set is in progress, the results of the iteration are undefined.) The set supports element
	 * removal, which removes the corresponding entry from the map, via the <code>Iterator.remove</code>,
	 * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code> and <code>clear</code> operations. It does not
	 * support the <code>add</code> or <code>addAll</code> operations.
	 *
	 * @return A set view of the mappings contained in this multimap.
	 */
	public Set entrySet ()
	{
		if (entryView == null)
		{
			entryView = new AbstractSet()
			{
				public Iterator iterator ()
				{
					return new Iterator ()
					{
						private Iterator i = entries.iterator();

						public boolean hasNext ()
						{
							return i.hasNext();
						}

						public Object next ()
						{
							return ((Entry) i.next());
						}

						public void remove ()
						{
							i.remove();
						}
					};
				}

				public int size()
				{
					return entries.size();
				}

				public boolean contains(Object o)
				{
					return entries.contains(o);
				}

				public boolean add (Object o)
				{
					MultiMap.Entry entry = (MultiMap.Entry) o;
					// I want to allow duplicates
					/*if (contains(o))
					{
						return false;
					}*/
					return entries.add(o);
				}

				public boolean remove (Object o)
				{
					MultiMap.Entry entry = (MultiMap.Entry) o;
					if (!(contains(o)))
					{
						return false;
					}
					return entries.remove(o);
				}
			};
		}
		return entryView;
	}


	/**
	 * VectorMultiMap list entry.
	 */
	private static class Entry implements MultiMap.Entry
	{
		/**
		 * The key
		 */
		private Object key;
		
		/**
		 * The value
		 */
		private Object value;

		/**
		 * Construct a new Entry
		 * @param k the key
		 * @param v the value
		 */		
		public Entry(Object k, Object v)
		{
			key = k;
			value = v;
		}

		/**
		 * Get the key
		 * @return Return the key
		 */		
		public Object getKey()
		{
			return key;
		}

		/** 
		 * Get the value
		 * @return the value
		 */		
		public Object getValue()
		{
			return value;
		}

		/**
		 * Set the Value
		 * @param o The value to set
		 * @return The previous value, otherwise null
		 */		
		public Object setValue(Object o)
		{
			Object oldValue = value;
			value = o;
			return oldValue;
		}

		/**
		 * Test if this Entry is equal to another Object
		 * @param o Object to compare to
		 * @return true if this object is equal to the argument
		 */		
		public boolean equals(Object o)
		{
			if (!(o instanceof Entry))
			{
				return false;
			}
			Entry e = (Entry) o;
			return (key.equals(e.getKey()) && value.equals(e.getValue()));
		}

		/**
		 * Get the hashcode of the object
		 * @return the hash code of the object
		 */		
		public int hashCode()
		{
			return key.hashCode() + value.hashCode();
		}
	}


	/**
	 * Collection holding the <code>MultiMap.Entry</code> objects.
	 */
	private Vector entries;
}
