/**
 * A key (Comparable) together with an associated value (Object).
 * Keys are immutable and must be non-null.
 */
public class DictionaryElement<KeyType extends Comparable, ValueType>
                              implements Comparable {

    /** The key of this element.  Immutable and non-null. */
    private KeyType key;

    /** The value of this element. */
    private ValueType value;

    /**
     * Initialize this element.
     * Precondition:  key != null
     * @param  key  the key for this element
     * @param  value  the value for this element
     * @throws  IllegalArgumentException  if key == null
     */
    public DictionaryElement(KeyType key, ValueType value) {
        if (key == null)
            throw new IllegalArgumentException("Key cannot be null.");
        this.key = key;
        setValue(value);
    }

    /**
     * Return a String representation of this element.
     * @return  a String representation of this element
     */
    public String toString() {
        if (value == null)
            return key.toString() + ": null";
        else
            return key.toString() + ": " + value.toString();
    }

    /**
     * Implement the comparable interface using the key as the comparator.
     * Note that compareTo and equals will not always agree if an item is 
     * equal, since compareTo only looks at keys.
     * @param	obj	the object to be compared to this object
     * @throws  ClassCastException if other cannot be compared to this
     * @return	<0 if this < other, 0 if the same, and >0 otherwise
     */
    public int compareTo(Object obj) {
	    // Should be able to return a comparison if obj is of the DictionaryElement
	    // class -or- if it is of the key class.
	    if (!(obj != null && (this.getClass() == obj.getClass() ||
	                          this.key.getClass() == obj.getClass())))
			throw new ClassCastException("DictionaryElement object expected."); 

	    @SuppressWarnings("unchecked")
	    KeyType other;
	    if (this.getClass() == obj.getClass()) {
	        other = ((DictionaryElement<KeyType, ValueType>)obj).key;
	    }
	    else { // this.key.getClass() == obj.getClass();
	        other = (KeyType)obj;
    	}
	    
		return this.key.compareTo(other);
    }

    /**
     * Indicates whether some other object is equal to this one.  The two 
     * objects must have the same type (up to and including parameter types). 
     * (Unfortunately, "type erasure" makes this harder than it should be!) 
     * The elements' keys and values must be the same.
     * @param  obj  the reference object with which to compare
     * @return  true if this object is the same as 'obj'; false otherwise
     */
    public boolean equals(Object obj) {
	    if (obj != null && 
	        this.getClass() == obj.getClass()) {
		
		    @SuppressWarnings("unchecked")
		    DictionaryElement<KeyType, ValueType> other = 
		            (DictionaryElement<KeyType, ValueType>) obj;
		
			if (this.key.equals(other.key)) 
            	return ((this.value == null && other.value == null) ||
                    	this.value.equals(other.value));
		}
        return false;
    }

	/**
	 * Since equals was redefined, hashCode should be redefined.  
	 * @return	a hash of this object's key and value fields.
	 */
	public int hashCode() {
		return this.key.hashCode() * 31 + 
		       (this.value != null ? this.value.hashCode() : 0);
	}

    /**
     * Get the key of this element.
     * @return  the key of this element
     */
    public KeyType getKey() {
        return key;
    }

    /**
     * Get the value of this element.
     * @return  the value of this element
     */
    public ValueType getValue() {
        return value;
    }

    /**
     * Set the value of this element.
     * @param  value  the new value for this element
     */
    public void setValue(ValueType value) {
        this.value = value;
    }

}
