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

/**
 *  Persistent, resizable-array implementation of the <tt>List</tt> interface
 */
public final class DbArrayList extends DbCollection implements List, RandomAccess {
	private static final Entry[] ZERO_LIST = new Entry[0];
	private int size;
	private Entry[] items;
	private int modCount;

	/** new object*/
	DbArrayList(final DbObject owner, final int fid) {
		super(owner, fid);
		size = 0;
		items = ZERO_LIST;
		modCount = 0;
	}

	/** pclean startup */
	DbArrayList(final DbObject owner, final int[] refIds, final int fid) {
		super(owner, fid);
		modCount = 0;
		restoreFromImage(refIds);
	}

	private void restoreFromImage(final int[] refIds) {
		final PainDB db = owner._getDB();
		items = new Entry[(int) (refIds.length * 1.1)];
		size = refIds.length;
		for (int i = 0; i < size; i++) {
			final int id = refIds[i];
			items[i] = (id == -1) ? new Entry(null) : new Entry(db.getObjectByIndexId(refIds[i]));
		}
	}

	final int _size() {
		return size;
	}

	final void _clear() {
		if (size == 0) {
			return;
		}
		for (int i = size; --i >= 0;) {
			_remove(i);
		}
		modCount++;
		size = 0;
	}

	final DbArrayList.Entry[] getItems() {
		return items;
	}


	/**
	 * remove with no explicit checks, onChange not called also
	 * @param index
	 * @return
	 */
	private Object _remove(final int index) {
		modCount++;
		final Entry oldValue = items[index];
		final int numMoved = size - index - 1;
		if (numMoved > 0) {
			System.arraycopy(items, index + 1, items, index, numMoved);
		}
		items[--size] = null;
		if (oldValue == null) {
			return null;
		}
		oldValue.onReferenceDestroy();
		return oldValue.obj;
	}

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

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

	public boolean contains(final Object o) {
		return indexOf(o) >= 0;
	}

	public java.util.Iterator iterator() {
		return listIterator();
	}

	public Object[] toArray() {
		owner.ensureReal();
		final Object[] result = new Object[size];
		for (int i = 0; i < size; i++) {
			result[i] = items[i].obj;
		}
		return result;
	}

