/*
 *  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: QNamePool.java 1577 2005-08-14 17:29:40Z wolfgang_m $
 */
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];
    }

	/**
	 * 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;
	}
}
