All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sirix.index.avltree.AVLTreeReader Maven / Gradle / Ivy

package org.sirix.index.avltree;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Deque;
import java.util.Optional;
import javax.annotation.Nonnegative;

import org.sirix.api.Move;
import org.sirix.api.NodeCursor;
import org.sirix.api.PageReadOnlyTrx;
import org.sirix.exception.SirixIOException;
import org.sirix.index.IndexType;
import org.sirix.index.SearchMode;
import org.sirix.index.avltree.interfaces.References;
import org.sirix.node.NodeKind;
import org.sirix.node.NullNode;
import org.sirix.node.interfaces.Node;
import org.sirix.node.interfaces.Record;
import org.sirix.node.interfaces.StructNode;
import org.sirix.node.interfaces.immutable.ImmutableNode;
import org.sirix.node.xml.XmlDocumentRootNode;
import org.sirix.page.PageKind;
import org.sirix.settings.Constants;
import org.sirix.settings.Fixed;
import org.sirix.utils.LogWrapper;
import org.slf4j.LoggerFactory;
import com.google.common.collect.AbstractIterator;

/**
 * Simple AVLTreeReader (balanced binary search-tree -- based on BaseX(.org) version).
 *
 * @author Johannes Lichtenberger, University of Konstanz
 *
 * @param  the key to search for or insert
 * @param  the value
 */
public final class AVLTreeReader, V extends References> implements NodeCursor {

  /** {@link LogWrapper} reference. */
  private static final LogWrapper LOGWRAPPER = new LogWrapper(LoggerFactory.getLogger(AVLTreeReader.class));

  /** Determines if tree is closed or not. */
  private boolean mClosed;

  /** Strong reference to currently selected node. */
  private Node mCurrentNode;

  /** {@link PageReadOnlyTrx} for persistent storage. */
  final PageReadOnlyTrx mPageReadTrx;

  /** Page kind. */
  final PageKind mPageKind;

  /** Index number. */
  final int mIndex;

  /** Determines movement of the internal cursor. */
  public enum MoveCursor {
    /** Cursor should be moved document root. */
    TO_DOCUMENT_ROOT,

    /** Cursor should not be moved. */
    NO_MOVE
  }

  /**
   * Private constructor.
   *
   * @param pageReadTrx {@link PageReadOnlyTrx} for persistent storage
   * @param type kind of index
   * @param index the index number
   */
  private AVLTreeReader(final PageReadOnlyTrx pageReadTrx, final IndexType type, final int index) {
    mPageReadTrx = checkNotNull(pageReadTrx);
    switch (type) {
      case PATH:
        mPageKind = PageKind.PATHPAGE;
        break;
      case CAS:
        mPageKind = PageKind.CASPAGE;
        break;
      case NAME:
        mPageKind = PageKind.NAMEPAGE;
        break;
      default:
        throw new IllegalStateException();
    }
    mClosed = false;
    mIndex = index;

    try {
      final Optional node =
          mPageReadTrx.getRecord(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(), mPageKind, index);
      if (node.isPresent()) {
        mCurrentNode = (Node) node.get();
      } else {
        throw new IllegalStateException("Node couldn't be fetched from persistent storage!");
      }
    } catch (final SirixIOException e) {
      LOGWRAPPER.error(e.getMessage(), e);
    }
  }

  /**
   * Get a new instance.
   *
   * @param pageReadTrx {@link PageReadOnlyTrx} for persistent storage
   * @param type type of index
   * @return new tree instance
   */
  public static , V extends References> AVLTreeReader getInstance(
      final PageReadOnlyTrx pageReadTrx, final IndexType type, final @Nonnegative int index) {
    return new AVLTreeReader(pageReadTrx, type, index);
  }

  /**
   * Dump the AVLTree in preorder.
   *
   * @param out {@link PrintStream} to print to
   */
  public void dump(final PrintStream out) {
    assertNotClosed();
    moveToDocumentRoot();
    if (((XmlDocumentRootNode) getNode()).hasFirstChild()) {
      moveToFirstChild();
    } else {
      return;
    }

    internalDump(out);
  }

  // Internal function to dump data to a PrintStream instance.
  private void internalDump(final PrintStream out) {
    out.println(getAVLNode());
    final long nodeKey = getAVLNode().getNodeKey();
    if (getAVLNode().hasLeftChild()) {
      moveToFirstChild();
      internalDump(out);
    }
    moveTo(nodeKey);
    if (getAVLNode().hasRightChild()) {
      moveToLastChild();
      internalDump(out);
    }
  }

  /**
   * Get the {@link AVLNode}.
   *
   * @return {@link AVLNode} instance
   */
  AVLNode getAVLNode() {
    assertNotClosed();
    if (mCurrentNode.getKind() != NodeKind.XDM_DOCUMENT) {
      @SuppressWarnings("unchecked")
      final AVLNode node = (AVLNode) mCurrentNode;
      return node;
    }
    return null;
  }

