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