/* DBBaseFieldTable.java A customized variant of the java.util.Hashtable class that is tuned for use as Ganymede's base field 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 DBBaseFieldTable ------------------------------------------------------------------------------*/ /** * <p>A customized variant of the java.util.Hashtable class that is * tuned for use in managing * {@link arlut.csd.ganymede.server.DBObjectBaseField DBObjectBaseField}s * in a Ganymede {@link arlut.csd.ganymede.server.DBObjectBase DBObjectBase}.</p> * * @author Jonathan Abbey, jonabbey@arlut.utexas.edu, ARL:UT */ public final class DBBaseFieldTable implements Iterable<DBObjectBaseField> { /** * Array of DBObjectBaseField objects, sorted in id order. */ private transient DBObjectBaseField[] table; /** * Tracking value to detect concurrent modification for the * enumerations and iterators we generate. */ private transient int modGen = Integer.MIN_VALUE; /** * Constructs a new, empty DBBaseFieldTable. */ public DBBaseFieldTable() { table = new DBObjectBaseField[0]; } /** * Returns a modification generation value for the enumerator and * iterator that are generated from this DBBaseFieldTable.. */ public int getModGen() { return this.modGen; } /** * This method sets the contents of the DBBaseFieldTable. The * fieldAry array must be sorted in increasing field id order. */ public void replaceContents(DBObjectBaseField fieldAry[]) { this.modGen++; this.table = fieldAry; } /** * Returns the number of objects in this DBBaseFieldTable. * * @return the number of objects in this DBBaseFieldTable. */ public int size() { return table.length; } /** * Tests if this DBBaseFieldTable contains no objects. * * @return <code>true</code> if this DBBaseFieldTable contains no values; * <code>false</code> otherwise. */ public boolean isEmpty() { return table.length == 0; } /** * <p>Returns an Iterator of the objects in this DBBaseFieldTable.</p> * * <p>Use the Iterator methods on the returned object to fetch the * elements sequentially.</p> * * <p>This method allows DBBaseFieldTable 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<DBObjectBaseField> iterator() { return new DBBaseFieldTableIterator(table, this); } /** * <p>Returns an Iterator of the built-in DBObjectBaseField objects in * this DBBaseFieldTable.</p> * * <p>Use the Iterator methods on the returned object to fetch the * elements sequentially.</p> */ public synchronized Iterator<DBObjectBaseField> builtInIterator() { return new DBBaseFieldTableBuiltInIterator(table, this); } /** * Returns an enumeration of the objects in this DBBaseFieldTable. * Use the Enumeration methods on the returned object to fetch the elements * sequentially. * * @return an enumeration of the objects in this DBBaseFieldTable. * @see java.util.Enumeration * */ public synchronized Enumeration elements() { return new DBBaseFieldTableEnumerator(table, this); } /** * Tests if the DBObjectBaseField value is contained in this DBBaseFieldTable. * * @param value a DBObjectBaseField to search for. * @exception NullPointerException if the value is <code>null</code>. * */ public boolean contains(DBObjectBaseField value) { int index = java.util.Arrays.binarySearch(table, value); if (index < 0) { return false; } return table[index].equals(value); } /** * Tests if a DBObjectBaseField with the specified object id is in this DBBaseFieldTable. * * @param key possible object id. */ public boolean containsKey(Short key) { return java.util.Arrays.binarySearch(table, key) >= 0; } /** * Tests if a DBObjectBaseField with the specified object id is in this DBBaseFieldTable. * * @param key possible object id. */ public synchronized boolean containsKey(short key) { return java.util.Arrays.binarySearch(table, key) >= 0; } /** * Returns a shallow copy of the fields belonging to this * DBBaseFieldTable, in increasing id order. */ public synchronized DBObjectBaseField[] getIDSortedArray() { DBObjectBaseField[] copy = new DBObjectBaseField[table.length]; for (int i = 0; i < copy.length; i++) { copy[i] = table[i]; } return copy; } /** * Returns the DBObjectBaseField with the specified key from this DBBaseFieldTable, or * null if no object with that id is in this table. */ public DBObjectBaseField getNoSync(short key) { int index = java.util.Arrays.binarySearch(table, key); if (index < 0) { return null; } return table[index]; } /** * Returns the DBObjectBaseField with the specified key from this DBBaseFieldTable, or * null if no object with that id is in this table. */ public synchronized DBObjectBaseField get(short key) { int index = java.util.Arrays.binarySearch(table, key); if (index < 0) { return null; } return table[index]; } /** * <p>Returns the DBObjectBaseField with the specified name from * this DBBaseFieldTable, or null if no object with that name is in * this table.</p> * * <p>This method is unprotected by synchronization, so you must be * sure to use higher level synchronization to use this safely.</p> */ public DBObjectBaseField getNoSync(String name) { return this.findByName(name); } /** * <p>Returns the DBObjectBaseField with the specified name from * this DBBaseFieldTable, or null if no object with that name is in * this table.</p> * * <p>The comparisons done in this method are case insensitive.</p> */ public synchronized DBObjectBaseField get(String name) { return this.findByName(name); } /** * <p>Inserts a DBObjectBaseField into this DBBaseFieldTable.</p> * * <p>This put is not sync'ed, and should only be used with higher * level sync provisions.</p> */ public void putNoSync(DBObjectBaseField value) { this.modGen++; // Make sure the value is not null if (value == null) { throw new NullPointerException(); } int index = java.util.Arrays.binarySearch(table, value); if (index < 0) { // we'll need to expand to make room DBObjectBaseField[] newTable = new DBObjectBaseField[table.length + 1]; if (table.length == 0) { newTable[0] = value; } else { boolean found = false; int j = 0; int i = 0; while (j < newTable.length) { if (i < table.length) { DBObjectBaseField field = table[i]; if (!found && value.getID() < field.getID()) { // insert into the beginning or middle newTable[j++] = value; found = true; } else { newTable[j++] = table[i++]; } } else { // append to the end newTable[j++] = value; } } } table = newTable; return; } // else, we're replacing table[index] = value; } /** * Inserts a DBObjectBaseField into this DBBaseFieldTable */ public synchronized void put(DBObjectBaseField value) { putNoSync(value); } /** * <p>Inserts a DBObjectBaseField into this DBBaseFieldTable.</p> * * <p>This put is not sync'ed, and should only be used with * higher level sync provisions.</p> */ public void putNoSyncNoRemove(DBObjectBaseField value) { putNoSync(value); } /** * Removes the DBObjectBaseField with the given id from this DBBaseFieldTable. */ private void removeNoSync(short key) { this.modGen++; int index = java.util.Arrays.binarySearch(table, key); if (index < 0) { return; } // we'll need to shrink by one DBObjectBaseField[] newTable = new DBObjectBaseField[table.length - 1]; for (int j = 0, i = 0; i < table.length; i++) { if (i != index) { newTable[j++] = table[i]; } } table = newTable; } /** * Removes the DBObjectBaseField with the given id from this DBBaseFieldTable. */ public synchronized void remove(short key) { removeNoSync(key); } /** * Clears this DBBaseFieldTable. */ public synchronized void clear() { this.modGen++; table = new DBObjectBaseField[0]; } /** * This unsynchronized private helper method looks up * DBObjectBaseFields by name, using a case-insensitive comparison. */ private final DBObjectBaseField findByName(String name) { for (DBObjectBaseField field: table) { if (field.getName().equalsIgnoreCase(name)) { return field; } } return null; } /** * <p>Returns a non-editable Collection view of this DBBaseFieldTable.</p> */ public Collection<DBObjectBaseField> values() { return new DBBaseFieldTableContainer(this); } } /*------------------------------------------------------------------------------ class DBBaseFieldTableEnumerator ------------------------------------------------------------------------------*/ /** * A DBBaseFieldTable enumerator class. This class should remain opaque * to the client. It will use the Enumeration interface. */ final class DBBaseFieldTableEnumerator implements Enumeration { short index; DBObjectBaseField table[]; private DBBaseFieldTable parent; private int modGen; /* -- */ DBBaseFieldTableEnumerator(DBObjectBaseField table[], DBBaseFieldTable parent) { this.table = table; this.index = 0; this.parent = parent; this.modGen = parent.getModGen(); } public boolean hasMoreElements() { if (parent.getModGen() != this.modGen) { throw new ConcurrentModificationException(); } return index < table.length; } public Object nextElement() { if (parent.getModGen() != this.modGen) { throw new ConcurrentModificationException(); } return table[index++]; } } /*------------------------------------------------------------------------------ class DBBaseFieldTableIterator ------------------------------------------------------------------------------*/ /** * A DBBaseFieldTable Iterator class. This class should remain opaque * to the client. It will use the Iterator interface. */ final class DBBaseFieldTableIterator implements Iterator<DBObjectBaseField> { short index; DBObjectBaseField table[]; private DBBaseFieldTable parent; private int modGen; /* -- */ DBBaseFieldTableIterator(DBObjectBaseField table[], DBBaseFieldTable parent) { this.table = table; this.index = 0; this.parent = parent; this.modGen = parent.getModGen(); } public boolean hasNext() { if (parent.getModGen() != this.modGen) { throw new ConcurrentModificationException(); } return index < table.length; } public DBObjectBaseField next() { if (parent.getModGen() != this.modGen) { throw new ConcurrentModificationException(); } return table[index++]; } public void remove() { throw new UnsupportedOperationException(); } } /*------------------------------------------------------------------------------ class DBBaseFieldTableBuiltInIterator ------------------------------------------------------------------------------*/ /** * <p>A DBBaseFieldTable Iterator class that iterates over the built-in * field definitions in a DBObjectBase, in ascending field id order.</p> * * <p>This class should remain opaque to the client.</p> */ final class DBBaseFieldTableBuiltInIterator implements Iterator<DBObjectBaseField> { short index; DBObjectBaseField table[]; private DBBaseFieldTable parent; private int modGen; /* -- */ DBBaseFieldTableBuiltInIterator(DBObjectBaseField table[], DBBaseFieldTable parent) { this.table = table; this.index = 0; this.parent = parent; this.modGen = parent.getModGen(); } public boolean hasNext() { if (parent.getModGen() != this.modGen) { throw new ConcurrentModificationException(); } // we know all the built-ins will come before the custom fields, // because we the table is kept in ascending field id order. when // we see a non-builtIn() field def, we'll stop. return index < table.length && table[index].isBuiltIn(); } public DBObjectBaseField next() { if (parent.getModGen() != this.modGen) { throw new ConcurrentModificationException(); } return table[index++]; } public void remove() { throw new UnsupportedOperationException(); } } /*------------------------------------------------------------------------------ class DBBaseFieldTableContainer ------------------------------------------------------------------------------*/ final class DBBaseFieldTableContainer extends AbstractCollection<DBObjectBaseField> { private DBBaseFieldTable parent; /* -- */ DBBaseFieldTableContainer(DBBaseFieldTable parent) { this.parent = parent; } public int size() { return parent.size(); } public Iterator<DBObjectBaseField> iterator() { return parent.iterator(); } }