/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.util.collection;
import java.util.Arrays;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.AbstractSequentialList;
import java.util.NoSuchElementException;
import org.apache.sis.util.ArraysExt;
import org.geotoolkit.resources.Errors;
/**
* An immutable list filled when needed from the values returned by an iterator.
*
* @param <E> The type of elements in the list.
*
* @author Martin Desruisseaux (IRD)
* @version 3.00
*
* @since 2.0
* @module
*/
public class LazyList<E> extends AbstractSequentialList<E> {
/**
* The iterator to use for filling this set.
* Will be set to {@code null} when the iteration is over.
*/
private Iterator<? extends E> iterator;
/**
* The elements in this set. This array will grown as needed.
*/
private E[] elements;
/**
* Index past the last valid element. This value will increases as long as there is some
* elements remaining in the iterator.
*/
private int next;
/**
* Constructs a list to be filled using the specified iterator.
* Iteration will occurs only when needed.
*
* @param iterator The iterator to use for filling this list.
*/
@SuppressWarnings("unchecked")
public LazyList(final Iterator<? extends E> iterator) {
this.iterator = iterator;
elements = (E[]) new Object[4];
}
/**
* Adds the next element from the iterator to this list. This method doesn't check if more
* elements are available; the check must have been done before to invoke this method.
*/
private void addNext() {
if (next >= elements.length) {
elements = Arrays.copyOf(elements, next*2);
}
elements[next++] = iterator.next();
}
/**
* Returns {@code true} if this list has no elements.
*/
@Override
public final boolean isEmpty() {
if (next != 0) {
return false;
}
if (iterator != null) {
if (iterator.hasNext()) {
return false;
}
trimToSize();
}
return true;
}
/**
* Returns the number of elements in this list. Invoking this method
* force the set to immediately iterates through all remaining elements.
*/
@Override
public final int size() {
if (iterator != null) {
while (iterator.hasNext()) {
addNext();
}
trimToSize();
}
return next;
}
/**
* Returns the element at the specified position in this list.
*
* @param index The index at which to obtain an element.
* @return The element at the given index.
* @throws IndexOutOfBoundsException If the given index is out of bounds.
*/
@Override
public final E get(final int index) throws IndexOutOfBoundsException {
if (index >= 0) {
if (index < next) {
return elements[index];
}
if (iterator != null) {
while (iterator.hasNext()) {
addNext();
if (index < next) {
return elements[index];
}
}
trimToSize();
}
}
throw new IndexOutOfBoundsException(Errors.format(Errors.Keys.IndexOutOfBounds_1, index));
}
/**
* Returns an iterator over the elements in this list.
*
* @param index Index of first element to be returned from the iterator.
* @return An iterator over the elements in this list.
* @throws IndexOutOfBoundsException If the given index is out of bounds.
*/
@Override
public ListIterator<E> listIterator(final int index) throws IndexOutOfBoundsException {
if (index >= 0) {
if (index < next) {
return new Iter(index);
}
if (iterator != null) {
while (iterator.hasNext()) {
if (index == next) {
return new Iter(index);
}
addNext();
}
trimToSize();
}
}
throw new IndexOutOfBoundsException(Errors.format(Errors.Keys.IndexOutOfBounds_1, index));
}
/**
* Invoked when we have reached the end of iteration.
*/
private void trimToSize() {
iterator = null; // Lets GC do its work.
elements = ArraysExt.resize(elements, next);
}
/**
* Returns {@code true} if an element exists at the given index.
* The element is not loaded immediately.
* <p>
* <strong>NOTE: This method is for use by iterators only.</strong>
* It is not suited for more general usage since it doesn't check
* for negative index and for skipped elements.
*/
final boolean exists(final int index) {
return index < next || (iterator != null && iterator.hasNext());
}
/**
* The iterator implementation for the {@linkplain LazySet lazy set}.
*/
private final class Iter implements ListIterator<E> {
/** Index of the next element to be returned. */
private int cursor;
/** Creates an iterator starting at the given index. */
public Iter(final int index) {
cursor = index;
}
@Override
public boolean hasNext() {
return exists(cursor);
}
@Override
public boolean hasPrevious() {
return cursor != 0;
}
@Override
public int nextIndex() {
return cursor;
}
@Override
public int previousIndex() {
return cursor - 1;
}
@Override
public E next() {
if (!exists(cursor)) {
throw new NoSuchElementException();
}
return get(cursor++);
}
@Override
public E previous() {
if (cursor == 0) {
throw new NoSuchElementException();
}
return get(--cursor);
}
@Override
public void set(E e) {
throw new UnsupportedOperationException();
}
@Override
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}