/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-04 Wolfgang M. Meier
* wolfgang@exist-db.org
* http://exist-db.org
*
* This program 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
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.dom;
import java.util.Iterator;
import org.exist.util.hashtable.AbstractHashtable;
/**
* A pool for QNames. This is a temporary pool for QName objects to avoid
* allocating the same QName multiple times. If the pool is full, it will just be
* cleared.
*
* @author wolf
*/
public class QNamePool extends AbstractHashtable {
private QName[] values;
private QName temp = new QName("", "");
public QNamePool() {
super(512);
values = new QName[tabSize];
}
public QNamePool(int iSize) {
super(iSize);
values = new QName[tabSize];
}
/**
* Return a QName object for the given local name, namespace and
* prefix. Return null if the QName has not yet been added to the pool.
*
* @param type
* @param namespaceURI
* @param localName
* @param prefix
* @return QName object
*/
public QName get(byte type, String namespaceURI, String localName, String prefix) {
temp.setLocalName(localName);
temp.setNamespaceURI(namespaceURI);
temp.setPrefix(prefix);
temp.setNameType(type);
int idx = temp.hashCode() % tabSize;
if (idx < 0)
idx *= -1;
if (values[idx] == null)
return null; // key does not exist
else if (values[idx].equals(temp)) {
return values[idx];
}
int rehashVal = rehash(idx);
for (int i = 0; i < tabSize; i++) {
idx = (idx + rehashVal) % tabSize;
if (values[idx] == null) {
return null; // key not found
} else if (values[idx].equals(temp)) {
return values[idx];
}
}
return null;
}
/**
* Add a QName, consisting of namespace, local name and prefix, to the
* pool.
*/
public QName add(byte type, String namespaceURI, String localName, String prefix) {
temp.setLocalName(localName);
temp.setNamespaceURI(namespaceURI);
temp.setPrefix(prefix);
temp.setNameType(type);
try {
return insert(temp);
} catch(HashtableOverflowException e) {
// just clear the pool and try again
values = new QName[tabSize];
items = 0;
try {
return insert(temp);
} catch (HashtableOverflowException e1) {
}
// should never happen, but just to be sure
return new QName(temp);
}
}
protected QName insert(QName value) throws HashtableOverflowException {
if (value == null)
throw new IllegalArgumentException("Illegal value: null");
int idx = value.hashCode() % tabSize;
if (idx < 0)
idx *= -1;
int bucket = -1;
// look for an empty bucket
if (values[idx] == null) {
values[idx] = new QName(value);
++items;
return values[idx];
} else if (values[idx] == REMOVED) {
// remember the bucket, but continue to check
// for duplicate keys
bucket = idx;
} else if (values[idx].equals(value)) {
// duplicate value
return values[idx];
}
// System.out.println("Hash collision: " + value + " with " + values[idx]);
int rehashVal = rehash(idx);
int rehashCnt = 1;
for (int i = 0; i < tabSize; i++) {
idx = (idx + rehashVal) % tabSize;
if (values[idx] == REMOVED) {
bucket = idx;
} else if (values[idx] == null) {
if (bucket > -1) {
// store key into the empty bucket first found
idx = bucket;
}
values[idx] = new QName(value);
++items;
return values[idx];
} else if (values[idx].equals(value)) {
// duplicate value
return values[idx];
}
++rehashCnt;
}
// should never happen, but just to be sure:
// if the key has not been inserted yet, do it now
if (bucket > -1) {
values[bucket] = new QName(value);
++items;
return values[bucket];
}
throw new HashtableOverflowException();
}
protected int rehash(int iVal) {
int retVal = (iVal + iVal / 2) % tabSize;
if (retVal == 0)
retVal = 1;
return retVal;
}
public Iterator iterator() {
return null;
}
public Iterator valueIterator() {
return null;
}
}