  /**
   * Finds the specified key in the index and returns its value.
   *
   * @param startNodeKey the key of the node to start from
   * @param key key to be found
   * @param mode the search mode
   * @return {@link Optional} reference (with the found value, or a reference which indicates that the
   *         value hasn't been found)
   */
  public Optional get(final long startNodeKey, final K key, final SearchMode mode) {
    assertNotClosed();
    final boolean movedToStartNode = moveTo(startNodeKey).hasMoved();
    if (!movedToStartNode) {
      return Optional.empty();
    }
    moveToFirstChild();
    AVLNode node = getAVLNode();
    while (true) {
      final int c = mode.compare(key, node.getKey());
      if (c == 0) {
        return Optional.ofNullable(node.getValue());
      }
      final boolean moved = c < 0
          ? moveToFirstChild().hasMoved()
          : moveToLastChild().hasMoved();
      if (moved) {
        node = getAVLNode();
      } else {
        break;
      }
    }
    return Optional.empty();
  }

  /**
   * Finds the specified key in the index and returns its value.
   *
   * @param key key to be found
   * @param mode the search mode
   * @return {@link Optional} reference (with the found value, or a reference which indicates that the
   *         value hasn't been found)
   */
  public Optional get(final K key, final SearchMode mode) {
    assertNotClosed();
    moveToDocumentRoot();
    if (!((XmlDocumentRootNode) getNode()).hasFirstChild()) {
      return Optional.empty();
    }
    moveToFirstChild();
    AVLNode node = getAVLNode();
    while (true) {
      final int c = mode.compare(key, node.getKey());
      if (c == 0) {
        return Optional.ofNullable(node.getValue());
      }
      final boolean moved = c < 0
          ? moveToFirstChild().hasMoved()
          : moveToLastChild().hasMoved();
      if (moved) {
        node = getAVLNode();
      } else {
        break;
      }
    }
    return Optional.empty();
  }

  /**
   * Finds the specified key in the index starting from the specified node key and returns its
   * AVLNode.
   *
   * @param key key to be found
   * @param mode the search mode
   * @return Optional {@link AVLNode} reference
   */
  public Optional> getAVLNode(final long startNodeKey, final K key, final SearchMode mode) {
    assertNotClosed();
    final boolean movedToStartNode = moveTo(startNodeKey).hasMoved();
    if (!movedToStartNode) {
      return Optional.empty();
    }
    moveToFirstChild();
    AVLNode node = getAVLNode();
    while (true) {
      final int c = mode.compare(key, node.getKey());
      if (c == 0) {
        return Optional.ofNullable(node);
      }
      final boolean moved = c < 0
          ? moveToFirstChild().hasMoved()
          : moveToLastChild().hasMoved();
      if (moved) {
        node = getAVLNode();
      } else {
        break;
      }
    }
    return Optional.empty();
  }

  /**
   * Finds the specified key in the index and returns its AVLNode.
   *
   * @param key key to be found
   * @param mode the search mode
   * @return Optional {@link AVLNode} reference
   */
  public Optional> getAVLNode(final K key, final SearchMode mode) {
    assertNotClosed();
    moveToDocumentRoot();
    if (!((XmlDocumentRootNode) getNode()).hasFirstChild()) {
      return Optional.empty();
    }
    moveToFirstChild();
    AVLNode node = getAVLNode();
    while (true) {
      final int c = mode.compare(key, node.getKey());
      if (c == 0) {
        return Optional.ofNullable(node);
      }
      final boolean moved = c < 0
          ? moveToFirstChild().hasMoved()
          : moveToLastChild().hasMoved();
      if (moved) {
        node = getAVLNode();
      } else {
        break;
      }
    }
    return Optional.empty();
  }

  /**
   * Finds the specified key in the index and returns its AVLNode.
   *
   * @param key key to be found
   * @param mode the search mode
   * @return Optional {@link AVLNode} reference
   */
  public Optional> getAVLNode(final K key, final SearchMode mode, final Comparator comp) {
    assertNotClosed();
    moveToDocumentRoot();
    if (!((XmlDocumentRootNode) getNode()).hasFirstChild()) {
      return Optional.empty();
    }
    moveToFirstChild();
    AVLNode node = getAVLNode();
    while (true) {
      final int c = mode.compare(key, node.getKey(), comp);
      if (c == 0) {
        return Optional.ofNullable(node);
      }
      final boolean moved = c < 0
          ? moveToFirstChild().hasMoved()
          : moveToLastChild().hasMoved();
      if (moved) {
        node = getAVLNode();
      } else {
        break;
      }
    }
    return Optional.empty();
  }

