/**
* Author: Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*/
package at.iaik.suraq.util;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;
/**
* @author Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*
*/
public class ImmutableSet<E> implements Set<E>, Serializable {
/**
*
*/
private static final long serialVersionUID = 8782268576752007761L;
private final Set<E> internalSet;
private int hashCode;
private static WeakHashMap<ImmutableSet<?>, WeakReference<ImmutableSet<?>>> instances = new WeakHashMap<ImmutableSet<?>, WeakReference<ImmutableSet<?>>>();
private static WeakHashMap<Object, WeakReference<Object>> uniqueElements = new WeakHashMap<Object, WeakReference<Object>>();
/**
* Constructs a new <code>ImmutableSet</code>.
*
* @param set
*/
private ImmutableSet(Collection<? extends E> set) {
assert (set != null);
internalSet = new HashSet<E>();
for (E element : set) {
if (element == null) {
internalSet.add(null);
} else {
assert (element != null);
@SuppressWarnings("unchecked")
WeakReference<E> uniqueElementReference = (WeakReference<E>) ImmutableSet.uniqueElements
.get(element);
E uniqueElement = uniqueElementReference == null ? null
: uniqueElementReference.get();
if (uniqueElement != null) {
internalSet.add(uniqueElement);
} else {
ImmutableSet.uniqueElements.put(element,
new WeakReference<Object>(element));
internalSet.add(element);
}
}
}
// Set hashCode before passing this pointer
this.hashCode = internalSet.hashCode();
ImmutableSet.instances.put(this, new WeakReference<ImmutableSet<?>>(
this));
}
public static synchronized <T> ImmutableSet<T> create(
Collection<? extends T> set) {
if (set == null)
throw new NullPointerException(
"Cannot create ImmutableSet from null.");
WeakReference<ImmutableSet<?>> existingSetReference = ImmutableSet.instances
.get(set);
if (existingSetReference != null) {
ImmutableSet<?> existingSet = existingSetReference.get();
if (existingSet != null) {
if (set.isEmpty()) {
assert (existingSet.isEmpty());
ImmutableSet<?> tmp = existingSet;
assert (tmp.isEmpty());
@SuppressWarnings("unchecked")
ImmutableSet<T> castResult = (ImmutableSet<T>) tmp;
return castResult;
}
assert (!set.isEmpty());
assert (!existingSet.isEmpty());
assert (set.size() == existingSet.size());
assert (existingSet.iterator().next().getClass().isInstance(set
.iterator().next()));
@SuppressWarnings("unchecked")
ImmutableSet<T> castResult = (ImmutableSet<T>) existingSet;
return castResult;
}
}
ImmutableSet<T> newSet = new ImmutableSet<T>(set);
return newSet;
}
/**
* @see java.util.Set#size()
*/
@Override
public int size() {
return internalSet.size();
}
/**
* @see java.util.Set#isEmpty()
*/
@Override
public boolean isEmpty() {
return internalSet.isEmpty();
}
/**
* @see java.util.Set#contains(java.lang.Object)
*/
@Override
public boolean contains(Object o) {
return internalSet.contains(o);
}
/**
* @see java.util.Set#iterator()
*/
@Override
public Iterator<E> iterator() {
return new ImmutableIterator<E>(internalSet.iterator());
}
/**
* @see java.util.Set#toArray()
*/
@Override
public Object[] toArray() {
return internalSet.toArray();
}
/**
* @see java.util.Set#toArray(T[])
*/
@Override
public <T> T[] toArray(T[] a) {
return internalSet.toArray(a);
}
/**
* <strong>UNSUPPORTED OPERATION!</strong>
*
* @see java.util.Set#add(java.lang.Object)
*/
@Override
@Deprecated
public boolean add(E e) {
throw new UnsupportedOperationException(
"'add' called on immutable set!");
}
/**
* <strong>UNSUPPORTED OPERATION!</strong>
*
* @see java.util.Set#remove(java.lang.Object)
*/
@Override
@Deprecated
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"'remove' called on immutable set!");
}
/**
* @see java.util.Set#containsAll(java.util.Collection)
*/
@Override
public boolean containsAll(Collection<?> c) {
return internalSet.containsAll(c);
}
/**
* <strong>UNSUPPORTED OPERATION!</strong>
*
* @see java.util.Set#addAll(java.util.Collection)
*/
@Override
@Deprecated
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException(
"'addAll' called on immutable set!");
}
/**
* <strong>UNSUPPORTED OPERATION!</strong>
*
* @see java.util.Set#retainAll(java.util.Collection)
*/
@Override
@Deprecated
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException(
"'retainAll' called on immutable set!");
}
/**
* <strong>UNSUPPORTED OPERATION!</strong>
*
* @see java.util.Set#removeAll(java.util.Collection)
*/
@Override
@Deprecated
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException(
"'removeAll' called on immutable set!");
}
/**
* <strong>UNSUPPORTED OPERATION!</strong>
*
* @see java.util.Set#clear()
*/
@Override
@Deprecated
public void clear() {
throw new UnsupportedOperationException(
"'removeAll' called on immutable set!");
}
/**
* Returns a new immutable set that contains all elements of
* <code>this</code> set, plus the given <code>element</code>.
* <code>this</code> set is not altered.
*
* @param element
* @return <code>this</code> union <code>element</code>.
*/
public ImmutableSet<E> addToCopy(E element) {
Set<E> tmp = new HashSet<E>();
tmp.addAll(internalSet);
tmp.add(element);
return ImmutableSet.create(tmp);
}
/**
* Returns a new immutable set that contains all elements of
* <code>this</code> set, plus the given <code>set</code>. <code>this</code>
* set is not altered.
*
* @param set
* @return <code>this</code> union <code>set</code>.
*/
public ImmutableSet<E> addAllToCopy(Collection<? extends E> set) {
if (set == null)
return this;
Set<E> tmp = new HashSet<E>();
tmp.addAll(internalSet);
tmp.addAll(set);
return ImmutableSet.create(tmp);
}
/**
* Returns a new immutable set that contains all elements of
* <code>this</code> set, minus the given <code>element</code>.
* <code>this</code> set is not altered. If <code>element</code> is not in
* <code>this</code> set, then a copy of <code>this</code> set is returned.
*
* @param element
* @return <code>this</code> union <code>element</code>.
*/
public ImmutableSet<E> removeFromCopy(E element) {
Set<E> tmp = new HashSet<E>();
tmp.addAll(internalSet);
tmp.remove(element);
return ImmutableSet.create(tmp);
}
/**
* Returns a new immutable set that contains all elements of
* <code>this</code> set, minus the given <code>set</code>.
* <code>this</code> set is not altered. Any elements of <code>set</code>
* not contained in <code>this</code> are basically ignored.
*
* @param set
* @return <code>this</code> union <code>set</code>.
*/
public ImmutableSet<E> removeAllFromCopy(Collection<? extends E> set) {
if (set == null)
return this;
Set<E> tmp = new HashSet<E>();
tmp.addAll(internalSet);
tmp.removeAll(set);
return ImmutableSet.create(tmp);
}
@Override
public String toString() {
return internalSet.toString();
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Set))
return false;
if (this.hashCode != obj.hashCode())
return false;
return internalSet.equals(obj);
}
/**
* @return the <code>instances</code>
*/
public synchronized static WeakHashMap<ImmutableSet<?>, WeakReference<ImmutableSet<?>>> getInstances() {
return ImmutableSet.instances;
}
/**
* Necessary for restore after load from cache. Do not tamper with the
* instances otherwise!
*
* @param <code>instances</code> the new value for <code>instances</code>
*/
public synchronized static void setInstances(
WeakHashMap<ImmutableSet<?>, WeakReference<ImmutableSet<?>>> instances) {
ImmutableSet.instances = instances;
}
/**
* @return the <code>uniqueElements</code>
*/
public synchronized static WeakHashMap<Object, WeakReference<Object>> getUniqueElements() {
return ImmutableSet.uniqueElements;
}
/**
* Necessary for restore after load from cache. Do not tamper with the
* unique elements otherwise!
*
* @param <code>uniqueElements</code> the new value for
* <code>uniqueElements</code>
*/
public synchronized static void setUniqueElements(
WeakHashMap<Object, WeakReference<Object>> uniqueElements) {
ImmutableSet.uniqueElements = uniqueElements;
}
}