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

abstract class DbAbstractMap extends DbCollection {
	static final AMapEntry[] ZERO_DATA = new AMapEntry[0];
	final static float DEFAULT_LOAD_FACTOR = 0.9F;
	private final static float DEFAULT_EXTENTION_FACTOR = 1.3F;

	static final UniversalKey ukey = new UniversalKey();

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

	DbAbstractMap(final DbObject owner, final int fid) { // new object during runtime
		super(owner, fid);
		nElements = 0;
		data = ZERO_DATA;
	}

// ---- PAiN DB internal usage methods --- /// 
	final int _size() {
		return nElements;
	}

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

	AMapEntry[] _getData() {
		return data;
	}


	final void restoreFromBackup(final Object o) {
		_clear();
		restoreFromImage(o);
	}

// --  internal private helpers --
	private DbObject p_checkObject(final Object o) {
		final DbObject obj = (DbObject) o;
		obj.ensureReal();
		owner.ensureDb(obj);
		return obj;
	}

	private DbObject p_remove(final AMapEntry e) {
		final DbObject result = e.obj;
//		check();
		e.unlinkFromMap();
		nElements--;
		modCount++;
//		check();
		return result;
	}

	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 AMapEntry[] oldData = data;
		data = new AMapEntry[newCapacity];
		final int oldLen = oldData.length;
		for (int i = 0; i < oldLen; i++) {
			for (AMapEntry e = oldData[i]; e != null;) {
				final AMapEntry next = e.next;
				e.rehash();
				e = next;
			}
		}
//		check();
	}
	
// -------- ////
	

// --- MAP --//
// Map interface analog. We should not expose public Map interface from this class
// since one of it's subclasses has Set interface	
	
	final Object x_get(UniversalKey key) {
		owner.ensureReal();
		if (nElements == 0) {
			return null;
		}
		final int index = a_getIndex(key);
		for (AMapEntry e = data[index]; e != null; e = e.next) {
			if (e.hasSameKey(ukey)) {
				return e.obj;
			}
		}
		return null;
	}

	final DbObject x_remove(final UniversalKey ukey) {
		owner.ensureReal();
		if (nElements == 0) {
			return null;
		}
		final int index = a_getIndex(ukey);
		for (AMapEntry e = data[index]; e != null; e = e.next) {
			if (e.hasSameKey(ukey)) {
				owner.onCollectionChange(this);
				return p_remove(e);
			}
		}
		return null;
	}


	final int x_size() {
		owner.ensureReal();
		return nElements;
	}

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


	final boolean x_containsKey(final UniversalKey key) {
		return x_get(key) != null;
	}


	final boolean x_containsValue(final Object o) {
		owner.ensureReal();
		if (o == null) {
			return false;
		}
		final DbObject obj = p_checkObject(o);
		final int len = data.length;
		for (int i = 0; i < len; i++) {
			for (AMapEntry e = data[i]; e != null; e = e.next) {
				if (e.obj == obj) {
					return true;
				}
			}
		}
		return false;
	}


	final void x_putAll(final Map t) {
		throw new UnsupportedOperationException();
	}

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

	final Set x_keySet() {
		owner.ensureReal();
		return new Keys();
	}

	final Collection x_values() {
		owner.ensureReal();
		return new Values();
	}

	final Set x_entrySet() {
		owner.ensureReal();
		return new EntrySet();
	}


	final Object x_put(UniversalKey ukey, Object o) {
		owner.ensureReal();
		if (o == null) {
			throw new NullPointerException();
		}
		final DbObject obj = p_checkObject(o);
		owner.onCollectionChange(this);
		p_checkRehash(1);
		final int index = a_getIndex(ukey);
		DbObject result = null;
		for (AMapEntry e = data[index]; e != null; e = e.next) {
			if (e.hasSameKey(ukey)) {
				result = e.obj;
				p_remove(e);
				break;
			}
		}
		modCount++;
		a_addNewEntry(ukey, obj);
		nElements++;
//		check();
		return result;
	}


// -- abstracts  --- //
	abstract int a_getIndex(final UniversalKey key);

	abstract Object a_get(Object key);

	abstract void restoreFromImage(Object image);

	abstract Object createBackupImage();

	abstract void a_addNewEntry(UniversalKey key, DbObject value);	
	
