Iterators.java

// Generated by delombok at Mon Nov 18 07:27:48 UTC 2024
package de.larssh.utils.collection;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import edu.umd.cs.findbugs.annotations.Nullable;

/**
 * This class contains helper methods for {@link Iterator}.
 */
public final class Iterators {
	/**
	 * Wraps {@code elementsSupplier} into a {@link PeekableIterator}. This is
	 * probably the simplest way of writing an iterator.
	 *
	 * <p>
	 * Note: This iterator is <b>not</b> thread safe.
	 *
	 * <p>
	 * <b>Usage example:</b> The following shows how to create an iterator returning
	 * every second element only.
	 *
	 * <pre>
	 * Iterator&lt;E&gt; oldIterator = ...;
	 * Iterator&lt;E&gt; newIterator = Iterators.iterator(state -&gt; {
	 *     if (!oldIterator.hasNext() &amp;&amp; !oldIterator.hasNext()) {
	 *         return state.endOfData();
	 *     }
	 *     return oldIterator.next();
	 * });
	 * </pre>
	 *
	 * @param <E>              the type of the iterator elements
	 * @param elementsSupplier the elements supplier with the state as parameter
	 * @return a {@link PeekableIterator} wrapping {@code elementsSupplier}
	 */
	public static <E> PeekableIterator<E> iterator(final Function<ElementsSupplierStateHandler<E>, E> elementsSupplier) {
		return new ElementsSupplierIterator<>(elementsSupplier);
	}

	/**
	 * Wraps {@code iterator} into a {@link PeekableIterator}.
	 *
	 * <p>
	 * Note: The resulting iterator is <b>not</b> thread safe.
	 *
	 * @param <E>      the type of the iterator elements
	 * @param iterator the iterator
	 * @return a {@link PeekableIterator} wrapping {@code iterator}
	 */
	public static <E> PeekableIterator<E> peekableIterator(final Iterator<E> iterator) {
		if (iterator instanceof PeekableIterator) {
			return (PeekableIterator<E>) iterator;
		}
		return iterator(state -> iterator.hasNext() ? iterator.next() : state.endOfData());
	}

	/**
	 * Returns a sequential {@link Stream} with the {@code elementsSupplier} as its
	 * source.
	 *
	 * <p>
	 * Check out {@link #iterator(Function)} for more information about
	 * {@code elementsSupplier}.
	 *
	 * @param <E>              the type of the iterator elements
	 * @param elementsSupplier the elements supplier with the state as parameter
	 * @return a {@code Stream} with the elements of {@code elementsSupplier}
	 */
	public static <E> Stream<E> stream(final Function<ElementsSupplierStateHandler<E>, E> elementsSupplier) {
		return stream(iterator(elementsSupplier));
	}

	/**
	 * Returns a sequential {@link Stream} with the {@code iterator} as its source.
	 *
	 * @param <E>      the type of the iterator elements
	 * @param iterator the iterator
	 * @return a {@code Stream} with the elements of {@code iterator}
	 */
	public static <E> Stream<E> stream(final Iterator<E> iterator) {
		return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
	}


	/**
	 * Implementation of a {@link PeekableIterator} based upon an elements supplier.
	 *
	 * <p>
	 * Check out {@link #iterator(Function)} for more information about the elements
	 * supplier.
	 *
	 * @param <E> the type of the iterator elements
	 */
	private static class ElementsSupplierIterator<E> implements PeekableIterator<E> {
		/**
		 * An elements supplier with the state as parameter.
		 *
		 * <p>
		 * Check out {@link #iterator(Function)} for more information.
		 */
		private final Function<ElementsSupplierStateHandler<E>, E> elementsSupplier;
		/**
		 * The next element supplied by {@link #elementsSupplier} if {@link #state} is
		 * {@link ElementsSupplierState#PEEKED}, else undefined.
		 */
		@Nullable
		private E peekedElement = null;
		/**
		 * The iterator's current inner state
		 */
		private ElementsSupplierState state = ElementsSupplierState.CALL_FOR_NEXT;
		/**
		 * Implementation of {@link ElementsSupplierStateHandler} to update this
		 * iterator's current state
		 */
		private final ElementsSupplierStateHandler<E> stateHandler = () -> {
			state = ElementsSupplierState.END_OF_DATA;
			return null;
		};

		/**
		 * {@inheritDoc}
		 */
		@Override
		@SuppressWarnings("checkstyle:XIllegalCatchDefault")
		public final boolean hasNext() {
			if (state == ElementsSupplierState.CALL_FOR_NEXT) {
				peekedElement = elementsSupplier.apply(stateHandler);
				if (state == ElementsSupplierState.CALL_FOR_NEXT) {
					state = ElementsSupplierState.PEEKED;
				}
			}
			return state == ElementsSupplierState.PEEKED;
		}

		/**
		 * {@inheritDoc}
		 */
		@Nullable
		@Override
		@SuppressWarnings("PMD.NullAssignment")
		public final E next() {
			// Method "hasNext" peeks the next element (if required)
			if (!hasNext()) {
				throw new NoSuchElementException();
			}
			final E next = peekedElement;
			state = ElementsSupplierState.CALL_FOR_NEXT;
			peekedElement = null;
			return next;
		}

		/**
		 * {@inheritDoc}
		 */
		@Nullable
		@Override
		public E peek() {
			// Method "hasNext" peeks the next element (if required)
			if (!hasNext()) {
				throw new NoSuchElementException();
			}
			return peekedElement;
		}

		/**
		 * Creates a new {@code ElementsSupplierIterator} instance.
		 *
		 * @param elementsSupplier An elements supplier with the state as parameter.
		 *
		 * <p>
		 * Check out {@link #iterator(Function)} for more information.
		 */
		@java.lang.SuppressWarnings("all")
		@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
		@lombok.Generated
		public ElementsSupplierIterator(final Function<ElementsSupplierStateHandler<E>, E> elementsSupplier) {
			this.elementsSupplier = elementsSupplier;
		}
	}


	/**
	 * This enumeration contains the possible inner states of
	 * {@link ElementsSupplierIterator}.
	 */
	@SuppressWarnings("PMD.UnnecessaryModifier")
	private static enum ElementsSupplierState {
		/**
		 * Status of iterators, that need to call the elements supplier once information
		 * about the next element is required.
		 */
		CALL_FOR_NEXT, /**
		 * Status representing the end of data. The elements supplier must not be called
		 * any longer. This is an end state and must not change once reached.
		 */
		END_OF_DATA, /**
		 * Status of iterators, that peeked the next element already.
		 */
		PEEKED;
	}


	/**
	 * An instance of this is passed to the elements supplier at the time of
	 * calculating the next element. It is used to mark the iteration's end. Check
	 * out {@link #endOfData()} for more information.
	 *
	 * @param <E> the type of the iterator elements
	 */
	@FunctionalInterface
	@SuppressWarnings("PMD.UnnecessaryModifier")
	public static interface ElementsSupplierStateHandler<E> {
		/**
		 * Marks the iteration's end, the so called <i>end of data</i>.
		 *
		 * <p>
		 * If this has been called the return value of the elements supplier will not be
		 * taken into account.
		 *
		 * <p>
		 * Check out {@link #iterator(Function)} for an usage example.
		 *
		 * @return any valid value, probably {@code null}
		 */
		@Nullable
		E endOfData();
	}

	@java.lang.SuppressWarnings("all")
	@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(justification = "generated code")
	@lombok.Generated
	private Iterators() {
		throw new java.lang.UnsupportedOperationException("This is a utility class and cannot be instantiated");
	}
}