CloseableIterationSpliterator.java

/*******************************************************************************
 * Copyright (c) 2022 Eclipse RDF4J contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *******************************************************************************/

package org.eclipse.rdf4j.common.iteration;

import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;

/**
 * A {@link Spliterator} implementation that wraps a {@link CloseableIteration}.
 */
class CloseableIterationSpliterator<T> extends Spliterators.AbstractSpliterator<T> {

	private final CloseableIteration<T> iteration;

	/**
	 * Creates a {@link Spliterator} implementation that wraps the supplied {@link CloseableIteration}. It handles
	 * occurrence of checked exceptions by wrapping them in RuntimeException, and in addition ensures that the wrapped
	 * iteration is closed when exhausted.
	 *
	 * @param iteration the iteration to wrap
	 */
	public CloseableIterationSpliterator(CloseableIteration<T> iteration) {
		super(Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.NONNULL);
		this.iteration = iteration;
	}

	@Override
	public boolean tryAdvance(Consumer<? super T> action) {
		Objects.requireNonNull(action, "action may not be null");

		try {
			if (iteration.hasNext()) {
				action.accept(iteration.next());
				return true;
			} else {
				// iteration is empty, so we can close it
				iteration.close();
				return false;
			}
		} catch (Throwable e) {
			if (e instanceof InterruptedException) {
				Thread.currentThread().interrupt();
			}
			try {
				iteration.close();
			} catch (Exception ex) {
				if (e instanceof InterruptedException) {
					Thread.currentThread().interrupt();
				}
				throw new RuntimeException(ex);
			}
			if (e instanceof Error) {
				throw (Error) e;
			}
			if (e instanceof RuntimeException) {
				throw (RuntimeException) e;
			}
			throw new RuntimeException(e);
		}
	}

	@Override
	public void forEachRemaining(Consumer<? super T> action) {
		Objects.requireNonNull(action, "action may not be null");
		try (iteration) {
			while (iteration.hasNext()) {
				action.accept(iteration.next());
			}
		} catch (Exception e) {
			if (e instanceof InterruptedException) {
				Thread.currentThread().interrupt();
			}
			if (e instanceof RuntimeException) {
				throw (RuntimeException) e;
			}
			throw new RuntimeException(e);
		}
	}
}