//----------------- inners -----------------------//

	abstract class AMapEntry extends DbInverseRef implements Map.Entry {
		AMapEntry next;
		private AMapEntry prev;

		AMapEntry(final DbObject obj) {
			super(obj);
		}

		final void linkToMap() { // called by subclass at the end of constructor when all fields are set
			final int index = getIndex();
			next = data[index];
			data[index] = this;
			if (next != null) {
				next.prev = this;
			}
		}

		abstract int getIndex();

		public abstract Object getKey();

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

		final void _onTargetDelete() {
//			check();
			owner.onCollectionChange(DbAbstractMap.this);
			p_remove(this);
		}

		final public Object getValue() {
			return obj;
		}

		public final Object setValue(final Object value) {
			throw new UnsupportedOperationException();
		}


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

		abstract boolean hasSameKey(UniversalKey ukey);
	};

	final class Values implements Collection {

		public 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;
			}
			final DbObject obj = p_checkObject(o);
			final int len = data.length;
			for (int i = 0; i < len; i++) {
				for (AMapEntry e = data[i]; e != null; e = e.next) {
					if (e.obj == obj) {
						return true;
					}
				}
			}
			return false;

		}

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

		public Object[] toArray() {
			owner.ensureReal();
			final DbObject[] arr = new DbObject[nElements];
			int j = 0;
			final int len = data.length;
			for (int i = 0; i < len; i++) {
				for (AMapEntry e = data[i]; e != null; e = e.next) {
					arr[j] = e.obj;
					j++;
				}
			}
			return arr;
		}


		public Object[] toArray(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 (AMapEntry e = data[i]; e != null; e = e.next) {
					a[j] = e.obj;
					j++;
				}
			}
			if (a.length > nElements) {
				a[nElements] = null;
			}
			return 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 a_get(o) != null;
		}

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

		public Object[] toArray() {
			owner.ensureReal();
			final Object[] arr = new Object[nElements];
			int j = 0;
			final int len = data.length;
			for (int i = 0; i < len; i++) {
				for (AMapEntry e = data[i]; e != null; e = e.next) {
					arr[j] = e.getKey();
					j++;
				}

			}
			return arr;
		}

		public Object[] toArray(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 (AMapEntry e = data[i]; e != null; e = e.next) {
					a[j] = e.getKey();
					j++;
				}

			}
			if (a.length > nElements) {
				a[nElements] = null;
			}
			return 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 (a_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 AMapEntry f = (AMapEntry) o;
			final int len = data.length;
			for (int i = 0; i < len; i++) {
				for (AMapEntry e = data[i]; e != null; e = e.next) {
					if (e == f) {
						return true;
					}
				}
			}
			return false;
		}

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

		}

		public Object[] toArray() {
			throw new UnsupportedOperationException();
		}

		public Object[] toArray(final Object[] a) {
			throw new UnsupportedOperationException();
		}

		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();
		}
	};

	final class MapIterator implements Iterator {
		final static int VALUES = 1;
		final static int KEYS = 2;
		final static int ENTRIES = 3;

		private int expectedModCount;
		private final int type;
		private AMapEntry e = null;
		private int index = -1;
		int nPassed = 0;

		public MapIterator(final int type) {
			this.type = type;
			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 == KEYS) ? e.getKey() : type == VALUES ? (Object) e.obj : (Object) e;
		}

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

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


		public void remove() {
			checkState();
			if (e == null) {
				throw new IllegalStateException();
			}
			final AMapEntry re = e;
			e = (e.prev == null) ? findWithPrevIndex() : e.prev;
			owner.onCollectionChange(DbAbstractMap.this);
			p_remove(re);
			expectedModCount++;
			nPassed--;
		}

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

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

	final static class UniversalKey {
		int intKey;
		String strKey;
	}

//	public void check() {
//		for (int i = 0; i < data.length; i++) {
//			AMapEntry e = data[i];
//			if (e == null) {
//				continue;
//			}
//			assert e.prev == null;
//			while (e != null) {
//				assert e.getIndex() == i;
//				AMapEntry prev = e;
//				e = e.next;
//				if (e != null) {
//					assert e.prev == prev;
//				}
//			}
//			;
//		}
//	}
}