/
area/
classes/net/sourceforge/pain/logic/
classes/net/sourceforge/pain/logic/event/
classes/net/sourceforge/pain/logic/fn/util/
classes/net/sourceforge/pain/network/console/
classes/net/sourceforge/pain/plugin/
classes/net/sourceforge/pain/plugin/reset/
classes/net/sourceforge/pain/plugin/shutdown/
classes/net/sourceforge/pain/plugin/social/
classest/net/sourceforge/pain/db/data/
doc/
doc/paindb/resources/
src/net/sourceforge/pain/logic/
src/net/sourceforge/pain/logic/event/
src/net/sourceforge/pain/logic/fn/util/
src/net/sourceforge/pain/network/console/
src/net/sourceforge/pain/network/console/telnet/
src/net/sourceforge/pain/plugin/
src/net/sourceforge/pain/plugin/command/
src/net/sourceforge/pain/plugin/reset/
src/net/sourceforge/pain/plugin/shutdown/
src/net/sourceforge/pain/plugin/social/
src/net/sourceforge/pain/util/
tests/
tests/net/sourceforge/pain/db/data/
package net.sourceforge.pain.db;

import java.util.*;

/**
 * Base class for StringMap and StringSet
 */

abstract class DbAbstractStringMap extends DbCollection {

	static final AStringMapEntry[] ZERO_DATA = new AStringMapEntry[0];
	final static float DEFAULT_LOAD_FACTOR = 0.9F;
	private final static float DEFAULT_EXTENTION_FACTOR = 1.3F;

	final static int VALUES = 1;
	final static int KEYS = 2;
	final static int ENTRIES = 3;


	int nElements;
	AStringMapEntry[] data;
	private int modCount = 0;

	/**load from image during startup*/
	DbAbstractStringMap(final DbObject owner, final String[] image, final int fid) {
		super(owner, fid);
		restoreFromImage(image);
	}

	/** new object with set field during runtime */
	DbAbstractStringMap(final DbObject owner, final int fid) {
		super(owner, fid);
		nElements = 0;
		data = ZERO_DATA;
	}

	abstract Object createBackupImage();

	abstract void restoreFromImage(String[] image);

	final void restoreFromBackup(final Object o) {
		final String image[] = o == null ? DbConstants.ZERO_STRING_ARRAY : (String[]) o;
		_clear();
		restoreFromImage(image);
	}


	DbAbstractStringMap.AStringMapEntry[] _getData() {
		return data;
	}

	int _size() {
		return nElements;
	}

	void _clear() {
		if (nElements == 0) {
			return;
		}
		final int len = data.length;
		for (int i = 0; i < len; i++) {
			for (AStringMapEntry e = data[i]; e != null; e = e.next) {
				e.unlinkFromMap();
			}
		}
		modCount++;
		nElements = 0;
	}

	private int p_getIndex(final Object key) {
		return (key.hashCode() & 0x7FFFFFFF) % data.length;
	}


	private void p_remove(final AStringMapEntry e) {
		e.unlinkFromMap();
		nElements--;
		modCount++;
	}

	private void p_checkRehash(final int n) {
		final int newNElements = nElements + n;
		if (newNElements > data.length * DEFAULT_LOAD_FACTOR) {
			p_rehash(newNElements);
		}
	}

	private void p_rehash(final int minCapacity) {
		modCount++;
		int newCapacity = (int) (data.length * DEFAULT_EXTENTION_FACTOR);
		if (newCapacity < minCapacity) {
			newCapacity = (int) (minCapacity * DEFAULT_EXTENTION_FACTOR);
			if (newCapacity < 4) {
				newCapacity = 4;
			}
		}
		final AStringMapEntry[] oldData = data;
		data = new AStringMapEntry[newCapacity];
		final int oldLen = oldData.length;
		for (int i = 0; i < oldLen; i++) {
			for (AStringMapEntry e = oldData[i]; e != null;) {
				final AStringMapEntry next = e.next;
				e.rehash();
				e = next;
			}
		}
	}


	/* ---------------- MAP interface ---------------*/
	final int x_size() {
		owner.ensureReal();
		return nElements;
	}

	final boolean x_isEmpty() {
		return x_size() == 0;
	}


	final Object x_get(final Object key) {
		owner.ensureReal();
		if (nElements == 0 || key == null || !(key instanceof String)) {
			return null;
		}
		final int index = p_getIndex(key);
		for (AStringMapEntry e = data[index]; e != null; e = e.next) {
			if (e.key.equals(key)) {
				return e.value;
			}
		}
		return null;
	}

