/* IdentityHashSet.java

	Purpose:
		
	Description:
		
	History:
		Fri Sep 13 11:18:11  2002, Created by tomyeh

Copyright (C) 2002 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.util;

import java.util.Collection;
import java.util.Set;
import java.util.AbstractSet;
import java.util.IdentityHashMap;
import java.util.Iterator;

import org.zkoss.lang.Objects;

/**
 * Like java.util.InternalHashMap, it uses == and System.identityHashCode
 * for doing HashSet.
 *
 * @author tomyeh
 * @see IdentityComparator
 */
public class IdentityHashSet<T> extends AbstractSet<T>
implements Set<T>, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 20060622L;

    private transient IdentityHashMap<T, Object> _map;

    /**
     * Constructs a new, empty set; the backing <tt>IdentityHashMap</tt>
     * instance has default capacity (32).
     */
    public IdentityHashSet() {
		_map = new IdentityHashMap<T, Object>();
	}

	/**
	 * Constructs a new set containing the elements in the specified
	 * collection.
	 *
	 * @param c the collection whose elements are to be placed into this set.
	 * @throws NullPointerException if the specified collection is null.
	 */
	public IdentityHashSet(Collection<T> c) {
		_map = new IdentityHashMap<T, Object>(Math.max((c.size()*4)/3, 16));
		addAll(c);
	}

	/**
     * Constructs a new, empty set with the specified expected maximum size.
	 *
     * @param expectedMaxSize the expected maximum size of the map.
     * @throws IllegalArgumentException if <tt>expectedMaxSize</tt> is negative
	 */
	public IdentityHashSet(int expectedMaxSize) {
		_map = new IdentityHashMap<T, Object>(expectedMaxSize);
	}

	//-- Set --//
	public Iterator<T> iterator() {
		return _map.keySet().iterator();
	}
	public int size() {
		return _map.size();
	}
	public boolean isEmpty() {
		return _map.isEmpty();
	}
	public boolean contains(Object o) {
		return _map.containsKey(o);
	}
	public boolean add(T o) {
		return _map.put(o, Objects.UNKNOWN)==null;
	}
	public boolean remove(Object o) {
		return _map.remove(o)==Objects.UNKNOWN;
	}
	public void clear() {
		_map.clear();
	}

	@SuppressWarnings("unchecked")
	public Object clone() {
		try { 
			IdentityHashSet<T> newSet = (IdentityHashSet<T>)super.clone();
			newSet._map = (IdentityHashMap<T, Object>)_map.clone();
			return newSet;
		} catch (CloneNotSupportedException e) { 
			throw new InternalError();
		}
	}

	//-- Serializable --//
	private synchronized void writeObject(java.io.ObjectOutputStream s)
	throws java.io.IOException {
		// Write out any hidden serialization magic
		s.defaultWriteObject();

		// Write out size
		s.writeInt(_map.size());

		// Write out all elements in the proper order.
		for (T key: _map.keySet())
			s.writeObject(key);
	}
	@SuppressWarnings("unchecked")
	private void readObject(java.io.ObjectInputStream s)
	throws java.io.IOException, ClassNotFoundException {
		// Read in any hidden serialization magic
		s.defaultReadObject();

        // Read in size (number of Mappings)
        int size = s.readInt();

		// Read in IdentityHashMap capacity and load factor and create backing IdentityHashMap
		_map = new IdentityHashMap<T, Object>((size*4)/3);
			// Allow for 33% growth (i.e., capacity is >= 2* size()).

		// Read in all elements in the proper order.
		for (int i=0; i<size; i++) {
			Object e = s.readObject();
			_map.put((T)e, Objects.UNKNOWN);
		}
	}
}