	public Object[] toArray(Object a[]) {
		owner.ensureReal();
		if (a.length < size) {
			a = (Object[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
		}
		for (int i = 0; i < size; i++) {
			a[i] = items[i].obj;
		}
		if (a.length > size) {
			a[size] = null;
		}
		return a;
	}

	private void ensureCapacity(final int minCapacity) {
		final int oldCapacity = items.length;
		if (minCapacity > oldCapacity) {
			modCount++;
			final Entry oldData[] = items;
			int newCapacity = (oldCapacity * 3) / 2 + 1;
			if (newCapacity < minCapacity) {
				newCapacity = minCapacity;
			}
			items = new Entry[newCapacity];
			System.arraycopy(oldData, 0, items, 0, size);
		}
	}

	public boolean add(final Object o) {
		owner.ensureReal();
		owner.onCollectionChange(this);
		_add(checkObject(o));
		modCount++;
		return true;
	}

	public void add(final int index, final Object o) {
		owner.ensureReal();
		checkRange(index);
		owner.onCollectionChange(this);
		final DbObject obj = checkObject(o);
		owner.onCollectionChange(this);
		ensureCapacity(index + 1);
		_add(index, obj);
		modCount++;
	}

	private void _add(final DbObject obj) {
		ensureCapacity(size + 1);
		items[size] = new Entry(obj);
		size++;
	}

	public boolean remove(final Object o) {
		final int index = indexOf(o);
		if (index == -1) {
			return false;
		}
		owner.onCollectionChange(this);
		_remove(index);
		return true;
	}

	public boolean containsAll(final Collection c) {
		owner.ensureReal();
		for (java.util.Iterator iterator = c.iterator(); iterator.hasNext();) {
			if (_indexOf(iterator.next()) == -1) {
				return false;
			}
		}
		return true;
	}

	public boolean addAll(final Collection c) {
		owner.ensureReal();
		owner.onCollectionChange(this);
		if (c == null || c.isEmpty()) {
			return false;
		}
		ensureCapacity(size + c.size());
		Object array[] = c.toArray();
		final int len = array.length;
		for (int i = 0; i < len; i++) {
			_add(checkObject(array[i]));
		}
		modCount++;
		return true;
	}

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


	private DbObject checkObject(final Object o) {
		if (o != null) {
			final DbObject obj = (DbObject) o;
			obj.ensureReal();
			owner.ensureDb(obj);
			return obj;
		}
		return null;
	}


	public boolean removeAll(final Collection c) {
		owner.ensureReal();
		boolean changed = false;
		for (java.util.Iterator it = c.iterator(); it.hasNext();) {
			changed |= remove(it.next());
		}
		return changed;
	}

	public boolean retainAll(final Collection c) {
		owner.ensureReal();
		final DbIntBuffer toRemove = new DbIntBuffer(size);
		for (int i = 0; i < size; i++) {
			if (!c.contains(items[i].obj)) {
				toRemove.add(i);
			}
		}
		if (toRemove.isEmpty()) {
			return false;
		}
		owner.onCollectionChange(this);
		for (int i = toRemove.getSize(); --i > 0;) {
			_remove(toRemove.data[i]);
		}
		return true;
	}

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

	public Object get(final int index) {
		owner.ensureReal();
		checkRange(index);
		final Entry e = items[index];
		return e.obj;
	}

	public Object set(final int index, final Object o) {
		owner.ensureReal();
		checkRange(index);
		final DbObject obj = checkObject(o);
		final Entry e = items[index];
		if (e.obj == obj) {
			return obj;
		}
		owner.onCollectionChange(this);
		final DbObject result;
		if (e != null) {
			e.onReferenceDestroy();
			result = e.obj;
		} else {
			result = null;
		}
		items[index] = new Entry(obj);
		return result;
	}


	private void _add(final int index, final DbObject o) {
		System.arraycopy(items, index, items, index + 1, size - index);
		items[index] = new Entry(o);
		modCount++;
		size++;
	}

	public Object remove(final int index) {
		owner.ensureReal();
		checkRange(index);
		return _remove(index);
	}


	public int indexOf(final Object obj) {
		owner.ensureReal();
		if (obj != null && (!(obj instanceof DbObject) || !hasSameDb((DbObject) obj))) {
			return -1;
		}

		return _indexOf(obj);
	}

	private int _indexOf(final Object o) {
		if (o != null && !hasSameDb((DbObject) o)) {
			return -1;
		}
		for (int i = 0; i < size; i++) {
			final Entry item = items[i];
			if (item.obj == o) {
				return i;
			}
		}
		return -1;
	}

	private boolean hasSameDb(final DbObject o) {
		return o._getDB() == owner._getDB();
	}

	public int lastIndexOf(final Object o) {
		owner.ensureReal();
		if (o != null && hasSameDb((DbObject) o)) {
			return -1;
		}
		for (int i = size; --i > 0;) {
			final Entry e = items[i];
			if (e.obj == o) {
				return i;
			}
		}
		return -1;
	}

	public ListIterator listIterator() {
		owner.ensureReal();
		return new DbArrayListIterator(0);
	}

	public ListIterator listIterator(final int index) {
		owner.ensureReal();
		return new DbArrayListIterator(index);
	}

	public List subList(final int fromIndex, final int toIndex) {
		throw new UnsupportedOperationException();
	}

	private void checkRange(final int index) {
		if (index >= size || index < 0) {
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
		}
	}

	Object createBackupImage() {
		if (size == 0) {
			return DbConstants.ZERO_INT_ARRAY;
		}
		final int[] refIds = new int[size];
		final int len = items.length;
		for (int i = 0; i < len; i++) {
			final Entry e = items[i];
			if (e.obj == null) {
				refIds[i] = -1;
			} else {
				refIds[i] = e.obj.indexId;
			}
		}
		return refIds;
	}

	void restoreFromBackup(final Object backup) {
		final int[] ids = (int[]) backup;
		_clear();
		restoreFromImage(ids);
	}



//--------------inners-----------------------
	final class Entry extends DbInverseRef {
		public Entry(final DbObject obj) {
			super(obj);
		}

		void _onTargetDelete() {
			owner.onCollectionChange(DbArrayList.this); //we keep only indexIds, -> should flush this object to avoid oid collision
		}
	}

	final class DbArrayListIterator implements ListIterator {
		private int cursor; //cursor - 1 == prev, cursor +1 == next, cursor = current
		private int expectedModCount;
		private int dir = 0;


		public DbArrayListIterator(final int index) {
			if (index > size || index < 0) {
				throw new IndexOutOfBoundsException("list iterator index: " + index + ", size: " + size);
			}
			this.cursor = index;
			this.expectedModCount = modCount;
			dir = 0;
		}

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

		public boolean hasNext() {
			checkState();
			return cursor < size;
		}

		public Object next() {
			if (!hasNext()) {
				throw new NoSuchElementException();
			}
			dir = 1;
			final Entry e = items[cursor];
			cursor++;
			return e.obj;
		}

		public boolean hasPrevious() {
			checkState();
			return cursor > 0;
		}

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

		public Object previous() {
			if (!hasPrevious()) {
				throw new NoSuchElementException();
			}
			cursor--;
			dir = -1;
			final Entry e = items[cursor];
			return e.obj;
		}

		public int nextIndex() {
			checkState();
			return cursor;
		}

		public int previousIndex() {
			checkState();
			return cursor - 1;
		}

		public void remove() {
			checkState();
			if (dir == 0) {
				throw new IllegalStateException();
			}
			owner.onCollectionChange(DbArrayList.this);

			if (dir == 1) {
				_remove(cursor - 1);
			} else {
				_remove(cursor);
			}
			cursor--;
			expectedModCount = DbArrayList.this.modCount;
		}

		public void set(final Object o) {
			checkState();
			throw new UnsupportedOperationException();
		}

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