	final boolean x_containsValue(final Object value) {
		owner.ensureReal();
		if (nElements == 0 || value == null || !(value instanceof String)) {
			return false;
		}
		for (int i = data.length; --i >= 0;) {
			for (AStringMapEntry e = data[i]; e != null; e = e.next) {
				if (e.value.equals(value)) {
					return true;
				}
			}
		}
		return false;
	}

	final Iterator x_iterator(int type) {
		owner.ensureReal();
		return new MapIterator(type);
	}

	final Object[] x_toArray(int type) {
		owner.ensureReal();
		final Object[] arr = new Object[nElements];
		int j = 0;
		final int len = data.length;
		for (int i = 0; i < len; i++) {
			for (AStringMapEntry e = data[i]; e != null; e = e.next) {
				arr[j] = type == KEYS ? e.key : type == VALUES ? (Object) e.value : e;
				j++;
			}
		}
		return arr;
	}


	final Object[] x_toArray(int type, Object a[]) {
		owner.ensureReal();
		if (a.length < nElements) {
			a = (Object[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), nElements);
		}
		int j = 0;
		final int len = data.length;
		for (int i = 0; i < len; i++) {
			for (AStringMapEntry e = data[i]; e != null; e = e.next) {
				a[j] = type == KEYS ? e.key : type == VALUES ? (Object) e.value : e;
				j++;
			}
		}
		if (a.length > nElements) {
			a[nElements] = null;
		}
		return a;
	}

	final Object x_put(final String key, final String value) {
		owner.ensureReal();
		if (key == null || value == null) {
			throw new NullPointerException();
		}
		owner.onCollectionChange(this);
		p_checkRehash(1);
		final int index = p_getIndex(key);
		Object result = null;
		for (AStringMapEntry e = data[index]; e != null; e = e.next) {
			if (e.key.equals(key)) {
				result = e.value;
				e.value = value;
			}
		}
		modCount++;
		if (result == null) {
			new AStringMapEntry(key, value);
			nElements++;
		}
		return result;
	}

	final Object x_remove(final String key) {
		owner.ensureReal();
		if (nElements == 0 || key == null) {
			return null;
		}
		final int index = p_getIndex(key);
		for (AStringMapEntry e = data[index]; e != null; e = e.next) {
			if (e.key.equals(key)) {
				owner.onCollectionChange(this);
				p_remove(e);
				return e.value;
			}
		}
		return null;

	}


	final void x_clear() {
		owner.ensureReal();
		owner.onCollectionChange(this);
		_clear();
	}

	final Collection x_values() {
		return new Values();
	}

	final Set x_entrySet() {
		return new EntrySet();
	}

	final Set x_keySet() {
		return new Keys();
	}
	
	

	

//----------------- inners -----------------------//
	final class AStringMapEntry implements Map.Entry {

		AStringMapEntry next;
		private AStringMapEntry prev;
		final String key;
		String value;

		AStringMapEntry(final String key, final String value) {
			this.key = key;
			this.value = value;
			final int index = p_getIndex(key);
			next = data[index];
			data[index] = this;
			if (next != null) {
				next.prev = this;
			}
		}

		void rehash() {
			final int index = p_getIndex(key);
			next = data[index];
			data[index] = this;
			if (next != null) {
				next.prev = this;
			}
		}


		private void unlinkFromMap() {
			final int index = p_getIndex(key);
			if (data[index] == this) {
				data[index] = next;
			} else {
				prev.next = next;
			}
			if (next != null) {
				next.prev = prev;
			}
		}

		public Object getKey() {
			return new Keys();
		}

		public Object getValue() {
			return new Values();
		}

		public Object setValue(Object value) {
			final String newValue = (String) value;
			if (newValue == null) {
				throw new NullPointerException();
			}
			owner.ensureReal();
			owner.onCollectionChange(DbAbstractStringMap.this);
			value = this.value;
			this.value = newValue;
			return value;
		}


	}

	final class MapIterator implements Iterator {

		private int expectedModCount;
		private AStringMapEntry e = null;
		private int index = -1;
		private int nPassed;
		private int type;

		MapIterator(int type) {
			this.type = type;
			nPassed = 0;
			expectedModCount = modCount;
		}

		public boolean hasNext() {
			checkState();
			return (nPassed < nElements);
		}

		public Object next() {
			if (!hasNext()) {
				throw new NoSuchElementException();
			}
			e = (e == null || e.next == null) ? findWithNextIndex() : e.next;
			nPassed++;
			return type == VALUES ? e.value : type == KEYS ? (Object) e.key : e;
		}

