/**
 *#########################################################################
 *
 * A component of the Gatherer application, part of the Greenstone digital
 * library suite from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *########################################################################
 */
package org.greenstone.gatherer.util;

/**************************************************************************************
 * Written:      20/08/02
 * Revised:      
 **************************************************************************************/
import java.util.HashMap;
import java.util.Iterator;

/** Provides a HashMap implementation that indexes by two keys. Perfect for the storage of metadata references based on their metadata element and assigned value. 
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
public class HashMap3D
    extends HashMap {

    private int capacity = 4;

    private int size = 0;

    private Object last_key_one = null;
    private Object last_key_two = null;
    private Object last_value = null;

    /** Default constructor.
     */
    public HashMap3D() {
	super();
    }
    /** Constructor with a specific initial capacity.
     * @param capacity The initial capacity as an <i>int</i>.
     */
    public HashMap3D(int capacity) {
	super(capacity);
	this.capacity = capacity;
    }
    /** Completely remove the contents of this HashMap and its child HashMaps. */
    public void clear() {
	size = 0;
	Iterator iterator = values().iterator();
	while(iterator.hasNext()) {
	    HashMap inner_mapping = (HashMap) iterator.next();
	    inner_mapping.clear();
	    inner_mapping = null;
	}
	iterator = null;
	super.clear();
    }
    /** Determine if this hash map contains an entry for the given keys. Also cache this entry because theres a good chance the next get call will ask for this entry.
     * @param key_one The first key as an <strong>Object</strong>.
     * @param key_two The second key as an <strong>Object</strong>.
     * @return <i>true</i> if such an entry exists, <i>false</i> otherwise.
     */
    public boolean contains(Object key_one, Object key_two) {
	boolean result = false;
	// Retrieve the hash mapping at key_one.
	HashMap map = (HashMap) get(key_one);
	// If there is such a map then retrieve the value at key_two.
	if(map != null) {
	    last_value = map.get(key_two);
	    if(last_value != null) {
		last_key_one = key_one;
		last_key_two = key_two;
		result = true;
	    }
	}
	return result;
    }

    /** Retrieve an entry from this three dimensional hash mapping.
     * @param key_one The first key as an <strong>Object</strong>.
     * @param key_two The second key as an <strong>Object</strong>.
     * @return The value <strong>Object</strong> located at (key_one, key_two), or <i>null</i> if no such value.
     */
    public Object get(Object key_one, Object key_two) {
	Object result = null;
	if(key_one.equals(last_key_one) && key_two.equals(last_key_two)) {
	    result = last_value;
	}
	else {
	    // Retrieve the hash mapping at key_one.
	    HashMap map = (HashMap) get(key_one);
	    // If there is such a map then retrieve the value at key_two.
	    if(map != null) {
		result = map.get(key_two);
		if(result != null) {
		    last_key_one = key_one;
		    last_key_two = key_two;
		    last_value = result;
		}
	    }
	}
	return result;
    }

    /** Put an entry into this three dimensional hash mapping.
     * @param key_one The first key, to store this value under, as an <strong>Object</strong>.
     * @param key_two The second key, to store this value under, as an <strong>Object</strong>.
     * @param value The value <strong>Object</strong> itself.
     */
    public void put(Object key_one, Object key_two, Object value) {
	// Retrieve the hash mapping at key_one, or if none exists create one.
	HashMap map = (HashMap) get(key_one);
	if(map == null) {
	    map = new HashMap(capacity);
	    put(key_one, map);
	}
	// Now add the value to this mapping.
	map.put(key_two, value);
	size++;
    }
}
