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

org.sirix.index.avltree.AVLTreeWriter 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.util.Optional;
import javax.annotation.Nonnegative;
import javax.annotation.Nullable;
import org.sirix.access.trx.node.AbstractForwardingNodeCursor;
import org.sirix.api.NodeCursor;
import org.sirix.api.PageTrx;
import org.sirix.cache.PageContainer;
import org.sirix.exception.SirixIOException;
import org.sirix.index.IndexType;
import org.sirix.index.SearchMode;
import org.sirix.index.avltree.AVLTreeReader.MoveCursor;
import org.sirix.index.avltree.interfaces.References;
import org.sirix.node.delegates.NodeDelegate;
import org.sirix.node.interfaces.Node;
import org.sirix.node.interfaces.Record;
import org.sirix.node.xml.XmlDocumentRootNode;
import org.sirix.page.CASPage;
import org.sirix.page.NamePage;
import org.sirix.page.PageReference;
import org.sirix.page.PathPage;
import org.sirix.page.RevisionRootPage;
import org.sirix.page.UnorderedKeyValuePage;
import org.sirix.settings.Fixed;
import org.sirix.utils.LogWrapper;
import org.slf4j.LoggerFactory;

/**
 * Simple AVLTreeWriter (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 AVLTreeWriter, V extends References>
    extends AbstractForwardingNodeCursor {
  /** Logger. */
  private static final LogWrapper LOGGER = new LogWrapper(LoggerFactory.getLogger(AVLTreeWriter.class));

  /** {@link AVLTreeReader} instance. */
  private final AVLTreeReader mAVLTreeReader;

  /** {@link PageTrx} instance. */
  private final PageTrx mPageWriteTrx;

  /**
   * Private constructor.
   *
   * @param pageWriteTrx {@link PageTrx} for persistent storage
   * @param type type of index
   */
  private AVLTreeWriter(final PageTrx pageWriteTrx, final IndexType type,
      final @Nonnegative int index) {
    try {
      final RevisionRootPage revisionRootPage = pageWriteTrx.getActualRevisionRootPage();
      final PageReference reference;
      switch (type) {
        case PATH:
          // Create path index tree if needed.
          final PathPage pathPage = pageWriteTrx.getPathPage(revisionRootPage);
          reference = revisionRootPage.getPathPageReference();
          pageWriteTrx.appendLogRecord(reference, PageContainer.getInstance(pathPage, pathPage));
          pathPage.createPathIndexTree(pageWriteTrx, index, pageWriteTrx.getLog());
          break;
        case CAS:
          // Create CAS index tree if needed.
          final CASPage casPage = pageWriteTrx.getCASPage(revisionRootPage);
          reference = revisionRootPage.getCASPageReference();
          pageWriteTrx.appendLogRecord(reference, PageContainer.getInstance(casPage, casPage));
          casPage.createCASIndexTree(pageWriteTrx, index, pageWriteTrx.getLog());
          break;
        case NAME:
          // Create name index tree if needed.
          final NamePage namePage = pageWriteTrx.getNamePage(revisionRootPage);
          reference = revisionRootPage.getNamePageReference();
          pageWriteTrx.appendLogRecord(reference, PageContainer.getInstance(namePage, namePage));
          namePage.createNameIndexTree(pageWriteTrx, index, pageWriteTrx.getLog());
          break;
        default:
          // Must not happen.
      }
    } catch (final SirixIOException e) {
      LOGGER.error(e.getMessage(), e);
    }
    mAVLTreeReader = AVLTreeReader.getInstance(pageWriteTrx, type, index);
    mPageWriteTrx = pageWriteTrx;
  }

  /**
   * Get a new instance.
   *
   * @param pageWriteTrx {@link PageTrx} for persistent storage
   * @param type type of index
   * @param index the index number
   * @return new tree instance
   */
  public static , V extends References> AVLTreeWriter getInstance(
      final PageTrx pageWriteTrx, final IndexType type, final int index) {
    return new AVLTreeWriter(pageWriteTrx, type, index);
  }

  /**
   * Checks if the specified token is already indexed; if yes, returns its reference. Otherwise,
   * creates a new index entry and returns a reference of the indexed token.
   *
   * @param key token to be indexed
   * @param value node key references
   * @param move determines if AVLNode cursor must be moved to document root/root node or not
   * @return indexed node key references
   * @throws SirixIOException if an I/O error occurs
   */
  @SuppressWarnings("unchecked")
  public V index(final K key, final V value, final MoveCursor move) throws SirixIOException {
    if (move == MoveCursor.TO_DOCUMENT_ROOT) {
      moveToDocumentRoot();
    }
    final RevisionRootPage root = mPageWriteTrx.getActualRevisionRootPage();
    if (mAVLTreeReader.getAVLNode() == null
        && ((XmlDocumentRootNode) getNode()).getFirstChildKey() == Fixed.NULL_NODE_KEY.getStandardProperty()) {
      // Index is empty.. create root node.
      final long nodeKey = getNewNodeKey(root);
      final AVLNode treeRoot = (AVLNode) mPageWriteTrx.createEntry(nodeKey,
          new AVLNode<>(key, value,
              new NodeDelegate(nodeKey, Fixed.DOCUMENT_NODE_KEY.getStandardProperty(), null, null, 0, null)),
          mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      final XmlDocumentRootNode document =
          (XmlDocumentRootNode) mPageWriteTrx.prepareEntryForModification(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      document.setFirstChildKey(treeRoot.getNodeKey());
      document.incrementChildCount();
      document.incrementDescendantCount();
      return value;
    }

    if (move == MoveCursor.TO_DOCUMENT_ROOT || mAVLTreeReader.getAVLNode() == null) {
      moveToDocumentRoot();
      moveToFirstChild();
    }
    AVLNode node = mAVLTreeReader.getAVLNode();
    while (true) {
      final int c = key.compareTo(node.getKey());
      if (c == 0) {
        if (!value.equals(node.getValue())) {
          final AVLNode avlNode = (AVLNode) mPageWriteTrx.prepareEntryForModification(node.getNodeKey(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
          avlNode.setValue(value);
        }
        return node.getValue();
      }

      final boolean moved = c < 0
          ? moveToFirstChild().hasMoved()
          : moveToLastChild().hasMoved();
      if (moved) {
        node = mAVLTreeReader.getAVLNode();
        continue;
      }

      final long nodeKey = getNewNodeKey(root);
      final AVLNode child = (AVLNode) mPageWriteTrx.createEntry(nodeKey,
          new AVLNode<>(key, value, new NodeDelegate(nodeKey, node.getNodeKey(), null, null, 0, null)), mAVLTreeReader.mPageKind,
          mAVLTreeReader.mIndex);
      node = (AVLNode) mPageWriteTrx.prepareEntryForModification(node.getNodeKey(), mAVLTreeReader.mPageKind,
          mAVLTreeReader.mIndex);
      if (c < 0) {
        node.setLeftChildKey(child.getNodeKey());
        adjust(child);
      } else {
        node.setRightChildKey(child.getNodeKey());
        adjust(child);
      }
      final XmlDocumentRootNode document =
          (XmlDocumentRootNode) mPageWriteTrx.prepareEntryForModification(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      document.incrementDescendantCount();
      return value;
    }
  }

  /**
   * Get the new maximum node key.
   *
   * @param root the current {@link RevisionRootPage}
   * @return maximum node key
   * @throws SirixIOException
   */
  private long getNewNodeKey(final RevisionRootPage root) throws SirixIOException {
    switch (mAVLTreeReader.mPageKind) {
      case PATHPAGE:
        return mPageWriteTrx.getPathPage(root).getMaxNodeKey(mAVLTreeReader.mIndex) + 1;
      case CASPAGE:
        return mPageWriteTrx.getCASPage(root).getMaxNodeKey(mAVLTreeReader.mIndex) + 1;
      case NAMEPAGE:
        return mPageWriteTrx.getNamePage(root).getMaxNodeKey(mAVLTreeReader.mIndex) + 1;
      case PATHSUMMARYPAGE:
        return mPageWriteTrx.getPathSummaryPage(root).getMaxNodeKey(mAVLTreeReader.mIndex) + 1;
      // $CASES-OMITTED$
      default:
        throw new IllegalStateException();
    }
  }

  /**
   * Remove a node key from the value, or remove the whole node, if no keys are stored anymore.
   *
   * @param key the key for which to search the value
   * @param nodeKey the nodeKey to remove from the value
   * @throws SirixIOException if an I/O error occured
   */
  public boolean remove(final K key, final @Nonnegative long nodeKey) throws SirixIOException {
    checkArgument(nodeKey >= 0, "nodeKey must be >= 0!");
    final Optional searchedValue = mAVLTreeReader.get(checkNotNull(key), SearchMode.EQUAL);
    boolean removed = false;
    if (searchedValue.isPresent()) {
      final V value = searchedValue.get();
      removed = value.removeNodeKey(nodeKey);
      // final boolean removed = value.removeNodeKey(nodeKey);
      //
      // @SuppressWarnings("unchecked")
      // final AVLNode node = (AVLNode) mAVLTreeReader.mPageWriteTrx
      // .prepareEntryForModification(mAVLTreeReader.getNodeKey(),
      // mAVLTreeReader.mPageKind,
      // Optional. empty());
      // node.setValue(value);
      //
      // if (removed) {
      // return;
      // }
      //
      // removeNode();
    }
    return removed;
  }

  /**
   * Adjusts the tree balance.
   *
   * @param node node to be adjusted
   * @throws SirixIOException if an I/O error occurs
   */
  private void adjust(AVLNode node) throws SirixIOException {
    setChanged(node, true);

    while (node != null && node.getParentKey() != Fixed.DOCUMENT_NODE_KEY.getStandardProperty() && parent(node) != null
        && parent(node).isChanged()) {
      if (parent(node).equals(left(parent(parent(node))))) {
        final AVLNode y = right(parent(parent(node)));
        if (y != null && y.isChanged()) {
          setChanged(parent(node), false);
          y.setChanged(false);
          setChanged(parent(parent(node)), true);
          node = parent(parent(node));
        } else {
          if (node.equals(right(parent(node)))) {
            node = parent(node);
            rotateLeft(node);
          }
          setChanged(parent(node), false);
          setChanged(parent(parent(node)), true);
          if (parent(parent(node)) != null)
            rotateRight(parent(parent(node)));
        }
      } else if (parent(node).equals(right(parent(parent(node))))) {
        final AVLNode y = left(parent(parent(node)));
        if (y != null && y.isChanged()) {
          setChanged(parent(node), false);
          setChanged(y, false);
          setChanged(parent(parent(node)), true);
          node = parent(parent(node));
        } else {
          if (node.equals(left(parent(node)))) {
            node = parent(node);
            rotateRight(node);
          }
          setChanged(parent(node), false);
          setChanged(parent(parent(node)), true);
          if (parent(parent(node)) != null)
            rotateLeft(parent(parent(node)));
        }
      } else {
        node = null;
      }
    }

    final long nodeKey = getNodeKey();
    moveToDocumentRoot();
    if (((XmlDocumentRootNode) getNode()).hasFirstChild()) {
      moveToFirstChild();
      setChanged(mAVLTreeReader.getAVLNode(), false);
    }
    moveTo(nodeKey);
  }

  /**
   * Set changed value.
   *
   * @param nodeToChange node to adjust
   * @param changed changed value
   * @throws SirixIOException if an I/O error occurs
   */
  private void setChanged(final AVLNode nodeToChange, final boolean changed) throws SirixIOException {
    @SuppressWarnings("unchecked")
    final AVLNode node = (AVLNode) mPageWriteTrx.prepareEntryForModification(nodeToChange.getNodeKey(),
        mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
    node.setChanged(changed);
  }

  /**
   * Returns the left child node.
   *
   * @param node node from which to move to and return the left sibling
   * @return left child node or {@code null}
   */
  private AVLNode left(@Nullable final AVLNode node) {
    if (node == null || node.getLeftChildKey() == Fixed.NULL_NODE_KEY.getStandardProperty()) {
      return null;
    }
    return moveTo(node.getLeftChildKey()).hasMoved()
        ? mAVLTreeReader.getAVLNode()
        : null;
  }

  /**
   * Returns the right child node.
   *
   * @param node node from which to move to and return the right sibling
   * @return right child node or {@code null}
   */
  private AVLNode right(@Nullable final AVLNode node) {
    if (node == null || node.getRightChildKey() == Fixed.NULL_NODE_KEY.getStandardProperty()) {
      return null;
    }
    return moveTo(node.getRightChildKey()).hasMoved()
        ? mAVLTreeReader.getAVLNode()
        : null;
  }

  /**
   * Returns the parent node.
   *
   * @param node node from which to move to and return the parent node
   * @return parent node or {@code null}
   */
  private AVLNode parent(@Nullable final AVLNode node) {
    if (node == null || node.getParentKey() == Fixed.NULL_NODE_KEY.getStandardProperty()) {
      return null;
    }
    return moveTo(node.getParentKey()).hasMoved()
        ? mAVLTreeReader.getAVLNode()
        : null;
  }

  /**
   * Left rotation.
   *
   * @param node node to be rotated
   * @throws SirixIOException if an I/O error occurs
   */
  @SuppressWarnings({"unchecked"})
  private void rotateLeft(AVLNode node) throws SirixIOException {
    moveTo(node.getNodeKey());

    AVLNode right = ((AVLTreeReader) moveToLastChild().trx()).getAVLNode();

    node = (AVLNode) mPageWriteTrx.prepareEntryForModification(node.getNodeKey(), mAVLTreeReader.mPageKind,
        mAVLTreeReader.mIndex);
    node.setRightChildKey(right.getLeftChildKey());

    if (right.hasLeftChild()) {
      final AVLNode rightLeftChild =
          (AVLNode) mPageWriteTrx.prepareEntryForModification(right.getLeftChildKey(), mAVLTreeReader.mPageKind,
              mAVLTreeReader.mIndex);
      rightLeftChild.setParentKey(node.getNodeKey());
    }

    right = (AVLNode) mPageWriteTrx.prepareEntryForModification(right.getNodeKey(), mAVLTreeReader.mPageKind,
        mAVLTreeReader.mIndex);
    right.setParentKey(node.getParentKey());

    if (node.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
      final XmlDocumentRootNode parent =
          (XmlDocumentRootNode) mPageWriteTrx.prepareEntryForModification(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      parent.setFirstChildKey(right.getNodeKey());
    } else if (moveTo(node.getParentKey()).hasMoved()
        && mAVLTreeReader.getAVLNode().getLeftChildKey() == node.getNodeKey()) {
      final AVLNode parent =
          (AVLNode) mPageWriteTrx.prepareEntryForModification(mAVLTreeReader.getNodeKey(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      parent.setLeftChildKey(right.getNodeKey());
    } else {
      final AVLNode parent =
          (AVLNode) mPageWriteTrx.prepareEntryForModification(mAVLTreeReader.getNodeKey(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      parent.setRightChildKey(right.getNodeKey());
    }

    right = (AVLNode) mPageWriteTrx.prepareEntryForModification(right.getNodeKey(), mAVLTreeReader.mPageKind,
        mAVLTreeReader.mIndex);
    right.setLeftChildKey(node.getNodeKey());

    node = (AVLNode) mPageWriteTrx.prepareEntryForModification(node.getNodeKey(), mAVLTreeReader.mPageKind,
        mAVLTreeReader.mIndex);
    node.setParentKey(right.getNodeKey());
  }

  /**
   * Right rotation.
   *
   * @param node node to be rotated
   * @throws SirixIOException if an I/O error occurs
   */
  @SuppressWarnings({"unchecked"})
  private void rotateRight(AVLNode node) throws SirixIOException {
    moveTo(node.getNodeKey());

    AVLNode leftChild = ((AVLTreeReader) moveToFirstChild().trx()).getAVLNode();
    node = (AVLNode) mPageWriteTrx.prepareEntryForModification(node.getNodeKey(), mAVLTreeReader.mPageKind,
        mAVLTreeReader.mIndex);
    node.setLeftChildKey(leftChild.getRightChildKey());

    if (leftChild.hasRightChild()) {
      final Node leftRightChild = (Node) mPageWriteTrx.prepareEntryForModification(leftChild.getRightChildKey(),
          mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      leftRightChild.setParentKey(node.getNodeKey());
    }

    leftChild = (AVLNode) mPageWriteTrx.prepareEntryForModification(leftChild.getNodeKey(),
        mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
    leftChild.setParentKey(node.getParentKey());

    if (node.getParentKey() == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
      final XmlDocumentRootNode parent =
          (XmlDocumentRootNode) mPageWriteTrx.prepareEntryForModification(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      parent.setFirstChildKey(leftChild.getNodeKey());
    } else if (moveTo(node.getParentKey()).hasMoved()
        && mAVLTreeReader.getAVLNode().getRightChildKey() == node.getNodeKey()) {
      final AVLNode parent =
          (AVLNode) mPageWriteTrx.prepareEntryForModification(mAVLTreeReader.getNodeKey(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      parent.setRightChildKey(leftChild.getNodeKey());
    } else {
      final AVLNode parent =
          (AVLNode) mPageWriteTrx.prepareEntryForModification(mAVLTreeReader.getNodeKey(),
              mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
      parent.setLeftChildKey(leftChild.getNodeKey());
    }

    leftChild = (AVLNode) mPageWriteTrx.prepareEntryForModification(leftChild.getNodeKey(),
        mAVLTreeReader.mPageKind, mAVLTreeReader.mIndex);
    leftChild.setRightChildKey(node.getNodeKey());

    node = (AVLNode) mPageWriteTrx.prepareEntryForModification(node.getNodeKey(), mAVLTreeReader.mPageKind,
        mAVLTreeReader.mIndex);
    node.setParentKey(leftChild.getNodeKey());
  }

  @Override
  public void close() {
    mAVLTreeReader.close();
  }

  @Override
  protected NodeCursor delegate() {
    return mAVLTreeReader;
  }

  /**
   * 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) {
    return mAVLTreeReader.get(checkNotNull(key), checkNotNull(mode));
  }

  /**
   * Get the {@link AVLTreeReader} used to navigate.
   *
   * @return {@link AVLTreeReader} reference
   */
  public AVLTreeReader getReader() {
    return mAVLTreeReader;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy