diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java
index 7df78e9c..b809808f 100644
--- a/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java
+++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserRepository.java
@@ -1,5 +1,5 @@
//
-// $Id: UserRepository.java,v 1.15 2001/08/12 01:34:31 mdb Exp $
+// $Id: UserRepository.java,v 1.16 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -31,7 +31,7 @@ import com.samskivert.Log;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.MySQLRepository;
import com.samskivert.jdbc.jora.*;
-import com.samskivert.util.IntMap;
+import com.samskivert.util.HashIntMap;
/**
* Interfaces with the RDBMS in which the user information is stored. The
@@ -334,7 +334,7 @@ public class UserRepository extends MySQLRepository
ids.append(userids[i]);
}
- final IntMap map = new IntMap();
+ final HashIntMap map = new HashIntMap();
// do the query
execute(new Operation () {
diff --git a/projects/samskivert/src/java/com/samskivert/util/Collections.java b/projects/samskivert/src/java/com/samskivert/util/Collections.java
new file mode 100644
index 00000000..92adee25
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/util/Collections.java
@@ -0,0 +1,256 @@
+//
+// $Id: Collections.java,v 1.1 2001/09/15 17:22:11 mdb Exp $
+
+package com.samskivert.util;
+
+import java.util.*;
+
+/**
+ * Like the java.util class of the same name, the
+ * Collections class provides utility functions related to
+ * the collections provided by the com.samskivert.util
+ * package.
+ */
+public class Collections
+{
+ /**
+ * Returns a synchronized (thread-safe) int map backed by the
+ * specified int map. In order to guarantee serial access, it is
+ * critical that all access to the backing int map is
+ * accomplished through the returned int map.
+ *
+ *
It is imperative that the user manually synchronize on the + * returned int map when iterating over any of its collection views: + * + *
+ * IntMap m = Collections.synchronizedIntMap(new HashIntMap());
+ * ...
+ * Set s = m.keySet(); // Needn't be in synchronized block
+ * ...
+ * synchronized(m) { // Synchronizing on m, not s!
+ * Iterator i = s.iterator(); // Must be in synchronized block
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ *
+ *
+ * Failure to follow this advice may result in non-deterministic
+ * behavior.
+ *
+ * The returned map will be serializable if the specified map is
+ * serializable.
+ *
+ * @param m the int map to be "wrapped" in a synchronized int map.
+ *
+ * @return a synchronized view of the specified int map.
+ */
+ public static IntMap synchronizedIntMap(IntMap m) {
+ return new SynchronizedIntMap(m);
+ }
+
+ /**
+ * Horked from the Java util class and extended for IntMap.
+ */
+ protected static class SynchronizedIntMap implements IntMap
+ {
+ private IntMap m; // Backing Map
+ private Object mutex; // Object on which to synchronize
+
+ SynchronizedIntMap(IntMap m) {
+ if (m == null) {
+ throw new NullPointerException();
+ }
+ this.m = m;
+ mutex = this;
+ }
+
+ SynchronizedIntMap(IntMap m, Object mutex) {
+ this.m = m;
+ this.mutex = mutex;
+ }
+
+ public int size() {
+ synchronized(mutex) {return m.size();}
+ }
+
+ public boolean isEmpty(){
+ synchronized(mutex) {return m.isEmpty();}
+ }
+
+ public boolean containsKey(int key) {
+ synchronized(mutex) {return m.containsKey(key);}
+ }
+
+ public boolean containsKey(Object key) {
+ synchronized(mutex) {return m.containsKey(key);}
+ }
+
+ public boolean containsValue(Object value){
+ synchronized(mutex) {return m.containsValue(value);}
+ }
+
+ public Object get(int key) {
+ synchronized(mutex) {return m.get(key);}
+ }
+
+ public Object get(Object key) {
+ synchronized(mutex) {return m.get(key);}
+ }
+
+ public Object put(int key, Object value) {
+ synchronized(mutex) {return m.put(key, value);}
+ }
+
+ public Object put(Object key, Object value) {
+ synchronized(mutex) {return m.put(key, value);}
+ }
+
+ public Object remove(int key) {
+ synchronized(mutex) {return m.remove(key);}
+ }
+
+ public Object remove(Object key) {
+ synchronized(mutex) {return m.remove(key);}
+ }
+
+ public void putAll(Map map) {
+ synchronized(mutex) {m.putAll(map);}
+ }
+
+ public void clear() {
+ synchronized(mutex) {m.clear();}
+ }
+
+ private transient Set keySet = null;
+ private transient Set entrySet = null;
+ private transient Collection values = null;
+
+ public Set keySet() {
+ synchronized(mutex) {
+ if (keySet==null)
+ keySet = new SynchronizedSet(m.keySet(), mutex);
+ return keySet;
+ }
+ }
+
+ public Set entrySet() {
+ synchronized(mutex) {
+ if (entrySet==null)
+ entrySet = new SynchronizedSet(m.entrySet(), mutex);
+ return entrySet;
+ }
+ }
+
+ public Collection values() {
+ synchronized(mutex) {
+ if (values==null)
+ values = new SynchronizedCollection(m.values(), mutex);
+ return values;
+ }
+ }
+
+ public boolean equals(Object o) {
+ synchronized(mutex) {return m.equals(o);}
+ }
+
+ public int hashCode() {
+ synchronized(mutex) {return m.hashCode();}
+ }
+
+ public String toString() {
+ synchronized(mutex) {return m.toString();}
+ }
+ }
+
+ /**
+ * I wish I could use this from the java.util.Collections
+ * class, but those crazy kids at Sun are always using private and
+ * default access and pointlessly preventing people from properly
+ * reusing their code. Yay!
+ */
+ protected static class SynchronizedSet
+ extends SynchronizedCollection implements Set {
+ SynchronizedSet(Set s) {
+ super(s);
+ }
+ SynchronizedSet(Set s, Object mutex) {
+ super(s, mutex);
+ }
+
+ public boolean equals(Object o) {
+ synchronized(mutex) {return c.equals(o);}
+ }
+ public int hashCode() {
+ synchronized(mutex) {return c.hashCode();}
+ }
+ }
+
+ /**
+ * I wish I could use this from the java.util.Collections
+ * class, but those crazy kids at Sun are always using private and
+ * default access and pointlessly preventing people from properly
+ * reusing their code. Yay!
+ */
+ protected static class SynchronizedCollection
+ implements Collection {
+ Collection c; // Backing Collection
+ Object mutex; // Object on which to synchronize
+
+ SynchronizedCollection(Collection c) {
+ if (c==null)
+ throw new NullPointerException();
+ this.c = c;
+ mutex = this;
+ }
+ SynchronizedCollection(Collection c, Object mutex) {
+ this.c = c;
+ this.mutex = mutex;
+ }
+
+ public int size() {
+ synchronized(mutex) {return c.size();}
+ }
+ public boolean isEmpty() {
+ synchronized(mutex) {return c.isEmpty();}
+ }
+ public boolean contains(Object o) {
+ synchronized(mutex) {return c.contains(o);}
+ }
+ public Object[] toArray() {
+ synchronized(mutex) {return c.toArray();}
+ }
+ public Object[] toArray(Object[] a) {
+ synchronized(mutex) {return c.toArray(a);}
+ }
+
+ public Iterator iterator() {
+ return c.iterator(); // Must be manually synched by user!
+ }
+
+ public boolean add(Object o) {
+ synchronized(mutex) {return c.add(o);}
+ }
+ public boolean remove(Object o) {
+ synchronized(mutex) {return c.remove(o);}
+ }
+
+ public boolean containsAll(Collection coll) {
+ synchronized(mutex) {return c.containsAll(coll);}
+ }
+ public boolean addAll(Collection coll) {
+ synchronized(mutex) {return c.addAll(coll);}
+ }
+ public boolean removeAll(Collection coll) {
+ synchronized(mutex) {return c.removeAll(coll);}
+ }
+ public boolean retainAll(Collection coll) {
+ synchronized(mutex) {return c.retainAll(coll);}
+ }
+ public void clear() {
+ synchronized(mutex) {c.clear();}
+ }
+ public String toString() {
+ synchronized(mutex) {return c.toString();}
+ }
+ }
+}
diff --git a/projects/samskivert/src/java/com/samskivert/util/HashIntMap.java b/projects/samskivert/src/java/com/samskivert/util/HashIntMap.java
index 3bf59c5f..deb71114 100644
--- a/projects/samskivert/src/java/com/samskivert/util/HashIntMap.java
+++ b/projects/samskivert/src/java/com/samskivert/util/HashIntMap.java
@@ -1,5 +1,5 @@
//
-// $Id: HashIntMap.java,v 1.3 2001/08/11 22:43:29 mdb Exp $
+// $Id: HashIntMap.java,v 1.4 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -20,63 +20,81 @@
package com.samskivert.util;
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
+import java.util.*;
/**
- * An int map is like a hashmap, but with integers as keys. We avoid the
- * annoyance of having to create integer objects every time we want to
- * lookup or insert values.
+ * An int map is like a regular map, but with integers as keys. We avoid
+ * the annoyance of having to create integer objects every time we want to
+ * lookup or insert values. The hash int map is an int map that uses a
+ * hashtable mechanism to store its key/value mappings.
*/
-public class IntMap
+public class HashIntMap
+ extends AbstractMap implements IntMap
{
+ /**
+ * The default number of buckets to use for the hash table.
+ */
public final static int DEFAULT_BUCKETS = 64;
- public IntMap (int buckets)
+ /**
+ * Constructs an empty hash int map with the specified number of hash
+ * buckets.
+ */
+ public HashIntMap (int buckets)
{
_buckets = new Record[buckets];
}
- public IntMap ()
+ /**
+ * Constructs an empty hash int map with the default number of hash
+ * buckets.
+ */
+ public HashIntMap ()
{
this(DEFAULT_BUCKETS);
}
- public synchronized int size ()
+ // documentation inherited
+ public int size ()
{
return _size;
}
- public synchronized void put (int key, Object value)
+ // documentation inherited
+ public boolean containsKey (Object key)
{
- int index = Math.abs(key)%_buckets.length;
- Record rec = _buckets[index];
-
- // either we start a new chain
- if (rec == null) {
- _buckets[index] = new Record(key, value);
- _size++; // we're bigger
- return;
- }
-
- // or we replace an element in an existing chain
- Record prev = rec;
- for (; rec != null; rec = rec.next) {
- if (rec.key == key) {
- rec.value = value; // we're not bigger
- return;
- }
- prev = rec;
- }
-
- // or we append it to this chain
- prev.next = new Record(key, value);
- _size++; // we're bigger
+ return containsKey(((Integer)key).intValue());
}
- public synchronized Object get (int key)
+ // documentation inherited
+ public boolean containsKey (int key)
{
- int index = Math.abs(key)%_buckets.length;
+ return get(key) != null;
+ }
+
+ // documentation inherited
+ public boolean containsValue (Object o)
+ {
+ for (int i = 0; i < _buckets.length; i++) {
+ for (Record r = _buckets[i]; r != null; r = r.next) {
+ if (r.value.equals(o)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // documentation inherited
+ public Object get (Object key)
+ {
+ return get(((Integer)key).intValue());
+ }
+
+ // documentation inherited
+ public Object get (int key)
+ {
+ int index = Math.abs(key) % _buckets.length;
for (Record rec = _buckets[index]; rec != null; rec = rec.next) {
if (rec.key == key) {
return rec.value;
@@ -85,16 +103,59 @@ public class IntMap
return null;
}
- public boolean contains (int key)
+ // documentation inherited
+ public Object put (Object key, Object value)
{
- return get(key) != null;
+ return put(((Integer)key).intValue(), value);
}
- public synchronized Object remove (int key)
+ // documentation inherited
+ public Object put (int key, Object value)
{
+ // disallow null values
+ if (value == null) {
+ throw new IllegalArgumentException();
+ }
+
int index = Math.abs(key)%_buckets.length;
Record rec = _buckets[index];
+ // either we start a new chain
+ if (rec == null) {
+ _buckets[index] = new Record(key, value);
+ _size++; // we're bigger
+ return null;
+ }
+
+ // or we replace an element in an existing chain
+ Record prev = rec;
+ for (; rec != null; rec = rec.next) {
+ if (rec.key == key) {
+ Object ovalue = rec.value;
+ rec.value = value; // we're not bigger
+ return ovalue;
+ }
+ prev = rec;
+ }
+
+ // or we append it to this chain
+ prev.next = new Record(key, value);
+ _size++; // we're bigger
+ return null;
+ }
+
+ // documentation inherited
+ public Object remove (Object key)
+ {
+ return remove(((Integer)key).intValue());
+ }
+
+ // documentation inherited
+ public Object remove (int key)
+ {
+ int index = Math.abs(key) % _buckets.length;
+ Record rec = _buckets[index];
+
// if there's no chain, there's no object
if (rec == null) {
return null;
@@ -120,7 +181,8 @@ public class IntMap
return null;
}
- public synchronized void clear ()
+ // documentation inherited
+ public void clear ()
{
// abandon all of our hash chains (the joy of garbage collection)
for (int i = 0; i < _buckets.length; i++) {
@@ -130,17 +192,99 @@ public class IntMap
_size = 0;
}
- public Enumeration keys ()
+ // documentation inherited
+ public Set entrySet ()
{
- return new Enumerator(_buckets, true);
+ return new AbstractSet() {
+ public int size ()
+ {
+ return _size;
+ }
+
+ public Iterator iterator ()
+ {
+ return new EntryIterator();
+ }
+ };
}
- public Enumeration elements ()
+ protected class EntryIterator implements Iterator
{
- return new Enumerator(_buckets, false);
+ public boolean hasNext ()
+ {
+ // if we're pointing to an entry, we've got more entries
+ if (_record != null) {
+ return true;
+ }
+
+ // search backward through the buckets looking for the next
+ // non-empty hash chain
+ while (_index-- > 0) {
+ if ((_record = _buckets[_index]) != null) {
+ return true;
+ }
+ }
+
+ // found no non-empty hash chains, we're done
+ return false;
+ }
+
+ public Object next ()
+ {
+ // if we're not pointing to an entry, search for the next
+ // non-empty hash chain
+ if (_record == null) {
+ while ((_index-- > 0) &&
+ ((_record = _buckets[_index]) == null));
+ }
+
+ // keep track of the last thing we returned
+ _last = _record;
+
+ // if we found a record, return it's value and move our record
+ // reference to it's successor
+ if (_record != null) {
+ _record = _last.next;
+ return _last;
+ }
+
+ throw new NoSuchElementException();
+ }
+
+ public void remove ()
+ {
+ if (_last == null) {
+ throw new IllegalStateException();
+ }
+
+ // remove the record the hard way
+ HashIntMap.this.remove(_last.key);
+ _last = null;
+ }
+
+ protected int _index = _buckets.length;
+ protected Record _record, _last;
}
- protected static class Record
+ /**
+ * Returns an iteration over the keys of this hash int map. The keys
+ * are returned as Integer objects.
+ */
+ public Iterator keys ()
+ {
+ return keySet().iterator();
+ }
+
+ /**
+ * Returns an iteration over the elements (values) of this hash int
+ * map.
+ */
+ public Iterator elements ()
+ {
+ return values().iterator();
+ }
+
+ protected static class Record implements Entry
{
public Record next;
public int key;
@@ -151,86 +295,74 @@ public class IntMap
this.key = key;
this.value = value;
}
+
+ public Object getKey ()
+ {
+ return new Integer(key);
+ }
+
+ public Object getValue ()
+ {
+ return value;
+ }
+
+ public Object setValue (Object value)
+ {
+ Object ovalue = this.value;
+ this.value = value;
+ return ovalue;
+ }
+
+ public boolean equals (Object o)
+ {
+ if (o instanceof Record) {
+ Record or = (Record)o;
+ return (key == or.key) && value.equals(or.value);
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode ()
+ {
+ return key ^ ((value == null) ? 0 : value.hashCode());
+ }
}
- class Enumerator implements Enumeration
- {
- public Enumerator (Record[] buckets, boolean returnKeys)
- {
- _buckets = buckets;
- _index = buckets.length;
- _returnKeys = returnKeys;
- }
+ public static void main (String[] args)
+ {
+ HashIntMap table = new HashIntMap();
- public boolean hasMoreElements ()
- {
- // if we're pointing to an entry, we've got more entries
- if (_record != null) {
- return true;
- }
+ System.out.print("Inserting: ");
+ for (int i = 10; i < 100; i++) {
+ Integer value = new Integer(i);
+ table.put(i, value);
+ System.out.print("(" + i + "," + value + ")");
+ }
+ System.out.println("");
- // search backward through the buckets looking for the next
- // non-empty hash chain
- while (_index-- > 0) {
- if ((_record = _buckets[_index]) != null) {
- return true;
- }
- }
+ System.out.print("Looking up: ");
+ for (int i = 10; i < 100; i++) {
+ System.out.print("(" + i + "," + table.get(i) + ")");
+ }
+ System.out.println("");
- // found no non-empty hash chains, we're done
- return false;
- }
+ System.out.println("Keys: " +
+ StringUtil.toString(table.keys()));
+ System.out.println("Elems: " +
+ StringUtil.toString(table.elements()));
- public Object nextElement ()
- {
- // if we're not pointing to an entry, search for the next
- // non-empty hash chain
- if (_record == null) {
- while ((_index-- > 0) &&
- ((_record = _buckets[_index]) == null));
- }
+ System.out.print("Removing: ");
+ for (int i = 10; i < 98; i++) {
+ System.out.print("(" + i + "," + table.remove(i) + ")");
+ }
+ System.out.println("");
- // if we found a record, return it's value and move our record
- // reference to it's successor
- if (_record != null) {
- Record r = _record;
- _record = r.next;
- return _returnKeys ? new Integer(r.key) : r.value;
- }
-
- throw new NoSuchElementException("IntMapEnumerator");
- }
-
- private int _index;
- private Record _record;
- private Record[] _buckets;
- private boolean _returnKeys;
- }
-
-// public static void main (String[] args)
-// {
-// IntMap table = new IntMap();
-
-// System.out.print("Inserting: ");
-// for (int i = 10; i < 100; i++) {
-// Integer value = new Integer(i);
-// table.put(i, value);
-// System.out.print("(" + i + "," + value + ")");
-// }
-// System.out.println("");
-
-// System.out.print("Looking up: ");
-// for (int i = 10; i < 100; i++) {
-// System.out.print("(" + i + "," + table.get(i) + ")");
-// }
-// System.out.println("");
-
-// System.out.print("Removing: ");
-// for (int i = 10; i < 100; i++) {
-// System.out.print("(" + i + "," + table.remove(i) + ")");
-// }
-// System.out.println("");
-// }
+ System.out.println("Keys: " +
+ StringUtil.toString(table.keys()));
+ System.out.println("Elems: " +
+ StringUtil.toString(table.elements()));
+ }
private Record[] _buckets;
private int _size;
diff --git a/projects/samskivert/src/java/com/samskivert/util/IntMap.java b/projects/samskivert/src/java/com/samskivert/util/IntMap.java
new file mode 100644
index 00000000..dff974e9
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/util/IntMap.java
@@ -0,0 +1,78 @@
+//
+// $Id: IntMap.java,v 1.1 2001/09/15 17:22:11 mdb Exp $
+//
+// samskivert library - useful routines for java programs
+// Copyright (C) 2001 Michael Bayne
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.util;
+
+import java.util.Map;
+
+/**
+ * An int map is a map that uses integers as keys and provides accessors
+ * that eliminate the need to create and manipulate superfluous
+ * Integer objects. It extends the Map interface
+ * and therefore provides all of the standard accessors (for which
+ * Integer objects should be supplied as keys).
+ */
+public interface IntMap extends Map
+{
+ /**
+ * Returns true if this map contains a mapping for the
+ * specified key.
+ *
+ * @param key key whose presence in this map is to be tested.
+ *
+ * @return true if this map contains a mapping for the
+ * specified key.
+ */
+ public boolean containsKey (int key);
+
+ /**
+ * Returns the value to which this map maps the specified key. Returns
+ * null if the map contains no mapping for this key.
+ *
+ * @param key key whose associated value is to be returned.
+ *
+ * @return the value to which this map maps the specified key, or
+ * null if the map contains no mapping for this key.
+ */
+ public Object get (int key);
+
+ /**
+ * Associates the specified value with the specified key in this map.
+ * If the map previously contained a mapping for this key, the old
+ * value is replaced.
+ *
+ * @param key key with which the specified value is to be associated.
+ * @param value value to be associated with the specified key.
+ *
+ * @return previous value associated with specified key, or
+ * null if there was no mapping for key.
+ */
+ public Object put (int key, Object value);
+
+ /**
+ * Removes the mapping for this key from this map if present.
+ *
+ * @param key key whose mapping is to be removed from the map.
+ *
+ * @return previous value associated with specified key, or
+ * null if there was no mapping for key.
+ */
+ public Object remove (int key);
+}
diff --git a/projects/samskivert/src/java/com/samskivert/util/IntervalManager.java b/projects/samskivert/src/java/com/samskivert/util/IntervalManager.java
index ebf916fc..5bf725b2 100644
--- a/projects/samskivert/src/java/com/samskivert/util/IntervalManager.java
+++ b/projects/samskivert/src/java/com/samskivert/util/IntervalManager.java
@@ -1,5 +1,5 @@
//
-// $Id: IntervalManager.java,v 1.3 2001/08/11 22:43:29 mdb Exp $
+// $Id: IntervalManager.java,v 1.4 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -21,6 +21,7 @@
package com.samskivert.util;
import java.util.*;
+import java.util.Collections;
import com.samskivert.Log;
/**
@@ -245,7 +246,7 @@ public class IntervalManager extends Thread
// to rewrite this such that we can add and remove intervaleds much
// faster.
protected static ArrayList _schedule = new ArrayList();
- protected static IntMap _hash = new IntMap();
+ protected static HashIntMap _hash = new HashIntMap();
protected static long _nextwake = Long.MAX_VALUE;
protected static int _helpers = 0; // # of created helpers