  /**
   * Returns the number of index entries.
   *
   * @return number of index entries
   */
  public long size() {
    return ((XmlDocumentRootNode) moveToDocumentRoot().trx().getNode()).getDescendantCount();
  }

  @Override
  public void close() {
    mClosed = true;
  }

  /**
   * Make sure that the path summary is not yet closed when calling this method.
   */
  final void assertNotClosed() {
    if (mClosed) {
      throw new IllegalStateException("Path summary is already closed.");
    }
  }

  /**
   * Set current node.
   *
   * @param node the node to set
   */
  public void setCurrentNode(final AVLNode node) {
    mCurrentNode = checkNotNull(node);
  }

  /**
   * Get the structural node of the current node.
   *
   * @return structural node
   */
  private StructNode getStructuralNode() {
    assertNotClosed();
    if (mCurrentNode instanceof StructNode) {
      return (StructNode) mCurrentNode;
    } else {
      return new NullNode(mCurrentNode);
    }
  }

  @Override
  public boolean hasNode(final long key) {
    assertNotClosed();
    final long currKey = mCurrentNode.getNodeKey();
    final boolean moved = moveTo(key).hasMoved();
    final Move> movedCursor = moveTo(currKey);
    assert movedCursor.hasMoved() == true : "Must be moveable back!";
    return moved;
  }

  @Override
  public boolean hasParent() {
    assertNotClosed();
    return mCurrentNode.hasParent();
  }

  @Override
  public boolean hasFirstChild() {
    assertNotClosed();
    return getStructuralNode().hasFirstChild();
  }

  @Override
  public boolean hasLastChild() {
    assertNotClosed();
    if (mCurrentNode instanceof AVLNode) {
      return getAVLNode().hasRightChild();
    }
    return false;
  }

  @Override
  public boolean hasLeftSibling() {
    assertNotClosed();
    return getStructuralNode().hasLeftSibling();
  }

  @Override
  public boolean hasRightSibling() {
    assertNotClosed();
    return getStructuralNode().hasRightSibling();
  }

  @Override
  public Move> moveTo(final long nodeKey) {
    assertNotClosed();

    if (nodeKey == Fixed.NULL_NODE_KEY.getStandardProperty()) {
      return Move.notMoved();
    }

    // Remember old node and fetch new one.
    final Node oldNode = mCurrentNode;
    Optional newNode;
    try {
      // Immediately return node from item list if node key negative.
      @SuppressWarnings("unchecked")
      final Optional node =
          (Optional) mPageReadTrx.getRecord(nodeKey, mPageKind, mIndex);
      newNode = node;
    } catch (final SirixIOException e) {
      newNode = Optional.empty();
    }

    if (newNode.isPresent()) {
      mCurrentNode = newNode.get();
      return Move.moved(this);
    } else {
      mCurrentNode = oldNode;
      return Move.notMoved();
    }
  }

  @Override
  public Move> moveToDocumentRoot() {
    assertNotClosed();
    return moveTo(Fixed.DOCUMENT_NODE_KEY.getStandardProperty());
  }

  @Override
  public Move> moveToParent() {
    assertNotClosed();
    return moveTo(mCurrentNode.getParentKey());
  }

  @Override
  public Move> moveToFirstChild() {
    assertNotClosed();
    if (mCurrentNode instanceof AVLNode) {
      final AVLNode node = getAVLNode();
      if (!node.hasLeftChild()) {
        return Move.notMoved();
      }
      return moveTo(node.getLeftChildKey());
    }
    return moveTo(((XmlDocumentRootNode) mCurrentNode).getFirstChildKey());
  }

  @Override
  public Move> moveToLastChild() {
    assertNotClosed();
    if (mCurrentNode instanceof AVLNode) {
      final AVLNode node = getAVLNode();
      if (!node.hasRightChild()) {
        return Move.notMoved();
      }
      return moveTo(node.getRightChildKey());
    }
    return Move.notMoved();
  }

  @Override
  public Move moveToPrevious() {
    assertNotClosed();
    return moveToParent();
  }

  @Override
  public Move moveToNext() {
    assertNotClosed();
    if (mCurrentNode instanceof AVLNode) {
      @SuppressWarnings("unchecked")
      final AVLNode node = (AVLNode) mCurrentNode;
      if (node.hasLeftChild()) {
        moveToFirstChild();
      } else if (node.hasRightChild()) {
        moveToLastChild();
      } else {
        while (moveToParent().trx().getNode() instanceof AVLNode && !hasLastChild()) {
        }

        if (getNode() instanceof AVLNode) {
          return Move.moved(moveToLastChild().trx());
        } else {
          return Move.notMoved();
        }
      }
    }
    return Move.moved(moveToFirstChild().trx());
  }

