LoggingCloseableIteration.java

/*******************************************************************************
 * Copyright (c) 2020 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.sail.shacl.ast.planNodes;

import java.util.NoSuchElementException;

import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.sail.InterruptedSailException;
import org.eclipse.rdf4j.sail.SailException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class LoggingCloseableIteration implements CloseableIteration<ValidationTuple> {

	private static final Logger log = LoggerFactory.getLogger(LoggingCloseableIteration.class);
	private final ValidationExecutionLogger validationExecutionLogger;
	private final PlanNode planNode;
	private boolean closed;
	private boolean initialized;

	public LoggingCloseableIteration(PlanNode planNode, ValidationExecutionLogger validationExecutionLogger) {
		this.planNode = planNode;
		this.validationExecutionLogger = validationExecutionLogger;
	}

	@Override
	public final ValidationTuple next() throws SailException {
		assert initialized;

		ValidationTuple tuple = loggingNext();

		if (validationExecutionLogger.isEnabled()) {
			validationExecutionLogger.log(planNode.depth(), planNode.getClass().getSimpleName() + ".next()", tuple,
					planNode, planNode.getId(), null);
		}
		return tuple;
	}

	@Override
	public final boolean hasNext() throws SailException {
		if (closed) {
			return false;
		}

		if (Thread.currentThread().isInterrupted()) {
			close();
			return false;
		}

		if (!initialized) {
			initialized = true;
			try {
				init();
			} catch (InterruptedSailException e) {
				Thread.currentThread().interrupt();
				close();
				return false;
			} catch (Throwable t) {
				close();
				throw t;
			}

		}

		if (Thread.currentThread().isInterrupted()) {
			close();
			return false;
		}

		boolean hasNext = false;
		try {
			hasNext = localHasNext();
		} catch (NoSuchElementException loggedOnly) {
			log.trace("No more elements in iteration: {}", this.getClass(), loggedOnly);
		}

		if (!hasNext) {
			assert !localHasNext() || closed
					: "Iterator was initially empty, but still has more elements! " + this.getClass();
			close();
		}

		return hasNext;
	}

	@Override
	public void close() throws SailException {
		if (!closed) {
			this.closed = true;
			localClose();
		}
	}

	protected abstract void init();

	protected abstract ValidationTuple loggingNext();

	protected abstract boolean localHasNext();

	protected abstract void localClose();

	/**
	 * A default method since the iterators in the ShaclSail don't support remove.
	 *
	 * @throws SailException
	 */
	@Override
	public void remove() throws SailException {
		throw new UnsupportedOperationException();
	}

	public boolean isClosed() {
		return closed;
	}

}