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

org.sirix.index.redblacktree.RBTreeReader Maven / Gradle / Ivy

Go to download

SirixDB is a hybrid on-disk and in-memory document oriented, versioned database system. It has a lightweight buffer manager, stores everything in a huge persistent and durable tree and allows efficient reconstruction of every revision. Furthermore, SirixDB implements change tracking, diffing and supports time travel queries.

There is a newer version: 0.11.0
Show newest version
package org.sirix.index.redblacktree;

import com.google.common.collect.AbstractIterator;
import org.sirix.api.Move;
import org.sirix.api.NodeCursor;
import org.sirix.api.PageReadOnlyTrx;
import org.sirix.api.PageTrx;
import org.sirix.axis.DescendantAxis;
import org.sirix.axis.IncludeSelf;
import org.sirix.cache.Cache;
import org.sirix.cache.RBIndexKey;
import org.sirix.exception.SirixIOException;
import org.sirix.index.IndexType;
import org.sirix.index.SearchMode;
import org.sirix.index.redblacktree.interfaces.References;
import org.sirix.node.NodeKind;
import org.sirix.node.NullNode;
import org.sirix.node.interfaces.DataRecord;
import org.sirix.node.interfaces.Node;
import org.sirix.node.interfaces.StructNode;
import org.sirix.node.interfaces.immutable.ImmutableNode;
import org.sirix.settings.Constants;
import org.sirix.settings.Fixed;

