/*
* Copyright (C) 2007 Google Inc.
*
* 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 com.google.inject.internal.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* This class contains static utility methods that operate on or return objects
* of type {@code Iterator}. Also see the parallel implementations in {@link
* Iterables}.
*
* @author Kevin Bourrillion
* @author Scott Bonneau
*/
public final class Iterators {
private Iterators() {}
static final Iterator<Object> EMPTY_ITERATOR
= new UnmodifiableIterator<Object>() {
public boolean hasNext() {
return false;
}
public Object next() {
throw new NoSuchElementException();
}
};
/** Returns the empty {@code Iterator}. */
// Casting to any type is safe since there are no actual elements.
@SuppressWarnings("unchecked")
public static <T> UnmodifiableIterator<T> emptyIterator() {
return (UnmodifiableIterator<T>) EMPTY_ITERATOR;
}
private static final ListIterator<Object> EMPTY_LIST_ITERATOR =
new ListIterator<Object>() {
public boolean hasNext() {
return false;
}
public boolean hasPrevious() {
return false;
}
public int nextIndex() {
return 0;
}
public int previousIndex() {
return -1;
}
public Object next() {
throw new NoSuchElementException();
}
public Object previous() {
throw new NoSuchElementException();
}
public void set(Object o) {
throw new UnsupportedOperationException();
}
public void add(Object o) {
throw new UnsupportedOperationException();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
/** Returns the empty {@code ListIterator}. */
// Casting to any type is safe since there are no actual elements.
@SuppressWarnings("unchecked")
public static <T> ListIterator<T> emptyListIterator() {
return (ListIterator<T>) EMPTY_LIST_ITERATOR;
}
/** Returns an unmodifiable view of {@code iterator}. */
public static <T> UnmodifiableIterator<T> unmodifiableIterator(
final Iterator<T> iterator) {
Preconditions.checkNotNull(iterator);
return new UnmodifiableIterator<T>() {
public boolean hasNext() {
return iterator.hasNext();
}
public T next() {
return iterator.next();
}
};
}
/**
* Returns a string representation of {@code iterator}, with the format
* {@code [e1, e2, ..., en]}. The iterator will be left exhausted: its
* {@code hasNext()} method will return {@code false}.
*/
public static String toString(Iterator<?> iterator) {
if (!iterator.hasNext()) {
return "[]";
}
StringBuilder builder = new StringBuilder();
builder.append('[').append(iterator.next());
while (iterator.hasNext()) {
builder.append(", ").append(iterator.next());
}
return builder.append(']').toString();
}
/**
* Returns the single element contained in {@code iterator}.
*
* @throws NoSuchElementException if the iterator is empty
* @throws IllegalArgumentException if the iterator contains multiple
* elements. The state of the iterator is unspecified.
*/
public static <T> T getOnlyElement(Iterator<T> iterator) {
T first = iterator.next();
if (!iterator.hasNext()) {
return first;
}
StringBuilder sb = new StringBuilder();
sb.append("expected one element but was: <" + first);
for (int i = 0; i < 4 && iterator.hasNext(); i++) {
sb.append(", " + iterator.next());
}
if (iterator.hasNext()) {
sb.append(", ...");
}
sb.append(">");
throw new IllegalArgumentException(sb.toString());
}
/**
* Combines multiple iterators into a single iterator. The returned iterator
* iterates across the elements of each iterator in {@code inputs}. The input
* iterators are not polled until necessary.
*
* <p>The returned iterator supports {@code remove()} when the corresponding
* input iterator supports it. The methods of the returned iterator may throw
* {@code NullPointerException} if any of the input iterators are null.
*/
public static <T> Iterator<T> concat(
final Iterator<? extends Iterator<? extends T>> inputs) {
Preconditions.checkNotNull(inputs);
return new Iterator<T>() {
Iterator<? extends T> current = emptyIterator();
Iterator<? extends T> removeFrom;
public boolean hasNext() {
while (!current.hasNext() && inputs.hasNext()) {
current = inputs.next();
}
return current.hasNext();
}
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
removeFrom = current;
return current.next();
}
public void remove() {
Preconditions.checkState(removeFrom != null,
"no calls to next() since last call to remove()");
removeFrom.remove();
removeFrom = null;
}
};
}
/**
* Returns an iterator that applies {@code function} to each element of {@code
* fromIterator}.
*
* <p>The returned iterator supports {@code remove()} if the provided iterator
* does. After a successful {@code remove()} call, {@code fromIterator} no
* longer contains the corresponding element.
*/
public static <F, T> Iterator<T> transform(final Iterator<F> fromIterator,
final Function<? super F, ? extends T> function) {
Preconditions.checkNotNull(fromIterator);
Preconditions.checkNotNull(function);
return new Iterator<T>() {
public boolean hasNext() {
return fromIterator.hasNext();
}
public T next() {
F from = fromIterator.next();
return function.apply(from);
}
public void remove() {
fromIterator.remove();
}
};
}
// Methods only in Iterators, not in Iterables
/**
* Returns an iterator containing the elements of {@code array} in order. The
* returned iterator is a view of the array; subsequent changes to the array
* will be reflected in the iterator.
*
* <p><b>Note:</b> It is often preferable to represent your data using a
* collection type, for example using {@link Arrays#asList(Object[])}, making
* this method unnecessary.
*/
public static <T> UnmodifiableIterator<T> forArray(final T... array) {
// optimized. benchmarks at nearly 2x of the straightforward impl
return new UnmodifiableIterator<T>() {
final int length = array.length;
int i = 0;
public boolean hasNext() {
return i < length;
}
public T next() {
try {
// 'return array[i++];' almost works
T t = array[i];
i++;
return t;
} catch (ArrayIndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
};
}
/**
* Returns an iterator containing the elements in the specified range of
* {@code array} in order. The returned iterator is a view of the array;
* subsequent changes to the array will be reflected in the iterator.
*
* @param array array to read elements out of
* @param offset index of first array element to retrieve
* @param length number of elements in iteration
*
* @throws IndexOutOfBoundsException if {@code offset} is negative,
* {@code length} is negative, or {@code offset + length > array.length}
*/
public static <T> UnmodifiableIterator<T> forArray(
final T[] array, final int offset, final int length) {
Preconditions.checkArgument(length >= 0);
final int end = offset + length;
// Technically we should give a slightly more descriptive error on overflow
Preconditions.checkPositionIndexes(offset, end, array.length);
// If length == 0 is a common enough case, we could return emptyIterator().
return new UnmodifiableIterator<T>() {
int i = offset;
public boolean hasNext() {
return i < end;
}
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return array[i++];
}
};
}
/**
* Returns an iterator containing only {@code value}.
*/
public static <T> UnmodifiableIterator<T> singletonIterator(
@Nullable final T value) {
return new UnmodifiableIterator<T>() {
boolean done;
public boolean hasNext() {
return !done;
}
public T next() {
if (done) {
throw new NoSuchElementException();
}
done = true;
return value;
}
};
}
/**
* Adapts an {@code Iterator} to the {@code Enumeration} interface.
*
* @see Collections#enumeration(Collection)
*/
public static <T> Enumeration<T> asEnumeration(final Iterator<T> iterator) {
Preconditions.checkNotNull(iterator);
return new Enumeration<T>() {
public boolean hasMoreElements() {
return iterator.hasNext();
}
public T nextElement() {
return iterator.next();
}
};
}
}