io.sirix.service.xml.xpath.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.service.xml.xpath;
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.axis.IncludeSelf;
import it.unimi.dsi.fastutil.longs.LongIterator;
import org.checkerframework.checker.index.qual.NonNegative;
import io.sirix.index.path.summary.PathSummaryReader;
import io.sirix.settings.Fixed;
import java.util.NoSuchElementException;
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.
*
*/
public abstract class AbstractAxis implements Axis {
/** Iterate over transaction exclusive to this step. */
private final NodeCursor rtx;
/** Key of next node. */
protected long key;
/**
* Make sure {@code next()} can only be called after {@code hasNext()} has been called.
*/
private boolean next;
/** Key of node where axis started. */
private long startKey;
/** Include self? */
private final IncludeSelf includeSelf;
/** Determines if a next node follows or not. */
private boolean hasNext;
/**
* Bind axis step to transaction.
*
* @param rtx transaction to operate with
* @throws NullPointerException if {@code rtx} is {@code null}
*/
public AbstractAxis(final NodeCursor rtx) {
this.rtx = requireNonNull(rtx);
includeSelf = IncludeSelf.NO;
hasNext = true;
reset(rtx.getNodeKey());
}
/**
* Bind axis step to transaction.
*
* @param pRtx transaction to operate with
* @param pIncludeSelf determines if self is included
*/
public AbstractAxis(final NodeCursor pRtx, final IncludeSelf pIncludeSelf) {
rtx = requireNonNull(pRtx);
includeSelf = requireNonNull(pIncludeSelf);
hasNext = true;
reset(pRtx.getNodeKey());
}
@Override
public final LongIterator iterator() {
return this;
}
/**
* Signals that axis traversal is done, that is {@code hasNext()} must return false. Can be called
* from subclasses to signal that axis is done.
*
* @return null node key
*/
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 should implement {@code nextKey()} instead which is a template method called
* from this {@code hasNext()} method.
*
*/
@Override
public boolean hasNext() {
if (!isHasNext()) {
// End of the axis reached.
return false;
}
if (isNext()) {
// hasNext() has been called before without an intermediate next()-call.
return true;
}
// Reset to last node key.
resetToLastKey();
// Template method.
key = nextKey();
if (key == Fixed.NULL_NODE_KEY.getStandardProperty()) {
// Reset to the start key before invoking the axis.
resetToStartKey();
return false;
} else {
return true;
}
}
/**
* Please do not override {@code hasNext()} directly. Use this template method instead. It
* determines the next node key in the axis. Override this method to simplify {@code hasNext()}.
* Simply return {@code EFixed.NULL_NODE_KEY.getStandardProperty()} if no more node is following in
* the axis, otherwise return the node key of the next node.
*
* @return next node key
*/
protected long nextKey() {
return 0;
}
@Override
public final long nextLong() {
if (!hasNext) {
throw new NoSuchElementException("No more nodes in the axis!");
}
if (!next) {
if (!hasNext()) {
throw new NoSuchElementException("No more nodes in the axis!");
}
}
if (key >= 0) {
if (rtx.hasNode(key)) {
rtx.moveTo(key);
} else {
throw new IllegalStateException("Failed to move to nodeKey: " + key);
}
} else {
rtx.moveTo(key);
}
next = false;
return key;
}
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
/**
* Resetting the nodekey of this axis to a given nodekey.
*
* @param pNodeKey the nodekey where the reset should occur to
*/
@Override
public void reset(@NonNegative final long pNodeKey) {
startKey = pNodeKey;
key = pNodeKey;
next = false;
hasNext = true;
}
@Override
public XmlNodeReadOnlyTrx asXmlNodeReadTrx() {
if (rtx instanceof XmlNodeReadOnlyTrx rtx) {
return rtx;
}
throw new ClassCastException("Node cursor is no XML node transaction.");
}
@Override
public JsonNodeReadOnlyTrx asJsonNodeReadTrx() {
if (rtx instanceof JsonNodeReadOnlyTrx rtx) {
return rtx;
}
throw new ClassCastException("Node cursor is no JSON node transaction.");
}
@Override
public NodeReadOnlyTrx getTrx() {
if (rtx instanceof NodeReadOnlyTrx) {
return (XmlNodeReadOnlyTrx) rtx;
}
throw new IllegalStateException("Node cursor is no transaction cursor.");
}
@Override
public PathSummaryReader asPathSummary() {
if (rtx instanceof PathSummaryReader) {
return (PathSummaryReader) rtx;
}
throw new ClassCastException("Node cursor is no path summary reader.");
}
@Override
public NodeCursor getCursor() {
return rtx;
}
/**
* Determines if axis might have more results.
*
* @return {@code true} if axis might have more results, {@code false} otherwise
*/
public boolean isHasNext() {
return hasNext;
}
/**
* 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()}
*/
protected final long resetToStartKey() {
// No check because of IAxis Convention 4.
rtx.moveTo(startKey);
next = false;
hasNext = false;
return startKey;
}
/**
* 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 IAxis Convention 4.
rtx.moveTo(key);
next = true;
return key;
}
@Override
public final long getStartKey() {
return startKey;
}
@Override
public final IncludeSelf includeSelf() {
return includeSelf;
}
@Override
public long peek() {
return Fixed.NULL_NODE_KEY.getStandardProperty();
}
/**
* Get mNext which determines if {@code hasNext()} has at least been called once before the call to
* {@code next()}.
*
* @return {@code true} if {@code hasNext()} has been called before calling {@code next()},
* {@code false} otherwise
*/
public final boolean isNext() {
return next;
}
/**
* Implements a simple foreach-method.
*
* @param pVisitor {@link XmlNodeVisitor} implementation
*/
@Override
public final void foreach(final XmlNodeVisitor pVisitor) {
requireNonNull(pVisitor);
for (; hasNext(); nextLong()) {
((XmlNodeReadOnlyTrx) rtx).acceptVisitor(pVisitor);
}
}
@Override
public synchronized final long nextNode() {
synchronized (rtx) {
long retVal = Fixed.NULL_NODE_KEY.getStandardProperty();
if (hasNext()) {
retVal = nextLong();
}
return retVal;
}
}
}