import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Deque;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

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

  /**
   * Cache.
   */
  private final Cache> cache;

  /**
   * The index type.
   */
  final IndexType indexType;

  /**
   * The index number.
   */
  private final int indexNumber;

  /**
   * The revision number.
   */
  private final int revisionNumber;

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

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

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

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

  /**
   * 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
  }

  /**
   * Get a new instance.
   *
   * @param          key instance which extends comparable
   * @param          value
   * @param cache       a cache shared between all read-only tree readers
   * @param pageReadTrx {@link PageReadOnlyTrx} for persistent storage
   * @param type        type of index
   * @param index       index
   * @return new tree instance
   */
  public static , V extends References> RBTreeReader getInstance(
      final Cache> cache, final PageReadOnlyTrx pageReadTrx, final IndexType type,
      @NonNegative final int index) {
    return new RBTreeReader<>(cache, pageReadTrx, type, index);
  }

  /**
   * Private constructor.
   *
   * @param cache           a cache shared between all read-only tree readers
   * @param pageReadOnlyTrx {@link PageReadOnlyTrx} for persistent storage
   * @param indexType       kind of indexType
   * @param indexNumber     the indexNumber number
   */
  private RBTreeReader(final Cache> cache, final PageReadOnlyTrx pageReadOnlyTrx,
      final IndexType indexType, final int indexNumber) {
    this.cache = checkNotNull(cache);
    this.pageReadOnlyTrx = checkNotNull(pageReadOnlyTrx);
    this.indexType = checkNotNull(indexType);
    this.indexNumber = indexNumber;
    revisionNumber = pageReadOnlyTrx.getRevisionNumber();
    isClosed = false;
    this.index = indexNumber;

    final Optional node =
        this.pageReadOnlyTrx.getRecord(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(), IndexType.PATH_SUMMARY, 0);
    currentNode = (StructNode) node.orElseThrow(() -> new IllegalStateException(
        "Node couldn't be fetched from persistent storage!"));

    for (final long nodeKey : new DescendantAxis(this, IncludeSelf.YES)) {
      if (nodeKey == 0) {
        continue;
      }

      if (pageReadOnlyTrx instanceof PageTrx) {
        continue;
      }
      this.cache.put(new RBIndexKey(nodeKey, revisionNumber, indexType, indexNumber), getCurrentNode());
    }
  }

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

    internalDump(out);
  }

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

  /**
   * Get the {@link RBNode}.
   *
   * @return {@link RBNode} instance
   */
  RBNode getCurrentNode() {
    assertNotClosed();
    if (currentNode.getKind() != NodeKind.XML_DOCUMENT && currentNode.getKind() != NodeKind.JSON_DOCUMENT) {
      @SuppressWarnings("unchecked")
      final RBNode node = (RBNode) currentNode;
      return node;
    }
    return null;
  }

  /**
   * Set the {@link RBNode}.
   *
   * @param node the node to set
   * @return {@link RBNode} instance
   */
  RBNode setCurrentAVLNode(final RBNode node) {
    assertNotClosed();
    currentNode = node;
    return node;
  }

  /**
   * 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();
    RBNode node = getCurrentNode();
    return getNode(key, mode, node);
  }

  @NonNull
  private Optional getNode(K key, SearchMode mode, RBNode node) {
    while (true) {
      final int c = mode.compare(key, node.getKey());
      if (c == 0) {
        return Optional.ofNullable(node.getValue());
      }

      boolean moved;

      if (c < 0) {
        if (node.getLeftChild() != null) {
          node = node.getLeftChild();
          currentNode = node;
          moved = true;
        } else if (node.hasLeftChild()) {
          //noinspection unchecked
          node = pageReadOnlyTrx instanceof PageTrx
              ? null
              : (RBNode) cache.get(new RBIndexKey(node.getLeftChildKey(),
                                                        revisionNumber,
                                                        indexType,
                                                        indexNumber));

          if (node == null) {
            moved = moveToFirstChild().hasMoved();
            if (moved) {
              node = getCurrentNode();
            }
          } else {
            currentNode = node;
            moved = true;
          }
        } else {
          moved = false;
        }
      } else {
        if (node.getRightChild() != null) {
          node = node.getRightChild();
          currentNode = node;
          moved = true;
        } else if (node.hasRightChild()) {
          //noinspection unchecked
          node = pageReadOnlyTrx instanceof PageTrx
              ? null
              : (RBNode) cache.get(new RBIndexKey(node.getRightChildKey(),
                                                        revisionNumber,
                                                        indexType,
                                                        indexNumber));

          if (node == null) {
            moved = moveToLastChild().hasMoved();
            if (moved) {
              node = getCurrentNode();
            }
          } else {
            currentNode = node;
            moved = true;
          }
        } else {
          moved = false;
        }
      }

      if (!moved) {
        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 (!((StructNode) getNode()).hasFirstChild()) {
      return Optional.empty();
    }
    moveToFirstChild();
    RBNode node = getCurrentNode();
    return getNode(key, mode, node);
  }

  /**
   * Finds the specified key in the index starting from the specified node key and returns its
   * AVLNode.
   *
   * @param startNodeKey specified node
   * @param key          key to be found
   * @param mode         the search mode
   * @return Optional {@link RBNode} reference
   */
  public Optional> getCurrentNode(final long startNodeKey, final K key, final SearchMode mode) {
    assertNotClosed();
    final boolean movedToStartNode = moveTo(startNodeKey).hasMoved();
    if (!movedToStartNode) {
      return Optional.empty();
    }
    RBNode node = getCurrentNode();
    return getTheSearchedNode(key, mode, node);
  }

  @NonNull
  private Optional> getTheSearchedNode(K key, SearchMode mode, RBNode node) {
    while (true) {
      final int c = key.compareTo(node.getKey());
      if (mode != SearchMode.EQUAL && mode.compare(key, node.getKey()) == 0) {
        return Optional.ofNullable(node);
      }
      if (c == 0) {
        if (mode == SearchMode.EQUAL) {
          return Optional.ofNullable(node);
        }
      }
      final boolean moved = c < 0 ? moveToFirstChild().hasMoved() : moveToLastChild().hasMoved();
      if (moved) {
        node = getCurrentNode();
      } 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 RBNode} reference
   */
  public Optional> getCurrentNode(final K key, final SearchMode mode) {
    assertNotClosed();
    moveToDocumentRoot();
    if (!((StructNode) getNode()).hasFirstChild()) {
      return Optional.empty();
    }
    moveToFirstChild();
    RBNode node = getCurrentNode();
    return getTheSearchedNode(key, mode, node);
  }

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

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

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

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

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

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

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

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

  @Override
  public boolean hasLastChild() {
    assertNotClosed();
    if (currentNode instanceof RBNode) {
      return getCurrentNode().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 = currentNode;
    Optional newNode;
    try {
      // Immediately return node from item list if node key negative.
      newNode = pageReadOnlyTrx.getRecord(nodeKey, indexType, index);
    } catch (final SirixIOException e) {
      newNode = Optional.empty();
    }

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

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

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

  @Override
  public Move> moveToFirstChild() {
    assertNotClosed();
    if (currentNode instanceof RBNode) {
      final RBNode node = getCurrentNode();
      if (!node.hasLeftChild()) {
        return Move.notMoved();
      }
      final var move = moveTo(node.getLeftChildKey());
      @SuppressWarnings("unchecked")
      final var currentNode = (RBNode) this.currentNode;
      node.setLeftChild(currentNode);
      currentNode.setParent(node);
      return move;
    }
    return moveTo(((StructNode) currentNode).getFirstChildKey());
  }

  @Override
  public Move> moveToLastChild() {
    assertNotClosed();
    if (currentNode instanceof RBNode) {
      final RBNode node = getCurrentNode();
      if (!node.hasRightChild()) {
        return Move.notMoved();
      }
      final var move = moveTo(node.getRightChildKey());
      @SuppressWarnings("unchecked")
      final var currentNode = (RBNode) this.currentNode;
      node.setRightChild(currentNode);
      currentNode.setParent(node);
      return move;
    }
    return Move.notMoved();
  }

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

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

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

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

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

  @Override
  public long getNodeKey() {
    assertNotClosed();
    return currentNode.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;
    if (moveToFirstChild().hasMoved()) {
      firstChildKind = getNode().getKind();
      moveToParent();
    } else {
      firstChildKind = NodeKind.UNKNOWN;
    }
    return firstChildKind;
  }

  @Override
  public NodeKind getLastChildKind() {
    assertNotClosed();
    final NodeKind lastChildKind;
    if (moveToLastChild().hasMoved()) {
      lastChildKind = getNode().getKind();
      moveToParent();
    } else {
      lastChildKind = NodeKind.UNKNOWN;
    }
    return lastChildKind;
  }

  @Override
  public NodeKind getParentKind() {
    assertNotClosed();
    if (hasParent()) {
      final long nodeKey = currentNode.getNodeKey();
      final NodeKind parentKind = moveToParent().trx().getKind();
      moveTo(nodeKey);
      return parentKind;
    }
    return NodeKind.UNKNOWN;
  }

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

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

  @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 RBNodeIterator extends AbstractIterator> {

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

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

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

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

    @Override
    protected RBNode computeNext() {
      if (!first) {
        if (!keys.isEmpty()) {
          // Subsequent results.
          final RBNode node = moveTo(keys.pop()).trx().getCurrentNode();
          stackOperation(node);
          return node;
        }
        return endOfData();
      }

      // First search.
      first = false;
      boolean moved = moveTo(key).hasMoved();
      if (key == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
        moved = moveToFirstChild().hasMoved();
      }
      if (moved) {
        final RBNode node = getCurrentNode();
        stackOperation(node);
        return node;
      }
      return endOfData();
    }

    private void stackOperation(final RBNode node) {
      if (node.hasRightChild()) {
        final RBNode right = moveToLastChild().trx().getCurrentNode();
        keys.push(right.getNodeKey());
      }
      moveTo(node.getNodeKey());
      if (node.hasLeftChild()) {
        final RBNode left = moveToFirstChild().trx().getCurrentNode();
        keys.push(left.getNodeKey());
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy