package freenet.support;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;
/**
* A hashtable that can store several values for each entry.
*
* FIXME improve efficiency - map to Object internally and either use a single V or an
* ArrayList of V. Then take over where we do this in the code e.g. PrioritizedTicker.
*
* @author oskar
*/
public class MultiValueTable<K,V> {
private Hashtable<K, Vector<V>> table;
private int ies;
public MultiValueTable() {
this(16, 3);
}
public MultiValueTable(int initialSize) {
this(initialSize, 3);
}
public MultiValueTable(int initialSize, int initialEntrySize) {
table = new Hashtable<K, Vector<V>>(initialSize);
ies = initialEntrySize;
}
public void put(K key, V value) {
synchronized (table) {
Vector<V> v = table.get(key);
if (v == null) {
v = new Vector<V>(ies);
table.put(key, v);
}
v.addElement(value);
}
}
/**
* Returns the first element for this key.
*/
public V get(K key) {
synchronized (table) {
Vector<V> v = table.get(key);
return (v == null ?
null :
v.firstElement());
}
}
public boolean containsKey(K key) {
synchronized (table) {
return table.containsKey(key);
}
}
public boolean containsElement(K key, V value) {
synchronized (table) {
Vector<V> v = table.get(key);
return (v != null) && v.contains(value);
}
}
/**
* Users will have to handle synchronizing.
*/
public Enumeration<V> getAll(K key) {
Vector<V> v;
synchronized (table) {
v = table.get(key);
}
return (v == null ?
new EmptyEnumeration<V>() :
v.elements());
}
/**
* To be used in for(x : y).
*/
public Iterable<V> iterateAll(K key) {
synchronized (table) {
return(table.get(key));
}
}
public int countAll(K key) {
Vector<V> v;
synchronized (table) {
v = table.get(key);
}
if(v != null)
return v.size();
else
return 0;
}
public Object getSync(K key) {
synchronized (table) {
return table.get(key);
}
}
public Object[] getArray(K key) {
synchronized (table) {
Vector<V> v = table.get(key);
if (v == null)
return null;
else {
Object[] r = new Object[v.size()];
v.copyInto(r);
return r;
}
}
}
public void remove(K key) {
synchronized (table) {
table.remove(key);
}
}
public boolean isEmpty() {
synchronized (table) {
return table.isEmpty();
}
}
public void clear() {
synchronized (table) {
table.clear();
}
}
public boolean removeElement(K key, V value) {
synchronized (table) {
Vector<V> v = table.get(key);
if (v == null)
return false;
else {
boolean b = v.removeElement(value);
if (v.isEmpty())
table.remove(key);
return b;
}
}
}
public Enumeration<K> keys() {
synchronized (table) {
return table.keys();
}
}
public Enumeration<V> elements() {
synchronized (table) {
if (table.isEmpty())
return new EmptyEnumeration<V>();
else
return new MultiValueEnumeration();
}
}
private static class EmptyEnumeration<E> implements Enumeration<E> {
@Override
public final boolean hasMoreElements() {
return false;
}
@Override
public final E nextElement() {
throw new NoSuchElementException();
}
}
private class MultiValueEnumeration implements Enumeration<V> {
private Enumeration<V> current;
private Enumeration< Vector<V>> global;
public MultiValueEnumeration() {
synchronized (table) {
global = table.elements();
}
current = global.nextElement().elements();
step();
}
public final void step() {
while (!current.hasMoreElements() && global.hasMoreElements())
current = global.nextElement().elements();
}
@Override
public final boolean hasMoreElements() {
return global.hasMoreElements(); // || current.hasMoreElements();
}
@Override
public final V nextElement() {
V o = current.nextElement();
step();
return o;
}
}
}