001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkState;
020import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;
021
022import com.google.common.annotations.GwtCompatible;
023import com.google.errorprone.annotations.CanIgnoreReturnValue;
024import java.util.NoSuchElementException;
025import org.jspecify.annotations.Nullable;
026
027/**
028 * This class provides a skeletal implementation of the {@code Iterator} interface, to make this
029 * interface easier to implement for certain types of data sources.
030 *
031 * <p>{@code Iterator} requires its implementations to support querying the end-of-data status
032 * without changing the iterator's state, using the {@link #hasNext} method. But many data sources,
033 * such as {@link java.io.Reader#read()}, do not expose this information; the only way to discover
034 * whether there is any data left is by trying to retrieve it. These types of data sources are
035 * ordinarily difficult to write iterators for. But using this class, one must implement only the
036 * {@link #computeNext} method, and invoke the {@link #endOfData} method when appropriate.
037 *
038 * <p>Another example is an iterator that skips over null elements in a backing iterator. This could
039 * be implemented as:
040 *
041 * <pre>{@code
042 * public static Iterator<String> skipNulls(final Iterator<String> in) {
043 *   return new AbstractIterator<String>() {
044 *     protected String computeNext() {
045 *       while (in.hasNext()) {
046 *         String s = in.next();
047 *         if (s != null) {
048 *           return s;
049 *         }
050 *       }
051 *       return endOfData();
052 *     }
053 *   };
054 * }
055 * }</pre>
056 *
057 * <p>This class supports iterators that include null elements.
058 *
059 * @author Kevin Bourrillion
060 * @since 2.0
061 */
062// When making changes to this class, please also update the copy at
063// com.google.common.base.AbstractIterator
064@GwtCompatible
065public abstract class AbstractIterator<T extends @Nullable Object> extends UnmodifiableIterator<T> {
066  private State state = State.NOT_READY;
067
068  /** Constructor for use by subclasses. */
069  protected AbstractIterator() {}
070
071  private enum State {
072    /** We have computed the next element and haven't returned it yet. */
073    READY,
074
075    /** We haven't yet computed or have already returned the element. */
076    NOT_READY,
077
078    /** We have reached the end of the data and are finished. */
079    DONE,
080
081    /** We've suffered an exception and are kaput. */
082    FAILED,
083  }
084
085  private @Nullable T next;
086
087  /**
088   * Returns the next element. <b>Note:</b> the implementation must call {@link #endOfData()} when
089   * there are no elements left in the iteration. Failure to do so could result in an infinite loop.
090   *
091   * <p>The initial invocation of {@link #hasNext()} or {@link #next()} calls this method, as does
092   * the first invocation of {@code hasNext} or {@code next} following each successful call to
093   * {@code next}. Once the implementation either invokes {@code endOfData} or throws an exception,
094   * {@code computeNext} is guaranteed to never be called again.
095   *
096   * <p>If this method throws an exception, it will propagate outward to the {@code hasNext} or
097   * {@code next} invocation that invoked this method. Any further attempts to use the iterator will
098   * result in an {@link IllegalStateException}.
099   *
100   * <p>The implementation of this method may not invoke the {@code hasNext}, {@code next}, or
101   * {@link #peek()} methods on this instance; if it does, an {@code IllegalStateException} will
102   * result.
103   *
104   * @return the next element if there was one. If {@code endOfData} was called during execution,
105   *     the return value will be ignored.
106   * @throws RuntimeException if any unrecoverable error happens. This exception will propagate
107   *     outward to the {@code hasNext()}, {@code next()}, or {@code peek()} invocation that invoked
108   *     this method. Any further attempts to use the iterator will result in an {@link
109   *     IllegalStateException}.
110   */
111  protected abstract @Nullable T computeNext();
112
113  /**
114   * Implementations of {@link #computeNext} <b>must</b> invoke this method when there are no
115   * elements left in the iteration.
116   *
117   * @return {@code null}; a convenience so your {@code computeNext} implementation can use the
118   *     simple statement {@code return endOfData();}
119   */
120  @CanIgnoreReturnValue
121  protected final @Nullable T endOfData() {
122    state = State.DONE;
123    return null;
124  }
125
126  @Override
127  public final boolean hasNext() {
128    checkState(state != State.FAILED);
129    switch (state) {
130      case DONE:
131        return false;
132      case READY:
133        return true;
134      default:
135    }
136    return tryToComputeNext();
137  }
138
139  private boolean tryToComputeNext() {
140    state = State.FAILED; // temporary pessimism
141    next = computeNext();
142    if (state != State.DONE) {
143      state = State.READY;
144      return true;
145    }
146    return false;
147  }
148
149  @CanIgnoreReturnValue // TODO(kak): Should we remove this?
150  @Override
151  @ParametricNullness
152  public final T next() {
153    if (!hasNext()) {
154      throw new NoSuchElementException();
155    }
156    state = State.NOT_READY;
157    // Safe because hasNext() ensures that tryToComputeNext() has put a T into `next`.
158    T result = uncheckedCastNullableTToT(next);
159    next = null;
160    return result;
161  }
162
163  /**
164   * Returns the next element in the iteration without advancing the iteration, according to the
165   * contract of {@link PeekingIterator#peek()}.
166   *
167   * <p>Implementations of {@code AbstractIterator} that wish to expose this functionality should
168   * implement {@code PeekingIterator}.
169   */
170  @ParametricNullness
171  public final T peek() {
172    if (!hasNext()) {
173      throw new NoSuchElementException();
174    }
175    // Safe because hasNext() ensures that tryToComputeNext() has put a T into `next`.
176    return uncheckedCastNullableTToT(next);
177  }
178}