564 lines
16 KiB
Java
564 lines
16 KiB
Java
package org.apache.commons.collections;
|
|
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.lang.ref.Reference;
|
|
import java.lang.ref.ReferenceQueue;
|
|
import java.lang.ref.SoftReference;
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.AbstractCollection;
|
|
import java.util.AbstractMap;
|
|
import java.util.AbstractSet;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.ConcurrentModificationException;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.Set;
|
|
|
|
public class ReferenceMap extends AbstractMap {
|
|
private static final long serialVersionUID = -3370601314380922368L;
|
|
|
|
public static final int HARD = 0;
|
|
|
|
public static final int SOFT = 1;
|
|
|
|
public static final int WEAK = 2;
|
|
|
|
private int keyType;
|
|
|
|
private int valueType;
|
|
|
|
private float loadFactor;
|
|
|
|
private transient ReferenceQueue queue = new ReferenceQueue();
|
|
|
|
private transient Entry[] table;
|
|
|
|
private transient int size;
|
|
|
|
private transient int threshold;
|
|
|
|
private volatile transient int modCount;
|
|
|
|
private transient Set keySet;
|
|
|
|
private transient Set entrySet;
|
|
|
|
private transient Collection values;
|
|
|
|
public ReferenceMap() {
|
|
this(0, 1);
|
|
}
|
|
|
|
public ReferenceMap(int paramInt1, int paramInt2) {
|
|
this(paramInt1, paramInt2, 16, 0.75F);
|
|
}
|
|
|
|
public ReferenceMap(int paramInt1, int paramInt2, int paramInt3, float paramFloat) {
|
|
verify("keyType", paramInt1);
|
|
verify("valueType", paramInt2);
|
|
if (paramInt3 <= 0)
|
|
throw new IllegalArgumentException("capacity must be positive");
|
|
if (paramFloat <= 0.0F || paramFloat >= 1.0F)
|
|
throw new IllegalArgumentException("Load factor must be greater than 0 and less than 1.");
|
|
this.keyType = paramInt1;
|
|
this.valueType = paramInt2;
|
|
int i;
|
|
for (i = 1; i < paramInt3; i *= 2);
|
|
this.table = new Entry[i];
|
|
this.loadFactor = paramFloat;
|
|
this.threshold = (int)(i * paramFloat);
|
|
}
|
|
|
|
public void clear() {
|
|
Arrays.fill((Object[])this.table, (Object)null);
|
|
this.size = 0;
|
|
do {
|
|
|
|
} while (this.queue.poll() != null);
|
|
}
|
|
|
|
public boolean containsKey(Object paramObject) {
|
|
purge();
|
|
Entry entry = getEntry(paramObject);
|
|
return (entry == null) ? false : (!(entry.getValue() == null));
|
|
}
|
|
|
|
public Set entrySet() {
|
|
if (this.entrySet != null)
|
|
return this.entrySet;
|
|
this.entrySet = new AbstractSet(this) {
|
|
private final ReferenceMap this$0;
|
|
|
|
public void clear() {
|
|
this.this$0.clear();
|
|
}
|
|
|
|
public boolean contains(Object param1Object) {
|
|
if (param1Object == null)
|
|
return false;
|
|
if (!(param1Object instanceof Map.Entry))
|
|
return false;
|
|
Map.Entry entry = (Map.Entry)param1Object;
|
|
ReferenceMap.Entry entry1 = this.this$0.getEntry(entry.getKey());
|
|
return !(entry1 == null || !entry.equals(entry1));
|
|
}
|
|
|
|
public Iterator iterator() {
|
|
return new ReferenceMap.EntryIterator(this.this$0);
|
|
}
|
|
|
|
public boolean remove(Object param1Object) {
|
|
boolean bool = contains(param1Object);
|
|
if (bool) {
|
|
Map.Entry entry = (Map.Entry)param1Object;
|
|
this.this$0.remove(entry.getKey());
|
|
}
|
|
return bool;
|
|
}
|
|
|
|
public int size() {
|
|
return this.this$0.size();
|
|
}
|
|
|
|
public Object[] toArray() {
|
|
return toArray(new Object[0]);
|
|
}
|
|
|
|
public Object[] toArray(Object[] param1ArrayOfObject) {
|
|
ArrayList arrayList = new ArrayList();
|
|
for (ReferenceMap.Entry entry : this)
|
|
arrayList.add(new DefaultMapEntry(entry.getKey(), entry.getValue()));
|
|
return arrayList.toArray(param1ArrayOfObject);
|
|
}
|
|
};
|
|
return this.entrySet;
|
|
}
|
|
|
|
public Object get(Object paramObject) {
|
|
purge();
|
|
Entry entry = getEntry(paramObject);
|
|
return (entry == null) ? null : entry.getValue();
|
|
}
|
|
|
|
private Entry getEntry(Object paramObject) {
|
|
if (paramObject == null)
|
|
return null;
|
|
int i = paramObject.hashCode();
|
|
int j = indexFor(i);
|
|
for (Entry entry = this.table[j]; entry != null; entry = entry.next) {
|
|
if (entry.hash == i && paramObject.equals(entry.getKey()))
|
|
return entry;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private int indexFor(int paramInt) {
|
|
paramInt += paramInt << 15 ^ 0xFFFFFFFF;
|
|
paramInt ^= paramInt >>> 10;
|
|
paramInt += paramInt << 3;
|
|
paramInt ^= paramInt >>> 6;
|
|
paramInt += paramInt << 11 ^ 0xFFFFFFFF;
|
|
paramInt ^= paramInt >>> 16;
|
|
return paramInt & this.table.length - 1;
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
purge();
|
|
return !(this.size != 0);
|
|
}
|
|
|
|
public Set keySet() {
|
|
if (this.keySet != null)
|
|
return this.keySet;
|
|
this.keySet = new AbstractSet(this) {
|
|
private final ReferenceMap this$0;
|
|
|
|
public void clear() {
|
|
this.this$0.clear();
|
|
}
|
|
|
|
public boolean contains(Object param1Object) {
|
|
return this.this$0.containsKey(param1Object);
|
|
}
|
|
|
|
public Iterator iterator() {
|
|
return new ReferenceMap.KeyIterator(this.this$0);
|
|
}
|
|
|
|
public boolean remove(Object param1Object) {
|
|
Object object = this.this$0.remove(param1Object);
|
|
return !(object == null);
|
|
}
|
|
|
|
public int size() {
|
|
return this.this$0.size;
|
|
}
|
|
};
|
|
return this.keySet;
|
|
}
|
|
|
|
private void purge() {
|
|
for (Reference reference = this.queue.poll(); reference != null; reference = this.queue.poll())
|
|
purge(reference);
|
|
}
|
|
|
|
private void purge(Reference paramReference) {
|
|
int i = paramReference.hashCode();
|
|
int j = indexFor(i);
|
|
Entry entry1 = null;
|
|
for (Entry entry2 = this.table[j]; entry2 != null; entry2 = entry2.next) {
|
|
if (entry2.purge(paramReference)) {
|
|
if (entry1 == null) {
|
|
this.table[j] = entry2.next;
|
|
} else {
|
|
entry1.next = entry2.next;
|
|
}
|
|
this.size--;
|
|
return;
|
|
}
|
|
entry1 = entry2;
|
|
}
|
|
}
|
|
|
|
public Object put(Object paramObject1, Object paramObject2) {
|
|
if (paramObject1 == null)
|
|
throw new NullPointerException("null keys not allowed");
|
|
if (paramObject2 == null)
|
|
throw new NullPointerException("null values not allowed");
|
|
purge();
|
|
if (this.size + 1 > this.threshold)
|
|
resize();
|
|
int i = paramObject1.hashCode();
|
|
int j = indexFor(i);
|
|
for (Entry entry = this.table[j]; entry != null; entry = entry.next) {
|
|
if (i == entry.hash && paramObject1.equals(entry.getKey())) {
|
|
Object object = entry.getValue();
|
|
entry.setValue(paramObject2);
|
|
return object;
|
|
}
|
|
}
|
|
this.size++;
|
|
this.modCount++;
|
|
paramObject1 = toReference(this.keyType, paramObject1, i);
|
|
paramObject2 = toReference(this.valueType, paramObject2, i);
|
|
this.table[j] = new Entry(this, paramObject1, i, paramObject2, this.table[j]);
|
|
return null;
|
|
}
|
|
|
|
private void readObject(ObjectInputStream paramObjectInputStream) throws IOException, ClassNotFoundException {
|
|
paramObjectInputStream.defaultReadObject();
|
|
this.table = new Entry[paramObjectInputStream.readInt()];
|
|
this.threshold = (int)(this.table.length * this.loadFactor);
|
|
this.queue = new ReferenceQueue();
|
|
for (Object object = paramObjectInputStream.readObject(); object != null; object = paramObjectInputStream.readObject()) {
|
|
Object object1 = paramObjectInputStream.readObject();
|
|
put(object, object1);
|
|
}
|
|
}
|
|
|
|
public Object remove(Object paramObject) {
|
|
if (paramObject == null)
|
|
return null;
|
|
purge();
|
|
int i = paramObject.hashCode();
|
|
int j = indexFor(i);
|
|
Entry entry1 = null;
|
|
for (Entry entry2 = this.table[j]; entry2 != null; entry2 = entry2.next) {
|
|
if (i == entry2.hash && paramObject.equals(entry2.getKey())) {
|
|
if (entry1 == null) {
|
|
this.table[j] = entry2.next;
|
|
} else {
|
|
entry1.next = entry2.next;
|
|
}
|
|
this.size--;
|
|
this.modCount++;
|
|
return entry2.getValue();
|
|
}
|
|
entry1 = entry2;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void resize() {
|
|
Entry[] arrayOfEntry = this.table;
|
|
this.table = new Entry[arrayOfEntry.length * 2];
|
|
for (byte b = 0; b < arrayOfEntry.length; b++) {
|
|
Entry entry = arrayOfEntry[b];
|
|
while (entry != null) {
|
|
Entry entry1 = entry;
|
|
entry = entry.next;
|
|
int i = indexFor(entry1.hash);
|
|
entry1.next = this.table[i];
|
|
this.table[i] = entry1;
|
|
}
|
|
arrayOfEntry[b] = null;
|
|
}
|
|
this.threshold = (int)(this.table.length * this.loadFactor);
|
|
}
|
|
|
|
public int size() {
|
|
purge();
|
|
return this.size;
|
|
}
|
|
|
|
private Object toReference(int paramInt1, Object paramObject, int paramInt2) {
|
|
switch (paramInt1) {
|
|
case 0:
|
|
return paramObject;
|
|
case 1:
|
|
return new SoftRef(paramInt2, paramObject, this.queue);
|
|
case 2:
|
|
return new WeakRef(paramInt2, paramObject, this.queue);
|
|
}
|
|
throw new Error();
|
|
}
|
|
|
|
public Collection values() {
|
|
if (this.values != null)
|
|
return this.values;
|
|
this.values = new AbstractCollection(this) {
|
|
private final ReferenceMap this$0;
|
|
|
|
public void clear() {
|
|
this.this$0.clear();
|
|
}
|
|
|
|
public Iterator iterator() {
|
|
return new ReferenceMap.ValueIterator(this.this$0);
|
|
}
|
|
|
|
public int size() {
|
|
return this.this$0.size;
|
|
}
|
|
};
|
|
return this.values;
|
|
}
|
|
|
|
private static void verify(String paramString, int paramInt) {
|
|
if (paramInt < 0 || paramInt > 2)
|
|
throw new IllegalArgumentException(String.valueOf(paramString) + " must be HARD, SOFT, WEAK.");
|
|
}
|
|
|
|
private void writeObject(ObjectOutputStream paramObjectOutputStream) throws IOException {
|
|
paramObjectOutputStream.defaultWriteObject();
|
|
paramObjectOutputStream.writeInt(this.table.length);
|
|
for (Map.Entry entry : entrySet()) {
|
|
paramObjectOutputStream.writeObject(entry.getKey());
|
|
paramObjectOutputStream.writeObject(entry.getValue());
|
|
}
|
|
paramObjectOutputStream.writeObject(null);
|
|
}
|
|
|
|
private class Entry implements Map.Entry {
|
|
private final ReferenceMap this$0;
|
|
|
|
Object key;
|
|
|
|
Object value;
|
|
|
|
int hash;
|
|
|
|
Entry next;
|
|
|
|
public Entry(ReferenceMap this$0, Object param1Object1, int param1Int, Object param1Object2, Entry param1Entry) {
|
|
this.this$0 = this$0;
|
|
this.key = param1Object1;
|
|
this.hash = param1Int;
|
|
this.value = param1Object2;
|
|
this.next = param1Entry;
|
|
}
|
|
|
|
public boolean equals(Object param1Object) {
|
|
if (param1Object == null)
|
|
return false;
|
|
if (param1Object == this)
|
|
return true;
|
|
if (!(param1Object instanceof Map.Entry))
|
|
return false;
|
|
Map.Entry entry = (Map.Entry)param1Object;
|
|
Object object1 = entry.getKey();
|
|
Object object2 = entry.getValue();
|
|
return (object1 == null || object2 == null) ? false : (!(!object1.equals(getKey()) || !object2.equals(getValue())));
|
|
}
|
|
|
|
public Object getKey() {
|
|
return (this.this$0.keyType > 0) ? ((Reference)this.key).get() : this.key;
|
|
}
|
|
|
|
public Object getValue() {
|
|
return (this.this$0.valueType > 0) ? ((Reference)this.value).get() : this.value;
|
|
}
|
|
|
|
public int hashCode() {
|
|
Object object = getValue();
|
|
return this.hash ^ ((object == null) ? 0 : object.hashCode());
|
|
}
|
|
|
|
boolean purge(Reference param1Reference) {
|
|
boolean bool = (this.this$0.keyType <= 0 || this.key != param1Reference) ? false : true;
|
|
bool = (!bool && (this.this$0.valueType <= 0 || this.value != param1Reference)) ? false : true;
|
|
if (bool) {
|
|
if (this.this$0.keyType > 0)
|
|
((Reference)this.key).clear();
|
|
if (this.this$0.valueType > 0)
|
|
((Reference)this.value).clear();
|
|
}
|
|
return bool;
|
|
}
|
|
|
|
public Object setValue(Object param1Object) {
|
|
Object object = getValue();
|
|
if (this.this$0.valueType > 0)
|
|
((Reference)this.value).clear();
|
|
this.value = this.this$0.toReference(this.this$0.valueType, param1Object, this.hash);
|
|
return object;
|
|
}
|
|
|
|
public String toString() {
|
|
return String.valueOf(String.valueOf(getKey())) + "=" + getValue();
|
|
}
|
|
}
|
|
|
|
private class EntryIterator implements Iterator {
|
|
private final ReferenceMap this$0;
|
|
|
|
int index;
|
|
|
|
ReferenceMap.Entry entry;
|
|
|
|
ReferenceMap.Entry previous;
|
|
|
|
Object nextKey;
|
|
|
|
Object nextValue;
|
|
|
|
Object currentKey;
|
|
|
|
Object currentValue;
|
|
|
|
int expectedModCount;
|
|
|
|
public EntryIterator(ReferenceMap this$0) {
|
|
this.this$0 = this$0;
|
|
this.index = (this$0.size() != 0) ? this$0.table.length : 0;
|
|
this.expectedModCount = this$0.modCount;
|
|
}
|
|
|
|
private void checkMod() {
|
|
if (this.this$0.modCount != this.expectedModCount)
|
|
throw new ConcurrentModificationException();
|
|
}
|
|
|
|
public boolean hasNext() {
|
|
checkMod();
|
|
while (nextNull()) {
|
|
ReferenceMap.Entry entry = this.entry;
|
|
int i = this.index;
|
|
while (entry == null && i > 0)
|
|
entry = this.this$0.table[--i];
|
|
this.entry = entry;
|
|
this.index = i;
|
|
if (entry == null) {
|
|
this.currentKey = null;
|
|
this.currentValue = null;
|
|
return false;
|
|
}
|
|
this.nextKey = entry.getKey();
|
|
this.nextValue = entry.getValue();
|
|
if (nextNull())
|
|
this.entry = this.entry.next;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public Object next() {
|
|
return nextEntry();
|
|
}
|
|
|
|
protected ReferenceMap.Entry nextEntry() {
|
|
checkMod();
|
|
if (nextNull() && !hasNext())
|
|
throw new NoSuchElementException();
|
|
this.previous = this.entry;
|
|
this.entry = this.entry.next;
|
|
this.currentKey = this.nextKey;
|
|
this.currentValue = this.nextValue;
|
|
this.nextKey = null;
|
|
this.nextValue = null;
|
|
return this.previous;
|
|
}
|
|
|
|
private boolean nextNull() {
|
|
return !(this.nextKey != null && this.nextValue != null);
|
|
}
|
|
|
|
public void remove() {
|
|
checkMod();
|
|
if (this.previous == null)
|
|
throw new IllegalStateException();
|
|
this.this$0.remove(this.currentKey);
|
|
this.previous = null;
|
|
this.currentKey = null;
|
|
this.currentValue = null;
|
|
this.expectedModCount = this.this$0.modCount;
|
|
}
|
|
}
|
|
|
|
private class ValueIterator extends EntryIterator {
|
|
private final ReferenceMap this$0;
|
|
|
|
ValueIterator(ReferenceMap this$0) {
|
|
super(this$0);
|
|
this.this$0 = this$0;
|
|
}
|
|
|
|
public Object next() {
|
|
return nextEntry().getValue();
|
|
}
|
|
}
|
|
|
|
private class KeyIterator extends EntryIterator {
|
|
private final ReferenceMap this$0;
|
|
|
|
KeyIterator(ReferenceMap this$0) {
|
|
super(this$0);
|
|
this.this$0 = this$0;
|
|
}
|
|
|
|
public Object next() {
|
|
return nextEntry().getKey();
|
|
}
|
|
}
|
|
|
|
private static class SoftRef extends SoftReference {
|
|
private int hash;
|
|
|
|
public SoftRef(int param1Int, Object param1Object, ReferenceQueue param1ReferenceQueue) {
|
|
super((T)param1Object, param1ReferenceQueue);
|
|
this.hash = param1Int;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return this.hash;
|
|
}
|
|
}
|
|
|
|
private static class WeakRef extends WeakReference {
|
|
private int hash;
|
|
|
|
public WeakRef(int param1Int, Object param1Object, ReferenceQueue param1ReferenceQueue) {
|
|
super((T)param1Object, param1ReferenceQueue);
|
|
this.hash = param1Int;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return this.hash;
|
|
}
|
|
}
|
|
}
|