// Copyright 2002, SLAC, Stanford, U.S.A.
package org.freehep.util;
import java.io.*;
import java.util.*;
/**
* Stores a hashtable of hashtables, which can be indexed by a key and a subkey.
* Keys and Values can be null.
*
* @author Mark Donszelmann
* @version $Id: DoubleHashtable.java 8584 2006-08-10 23:06:37Z duns $
*/
public class DoubleHashtable extends AbstractCollection implements Serializable {
/**
*
*/
private static final long serialVersionUID = -545653328241864972L;
private Hashtable table;
/**
* creates a hashtable of hashtables
*/
public DoubleHashtable() {
table = new Hashtable();
}
/**
* removes all entries and sub-tables
*/
public void clear() {
table.clear();
}
/**
* removes all entries from a subtable
*/
public void clear(Object key) {
Hashtable subtable = get(key);
if (subtable != null) {
subtable.clear();
}
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("DoubleHashtable.clone() is not (yet) supported.");
}
/**
* @return true if value exists in some sub-table
*/
public boolean contains(Object value) {
if (value == null) value = this;
for (Enumeration e=table.keys(); e.hasMoreElements(); ) {
Hashtable subtable = get(e.nextElement());
if (subtable.contains(value)) return true;
}
return false;
}
/**
* @return true if sub-table exists for key
*/
public boolean containsKey(Object key) {
if (key == null) key = this;
return table.containsKey(key);
}
/**
* @return true if value exists for key and subkey
*/
public boolean containsKey(Object key, Object subKey) {
if (subKey == null) subKey = this;
Hashtable subtable = get(key);
return (subtable != null) ? subtable.containsKey(subKey) : false;
}
/**
* @return enumeration over all values in all sub-tables
*/
public Enumeration elements() {
return new Enumeration() {
private Enumeration subtableEnumeration = table.elements();
private Enumeration valueEnumeration;
private Object nullValue = DoubleHashtable.this;
public boolean hasMoreElements() {
if ((valueEnumeration == null) || (!valueEnumeration.hasMoreElements())) {
if (!subtableEnumeration.hasMoreElements()) {
return false;
}
valueEnumeration = ((Hashtable)subtableEnumeration.nextElement()).elements();
}
return true;
}
public Object nextElement() {
hasMoreElements();
Object value = valueEnumeration.nextElement();
return (value == nullValue) ? null : value;
}
};
}
/**
* @return iterator over all values in all sub-tables
*/
public Iterator iterator() {
return new Iterator() {
private Iterator subtableIterator = table.entrySet().iterator();
private Map subtable;
private Iterator valueIterator;
private Object nullValue = DoubleHashtable.this;
public boolean hasNext() {
if ((valueIterator == null) || (!valueIterator.hasNext())) {
if (!subtableIterator.hasNext()) {
return false;
}
Map.Entry entry = (Map.Entry)subtableIterator.next();
subtable = (Map)entry.getValue();
valueIterator = subtable.entrySet().iterator();
}
return true;
}
public Object next() {
hasNext();
Map.Entry entry = (Map.Entry)valueIterator.next();
Object value = entry.getValue();
return (value == nullValue) ? null : value;
}
public void remove() {
valueIterator.remove();
if (subtable.isEmpty()) {
subtableIterator.remove();
}
}
};
}
/**
* @return sub-table for key
*/
public Hashtable get(Object key) {
if (key == null) key = this;
return (Hashtable)table.get(key);
}
/**
* @return value for key and subkey, null in non-existent or null value was stored
*/
public Object get(Object key, Object subKey) {
if (subKey == null) subKey = this;
Hashtable table = get(key);
Object value = (table==null) ? null : table.get(subKey);
return (value == this) ? null : value;
}
/**
* @return true if table is empty
*/
public boolean isEmpty() {
return table.isEmpty();
}
/**
* @return enumeration of keys in table
*/
public Enumeration keys() {
return table.keys();
}
/**
* @return enumeration in subkeys of sub-table pointed by key, and empty if sub-table does not exist
*/
public Enumeration keys(Object key) {
final Hashtable subtable = get(key);
return new Enumeration() {
private Enumeration subkeys = (subtable == null) ? null : subtable.keys();
private Object nullKey = DoubleHashtable.this;
public boolean hasMoreElements() {
return (subkeys == null) ? false : subkeys.hasMoreElements();
}
public Object nextElement() {
if (subkeys == null) {
throw new NoSuchElementException();
}
Object subkey = subkeys.nextElement();
return (subkey == nullKey) ? null : subkey;
}
};
}
/**
* puts a value in sub-table specified by key and subkey.
*
* @return previous value
*/
public Object put(Object key, Object subKey, Object value) {
// Make sure there exists a subtable
Hashtable subtable = get(key);
if (subtable == null) {
subtable = new Hashtable();
if (key == null) key = this;
table.put(key, subtable);
}
// add entry and handle nulls
if (subKey == null) subKey = this;
if (value == null) value = this;
Object old = subtable.get(subKey);
subtable.put(subKey, value);
// return previous entry
return (old == this) ? null : old;
}
/**
* removes value from sub-table specified by key and subkey.
*
* @return previous value
*/
public Object remove(Object key, Object subKey) {
// look for subtable
Hashtable subtable = get(key);
if (subtable == null) return null;
// remove from subtable
if (subKey == null) subKey = this;
Object old = subtable.remove(subKey);
// remove subtable if needed
if (subtable.isEmpty()) {
if (key == null) key = this;
table.remove(key);
}
// return old value
return (old == this) ? null : old;
}
/**
* @return size of all tables
*/
public int size() {
int size =0;
for (Enumeration e = table.keys(); e.hasMoreElements(); ) {
Object key = e.nextElement();
Hashtable subtable = get(key);
size += subtable.size();
}
return size;
}
/**
* @return a string representation of the table
*/
public String toString() {
return "DoubleHashtable@"+hashCode();
}
}