/*
* Kodkod -- Copyright (c) 2005-present, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.util.ints;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A skeletal implementation of the SparseSequence interface.
* The class provides an implementation for the <code>isEmpty</code>,
* <code>putAll</code>, <code>contains</code>, <code>indices</code>, <code>equals</code>, <code>hashCode</code>,
* and <code>toString</code> methods. All other methods must be
* implemented by the subclasses.
*
* @specfield entries: int -> lone V
*
* @author Emina Torlak
*/
public abstract class AbstractSparseSequence<V> implements SparseSequence<V> {
/**
* Constructs a sparse sequence
* @ensures no this.entries'
*/
protected AbstractSparseSequence() {}
/**
* Returns true if the size of this sequence is 0.
* @return this.size()==0
*/
public boolean isEmpty() {
return size()==0;
}
/**
* Returns an iterator over the entries in this sequence
* in the ascending order of indeces, starting at this.first().
* This method calls this.iterator(Integer.MIN_VALUE, Integer.MAX_VALUE).
* @return an iterator over this.entries starting at the entry
* with the smallest index
*/
public Iterator<IndexedEntry<V>> iterator() {
return iterator(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
/**
* Returns the first element in this sequence, if any. This
* method first checks that the sequence is not empty, and
* if not, returns this.iterator().next();
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#first()
*/
public IndexedEntry<V> first() {
return isEmpty() ? null : iterator().next();
}
/**
* Returns the last element in this sequence, if any. This
* method first checks that the sequence is not empty, and
* if not, returns this.iterator(Integer.MAX_VALUE, Integer.MIN_VALUE).next();
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#last()
*/
public IndexedEntry<V> last() {
return isEmpty() ? null : iterator(Integer.MAX_VALUE, Integer.MIN_VALUE).next();
}
/**
* Returns the entry whose index is the ceiling of the given index in this sequence.
* This method calls this.iterator(index, Integer.MAX_VALUE), and if the resulting iterator has
* a next element returns it.
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#ceil(int)
*/
public IndexedEntry<V> ceil(int index) {
final Iterator<IndexedEntry<V>> itr = iterator(index, Integer.MAX_VALUE);
return itr.hasNext() ? itr.next() : null;
}
/**
* Returns the entry whose index is the floor of the given index in this sequence.
* This method calls this.iterator(index, Integer.MIN_VALUE), and if the resulting iterator has
* a next element returns it.
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#floor(int)
*/
public IndexedEntry<V> floor(int index) {
final Iterator<IndexedEntry<V>> itr = iterator(index, Integer.MIN_VALUE);
return itr.hasNext() ? itr.next() : null;
}
/**
* Returns the set of all indices mapped by this sparse sequence.
* The returned set supports removal iff this is not an unmodifiable
* sparse sequence.
* @return {s: IntSet | s.ints = this.entries.V}
*/
public IntSet indices() {
return new AbstractIntSet() {
public IntIterator iterator(final int from, final int to) {
return new IntIterator() {
Iterator<IndexedEntry<V>> iter = AbstractSparseSequence.this.iterator(from, to);
public boolean hasNext() {
return iter.hasNext();
}
public int next() {
return iter.next().index();
}
public void remove() {
iter.remove();
}
};
}
public int size() {
return AbstractSparseSequence.this.size();
}
public boolean contains(int i) {
return containsIndex(i);
}
public int min() {
final IndexedEntry<V> first = AbstractSparseSequence.this.first();
if (first==null)
throw new NoSuchElementException();
return first.index();
}
public int max() {
final IndexedEntry<V> last = AbstractSparseSequence.this.last();
if (last==null)
throw new NoSuchElementException();
return last.index();
}
public boolean remove(int i) {
final boolean isMapped = containsIndex(i);
AbstractSparseSequence.this.remove(i);
return isMapped;
}
public int floor(int i) {
final IndexedEntry<V> floor = AbstractSparseSequence.this.floor(i);
if (floor==null)
throw new NoSuchElementException();
return floor.index();
}
public int ceil(int i) {
final IndexedEntry<V> ceil = AbstractSparseSequence.this.ceil(i);
if (ceil==null)
throw new NoSuchElementException();
return ceil.index();
}
public void clear() {
AbstractSparseSequence.this.clear();
}
public IntSet clone() throws CloneNotSupportedException {
final IntSet s;
if (size()==0)
s = Ints.bestSet(Integer.MIN_VALUE, Integer.MAX_VALUE);
else
s = Ints.bestSet(min(), max());
s.addAll(this);
return s;
}
};
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#values()
*/
public Collection<V> values() {
return new AbstractCollection<V>() {
public int size() {
return AbstractSparseSequence.this.size();
}
public boolean isEmpty() {
return AbstractSparseSequence.this.isEmpty();
}
public boolean contains(Object arg0) {
return AbstractSparseSequence.this.contains(arg0);
}
public Iterator<V> iterator() {
return new Iterator<V>() {
Iterator<IndexedEntry<V>> iter = AbstractSparseSequence.this.iterator();
public boolean hasNext() {
return iter.hasNext();
}
public V next() {
return iter.next().value();
}
public void remove() {
iter.remove();
}
};
}
public void clear() {
AbstractSparseSequence.this.clear();
}
};
}
/**
* Returns true if this sparse sequence has an entry for the
* given index; otherwise returns false. This method returns
* the value of this.iterator(index,index).hasNext();
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#containsIndex(int)
*/
public boolean containsIndex(int index) {
return iterator(index,index).hasNext();
}
/**
* Iterates through all the entries in this sequence and returns
* true if one of the encountered entries has the given object as
* its value.
* @return {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#contains(java.lang.Object)
*/
public boolean contains(Object value) {
for(IndexedEntry<?> v: this) {
if (equal(value, v.value()))
return true;
}
return false;
}
/**
* Returns the result of calling super.clone().
* @see java.lang.Object#clone()
*/
@SuppressWarnings("unchecked")
public SparseSequence<V> clone() throws CloneNotSupportedException {
return (SparseSequence<V>) super.clone();
}
/*---------- adapted from java.util.AbstractMap -----------*/
/**
* Removes the entry with the given index, if it exists, and
* returns the value previously stored at the index. If the
* sequence had no previous mapping for the index, null is returned.
* This method obtains an iterator from index to index and removes
* its sole element, if any.
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#remove(int)
*/
public V remove(int index) {
final Iterator<IndexedEntry<V>> itr = iterator(index,index);
if (itr.hasNext()) {
final V ret = itr.next().value();
itr.remove();
return ret;
}
return null;
}
/**
* Removes all entries from this sequences. This method
* obtains an iterator over the sequences and calls remove()
* after each call to next().
* {@inheritDoc}
* @see kodkod.util.ints.SparseSequence#clear()
*/
public void clear() {
final Iterator<IndexedEntry<V>> itr = iterator();
while(itr.hasNext()) {
itr.next();
itr.remove();
}
}
/**
* Throws an UnsupportedOperationException.
* @throws UnsupportedOperationException
*/
public V put(int index, V value) {
throw new UnsupportedOperationException();
}
/**
* Copies all of the entries from the specified sparse sequence to
* this sequence. This implementation calls put(e.index, e.value) on
* this sequence once for each entry e in the specified sequence.
* @ensures this.entries' = this.entries ++ s.entries
*/
public void putAll(SparseSequence<? extends V> s) {
Iterator<? extends IndexedEntry<? extends V>> i = s.iterator();
while (i.hasNext()) {
IndexedEntry<? extends V> e = i.next();
put(e.index(), e.value());
}
}
/**
* Returns true if both o1 and o2 are null, or
* o1.equals(o2)
* @return o1 and o2 are equal
*/
static boolean equal(Object o1, Object o2) {
return o1==null ? o2==null : o1.equals(o2);
}
/**
* Returns true if the indexed entries e0 and e1 are equal to each other.
* @requires e0 != null && e1 != null
* @return e0.index = e1.index && e0.value = e1.value
*/
static boolean equal(IndexedEntry<?> e0, IndexedEntry<?> e1) {
return e0.index()==e1.index() && equal(e0.value(), e1.value());
}
/**
* Compares the specified object with this sequence for equality. Returns
* <tt>true</tt> if the given object is also a sequence and the two sequences
* represent the same function from integers to E.<p>
*
* This implementation first checks if the specified object is this sequence;
* if so it returns <tt>true</tt>. Then, it checks if the specified
* object is a sequence whose size is identical to the size of this set; if
* not, it returns <tt>false</tt>. If so, it iterates over this sequences's
* entries, and checks that the specified sequence
* contains each entry that this sequence contains. If the specified sequence
* fails to contain such an entry, <tt>false</tt> is returned. If the
* iteration completes, <tt>true</tt> is returned.
* @return o in SparseSequence && o.entries = this.entries
*/
@SuppressWarnings("unchecked")
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof SparseSequence)) return false;
SparseSequence<V> s = (SparseSequence<V>) o;
if (s.size() != size()) return false;
try {
final Iterator<IndexedEntry<V>> i1 = iterator(), i2 = s.iterator();
while (i1.hasNext()) {
if (!equal(i1.next(), i2.next()))
return false;
}
} catch(ClassCastException unused) {
return false;
} catch(NullPointerException unused) {
return false;
}
return true;
}
/**
* Returns the hashcode for an indexed entry.
* @requires e != null
* @return e.index ^ e.value.hashCode()
*/
static int hashCode(IndexedEntry<?> e) {
return e.index() ^ (e.value()==null ? 0 : e.value().hashCode());
}
/**
* Returns the hash code value for this sparse sequence.
* The hash code of a sparse sequence is defined to be the sum of the
* hashCodes of each entry of its entries. This ensures that t1.equals(t2)
* implies that t1.hashCode()==t2.hashCode() for any two sequences t1 and t2,
* as required by the general contract of Object.hashCode.
* This implementation iterates over this.entries, calling
* <tt>hashCode</tt> on each IndexedEntry in the sequence, and adding
* up the results.
* @return sum(this.entries.hashCode())
*/
public int hashCode() {
int h = 0;
for (IndexedEntry<V> e : this)
h += hashCode(e);
return h;
}
/**
* Returns a string representation of this sequence. The string representation
* consists of a list of index-value mappings in the order returned by the
* sequences <tt>iterator</tt>, enclosed in brackets
* (<tt>"[]"</tt>). Adjacent entries are separated by the characters
* <tt>", "</tt> (comma and space). Each index-value mapping is rendered as
* the index followed by an equals sign (<tt>"="</tt>) followed by the
* associated value. Elements are converted to strings as by
* <tt>String.valueOf(Object)</tt>.<p>
*
* This implementation creates an empty string buffer, appends a left
* bracket, and iterates over the map's entries, appending
* the string representation of each <tt>IndexedEntry</tt> in turn. After
* appending each entry except the last, the string <tt>", "</tt> is
* appended. Finally a right bracket is appended. A string is obtained
* from the stringbuffer, and returned.
*
* @return a String representation of this map.
*/
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append("[");
final Iterator<IndexedEntry<V>> i = iterator();
boolean hasNext = i.hasNext();
while (hasNext) {
IndexedEntry<V> e = i.next();
buf.append(e.index());
buf.append("=");
if (e.value() == this)
buf.append("(this sequence)");
else
buf.append(e.value());
hasNext = i.hasNext();
if (hasNext) buf.append(", ");
}
buf.append("]");
return buf.toString();
}
}