![JAR search and dependency download from the Maven repository](/logo.png)
org.eclipse.emf.common.util.BasicEList Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 2002-2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*/
package org.eclipse.emf.common.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;
/**
* A highly extensible list implementation.
*/
public class BasicEList extends AbstractEList implements RandomAccess, Cloneable, Serializable
{
private static final long serialVersionUID = 1L;
/**
* The size of the list.
*/
protected int size;
/**
* The underlying data storage of the list.
*/
protected transient Object [] data;
/**
* Creates an empty instance with no initial capacity.
* The data storage will be null.
*/
public BasicEList()
{
super();
}
/**
* Creates an empty instance with the given capacity.
* @param initialCapacity the initial capacity of the list before it must grow.
* @exception IllegalArgumentException if the initialCapacity
is negative.
*/
public BasicEList(int initialCapacity)
{
if (initialCapacity < 0)
{
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
data = newData(initialCapacity);
}
/**
* Creates an instance that is a copy of the collection.
* @param collection the initial contents of the list.
*/
public BasicEList(Collection extends E> collection)
{
size = collection.size();
// Conditionally create the data.
//
if (size > 0)
{
// Allow for a bit-shift of growth.
//
data = newData(size + size / 8 + 1);
collection.toArray(data);
}
}
/**
* Creates an initialized instance that directly uses the given arguments.
* @param size the size of the list.
* @param data the underlying storage of the list.
*/
protected BasicEList(int size, Object [] data)
{
this.size = size;
this.data = data;
}
/**
* Returns new allocated data storage.
* Clients may override this to create typed storage.
* The cost of type checking via a typed array is negligible.
* @return new data storage.
*/
protected Object [] newData(int capacity)
{
return new Object [capacity];
}
/**
* Assigns the object into the data storage at the given index and returns the object that's been stored.
* Clients can monitor access to the storage via this method.
* @param index the position of the new content.
* @param object the new content.
* @return the object that's been stored.
*
*/
protected E assign(int index, E object)
{
data[index] = object;
return object;
}
/**
* Returns the number of objects in the list.
* @return the number of objects in the list.
*/
@Override
public int size()
{
return size;
}
/**
* Returns whether the list has zero size.
* @return whether the list has zero size.
*/
@Override
public boolean isEmpty()
{
return size == 0;
}
/**
* Returns whether the list contains the object.
* This implementation uses either equals
or "=="
depending on {@link #useEquals useEquals}.
* @param object the object in question.
* @return whether the list contains the object.
* @see #useEquals
*/
@Override
public boolean contains(Object object)
{
if (useEquals() && object != null)
{
for (int i = 0; i < size; ++i)
{
if (object.equals(data[i]))
{
return true;
}
}
}
else
{
for (int i = 0; i < size; ++i)
{
if (data[i] == object)
{
return true;
}
}
}
return false;
}
/**
* Returns the position of the first occurrence of the object in the list.
* This implementation uses either equals
or "=="
depending on {@link #useEquals useEquals}.
* @param object the object in question.
* @return the position of the first occurrence of the object in the list.
*/
@Override
public int indexOf(Object object)
{
if (useEquals() && object != null)
{
for (int i = 0; i < size; ++i)
{
if (object.equals(data[i]))
{
return i;
}
}
}
else
{
for (int i = 0; i < size; ++i)
{
if (data[i] == object)
{
return i;
}
}
}
return -1;
}
/**
* Returns the position of the last occurrence of the object in the list.
* This implementation uses either equals
or "=="
depending on {@link #useEquals useEquals}.
* @param object the object in question.
* @return the position of the last occurrence of the object in the list.
*/
@Override
public int lastIndexOf(Object object)
{
if (useEquals() && object != null)
{
for (int i = size - 1; i >= 0; --i)
{
if (object.equals(data[i]))
{
return i;
}
}
}
else
{
for (int i = size - 1; i >= 0; --i)
{
if (data[i] == object)
{
return i;
}
}
}
return -1;
}
/**
* Returns an array containing all the objects in sequence.
* Clients may override {@link #newData newData} to create typed storage in this case.
* @return an array containing all the objects in sequence.
* @see #newData
*/
@Override
public Object[] toArray()
{
Object[] result = newData(size);
// Guard for no data.
//
if (size > 0)
{
System.arraycopy(data, 0, result, 0, size);
}
return result;
}
/**
* Returns an array containing all the objects in sequence.
* @param array the array that will be filled and returned, if it's big enough;
* otherwise, a suitably large array of the same type will be allocated and used instead.
* @return an array containing all the objects in sequence.
* @see #newData
*/
@Override
public T[] toArray(T[] array)
{
// Guard for no data.
//
if (size > 0)
{
if (array.length < size)
{
@SuppressWarnings("unchecked") T [] newArray = (T[])Array.newInstance(array.getClass().getComponentType(), size);
array = newArray;
}
System.arraycopy(data, 0, array, 0, size);
}
if (array.length > size)
{
array[size] = null;
}
return array;
}
/**
* Returns direct unsafe access to the underlying data storage.
* Clients may not modify this
* and may not assume that the array remains valid as the list is modified.
* @return direct unsafe access to the underlying data storage.
*/
public Object [] data()
{
return data;
}
/**
* Updates directly and unsafely the underlying data storage.
* Clients must be aware that this subverts all callbacks
* and hence possibly the integrity of the list.
*/
public void setData(int size, Object [] data)
{
this.size = size;
this.data = data;
++modCount;
}
/**
* An IndexOutOfBoundsException that constructs a message from the argument data.
* Having this avoids having the byte code that computes the message repeated/inlined at the creation site.
*/
protected static class BasicIndexOutOfBoundsException extends AbstractEList.BasicIndexOutOfBoundsException
{
private static final long serialVersionUID = 1L;
/**
* Constructs an instance with a message based on the arguments.
*/
public BasicIndexOutOfBoundsException(int index, int size)
{
super(index, size);
}
}
/**
* Returns the object at the index.
* This implementation delegates to {@link #resolve resolve}
* so that clients may transform the fetched object.
* @param index the position in question.
* @return the object at the index.
* @exception IndexOutOfBoundsException if the index isn't within the size range.
* @see #resolve
* @see #basicGet
*/
@SuppressWarnings("unchecked")
@Override
public E get(int index)
{
if (data == null || index >= size)
throw new BasicIndexOutOfBoundsException(index, size);
return resolve(index, (E)data[index]);
}
/**
* Returns the object at the index without {@link #resolve resolving} it.
* @param index the position in question.
* @return the object at the index.
* @exception IndexOutOfBoundsException if the index isn't within the size range.
* @see #resolve
* @see #get
*/
@Override
public E basicGet(int index)
{
if (data == null || index >= size)
throw new BasicIndexOutOfBoundsException(index, size);
return primitiveGet(index);
}
/**
* Returns the object at the index without {@link #resolve resolving} it and without range checking the index.
* @param index the position in question.
* @return the object at the index.
* @see #resolve
* @see #get
* @see #basicGet(int)
*/
@SuppressWarnings("unchecked")
@Override
protected E primitiveGet(int index)
{
return (E)data[index];
}
/**
* Sets the object at the index
* and returns the old object at the index;
* it does no ranging checking or uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didSet didSet}, and {@link #didChange didChange}.
* @param index the position in question.
* @param object the object to set.
* @return the old object at the index.
* @see #set
*/
@Override
public E setUnique(int index, E object)
{
@SuppressWarnings("unchecked") E oldObject = (E)data[index];
assign(index, validate(index, object));
didSet(index, object, oldObject);
didChange();
return oldObject;
}
/**
* Adds the object at the end of the list;
* it does no uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}.
* after uniqueness checking.
* @param object the object to be added.
* @see #add(Object)
*/
@Override
public void addUnique(E object)
{
// ++modCount
//
grow(size + 1);
assign(size, validate(size, object));
didAdd(size++, object);
didChange();
}
/**
* Adds the object at the given index in the list;
* it does no ranging checking or uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}.
* @param object the object to be added.
* @see #add(int, Object)
*/
@Override
public void addUnique(int index, E object)
{
// ++modCount
//
grow(size + 1);
E validatedObject = validate(index, object);
if (index != size)
{
System.arraycopy(data, index, data, index + 1, size - index);
}
assign(index, validatedObject);
++size;
didAdd(index, object);
didChange();
}
/**
* Adds each object of the collection to the end of the list;
* it does no uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}.
* @param collection the collection of objects to be added.
* @see #addAll(Collection)
*/
@Override
public boolean addAllUnique(Collection extends E> collection)
{
int growth = collection.size();
// ++modCount
//
grow(size + growth);
Iterator extends E> objects = collection.iterator();
int oldSize = size;
size += growth;
for (int i = oldSize; i < size; ++i)
{
E object = objects.next();
assign(i, validate(i, object));
didAdd(i, object);
didChange();
}
return growth != 0;
}
/**
* Adds each object of the collection at each successive index in the list
* and returns whether any objects were added;
* it does no ranging checking or uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}.
* @param index the index at which to add.
* @param collection the collection of objects to be added.
* @return whether any objects were added.
* @see #addAll(int, Collection)
*/
@Override
public boolean addAllUnique(int index, Collection extends E> collection)
{
int growth = collection.size();
// ++modCount
//
grow(size + growth);
int shifted = size - index;
if (shifted > 0)
{
System.arraycopy(data, index, data, index + growth, shifted);
}
Iterator extends E> objects = collection.iterator();
size += growth;
for (int i = 0; i < growth; ++i)
{
E object = objects.next();
assign(index, validate(index, object));
didAdd(index, object);
didChange();
++index;
}
return growth != 0;
}
/**
* Adds each object from start to end of the array at the index of list
* and returns whether any objects were added;
* it does no ranging checking or uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}.
* @param objects the objects to be added.
* @param start the index of first object to be added.
* @param end the index past the last object to be added.
* @return whether any objects were added.
* @see #addAllUnique(Object[], int, int)
*/
@Override
public boolean addAllUnique(Object [] objects, int start, int end)
{
int growth = end - start;
// ++modCount
//
grow(size + growth);
size += growth;
int index = size;
for (int i = start; i < end; ++i)
{
@SuppressWarnings("unchecked") E object = (E)objects[i];
assign(index, validate(index, object));
didAdd(index, object);
didChange();
++index;
}
return growth != 0;
}
/**
* Adds each object from start to end of the array at each successive index in the list
* and returns whether any objects were added;
* it does no ranging checking or uniqueness checking.
* This implementation delegates to {@link #assign assign}, {@link #didAdd didAdd}, and {@link #didChange didChange}.
* @param index the index at which to add.
* @param objects the objects to be added.
* @param start the index of first object to be added.
* @param end the index past the last object to be added.
* @return whether any objects were added.
* @see #addAllUnique(Object[], int, int)
*/
@Override
public boolean addAllUnique(int index, Object [] objects, int start, int end)
{
int growth = end - start;
// ++modCount
//
grow(size + growth);
int shifted = size - index;
if (shifted > 0)
{
System.arraycopy(data, index, data, index + growth, shifted);
}
size += growth;
for (int i = start; i < end; ++i)
{
@SuppressWarnings("unchecked") E object = (E)objects[i];
assign(index, validate(index, object));
didAdd(index, object);
didChange();
++index;
}
return growth != 0;
}
/**
* Removes the object at the index from the list and returns it.
* This implementation delegates to {@link #didRemove didRemove} and {@link #didChange didChange}.
* @param index the position of the object to remove.
* @return the removed object.
* @exception IndexOutOfBoundsException if the index isn't within the size range.
*/
@Override
public E remove(int index)
{
if (index >= size)
throw new BasicIndexOutOfBoundsException(index, size);
++modCount;
@SuppressWarnings("unchecked") E oldObject = (E)data[index];
int shifted = size - index - 1;
if (shifted > 0)
{
System.arraycopy(data, index+1, data, index, shifted);
}
// Don't hold onto a duplicate reference to the last object.
//
data[--size] = null;
didRemove(index, oldObject);
didChange();
return oldObject;
}
/**
* Clears the list of all objects.
* This implementation discards the data storage without modifying it
* and delegates to {@link #didClear didClear} and {@link #didChange didChange}.
*/
@Override
public void clear()
{
++modCount;
Object [] oldData = data;
int oldSize = size;
// Give it all back to the garbage collector.
//
data = null;
size = 0;
didClear(oldSize, oldData);
didChange();
}
/**
* Moves the object at the source index of the list to the target index of the list
* and returns the moved object.
* This implementation delegates to {@link #assign assign}, {@link #didMove didMove}, and {@link #didChange didChange}.
* @param targetIndex the new position for the object in the list.
* @param sourceIndex the old position of the object in the list.
* @return the moved object.
* @exception IndexOutOfBoundsException if either index isn't within the size range.
*/
@Override
public E move(int targetIndex, int sourceIndex)
{
++modCount;
if (targetIndex >= size)
throw new IndexOutOfBoundsException("targetIndex=" + targetIndex + ", size=" + size);
if (sourceIndex >= size)
throw new IndexOutOfBoundsException("sourceIndex=" + sourceIndex + ", size=" + size);
@SuppressWarnings("unchecked") E object = (E)data[sourceIndex];
if (targetIndex != sourceIndex)
{
if (targetIndex < sourceIndex)
{
System.arraycopy(data, targetIndex, data, targetIndex + 1, sourceIndex - targetIndex);
}
else
{
System.arraycopy(data, sourceIndex + 1, data, sourceIndex, targetIndex - sourceIndex);
}
assign(targetIndex, object);
didMove(targetIndex, object, sourceIndex);
didChange();
}
return object;
}
/**
* Shrinks the capacity of the list to the minimal requirements.
* @see #grow
*/
public void shrink()
{
++modCount;
// Conditionally create the data.
//
if (size == 0)
{
// Give it all back to the garbage collector.
//
data = null;
}
else if (size < data.length)
{
Object [] oldData = data;
data = newData(size);
System.arraycopy(oldData, 0, data, 0, size);
}
}
/**
* Grows the capacity of the list
* to ensure that no additional growth is needed until the size exceeds the specified minimum capacity.
* @see #shrink
*/
public void grow(int minimumCapacity)
{
++modCount;
int oldCapacity = data == null ? 0 : data.length;
if (minimumCapacity > oldCapacity)
{
Object oldData[] = data;
// This seems to be a pretty sweet formula that supports good growth.
// Adding an object to a list will create a list of capacity 4,
// which is just about the average list size.
//
int newCapacity = oldCapacity + oldCapacity / 2 + 4;
if (newCapacity < minimumCapacity)
{
newCapacity = minimumCapacity;
}
data = newData(newCapacity);
if (oldData != null)
{
System.arraycopy(oldData, 0, data, 0, size);
}
}
}
private synchronized void writeObject(ObjectOutputStream objectOutputStream) throws IOException
{
objectOutputStream.defaultWriteObject();
if (data == null)
{
objectOutputStream.writeInt(0);
}
else
{
objectOutputStream.writeInt(data.length);
for (int i = 0; i < size; ++i)
{
objectOutputStream.writeObject(data[i]);
}
}
}
private synchronized void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException
{
objectInputStream.defaultReadObject();
int arrayLength = objectInputStream.readInt();
if (arrayLength > 0)
{
try
{
data = newData(arrayLength);
}
catch (Throwable exception)
{
data = new Object[arrayLength];
}
for (int i = 0; i < size; ++i)
{
@SuppressWarnings("unchecked") E object = (E)objectInputStream.readObject();
didAdd(i, assign(i, object));
}
}
}
/**
* Returns a shallow copy of this list.
* @return a shallow copy of this list.
*/
@Override
public Object clone()
{
try
{
@SuppressWarnings("unchecked") BasicEList clone = (BasicEList)super.clone();
if (size > 0)
{
clone.size = size;
clone.data = newData(size);
System.arraycopy(data, 0, clone.data, 0, size);
}
return clone;
}
catch (CloneNotSupportedException exception)
{
throw new InternalError();
}
}
/**
* An extensible iterator implementation.
* @deprecated
* @see AbstractEList.EIterator
*/
@Deprecated
protected class EIterator extends AbstractEList.EIterator
{
// Pointless extension
}
/**
* An extended read-only iterator that does not {@link BasicEList#resolve resolve} objects.
* @deprecated
* @see AbstractEList.NonResolvingEIterator
*/
@Deprecated
protected class NonResolvingEIterator extends AbstractEList.NonResolvingEIterator
{
// Pointless extension
}
/**
* An extensible list iterator implementation.
* @deprecated
* @see AbstractEList.EListIterator
*/
@Deprecated
protected class EListIterator extends AbstractEList.EListIterator
{
/**
* Creates an instance.
*/
public EListIterator()
{
super();
}
/**
* Creates an instance advanced to the index.
* @param index the starting index.
*/
public EListIterator(int index)
{
super(index);
cursor = index;
}
}
/**
* An extended read-only list iterator that does not {@link BasicEList#resolve resolve} objects.
* @deprecated
* @see AbstractEList.NonResolvingEListIterator
*/
@Deprecated
protected class NonResolvingEListIterator extends AbstractEList.NonResolvingEListIterator
{
/**
* Creates an instance.
*/
public NonResolvingEListIterator()
{
super();
}
/**
* Creates an instance advanced to the index.
* @param index the starting index.
*/
public NonResolvingEListIterator(int index)
{
super(index);
}
}
/**
* An unmodifiable version of {@link BasicEList}.
*/
public static class UnmodifiableEList extends BasicEList
{
private static final long serialVersionUID = 1L;
/**
* Creates an initialized instance.
* @param size the size of the list.
* @param data the underlying storage of the list.
*/
public UnmodifiableEList(int size, Object [] data)
{
this.size = size;
this.data = data;
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public E set(int index, E object)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public boolean add(E object)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public void add(int index, E object)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public boolean addAll(Collection extends E> collection)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public boolean addAll(int index, Collection extends E> collection)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public boolean remove(Object object)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public E remove(int index)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public boolean removeAll(Collection> collection)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public boolean retainAll(Collection> collection)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public void clear()
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public void move(int index, E object)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public E move(int targetIndex, int sourceIndex)
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public void shrink()
{
throw new UnsupportedOperationException();
}
/**
* Throws an exception.
* @exception UnsupportedOperationException always because it's not supported.
*/
@Override
public void grow(int minimumCapacity)
{
throw new UnsupportedOperationException();
}
/**
* Returns the {@link BasicEList#basicIterator basic iterator}.
* @return the basic iterator.
*/
@Override
public Iterator iterator()
{
return basicIterator();
}
/**
* Returns the {@link #basicListIterator() basic list iterator}.
* @return the basic list iterator.
*/
@Override
public ListIterator listIterator()
{
return basicListIterator();
}
/**
* Returns the {@link #basicListIterator(int) basic list iterator} advanced to the index.
* @param index the starting index.
* @return the basic list iterator.
*/
@Override
public ListIterator listIterator(int index)
{
return basicListIterator(index);
}
}
/**
* Returns an unsafe list that provides a {@link #resolve non-resolving} view of the underlying data storage.
* @return an unsafe list that provides a non-resolving view of the underlying data storage.
*/
@Override
protected List basicList()
{
if (size == 0)
{
return ECollections.emptyEList();
}
else
{
return new UnmodifiableEList(size, data);
}
}
/**
* A BasicEList
that {@link #useEquals uses} ==
instead of equals
to compare members.
*/
public static class FastCompare extends BasicEList
{
private static final long serialVersionUID = 1L;
/**
* Creates an empty instance with no initial capacity.
*/
public FastCompare()
{
super();
}
/**
* Creates an empty instance with the given capacity.
* @param initialCapacity the initial capacity of the list before it must grow.
* @exception IllegalArgumentException if the initialCapacity
is negative.
*/
public FastCompare(int initialCapacity)
{
super(initialCapacity);
}
/**
* Creates an instance that is a copy of the collection.
* @param collection the initial contents of the list.
*/
public FastCompare(Collection extends E> collection)
{
super(collection.size());
addAll(collection);
}
/**
* Returns false
because this list uses ==
.
* @return false
.
*/
@Override
protected boolean useEquals()
{
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy