package org.infinispan.commons.util; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Array; import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import net.jcip.annotations.Immutable; /** * A lightweight, read-only copy of a List. Typically used in place of the common idiom: <code> return * Collections.unmodifiableList(new ArrayList( myInternalList )); </code> * <p/> * a it is far more efficient than making a defensive copy and then wrapping the defensive copy in a read-only wrapper. * <p/> * Also used whenever a read-only reference List is needed. * <p/> * * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>) * @since 4.0 */ @Immutable public class ImmutableListCopy<E> extends AbstractList<E> implements Externalizable, Immutables.Immutable { private static final long serialVersionUID = 10929568968966L; private E[] elements; /** * Constructs a new ImmutableListCopy. Required by Serialization. */ public ImmutableListCopy() { } /** * Only one copy constructor since the list is immutable. * * @param c collection to copy from */ @SuppressWarnings("unchecked") public ImmutableListCopy(Collection<? extends E> c) { int size = c.size(); Object[] el = new Object[size]; // no room for growth; el = c.toArray(el); elements = (E[]) el; } /** * Assumes that the array passed in is "safe", i.e., is not referenced from elsewhere. Use with care! * * @param array to reference */ public ImmutableListCopy(E[] array) { elements = array; } /** * Utility constructors to allow combining collections * * @param collection1 collection to copy from * @param collection2 collection to copy from */ @SuppressWarnings("unchecked") public ImmutableListCopy(Collection<? extends E> collection1, Collection<? extends E> collection2) { int size = collection1.size() + collection2.size(); elements = (E[]) new Object[size]; // no room for growth; Object[] c1 = new Object[collection1.size()]; Object[] c2 = new Object[collection2.size()]; c1 = collection1.toArray(c1); c2 = collection2.toArray(c2); System.arraycopy(c1, 0, elements, 0, c1.length); System.arraycopy(c2, 0, elements, c1.length, c2.length); } @Override public final int size() { return elements.length; } @Override public final boolean isEmpty() { return elements.length == 0; } @Override public final boolean contains(Object o) { return indexOf(o) >= 0; } @Override public final Iterator<E> iterator() { return new ImmutableIterator(); } @Override public final Object[] toArray() { int size = elements.length; Object[] result = new Object[size]; System.arraycopy(elements, 0, result, 0, size); return result; } @Override @SuppressWarnings("unchecked") public final <T> T[] toArray(T[] a) { int size = elements.length; if (a.length < size) { a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); } System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } @Override public final boolean add(E o) { throw new UnsupportedOperationException(); } @Override public final boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(Collection<? extends E> c) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(int index, Collection<? extends E> c) { throw new UnsupportedOperationException(); } @Override public final boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public final boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException(); } @Override public final E get(int index) { return elements[index]; } @Override public final int indexOf(Object o) { int size = elements.length; if (o == null) { for (int i = 0; i < size; i++) { if (elements[i] == null) return i; } } else { for (int i = 0; i < size; i++) { if (o.equals(elements[i])) return i; } } return -1; } @Override public final int lastIndexOf(Object o) { int size = elements.length; if (o == null) { for (int i = size - 1; i >= 0; i--) { if (elements[i] == null) return i; } } else { for (int i = size - 1; i >= 0; i--) { if (o.equals(elements[i])) return i; } } return -1; } @Override public final ListIterator<E> listIterator() { return new ImmutableIterator(); } @Override public final ListIterator<E> listIterator(int index) { return new ImmutableIterator(index); } @Override public final List<E> subList(int fromIndex, int toIndex) { return new ImmutableSubList<E>(fromIndex, toIndex); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ImmutableListCopy)) return super.equals(o); ImmutableListCopy<?> that = (ImmutableListCopy<?>) o; return Arrays.equals(elements, that.elements); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + Arrays.hashCode(elements); return result; } /** * Format: - entry array size (int) - elements (Object) * * @param out stream to write to * @throws IOException */ @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(elements.length); for (E e : elements) out.writeObject(e); } /** * See {@link #writeExternal(java.io.ObjectOutput)} for serialization format * * @param in stream * @throws IOException * @throws ClassNotFoundException */ @Override @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int size = in.readInt(); elements = (E[]) new Object[size]; for (int i = 0; i < size; i++) elements[i] = (E) in.readObject(); } private class ImmutableIterator implements ListIterator<E> { int cursor = 0; ImmutableIterator(int index) { if (index < 0 || index > size()) throw new IndexOutOfBoundsException("Index: " + index); cursor = index; } ImmutableIterator() { } @Override public boolean hasNext() { return cursor != elements.length; } @Override public E next() { try { return get(cursor++); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public boolean hasPrevious() { return cursor != 0; } @Override public E previous() { try { return get(--cursor); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } @Override public int nextIndex() { return cursor; } @Override public int previousIndex() { return cursor - 1; } @Override public void set(E o) { throw new UnsupportedOperationException(); } @Override public void add(E o) { throw new UnsupportedOperationException(); } } public class ImmutableSubList<E> extends AbstractList<E> { private int offset; private int size; ImmutableSubList(int fromIndex, int toIndex) { int size = ImmutableListCopy.this.elements.length; if (fromIndex < 0 || toIndex > size || fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + "), toIndex(" + toIndex + "), size (" + size + "), List=" + ImmutableListCopy.this.toString()); offset = fromIndex; this.size = toIndex - fromIndex; } @Override @SuppressWarnings("unchecked") public final E get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return (E) ImmutableListCopy.this.get(index + offset); } @Override public final int size() { return size; } @Override protected final void removeRange(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(Collection<? extends E> c) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(int index, Collection<? extends E> c) { throw new UnsupportedOperationException(); } @Override public final Iterator<E> iterator() { return super.listIterator(); } @Override public final ListIterator<E> listIterator(final int index) { if (index < 0 || (index != 0 && index >= size)) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return new ListIterator<E>() { private ListIterator<?> i = ImmutableListCopy.this.listIterator(index + offset); @Override public boolean hasNext() { return nextIndex() < size; } @Override @SuppressWarnings("unchecked") public E next() { if (hasNext()) return (E) i.next(); else throw new NoSuchElementException(); } @Override public boolean hasPrevious() { return previousIndex() >= 0; } @Override @SuppressWarnings("unchecked") public E previous() { if (hasPrevious()) return (E) i.previous(); else throw new NoSuchElementException(); } @Override public int nextIndex() { return i.nextIndex() - offset; } @Override public int previousIndex() { return i.previousIndex() - offset; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void set(E o) { throw new UnsupportedOperationException(); } @Override public void add(E o) { throw new UnsupportedOperationException(); } }; } @Override public final List<E> subList(int fromIndex, int toIndex) { return new ImmutableSubList<E>(offset + fromIndex, offset + toIndex); } } }