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