/* DBObjectTable.java A customized variant of the java.util.Hashtable class that is tuned for use as Ganymede's object hashes. Created: 9 June 1998 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996 - 2013 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 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, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.server; import java.lang.Iterable; import java.util.AbstractCollection; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.Iterator; import java.util.NoSuchElementException; /*------------------------------------------------------------------------------ class DBObjectTable ------------------------------------------------------------------------------*/ /** * <p>A customized variant of the java.util.Hashtable class that is * tuned for use in managing {@link arlut.csd.ganymede.server.DBObject * DBObject}s in a Ganymede {@link * arlut.csd.ganymede.server.DBObjectBase DBObjectBase}.</p> * * @author Jonathan Abbey, jonabbey@arlut.utexas.edu, ARL:UT */ public final class DBObjectTable implements Iterable<DBObject> { /** * The hash table data. */ private transient DBObject table[]; /** * The total number of entries in the hash table. */ private transient int count; /** * Rehashes the table when count exceeds this threshold. */ private int threshold; /** * The load factor for the hashtable. */ private float loadFactor; /** * Tracking value to detect concurrent modification for the * enumerations and iterators we generate. */ private transient int modGen = Integer.MIN_VALUE; /** * Constructs a new, empty DBObjectTable with the specified initial * capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hashtable. * @param loadFactor a number between 0.0 and 1.0. * @exception IllegalArgumentException if the initial capacity is less * than or equal to zero, or if the load factor is less than * or equal to zero. */ public DBObjectTable(int initialCapacity, float loadFactor) { if ((initialCapacity <= 0) || (loadFactor <= 0.0) || (loadFactor > 1.0)) { throw new IllegalArgumentException(); } this.loadFactor = loadFactor; this.table = new DBObject[initialCapacity]; this.threshold = (int)(initialCapacity * loadFactor); } /** * Constructs a new, empty DBObjectTable with the specified initial capacity * and default load factor. * * @param initialCapacity the initial capacity of the hashtable. */ public DBObjectTable(int initialCapacity) { this(initialCapacity, 0.75f); } /** * Constructs a new, empty DBObjectTable with a default capacity and load * factor. */ public DBObjectTable() { this(101, 0.75f); } /** * Returns a modification generation value for the enumerator and * iterator that are generated from this DBObjectTable. */ public int getModGen() { return this.modGen; } /** * Returns the number of objects in this DBObjectTable. * * @return the number of objects in this DBObjectTable. */ public int size() { return this.count; } /** * Tests if this DBObjectTable contains no objects. * * @return <code>true</code> if this DBObjectTable contains no values; * <code>false</code> otherwise. */ public boolean isEmpty() { return this.count == 0; } /** * <p>Returns an Iterator of the objects in this DBObjectTable.</p> * * <p>Use the Iterator methods on the returned object to fetch the * elements sequentially.</p> * * <p>This method allows DBObjectTable to support the Java 5 foreach * loop construct.</p> * * @return an Iterator of the objects in this DBObjectTable. * @see java.util.Iterator */ public synchronized Iterator<DBObject> iterator() { return new DBObjectTableIterator(table, this); } /** * <p>Returns an enumeration of the objects in this DBObjectTable. * Use the Enumeration methods on the returned object to fetch the * elements sequentially.</p> * * @return an enumeration of the objects in this DBObjectTable. * @see java.util.Enumeration */ public synchronized Enumeration elements() { return new DBObjectTableEnumerator(table, this); } /** * <p>Tests if the DBObject value is contained in this * DBObjectTable.</p> * * @param value a DBObject to search for. * @exception NullPointerException if the value is <code>null</code>. */ public boolean contains(DBObject value) { if (value == null) { throw new NullPointerException(); } return containsKey(value.hashCode()); } /** * <p>Tests if a DBObject with the specified object id is in this * DBObjectTable.</p> * * @param key possible object id. */ public synchronized boolean containsKey(int key) { int index = (key & 0x7FFFFFFF) % this.table.length; for (DBObject e = this.table[index] ; e != null ; e = e.next) { if (e.hashCode() == key) { return true; } } return false; } /** * <p>Returns the DBObject with the specified key from this * DBObjectTable, or null if no object with that id is in this * table.</p> */ public DBObject getNoSync(int key) { int index = (key & 0x7FFFFFFF) % this.table.length; for (DBObject e = this.table[index] ; e != null ; e = e.next) { if (e.hashCode() == key) { return e; } } return null; } /** * <p>Returns the DBObject with the specified key from this * DBObjectTable, or null if no object with that id is in this * table.</p> */ public synchronized DBObject get(int key) { int index = (key & 0x7FFFFFFF) % this.table.length; for (DBObject e = this.table[index] ; e != null ; e = e.next) { if (e.hashCode() == key) { return e; } } return null; } /** * <p>Rehashes the contents of the DBObjectTable into a * DBObjectTable with a larger capacity. This method is called * automatically when the number of keys in the hashtable exceeds * this DBObjectTable's capacity and load factor.</p> */ protected void rehash() { int oldCapacity = table.length; DBObject oldTable[] = table; int newCapacity = oldCapacity * 2 + 1; DBObject newTable[] = new DBObject[newCapacity]; this.threshold = (int)(newCapacity * loadFactor); //System.out.println("rehash old=" + oldCapacity + ", new=" + //newCapacity + ", thresh=" + threshold + ", count=" + count); for (int i = oldCapacity ; i-- > 0 ;) { for (DBObject old = oldTable[i] ; old != null ; old = old.next) { DBObject e = old; int index = (e.hashCode() & 0x7FFFFFFF) % newCapacity; e.next = newTable[index]; newTable[index] = e; } } this.table = newTable; } /** * <p>Inserts a DBObject into this DBObjectTable.</p> * * <p>This put is not sync'ed, and should only be used with higher * level sync provisions.</p> */ public void putNoSync(DBObject value) { this.modGen++; // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the object is not already in the hashtable. removeNoSync(value.hashCode()); int hash = value.hashCode(); int index = (hash & 0x7FFFFFFF) % this.table.length; if (count > threshold) { rehash(); putNoSync(value); return; } // Insert the new entry. value.next = this.table[index]; this.table[index] = value; this.count++; return; } /** * <p>Inserts a DBObject into this DBObjectTable</p> */ public synchronized void put(DBObject value) { if (value == null) { throw new NullPointerException(); } this.modGen++; // Makes sure the object is not already in the hashtable. // Note that we are sync'ed, so we can use the non-sync'ed // removeNoSync(). removeNoSync(value.hashCode()); if (this.count > this.threshold) { rehash(); } int hash = value.hashCode(); int index = (hash & 0x7FFFFFFF) % this.table.length; // Insert the new entry. value.next = this.table[index]; this.table[index] = value; this.count++; return; } /** * <p>Inserts a DBObject into this DBObjectTable.</p> * * <p>This put is not sync'ed, and should only be used with higher * level sync provisions.</p> */ public void putNoSyncNoRemove(DBObject value) { this.modGen++; // Make sure the value is not null if (value == null) { throw new NullPointerException(); } int hash = value.hashCode(); int index = (hash & 0x7FFFFFFF) % this.table.length; if (this.count > this.threshold) { rehash(); putNoSync(value); return; } // Insert the new entry. value.next = this.table[index]; this.table[index] = value; this.count++; return; } /** * <p>Removes the DBObject with the given id from this * DBObjectTable.</p> */ public void removeNoSync(int key) { this.modGen++; int index = (key & 0x7FFFFFFF) % this.table.length; for (DBObject e = this.table[index], prev = null ; e != null ; prev = e, e = e.next) { if (e.hashCode() == key) { if (prev != null) { prev.next = e.next; } else { this.table[index] = e.next; } this.count--; return; } } return; } /** * <p>Removes the DBObject with the given id from this * DBObjectTable.</p> */ public synchronized void remove(int key) { this.modGen++; int index = (key & 0x7FFFFFFF) % this.table.length; for (DBObject e = this.table[index], prev = null ; e != null ; prev = e, e = e.next) { if (e.hashCode() == key) { if (prev != null) { prev.next = e.next; } else { this.table[index] = e.next; } this.count--; return; } } return; } /** * <p>Replaces a DBObject in the table in-place without disturbing * any Iterators running.</p> */ public void replaceNoSync(DBObject replaceObject) { if (replaceObject == null) { throw new NullPointerException(); } int key = replaceObject.getInvid().getNum(); int index = (key & 0x7FFFFFFF) % this.table.length; for (DBObject e = this.table[index], prev = null ; e != null ; prev = e, e = e.next) { if (e.hashCode() == key) { if (prev == null) { replaceObject.next = this.table[index].next; this.table[index] = replaceObject; } else { replaceObject.next = e.next; prev.next = replaceObject; } return; } } throw new IllegalStateException("No original found to replace."); } /** * <p>Clears this DBObjectTable.</p> */ public synchronized void clear() { this.modGen++; for (int index = this.table.length; --index >= 0; ) { this.table[index] = null; } count = 0; } /** * <p>Returns a non-editable Collection view of this DBObjectTable.</p> */ public Collection<DBObject> values() { return new DBObjectTableContainer(this); } } /*------------------------------------------------------------------------------ class DBObjectTableEnumerator ------------------------------------------------------------------------------*/ /** * <p>A {@link arlut.csd.ganymede.server.DBObjectTable DBObjectTable} * enumerator class. This class should remain opaque to the client, * which will use the Enumeration interface.</p> */ final class DBObjectTableEnumerator implements Enumeration { int index; DBObject table[]; DBObject entry; private DBObjectTable parent; private int modGen; /* -- */ DBObjectTableEnumerator(DBObject table[], DBObjectTable parent) { this.table = table; this.index = table.length; this.parent = parent; this.modGen = parent.getModGen(); } public boolean hasMoreElements() { if (this.modGen != parent.getModGen()) { throw new ConcurrentModificationException(); } if (entry != null) { return true; } while (index-- > 0) { if ((entry = table[index]) != null) { return true; } } return false; } public Object nextElement() { if (this.modGen != parent.getModGen()) { throw new ConcurrentModificationException(); } if (entry == null) { while ((index-- > 0) && ((entry = table[index]) == null)); } if (entry != null) { DBObject e = entry; entry = e.next; return e; } throw new NoSuchElementException("HashtableEnumerator"); } } /*------------------------------------------------------------------------------ class DBObjectTableIterator ------------------------------------------------------------------------------*/ /** * <p>A {@link arlut.csd.ganymede.server.DBObjectTable DBObjectTable} * Iterator class. This class should remain opaque to the client, * which will use the Iterator interface.</p> */ final class DBObjectTableIterator implements Iterator<DBObject> { int index; DBObject table[]; DBObject entry; private DBObjectTable parent; private int modGen; /* -- */ DBObjectTableIterator(DBObject[] table, DBObjectTable parent) { this.table = table; this.index = table.length; this.parent = parent; this.modGen = parent.getModGen(); } public boolean hasNext() { if (this.modGen != parent.getModGen()) { throw new ConcurrentModificationException(); } if (entry != null) { return true; } while (index-- > 0) { if ((entry = table[index]) != null) { return true; } } return false; } public DBObject next() { if (this.modGen != parent.getModGen()) { throw new ConcurrentModificationException(); } if (entry == null) { while ((index-- > 0) && ((entry = table[index]) == null)); } if (entry != null) { DBObject e = entry; entry = e.next; return e; } throw new NoSuchElementException("HashtableEnumerator"); } public void remove() { throw new UnsupportedOperationException(); } } /*------------------------------------------------------------------------------ class DBObjectTableContainer ------------------------------------------------------------------------------*/ final class DBObjectTableContainer extends AbstractCollection<DBObject> { private DBObjectTable parent; /* -- */ DBObjectTableContainer(DBObjectTable parent) { this.parent = parent; } public int size() { return parent.size(); } public Iterator<DBObject> iterator() { return parent.iterator(); } }