io.sirix.axis.AbstractAxis Maven / Gradle / Ivy
/*
* Copyright (c) 2011, University of Konstanz, Distributed Systems Group All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met: * Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following disclaimer. * Redistributions
* in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of the University of Konstanz nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.sirix.axis;
import com.google.common.base.MoreObjects;
import it.unimi.dsi.fastutil.longs.LongIterator;
import org.checkerframework.checker.index.qual.NonNegative;
import io.sirix.api.Axis;
import io.sirix.api.NodeCursor;
import io.sirix.api.NodeReadOnlyTrx;
import io.sirix.api.json.JsonNodeReadOnlyTrx;
import io.sirix.api.visitor.XmlNodeVisitor;
import io.sirix.api.xml.XmlNodeReadOnlyTrx;
import io.sirix.index.path.summary.PathSummaryReader;
import io.sirix.settings.Fixed;
import java.util.NoSuchElementException;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
/**
*
* Provide standard Java iterator capability compatible with the new enhanced for loop available
* since Java 5.
*
*
* Override the "template method" {@code nextKey()} to implement an axis. Return {@code done()} if
* the axis has no more "elements".
*
*
* @author Johannes Lichtenberger
*/
public abstract class AbstractAxis implements Axis {
/** Iterate over transaction exclusive to this step. */
protected final NodeCursor nodeCursor;
/** Key of next node. */
private long nextNodeKey;
/** Key of node where axis started. */
private long startNodeKey;
/** Include self? */
private final IncludeSelf includeSelf;
/** Current state. */
private State state = State.NOT_READY;
/** State of the iterator. */
private enum State {
/** We have computed the next element and haven't returned it yet. */
READY,
/** We haven't yet computed or have already returned the element. */
NOT_READY,
/** We have reached the end of the data and are finished. */
DONE,
/** We've suffered an exception and are kaput. */
FAILED,
}
/**
* Bind axis step to transaction.
*
* @param nodeCursor node cursor
* @throws NullPointerException if {@code nodeCursor} is {@code null}
*/
public AbstractAxis(final NodeCursor nodeCursor) {
this.nodeCursor = requireNonNull(nodeCursor);
includeSelf = IncludeSelf.NO;
reset(nodeCursor.getNodeKey());
}
/**
* Bind axis step to transaction.
*
* @param nodeCursor node cursor
* @param includeSelf determines if self is included
* @throws NullPointerException if {@code nodeCursor} or {@code includeSelf} is {@code null}
*/
public AbstractAxis(final NodeCursor nodeCursor, final IncludeSelf includeSelf) {
this.nodeCursor = requireNonNull(nodeCursor);
this.includeSelf = requireNonNull(includeSelf);
reset(nodeCursor.getNodeKey());
}
@Override
public final LongIterator iterator() {
return this;
}
/**
* Signals that axis traversal is done, that is {@code hasNext()} must return false. Is callable
* from subclasses which implement {@link #nextKey()} to signal that the axis-traversal is done and
* {@link #hasNext()} must return false.
*
* @return null node key to indicate that the travesal is done
*/
protected long done() {
return Fixed.NULL_NODE_KEY.getStandardProperty();
}
/**
* {@inheritDoc}
*
*
* During the last call to {@code hasNext()}, that is {@code hasNext()} returns false, the
* transaction is reset to the start key.
*
*
*
* Implementors must implement {@code nextKey()} instead which is a template method called
* from this {@code hasNext()} method.
*
*/
@Override
public final boolean hasNext() {
// First check the state.
checkState(state != State.FAILED);
switch (state) {
case DONE:
return false;
case READY:
return true;
case FAILED:
case NOT_READY:
default:
}
// Reset to last node key.
resetToLastKey();
final boolean hasNext = tryToComputeNext();
if (hasNext) {
return true;
} else {
// Reset to the start key before invoking the axis.
resetToStartKey();
return false;
}
}
/**
* Try to compute the next node key.
*
* @return {@code true} if next node key exists, {@code false} otherwise
*/
private boolean tryToComputeNext() {
state = State.FAILED; // temporary pessimism
// Template method.
nextNodeKey = nextKey();
if (nextNodeKey == Fixed.NULL_NODE_KEY.getStandardProperty()) {
state = State.DONE;
}
if (state == State.DONE) {
return false;
}
state = State.READY;
return true;
}
/**
* Returns the next node key. Note: the implementation must either call
* {@link #done()} when there are no elements left in the iteration or return the node key
* {@code EFixed.NULL_NODE.getStandardProperty()}.
*
*
* The initial invocation of {@link #hasNext()} or {@link #next()} calls this method, as does the
* first invocation of {@code hasNext} or {@code next} following each successful call to
* {@code next}. Once the implementation either invokes {@link #done()}, returns
* {@code EFixed.NULL_NODE.getStandardProperty()} or throws an exception, {@code nextKey()} is
* guaranteed to never be called again.
*
*
*
* If this method throws an exception, it will propagate outward to the {@code hasNext} or
* {@code next} invocation that invoked this method. Any further attempts to use the iterator will
* result in an {@link IllegalStateException}.
*
*
*
* The implementation of this method may not invoke the {@code hasNext}, {@code next}, or
* {@link #peek()} methods on this instance; if it does, an {@code IllegalStateException} will
* result.
*
*
* @return the next node key
* @throws RuntimeException if any unrecoverable error happens. This exception will propagate
* outward to the {@code hasNext()}, {@code next()}, or {@code peek()} invocation that
* invoked this method. Any further attempts to use the iterator will result in an
* {@link IllegalStateException}.
*/
protected abstract long nextKey();
@Override
public final long nextLong() {
if (!hasNext()) {
throw new NoSuchElementException();
}
state = State.NOT_READY;
// Move to next.
if (nextNodeKey >= 0) {
if (!nodeCursor.moveTo(nextNodeKey)) {
throw new IllegalStateException("Failed to move to nodeKey: " + nextNodeKey);
}
} else {
nodeCursor.moveTo(nextNodeKey);
}
return nextNodeKey;
}
/**
* Remove is not supported.
*/
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
/**
* Resetting the nodekey of this axis to a given nodekey.
*
* @param nodeKey the nodekey where the reset should occur to
*/
@Override
public void reset(@NonNegative final long nodeKey) {
startNodeKey = nodeKey;
nextNodeKey = nodeKey;
state = State.NOT_READY;
}
@Override
public XmlNodeReadOnlyTrx asXmlNodeReadTrx() {
if (nodeCursor instanceof XmlNodeReadOnlyTrx) {
return (XmlNodeReadOnlyTrx) nodeCursor;
}
throw new ClassCastException("Node cursor is no XDM node transaction.");
}
@Override
public JsonNodeReadOnlyTrx asJsonNodeReadTrx() {
if (nodeCursor instanceof JsonNodeReadOnlyTrx) {
return (JsonNodeReadOnlyTrx) nodeCursor;
}
throw new ClassCastException("Node cursor is no JSON node transaction.");
}
@Override
public PathSummaryReader asPathSummary() {
if (nodeCursor instanceof PathSummaryReader) {
return (PathSummaryReader) nodeCursor;
}
throw new ClassCastException("Node cursor is no path summary reader.");
}
@Override
public NodeCursor getCursor() {
return nodeCursor;
}
@Override
public NodeReadOnlyTrx getTrx() {
if (nodeCursor instanceof NodeReadOnlyTrx) {
return (NodeReadOnlyTrx) nodeCursor;
}
throw new ClassCastException("Node cursor is no transactional cursor.");
}
/**
* Make sure the transaction points to the node it started with. This must be called just before
* {@code hasNext() == false}.
*
* @return key of node where transaction was before the first call of {@code hasNext()}
*/
private long resetToStartKey() {
// No check because of Axis Convention 4.
nodeCursor.moveTo(startNodeKey);
return startNodeKey;
}
/**
* Make sure the transaction points to the node after the last hasNext(). This must be called first
* in hasNext().
*
* @return key of node where transaction was after the last call of {@code hasNext()}
*/
protected final long resetToLastKey() {
// No check because of Axis convention 4.
if (nodeCursor.getNodeKey() != nextNodeKey) {
nodeCursor.moveTo(nextNodeKey);
}
return nextNodeKey;
}
@Override
public final long peek() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return nextNodeKey;
}
@Override
public final long getStartKey() {
return startNodeKey;
}
@Override
public final IncludeSelf includeSelf() {
return includeSelf;
}
/**
* Implements a simple foreach-method.
*
* @param visitor {@link XmlNodeVisitor} implementation
*/
@Override
public final void foreach(final XmlNodeVisitor visitor) {
requireNonNull(visitor);
if (nodeCursor instanceof XmlNodeReadOnlyTrx) {
while (hasNext()) {
nextLong();
((XmlNodeReadOnlyTrx) nodeCursor).acceptVisitor(visitor);
}
}
}
@Override
public synchronized final long nextNode() {
synchronized (nodeCursor) {
long retVal = Fixed.NULL_NODE_KEY.getStandardProperty();
if (hasNext()) {
retVal = nextLong();
}
return retVal;
}
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("trx", nodeCursor).toString();
}
}