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