blob: 7350da091c22ae4f829ca0e99dff7a79fcb71d00 [file] [log] [blame]
/*
Copyright 2001 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.batik.dom.util;
/**
* This class represents a doubly indexed hash table.
*
* @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
* @version $Id$
*/
public class DoublyIndexedTable {
/**
* The initial capacity
*/
protected int initialCapacity;
/**
* The underlying array
*/
protected Entry[] table;
/**
* The number of entries
*/
protected int count;
/**
* Creates a new DoublyIndexedTable.
*/
public DoublyIndexedTable() {
this(16);
}
/**
* Creates a new DoublyIndexedTable.
* @param c The inital capacity.
*/
public DoublyIndexedTable(int c) {
initialCapacity = c;
table = new Entry[c];
}
/**
* Returns the size of this table.
*/
public int size() {
return count;
}
/**
* Puts a value in the table.
* @return the old value or null
*/
public Object put(Object o1, Object o2, Object value) {
int hash = hashCode(o1, o2) & 0x7FFFFFFF;
int index = hash % table.length;
for (Entry e = table[index]; e != null; e = e.next) {
if ((e.hash == hash) && e.match(o1, o2)) {
Object old = e.value;
e.value = value;
return old;
}
}
// The key is not in the hash table
int len = table.length;
if (count++ >= (len * 3) >>> 2) {
rehash();
index = hash % table.length;
}
Entry e = new Entry(hash, o1, o2, value, table[index]);
table[index] = e;
return null;
}
/**
* Gets the value of an entry
* @return the value or null
*/
public Object get(Object o1, Object o2) {
int hash = hashCode(o1, o2) & 0x7FFFFFFF;
int index = hash % table.length;
for (Entry e = table[index]; e != null; e = e.next) {
if ((e.hash == hash) && e.match(o1, o2)) {
return e.value;
}
}
return null;
}
/**
* Removes an entry from the table.
* @return the value or null
*/
public Object remove(Object o1, Object o2) {
int hash = hashCode(o1, o2) & 0x7FFFFFFF;
int index = hash % table.length;
Entry e = table[index];
if (e == null) {
return null;
}
if (e.hash == hash && e.match(o1, o2)) {
table[index] = e.next;
count--;
return e.value;
}
Entry prev = e;
for (e = e.next; e != null; prev = e, e = e.next) {
if (e.hash == hash && e.match(o1, o2)) {
prev.next = e.next;
count--;
return e.value;
}
}
return null;
}
/**
* Returns an array of all of the values in the table.
*/
public Object[] getValuesArray() {
Object[] values = new Object[count];
int i = 0;
for (int index = 0; index < table.length; index++) {
for (Entry e = table[index]; e != null; e = e.next) {
values[i++] = e.value;
}
}
return values;
}
/**
* Clears the table.
*/
public void clear() {
table = new Entry[initialCapacity];
count = 0;
}
/**
* Rehash the table
*/
protected void rehash() {
Entry[] oldTable = table;
table = new Entry[oldTable.length * 2 + 1];
for (int i = oldTable.length-1; i >= 0; i--) {
for (Entry old = oldTable[i]; old != null;) {
Entry e = old;
old = old.next;
int index = e.hash % table.length;
e.next = table[index];
table[index] = e;
}
}
}
/**
* Computes a hash code corresponding to the given objects.
*/
protected int hashCode(Object o1, Object o2) {
int result = (o1 == null) ? 0 : o1.hashCode();
return result ^ ((o2 == null) ? 0 : o2.hashCode());
}
/**
* To manage collisions
*/
protected static class Entry {
/**
* The hash code
*/
public int hash;
/**
* The first key
*/
public Object key1;
/**
* The second key
*/
public Object key2;
/**
* The value
*/
public Object value;
/**
* The next entry
*/
public Entry next;
/**
* Creates a new entry
*/
public Entry(int hash, Object key1, Object key2, Object value, Entry next) {
this.hash = hash;
this.key1 = key1;
this.key2 = key2;
this.value = value;
this.next = next;
}
/**
* Whether this entry match the given keys.
*/
public boolean match(Object o1, Object o2) {
if (key1 != null) {
if (!key1.equals(o1)) {
return false;
}
} else if (o1 != null) {
return false;
}
if (key2 != null) {
return key2.equals(o2);
}
return o2 == null;
}
}
}