  @Override
  public Move> moveToLeftSibling() {
    assertNotClosed();
    return Move.notMoved();
  }

  @Override
  public Move> moveToRightSibling() {
    assertNotClosed();
    return Move.notMoved();
  }

  @Override
  public long getNodeKey() {
    assertNotClosed();
    return mCurrentNode.getNodeKey();
  }

  @Override
  public NodeKind getRightSiblingKind() {
    assertNotClosed();
    return NodeKind.UNKNOWN;
  }

  @Override
  public NodeKind getLeftSiblingKind() {
    assertNotClosed();
    return NodeKind.UNKNOWN;
  }

  @Override
  public NodeKind getFirstChildKind() {
    assertNotClosed();
    final NodeKind firstChildKind = moveToFirstChild().trx().getKind();
    moveToParent();
    return firstChildKind;
  }

  @Override
  public NodeKind getLastChildKind() {
    assertNotClosed();
    final NodeKind lastChildKind = moveToLastChild().trx().getKind();
    moveToParent();
    return lastChildKind;
  }

  @Override
  public NodeKind getParentKind() {
    assertNotClosed();
    if (hasParent()) {
      if (mCurrentNode.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
        return NodeKind.XDM_DOCUMENT;
      } else {
        final long nodeKey = mCurrentNode.getNodeKey();
        final NodeKind parentKind = moveToParent().trx().getKind();
        moveTo(nodeKey);
        return parentKind;
      }
    }
    return NodeKind.UNKNOWN;
  }

  @Override
  public NodeKind getKind() {
    assertNotClosed();
    return mCurrentNode.getKind();
  }

  @Override
  public ImmutableNode getNode() {
    assertNotClosed();
    return mCurrentNode;
  }

  @Override
  public Move moveToNextFollowing() {
    throw new UnsupportedOperationException();
  }

  @Override
  public long getLeftSiblingKey() {
    assertNotClosed();
    return Constants.NULL_ID_LONG;
  }

  @Override
  public long getRightSiblingKey() {
    assertNotClosed();
    return Constants.NULL_ID_LONG;
  }

  @Override
  public long getFirstChildKey() {
    assertNotClosed();

    final long firstChildKey;

    if (moveToFirstChild().hasMoved()) {
      firstChildKey = getNodeKey();
      moveToParent();
    } else {
      firstChildKey = Fixed.NULL_NODE_KEY.getStandardProperty();
    }

    return firstChildKey;
  }

  @Override
  public long getLastChildKey() {
    assertNotClosed();
    final long lastChildKey;

    if (moveToLastChild().hasMoved()) {
      lastChildKey = getNodeKey();
      moveToParent();
    } else {
      lastChildKey = Fixed.NULL_NODE_KEY.getStandardProperty();
    }

    return lastChildKey;
  }

  @Override
  public long getParentKey() {
    return getNode().getParentKey();
  }

  /**
   * Iterator supporting different search modes.
   *
   * @author Johannes Lichtenberger
   *
   */
  public final class AVLNodeIterator extends AbstractIterator> {

    /** Determines if it's the first call. */
    private boolean mFirst;

    /** All AVLNode keys which are part of the result sequence. */
    private final Deque mKeys;

    /** Start node key. */
    private final long mKey;

    /**
     * Constructor.
     *
     * @param nodeKey node key to start from, root node of AVLTree is selected if
     *        {@code Fixed.DOCUMENT_NODE_KEY.getStandardProperty} is specified.
     */
    public AVLNodeIterator(final long nodeKey) {
      mFirst = true;
      mKeys = new ArrayDeque<>();
      checkArgument(nodeKey >= 0, "nodeKey must be >= 0!");
      mKey = nodeKey;
    }

    @Override
    protected AVLNode computeNext() {
      if (!mFirst) {
        if (!mKeys.isEmpty()) {
          // Subsequent results.
          final AVLNode node = moveTo(mKeys.pop()).trx().getAVLNode();
          stackOperation(node);
          return node;
        }
        return endOfData();
      }

      // First search.
      mFirst = false;
      boolean moved = moveTo(mKey).hasMoved();
      if (mKey == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
        moved = moveToFirstChild().hasMoved();
      }
      if (moved) {
        final AVLNode node = getAVLNode();
        stackOperation(node);
        return node;
      }
      return endOfData();
    }

    private void stackOperation(final AVLNode node) {
      if (node.hasRightChild()) {
        final AVLNode right = moveToLastChild().trx().getAVLNode();
        mKeys.push(right.getNodeKey());
      }
      moveTo(node.getNodeKey());
      if (node.hasLeftChild()) {
        final AVLNode left = moveToFirstChild().trx().getAVLNode();
        mKeys.push(left.getNodeKey());
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy