/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.common.collection;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.util.CheckArg;
/**
* An immutable {@link List} that consists of a single element appended to another existing {@link List}. The result is a list
* that contains all of the elements in the parent list as well as the last appended element, but while reusing the existing
* parent list and without having to create a copy of the parent list.
*
* @param <T> the type of element
*/
@Immutable
public class ImmutableAppendedList<T> implements List<T> {
private final List<T> parent;
private final T element;
private final int size;
private transient int hc;
/**
* Create an instance using the supplied parent list and an element to be virtually appended to the parent. Note that the
* parent must be immutable (though this is not checked).
*
* @param parent the parent list
* @param element the child element (may be null)
* @throws IllegalArgumentException if the reference to the parent list is null
*/
public ImmutableAppendedList( List<T> parent,
T element ) {
CheckArg.isNotNull(parent, "parent");
this.parent = parent;
this.element = element;
this.size = parent.size() + 1;
}
@Override
public boolean contains( Object o ) {
return element == o || (element != null && element.equals(o)) || parent.contains(o);
}
@Override
public boolean containsAll( Collection<?> c ) {
Iterator<?> e = c.iterator();
while (e.hasNext()) {
if (!contains(e.next())) return false;
}
return true;
}
@Override
public T get( int index ) {
if (index == (size - 1)) return element;
return parent.get(index);
}
@Override
public int indexOf( Object o ) {
int index = parent.indexOf(o);
if (index == -1) {
return (element == o || (element != null && element.equals(o))) ? (size - 1) : -1;
}
return -1;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
@SuppressWarnings( "synthetic-access" )
public Iterator<T> iterator() {
final Iterator<T> parentIterator = parent.iterator();
return new Iterator<T>() {
boolean finished = false;
@Override
public boolean hasNext() {
return parentIterator.hasNext() || !finished;
}
@Override
public T next() {
if (parentIterator.hasNext()) return parentIterator.next();
if (finished) throw new NoSuchElementException();
finished = true;
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int lastIndexOf( Object o ) {
if (element == o || (element != null && element.equals(o))) return size - 1;
return parent.lastIndexOf(o);
}
@Override
public ListIterator<T> listIterator() {
return listIterator(0);
}
@Override
@SuppressWarnings( "synthetic-access" )
public ListIterator<T> listIterator( final int index ) {
return new ListIterator<T>() {
int cursor = index;
@Override
public boolean hasNext() {
return cursor < size;
}
@Override
public T next() {
try {
T next = get(cursor);
cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
@Override
public boolean hasPrevious() {
return cursor != 0;
}
@Override
public int nextIndex() {
return cursor;
}
@Override
public T previous() {
try {
int i = cursor - 1;
T previous = get(i);
cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
@Override
public int previousIndex() {
return cursor - 1;
}
@Override
public void set( T o ) {
throw new UnsupportedOperationException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void add( T o ) {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return size;
}
@Override
public List<T> subList( int fromIndex,
int toIndex ) {
if (fromIndex == 0 && toIndex == size) {
// The bounds are the same as this list, so just return this list ...
return this;
}
if (toIndex == size && fromIndex == (size - 1)) {
// The only list is the last element ...
return Collections.singletonList(element);
}
if (toIndex < size) {
// It is all within the range of the parent's list, so simply delegate
return parent.subList(fromIndex, toIndex);
}
// Otherwise, the sublist starts within the parent list and ends with the last element.
// So, create a sublist starting at the 'fromIndex' until the end of the parent list ...
List<T> sublist = parent.subList(fromIndex, toIndex - 1); // will catch out-of-bounds errors
// And wrap with another immutable appended list to add the last element ...
return new ImmutableAppendedList<T>(sublist, element);
}
@Override
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (T e : parent) {
result[i++] = e;
}
result[i] = element;
return result;
}
@Override
@SuppressWarnings( "unchecked" )
public <X> X[] toArray( X[] a ) {
if (a.length < size) a = (X[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
a = parent.toArray(a);
a[size - 1] = (X)element;
return a;
}
@Override
public int hashCode() {
if (hc == 0) {
int hashCode = 1;
for (T element : this) {
hashCode = 31 * hashCode + (element == null ? 0 : element.hashCode());
}
hc = hashCode;
}
return hc;
}
@Override
public boolean equals( Object obj ) {
if (obj == this) return true;
if (obj instanceof List<?>) {
List<?> that = (List<?>)obj;
if (this.size() != that.size()) return false;
Iterator<?> thisIter = this.iterator();
Iterator<?> thatIter = that.iterator();
while (thisIter.hasNext()) {
Object thisValue = thisIter.next();
Object thatValue = thatIter.next();
if (thisValue == null) {
if (thatValue != null) return false;
// assert thatValue == null;
} else {
if (!thisValue.equals(thatValue)) return false;
}
}
return true;
}
return super.equals(obj);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
Iterator<T> i = iterator();
boolean hasNext = i.hasNext();
while (hasNext) {
T o = i.next();
sb.append(o == this ? "(this Collection)" : String.valueOf(o));
hasNext = i.hasNext();
if (hasNext) sb.append(", ");
}
sb.append("]");
return sb.toString();
}
// ----------------------------------------------------------------------------------------------------------------
// Methods that modify are not supported
// ----------------------------------------------------------------------------------------------------------------
@Override
public void add( int index,
T element ) {
throw new UnsupportedOperationException();
}
@Override
public boolean add( T o ) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll( Collection<? extends T> c ) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll( int index,
Collection<? extends T> c ) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove( Object o ) {
throw new UnsupportedOperationException();
}
@Override
public T remove( int index ) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll( Collection<?> c ) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll( Collection<?> c ) {
throw new UnsupportedOperationException();
}
@Override
public T set( int index,
T element ) {
throw new UnsupportedOperationException();
}
}