		private AStringMapEntry findWithNextIndex() {
			final int len = data.length;
			while (++index < len) {
				final AStringMapEntry e = data[index];
				if (e != null) {
					return e;
				}
			}
			return null;
		}

		private AStringMapEntry findWithPrevIndex() {
			while (--index >= 0) {
				AStringMapEntry e = data[index];
				if (e != null) {
					while (e.next != null) {
						e = e.next;
					}
					return e;
				}
			}
			return null;
		}


		public void remove() {
			checkState();
			if (e == null) { //staying on first element
				throw new IllegalStateException();
			}
			final AStringMapEntry re = e;
			e = (e.prev == null) ? findWithPrevIndex() : e.prev;
			owner.onCollectionChange(DbAbstractStringMap.this);
			p_remove(re);
			expectedModCount++;
			nPassed--;
		}

		private void checkState() {
			owner.ensureReal();
			checkForComodifications();
		}

		private void checkForComodifications() {
			if (modCount != expectedModCount) {
				throw new ConcurrentModificationException();
			}
		}
	};




	final class Values implements Collection {

		Values() {
		}


		public int size() {
			owner.ensureReal();
			return nElements;
		}

		public boolean isEmpty() {
			owner.ensureReal();
			return nElements == 0;
		}

		public boolean contains(final Object o) {
			owner.ensureReal();
			if (o == null) {
				return false;
			}
			String value = (String) o;
			final int len = data.length;
			for (int i = 0; i < len; i++) {
				for (AStringMapEntry e = data[i]; e != null; e = e.next) {
					if (e.value.equals(value)) {
						return true;
					}
				}
			}
			return false;

		}

		public Iterator iterator() {
			owner.ensureReal();
			return new MapIterator(VALUES);
		}

		public Object[] toArray() {
			return x_toArray(VALUES);
		}


		public Object[] toArray(Object a[]) {
			return x_toArray(VALUES, a);
		}

		public boolean add(final Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean remove(final Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean containsAll(final Collection c) {
			owner.ensureReal();
			for (Iterator it = c.iterator(); it.hasNext();) {
				if (!x_containsValue(it.next())) {
					return false;
				}
			}
			return true;
		}


		public boolean addAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean removeAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean retainAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public void clear() {
			owner.ensureReal();
			_clear();
		}
	};

	final class Keys implements Set {
		Keys() {
		}

		public int size() {
			owner.ensureReal();
			return nElements;
		}

		public boolean isEmpty() {
			owner.ensureReal();
			return nElements == 0;
		}

		public boolean contains(final Object o) {
			return x_get(o) != null;
		}


		public Iterator iterator() {
			owner.ensureReal();
			return new MapIterator(KEYS);
		}

		public Object[] toArray() {
			return x_toArray(KEYS);
		}

		public Object[] toArray(Object a[]) {
			return x_toArray(KEYS, a);
		}

		public boolean add(final Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean remove(final Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean containsAll(final Collection c) {
			owner.ensureReal();
			for (Iterator it = c.iterator(); it.hasNext();) {
				if (x_get(it.next()) == null) {
					return false;
				}
			}
			return true;
		}

		public boolean addAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean retainAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean removeAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public void clear() {
			owner.ensureReal();
			_clear();
		}
	};

	final class EntrySet implements Set {
		EntrySet() {
		}

		public int size() {
			owner.ensureReal();
			return nElements;
		}

		public boolean isEmpty() {
			owner.ensureReal();
			return nElements == 0;
		}

		public boolean contains(final Object o) {
			owner.ensureReal();
			final AStringMapEntry f = (AStringMapEntry) o;
			final int len = data.length;
			for (int i = 0; i < len; i++) {
				for (AStringMapEntry e = data[i]; e != null; e = e.next) {
					if (e == f) {
						return true;
					}
				}
			}
			return false;
		}

		public Iterator iterator() {
			owner.ensureReal();
			return new MapIterator(ENTRIES);

		}

		public Object[] toArray() {
			return x_toArray(ENTRIES);
		}

		public Object[] toArray(final Object[] a) {
			return x_toArray(ENTRIES, a);
		}

		public boolean add(final Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean remove(final Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean containsAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean addAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean retainAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean removeAll(final Collection c) {
			throw new UnsupportedOperationException();
		}

		public void clear() {
			owner.ensureReal();
			_clear();
		}
	};

}