// 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(); } }