/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.sf.nmedit.nmutils.collections;
import java.io.IOException;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import net.sf.nmedit.nmutils.Arrays15;
public class InsertInsertionSet<E> extends AbstractSet<E> implements Set<E>, Serializable
{
/**
* Serial Version UID
*/
private static final long serialVersionUID = -8096374677355271025L;
private transient Object[] elements;
private transient int modCount;
private int size = 0;
protected boolean replaceEnabled = true;
public InsertInsertionSet(int initialCapacity, boolean replaceEnabled)
{
elements = new Object[initialCapacity];
this.replaceEnabled = replaceEnabled;
}
public boolean isOrderValueDefined(int ov)
{
return indexForOrderValue(ov, true) < size;
}
protected int orderValue(Object e)
{
return e.hashCode();
}
@SuppressWarnings("unchecked")
private final E fastGet(int index)
{
return (E) elements[index];
}
public E getElementByOrderValue(int ov)
{
int index = indexForOrderValue(ov, true);
return (index<size) ? fastGet(index) : null;
}
protected int indexForOrderValue(final int ov, final boolean find)
{
// O(ld(n)) complexity
int l = 0;
int r = size-1;
int index = size;
while (index<0)
{
if (l>r)
{
index = l;
break;
}
int mid = l+(r-l)/2;
int midov = orderValue(elements[mid]);
if (midov<ov)
{
l = mid+1;
}
else if (midov>ov)
{
r = mid-1;
}
else
{
if (!find) return -1;
index = mid;
}
}
return index;
}
public void ensureCapacity(int minCapacity)
{
modCount++;
int oldCapacity = elements.length;
if (minCapacity > oldCapacity)
{
// Object oldData[] = elements;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
// Java 6 only: elements = Arrays.copyOf(elements, newCapacity);
elements = Arrays15.copyOf(elements, newCapacity);
}
}
public boolean add(E e)
{
final int ov = orderValue(e);
final int index = indexForOrderValue(ov, replaceEnabled);
if (replaceEnabled)
{
if (index<size && ov==orderValue(elements[index]))
{
// replace
modCount ++;
elements[index] = e;
return true;
}
}
else if (index<0) return false; // element already defined
ensureCapacity(size+1); // increments modcount !!
if (index<size) // shift elements +1
System.arraycopy(elements, index, elements, index+1, elements.length-index);
elements[index] = e;
size++;
return true;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index)
{
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elements, index+1, elements, index, numMoved);
elements[--size] = null; // Let gc do its work
}
public E getElementAt(int index)
{
RangeCheck(index);
return fastGet(index);
}
public E removeElementAt(int index)
{
RangeCheck(index);
E e = fastGet(index);
fastRemove(index);
return e;
}
public void clear()
{
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elements[i] = null;
size = 0;
}
public int indexOf(Object o)
{
int index = indexForOrderValue(orderValue(o), true);
if (index>=0)
{
Object e = elements[index];
if (!(o == e || (o!=null && o.equals(e))))
return -1;
}
return index;
}
public boolean contains(Object o)
{
return indexOf(o)>=0;
}
public boolean isEmpty()
{
return size==0;
}
public Iterator<E> iterator()
{
return new Itr();
}
public boolean remove(Object o)
{
int index = indexOf(o);
if (index<0) return false;
fastRemove(index);
return true;
}
private void RangeCheck(int index)
{
if (index >= size)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
public int size()
{
return size;
}
public Object[] toArray()
{
return Arrays15.copyOf(elements, size);
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a)
{
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays15.copyOf(elements, size, a.getClass());
System.arraycopy(elements, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public boolean equals(Object o)
{
if (o == this) return true;
if (o == null || (!(o instanceof InsertInsertionSet))) return false;
Iterator<E> e1 = iterator();
Iterator e2 = ((InsertInsertionSet) o).iterator();
while(e1.hasNext() && e2.hasNext())
{
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
// remember mod count to ensure the collection has not changed
final int expectedModCount = modCount;
// write size and replaceEnabled fields
out.defaultWriteObject();
// write array length
out.writeInt(elements.length);
// write elements in the proper order.
for (int i=0; i<size; i++)
out.writeObject(elements[i]);
// check for concurrent modification
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
// read size and replaceEnabled fields
in.defaultReadObject();
// read in array length and allocate array
int arrayLength = in.readInt();
Object[] a = elements = new Object[arrayLength];
// read elements in the proper order.
for (int i=0; i<size; i++)
a[i] = in.readObject();
}
private class Itr implements Iterator<E>
{
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext()
{
return cursor != size();
}
public E next()
{
checkForComodification();
try
{
E next = getElementAt(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e)
{
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove()
{
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try
{
InsertInsertionSet.this.removeElementAt(lastRet);
if (lastRet < cursor) cursor--;
lastRet = -1;
expectedModCount = modCount;
}
catch (IndexOutOfBoundsException e)
{
throw new ConcurrentModificationException();
}
}
final void checkForComodification()
{
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}