/*
 * @(#)QuickHashtable.java    2.3  27 November 2003
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.quick;

import java.util.*;

/**
 * <P><CODE>QuickHashtable</CODE> extends <CODE>Hashtable</CODE> by
 * adding one constructor and three methods <CODE>putPairs</CODE>,
 * <CODE>setPairs</CODE>, and <CODE>removeKeys</CODE> that handle
 * an array of <CODE>Object</CODE> at once.</P>
 *
 * <P>In addition, symmetric methods are added that will hash both
 * a key to its value and a value to its key.</P>
 *
 * <P>All constructors of the original <CODE>Hashtable</CODE> class
 * are provided as well.</P>
 *
 * <P> The only inherited methods that are overridden are those that
 * will throw an exception on <CODE>null</CODE> arguments.  In this
 * class, such methods will handle <CODE>null</CODE> in a sensible
 * fashion.</P>
 *
 * @author  Richard Rasala
 * @version 2.3
 * @since   2.3
 */
public class QuickHashtable extends Hashtable {
    
    /** A constructor that delegates to class <CODE>Hashtable</CODE>. */
    public QuickHashtable() { }
    
    
    /** A constructor that delegates to class <CODE>Hashtable</CODE>. */
    public QuickHashtable(int initialCapacity) {
        super(initialCapacity);
    }
    
    
    /** A constructor that delegates to class <CODE>Hashtable</CODE>. */
    public QuickHashtable(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
    }
    
    
    /** A constructor that delegates to class <CODE>Hashtable</CODE>. */
    public QuickHashtable(Map m) {
        super(m);
    }
    
    
    /**
     * <P>Constructor that adds the given 2-dimensional array of key-value pairs
     * one-by-one to the hash table.</P>
     *
     * <P>Ignores key-value pairs in which either item is <CODE>null</CODE>.</P>
     *
     * <P>The capacity is set either to 10 or to 1.5 times the length of the
     * array <CODE>pairs</CODE> whichever is larger.</P>
     *
     * <P>The load factor is set to the default of 0.75.</P>
     *
     * @param pairs the array of pairs to add to this hash table
     * @see #putPairs(Object[][])
     */
    public QuickHashtable(Object[][] pairs) {
        super((pairs == null) ? 10 : Math.max((int) (1.5 * pairs.length), 10));
        
        putPairs(pairs);
    }
    
    
    /**
     * <P>Associates the specified value with the specified key in this hash table.
     * If the hash table previously contained a mapping for this key, the old value
     * is replaced.</P>
     *
     * <P>If the key or value is <CODE>null</CODE>, then simply returns
     * <CODE>null</CODE>.</P>
     * 
     * @param  key   the key associated with the specified value 
     * @param  value the value to be associated with the specified key
     * @return previous value associated with specified key, or null
     *         if there was no mapping for key
     * @see Hashtable#put(Object, Object)
     */
    public Object put(Object key, Object value) {
        if ((key == null) || (value == null))
            return null;
        
        return super.put(key, value);
    }
    
    
    /**
     * <P>Returns the value to which the hash table maps the specified key.
     * Returns <CODE>null</CODE> if the hash table contains no mapping for
     * the key.</P>
     *
     * <P>If the key is <CODE>null</CODE>, then simply returns
     * <CODE>null</CODE>.</P>
     * 
     * @param  key the key whose associated value is to be returned
     * @return the value to which the hash table maps the specified key,
     *         or null if the hash table contains no mapping for the key
     * @see Hashtable#get(Object)
     */
    public Object get(Object key) {
        if (key == null)
            return null;
        
        return super.get(key);
    }
    
    
    /**
     * <P>Removes the mapping for this key from this hash table if present.</P>
     *
     * <P>If the key is <CODE>null</CODE>, then simply returns
     * <CODE>null</CODE>.</P>
     * 
     * @param  key the key whose mapping should be removed
     * @return previous value associated with specified key, or null
     *         if there was no mapping for key
     * @see Hashtable#remove(Object)
     */
    public Object remove(Object key) {
        if (key == null)
            return null;
        
        return super.remove(key);
    }
    
    
    /**
     * <P>Returns <CODE>true</CODE> if the hash table contains the key.</P>
     *
     * <P>If the key is <CODE>null</CODE>, then simply returns
     * <CODE>false</CODE>.</P>
     * 
     * @param  key  the key whose presence in the hash table is to be tested
     * @return true if the key is non-null and is a key in the hash table
     * @see Hashtable#containsKey(Object)
     */
    public boolean containsKey(Object key) {
        if (key == null)
            return false;
        
        return super.containsKey(key);
    }
    
    
    /**
     * <P>Returns <CODE>true</CODE> if the hash table contains the value.</P>
     *
     * <P>If the value is <CODE>null</CODE>, then simply returns
     * <CODE>false</CODE>.</P>
     * 
     * @param  value the value whose presence in the hash table is to be tested
     * @return true  if the value is non-null and is a value in the hash table
     * @see Hashtable#containsValue(Object)
     */
    public boolean containsValue(Object value) {
        if (value == null)
            return false;
        
        return super.containsValue(value);
    }
    
    
    /**
     * <P>Returns <CODE>true</CODE> if the hash table contains the value.</P>
     *
     * <P>If the value is <CODE>null</CODE>, then simply returns
     * <CODE>false</CODE>.</P>
     * 
     * @param  value the value whose presence in the hash table is to be tested
     * @return true  if the value is non-null and is a value in the hash table
     * @see Hashtable#contains(Object)
     */
    public boolean contains(Object value) {
        if (value == null)
            return false;
        
        return super.contains(value);
    }
    
    
    /**
     * <P>Method that adds the given 2-dimensional array of key-value pairs
     * one-by-one to the hash table.</P>
     *
     * <P>If <CODE>pairs</CODE> is <CODE>null</CODE>, then does nothing.</P>
     *
     * <P>Ignores any subarray <CODE>pairs[i]</CODE> that is <CODE>null</CODE>
     * or not of length 2.</P>
     *
     * <P>Ignores key-value pairs in which either item is <CODE>null</CODE>.</P>
     *
     * @param pairs the array of pairs to add to this hash table
     * @see #QuickHashtable(Object[][])
     * @see #setPairs(Object[][])
     * @see #removeKeys(Object[])
     * @see #putSymmetric(Object, Object)
     * @see #putPairsSymmetric(Object[][])
     * @see #setPairsSymmetric(Object[][])
     */
    public void putPairs(Object[][] pairs) {
        if (pairs == null)
            return;
        
        int length = pairs.length;
        
        for (int i = 0; i < length; i++) {
            if ((pairs[i] != null) && (pairs[i].length == 2)) {
                if ((pairs[i][0] != null) && (pairs[i][1] != null)) {
                    put(pairs[i][0], pairs[i][1]);
                }
            }
        }
    }
    
    
    /**
     * <P>Method that first clears this hash table of any existing pairs and
     * then adds the given array of pairs one-by-one to the hash table.</P>
     *
     * <P>If <CODE>pairs</CODE> is <CODE>null</CODE>, then this method
     * is equivalent to the inherited method <CODE>clear</CODE>.</P>
     *
     * <P>Ignores any subarray <CODE>pairs[i]</CODE> that is <CODE>null</CODE>
     * or not of length 2.</P>
     *
     * <P>Ignores key-value pairs in which either item is <CODE>null</CODE>.</P>
     *
     * @param pairs the array of pairs to add to this hash table after clearance
     * @see #putPairs(Object[][])
     * @see #removeKeys(Object[])
     * @see #putSymmetric(Object, Object)
     * @see #putPairsSymmetric(Object[][])
     * @see #setPairsSymmetric(Object[][])
     */
    public void setPairs(Object[][] pairs) {
        clear();
        putPairs(pairs);
    }
    
    
    /**
     * <P>Method that removes the given array of keys one-by-one from the
     * hash table.</P>
     *
     * <P>If <CODE>keys</CODE> is <CODE>null</CODE>, then does nothing.</P>
     *
     * <P>Ignores key items that are <CODE>null</CODE>.</P>
     *
     * @param keys the array of keys to remove from this hash table
     * @see #putPairs(Object[][])
     * @see #setPairs(Object[][])
     * @see #putSymmetric(Object, Object)
     * @see #putPairsSymmetric(Object[][])
     * @see #setPairsSymmetric(Object[][])
     */
    public void removeKeys(Object[] keys) {
        if (keys == null)
            return;
        
        int length = keys.length;
        
        for (int i = 0; i < length; i++)
            if (keys[i] != null)
                remove(keys[i]);
    }
    
    
    /**
     * <P>Method that adds the key-value pair symmetrically to the hash table,
     * that is, the key maps to the value and the value maps to the key.</P>
     *
     * <P>Does nothing if the key or value is <CODE>null</CODE>.</P>
     *
     * @param key   the key   in the key-value pair
     * @param value the value in the key-value pair
     * @see #putPairs(Object[][])
     * @see #setPairs(Object[][])
     * @see #removeKeys(Object[])
     * @see #putPairsSymmetric(Object[][])
     * @see #setPairsSymmetric(Object[][])
     */
    public void putSymmetric(Object key, Object value) {
        if ((key == null) || (value == null))
            return;
        
        put(key, value);
        put(value, key);
    }
    
    
    /**
     * <P>Method that adds the given 2-dimensional array of key-value pairs
     * one-by-one symmetrically to the hash table.</P>
     *
     * <P>If <CODE>pairs</CODE> is <CODE>null</CODE>, then does nothing.</P>
     *
     * <P>Ignores any subarray <CODE>pairs[i]</CODE> that is <CODE>null</CODE>
     * or not of length 2.</P>
     *
     * <P>Ignores key-value pairs in which either item is <CODE>null</CODE>.</P>
     *
     * @param pairs the array of pairs to add symmetrically to this hash table
     * @see #putPairs(Object[][])
     * @see #setPairs(Object[][])
     * @see #removeKeys(Object[])
     * @see #putSymmetric(Object, Object)
     * @see #setPairsSymmetric(Object[][])
     */
    public void putPairsSymmetric(Object[][] pairs) {
        if (pairs == null)
            return;
        
        int length = pairs.length;
        
        for (int i = 0; i < length; i++) {
            if ((pairs[i] != null) && (pairs[i].length == 2)) {
                putSymmetric(pairs[i][0], pairs[i][1]);
            }
        }
    }
    
    
    /**
     * <P>Method that first clears this hash table of any existing pairs and
     * then adds the given array of pairs one-by-one symmetrically to the
     * hash table.</P>
     *
     * <P>If <CODE>pairs</CODE> is <CODE>null</CODE>, then this method
     * is equivalent to the inherited method <CODE>clear</CODE>.</P>
     *
     * <P>Ignores any subarray <CODE>pairs[i]</CODE> that is <CODE>null</CODE>
     * or not of length 2.</P>
     *
     * <P>Ignores key-value pairs in which either item is <CODE>null</CODE>.</P>
     *
     * @param pairs the array of pairs to add symmetrically to this hash table
     *              after clearance
     * @see #putPairs(Object[][])
     * @see #setPairs(Object[][])
     * @see #removeKeys(Object[])
     * @see #putSymmetric(Object, Object)
     * @see #putPairsSymmetric(Object[][])
     */
    public void setPairsSymmetric(Object[][] pairs) {
        clear();
        putPairsSymmetric(pairs);
    }
    
}
