package com.limegroup.gnutella;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A Set specifically for URNs.
* This is not backed by a HashSet because there are
* so few URN types that it doesn't need to be.
*
* This currently only supports SHA1 and TTROOT URNs. If further
* UrnTypes are created, this class will have to be updated.
* (Note that there'll have to be MANY changes.)
*
* If the set already has a URN of the specified type added to it,
* further additions of that type will be rejected.
*/
public class UrnSet implements Set<URN>, Iterable<URN>, Cloneable, Serializable {
private static final long serialVersionUID = -1065284624401321676L;
/** The sole URNs this knows about. */
private URN sha1, ttroot, nms1;
/** Returns a set of the UrnSet version of the set. */
public static UrnSet resolve(Set<? extends URN> set) {
if (set instanceof UnmodifiableUrnSet || set instanceof UrnSet) {
return (UrnSet) set;
}
else {
return new UrnSet(set);
}
}
/** Returns a set of the UrnSet version of the set. */
public static UrnSet modifiableSet(Set<? extends URN> set) {
if (set instanceof UnmodifiableUrnSet) {
UrnSet urnSet = (UrnSet) set;
return new UrnSet(urnSet.getSHA1(), urnSet.getTTRoot(), urnSet.getNMS1());
}
if(set instanceof UrnSet) {
return (UrnSet)set;
} else {
return new UrnSet(set);
}
}
/** Returns an unmodifiable set of the UrnSet version of the set. */
public static UrnSet unmodifiableSet(Set<? extends URN> set) {
if(set instanceof UnmodifiableUrnSet) {
return (UrnSet)set;
} else {
return new UnmodifiableUrnSet(set);
}
}
/** Constructs an empty UrnSet. */
public UrnSet() {}
/** Constructs a UrnSet with the given URN. */
public UrnSet(URN urn) {
add(urn);
}
/** Constructs a UrnSet with URNs from the given collection. */
public UrnSet(Collection<? extends URN> c) {
addAll(c);
}
private UrnSet(URN sha1, URN ttroot, URN nms1) {
this.sha1 = sha1;
this.ttroot = ttroot;
this.nms1 = nms1;
}
@Override
public String toString() {
return isEmpty() ? "{Empty UrnSet}" : "UrnSet of: " + sha1;
}
/** Clones this set. */
@Override
public UrnSet clone() {
return new UrnSet(sha1, ttroot, nms1);
}
/** Returns the hashcode for this UrnSet. */
@Override
public int hashCode() {
return sha1 == null ? 0 : sha1.hashCode();
}
public URN getSHA1() {
return sha1;
}
public URN getTTRoot() {
return ttroot;
}
public URN getNMS1() {
return nms1;
}
/**
* Determines if this set contains all the same objects
* as another set.
*/
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
/**
* Attempts to add the given URN into this set.
* If the set already contained a URN of the same type,
* the new URN is rejected and this returns false.
*
* If the URN's type is not supported, the URN is rejected
* and this returns false.
*
* @param o
* @return
*/
public boolean add(URN o) {
return addInternal(o);
}
protected boolean addInternal(URN o) {
if(o.isSHA1() && sha1 == null) {
sha1 = o;
return true;
} else if (o.isTTRoot() && ttroot == null) {
ttroot = o;
return true;
} else if(o.isNMS1() && nms1 == null) {
nms1 = o;
return true;
}
return false;
}
/**
* Attempts to add all the URNs in the given collection into this
* set. If the set was modified as a result of any of the additions,
* this will return true. Otherwise, it will return false.
*
* @param c
* @return
*/
public boolean addAll(Collection<? extends URN> c) {
return addAllInternal(c);
}
protected boolean addAllInternal(Collection<? extends URN> c) {
boolean ret = false;
for(URN urn : c)
ret |= addInternal(urn);
return ret;
}
public void clear() {
sha1 = null;
ttroot = null;
nms1 = null;
}
public boolean contains(Object o) {
return o.equals(sha1) || o.equals(ttroot) || o.equals(nms1);
}
public boolean containsAll(Collection<?> c) {
if(c.size() > 3)
return false;
if(c.isEmpty())
return true;
if(isEmpty())
return false;
boolean ret = true;
for (Object o : c)
ret &= contains(o);
return ret;
}
public boolean isEmpty() {
return sha1 == null && ttroot == null && nms1 == null;
}
public Iterator<URN> iterator() {
return new UrnIterator();
}
public boolean remove(Object o) {
if(sha1 != null && o.equals(sha1)) {
sha1 = null;
return true;
}
if(ttroot != null && o.equals(ttroot)) {
ttroot = null;
return true;
}
if(nms1 != null && o.equals(nms1)) {
nms1 = null;
return true;
}
return false;
}
public boolean removeAll(Collection<?> c) {
if(sha1 == null && ttroot == null && nms1 == null || c.isEmpty())
return false;
boolean ret = false;
for(Object o : c) {
ret |= remove(o);
if (isEmpty())
break;
}
return ret;
}
public boolean retainAll(Collection<?> c) {
boolean ret = false;
if(sha1 != null && !c.contains(sha1)) {
sha1 = null;
ret = true;
}
if(ttroot != null && !c.contains(ttroot)) {
ttroot = null;
ret = true;
}
if(nms1 != null && !c.contains(nms1)) {
nms1 = null;
ret = true;
}
return ret;
}
public int size() {
int ret = 0;
if(sha1 != null)
ret++;
if(ttroot != null)
ret++;
if(nms1 != null)
ret++;
return ret;
}
public Object[] toArray() {
switch(size()) {
case 0: return new Object[0];
case 1: if(sha1 != null)
return new Object[]{sha1};
else if(ttroot != null)
return new Object[]{ttroot};
else
return new Object[]{nms1};
case 2: if(sha1 != null && ttroot != null) {
return new Object[]{sha1, ttroot};
} else if(sha1 != null) {
return new Object[]{sha1, nms1};
} else {
return new Object[]{ttroot, nms1};
}
case 3: return new Object[]{sha1, ttroot, nms1};
default:
throw new IllegalStateException();
}
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
a = (T[])Array.newInstance(a.getClass().getComponentType(), size);
switch(size) {
case 1:
if(sha1 != null)
a[0] = (T)sha1;
else if(ttroot != null)
a[0] = (T)ttroot;
else
a[0] = (T)nms1;
break;
case 2:
if(sha1 != null && ttroot != null) {
a[0] = (T)sha1;
a[1] = (T)ttroot;
} else if(sha1 != null) {
a[0] = (T)sha1;
a[1] = (T)nms1;
} else {
a[0] = (T)ttroot;
a[1] = (T)nms1;
}
break;
case 3:
a[0] = (T)sha1;
a[1] = (T)ttroot;
a[2] = (T)nms1;
break;
}
if(a.length > size)
a[size] = null;
return a;
}
/** Iterator that returns each of the Urn Types in turn. */
private class UrnIterator implements Iterator<URN> {
private boolean givenSHA1, givenTTRoot, givenNMS1;
public boolean hasNext() {
return (!givenSHA1 && sha1 != null) ||
(!givenTTRoot && ttroot != null) ||
(!givenNMS1 && nms1 != null);
}
public URN next() {
if (!hasNext())
throw new NoSuchElementException();
if (!givenSHA1 && sha1 != null) {
givenSHA1 = true;
return sha1;
}
if (!givenTTRoot && ttroot != null) {
givenTTRoot = true;
return ttroot;
}
if (!givenNMS1 && nms1 != null) {
givenNMS1 = true;
return nms1;
}
throw new IllegalStateException();
}
public void remove() {
if (!(givenSHA1 || givenTTRoot || givenNMS1))
throw new IllegalStateException();
if(givenNMS1) {
nms1 = null;
} else if (givenTTRoot) {
ttroot = null;
} else if (givenSHA1) {
sha1 = null;
}
}
}
public static URN getSha1(Set<? extends URN> urns) {
if(urns instanceof UrnSet) {
return ((UrnSet)urns).sha1;
} else {
for(URN urn : urns) {
if(urn.isSHA1()) {
return urn;
}
}
return null;
}
}
public static URN getNMS1(Set<? extends URN> urns) {
if(urns instanceof UrnSet) {
return ((UrnSet)urns).nms1;
} else {
for(URN urn : urns) {
if(urn.isNMS1()) {
return urn;
}
}
return null;
}
}
private static class UnmodifiableUrnSet extends UrnSet {
public UnmodifiableUrnSet(Set<? extends URN> set) {
super.addAll(set);
}
private UnmodifiableUrnSet(URN sha1, URN ttroot, URN nms1) {
super(sha1, ttroot, nms1);
}
@Override
public boolean add(URN o) {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
@Override
public boolean addAll(Collection<? extends URN> c) {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
@Override
public void clear() {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
@Override
public Iterator<URN> iterator() {
final Iterator<URN> oldIterator = super.iterator();
return new Iterator<URN> () {
@Override
public boolean hasNext() {
return oldIterator.hasNext();
}
@Override
public URN next() {
return oldIterator.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Can't modify an UnmodifiableUrnSet");
}
};
}
@Override
public UrnSet clone() {
return new UnmodifiableUrnSet(getSHA1(), getTTRoot(), getNMS1());
}
}
}