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