@Override
  public OMVRBTreeEntry<K, V> getParent() {
    if (dataProvider == null) return null;

    if (parent == null && dataProvider.getParent().isValid()) {
      // LAZY LOADING OF THE PARENT NODE
      parent = pTree.loadEntry(null, dataProvider.getParent());

      checkEntryStructure();

      if (parent != null) {
        // TRY TO ASSIGN IT FOLLOWING THE RID
        if (parent.dataProvider.getLeft().isValid()
            && parent.dataProvider.getLeft().equals(dataProvider.getIdentity())) parent.left = this;
        else if (parent.dataProvider.getRight().isValid()
            && parent.dataProvider.getRight().equals(dataProvider.getIdentity()))
          parent.right = this;
        else {
          OLogManager.instance()
              .error(
                  this,
                  "getParent: Cannot assign node %s to parent. Nodes parent-left=%s, parent-right=%s",
                  dataProvider.getParent(),
                  parent.dataProvider.getLeft(),
                  parent.dataProvider.getRight());
        }
      }
    }
    return parent;
  }
  public void checkEntryStructure() {
    if (!tree.isRuntimeCheckEnabled()) return;

    if (dataProvider.getParent() == null)
      OLogManager.instance()
          .error(this, "checkEntryStructure: Node %s has parentRid null!\n", this);
    if (dataProvider.getLeft() == null)
      OLogManager.instance().error(this, "checkEntryStructure: Node %s has leftRid null!\n", this);
    if (dataProvider.getRight() == null)
      OLogManager.instance().error(this, "checkEntryStructure: Node %s has rightRid null!\n", this);

    if (this == left
        || dataProvider.getIdentity().isValid()
            && dataProvider.getIdentity().equals(dataProvider.getLeft()))
      OLogManager.instance()
          .error(this, "checkEntryStructure: Node %s has left that points to itself!\n", this);
    if (this == right
        || dataProvider.getIdentity().isValid()
            && dataProvider.getIdentity().equals(dataProvider.getRight()))
      OLogManager.instance()
          .error(this, "checkEntryStructure: Node %s has right that points to itself!\n", this);
    if (left != null && left == right)
      OLogManager.instance()
          .error(this, "checkEntryStructure: Node %s has left and right equals!\n", this);

    if (left != null) {
      if (!left.dataProvider.getIdentity().equals(dataProvider.getLeft()))
        OLogManager.instance()
            .error(this, "checkEntryStructure: Wrong left node loaded: " + dataProvider.getLeft());
      if (left.parent != this)
        OLogManager.instance()
            .error(
                this,
                "checkEntryStructure: Left node is not correctly connected to the parent"
                    + dataProvider.getLeft());
    }

    if (right != null) {
      if (!right.dataProvider.getIdentity().equals(dataProvider.getRight()))
        OLogManager.instance()
            .error(
                this, "checkEntryStructure: Wrong right node loaded: " + dataProvider.getRight());
      if (right.parent != this)
        OLogManager.instance()
            .error(
                this,
                "checkEntryStructure: Right node is not correctly connected to the parent"
                    + dataProvider.getRight());
    }
  }
  @Override
  public OMVRBTreeEntry<K, V> setParent(final OMVRBTreeEntry<K, V> iParent) {
    if (iParent != parent) {
      OMVRBTreeEntryPersistent<K, V> newParent = (OMVRBTreeEntryPersistent<K, V>) iParent;
      ORID newParentId =
          iParent == null ? ORecordId.EMPTY_RECORD_ID : newParent.dataProvider.getIdentity();

      parent = newParent;

      if (dataProvider.setParent(newParentId)) markDirty();

      if (parent != null) {
        ORID thisRid = dataProvider.getIdentity();
        if (parent.left == this && !parent.dataProvider.getLeft().equals(thisRid))
          if (parent.dataProvider.setLeft(thisRid)) parent.markDirty();
        if (parent.left != this
            && parent.dataProvider.getLeft().isValid()
            && parent.dataProvider.getLeft().equals(thisRid)) parent.left = this;
        if (parent.right == this && !parent.dataProvider.getRight().equals(thisRid))
          if (parent.dataProvider.setRight(thisRid)) parent.markDirty();
        if (parent.right != this
            && parent.dataProvider.getRight().isValid()
            && parent.dataProvider.getRight().equals(thisRid)) parent.right = this;
      }
    }
    return iParent;
  }
  /** Assures that all the links versus parent, left and right are consistent. */
  public OMVRBTreeEntryPersistent<K, V> save() throws OSerializationException {
    if (!dataProvider.isEntryDirty()) return this;

    final boolean isNew = dataProvider.getIdentity().isNew();

    // FOR EACH NEW LINK, SAVE BEFORE
    if (left != null && left.dataProvider.getIdentity().isNew()) {
      if (isNew) {
        // TEMPORARY INCORRECT SAVE FOR GETTING AN ID. WILL BE SET DIRTY AGAIN JUST AFTER
        left.dataProvider.save();
      } else left.save();
    }
    if (right != null && right.dataProvider.getIdentity().isNew()) {
      if (isNew) {
        // TEMPORARY INCORRECT SAVE FOR GETTING AN ID. WILL BE SET DIRTY AGAIN JUST AFTER
        right.dataProvider.save();
      } else right.save();
    }
    if (parent != null && parent.dataProvider.getIdentity().isNew()) {
      if (isNew) {
        // TEMPORARY INCORRECT SAVE FOR GETTING AN ID. WILL BE SET DIRTY AGAIN JUST AFTER
        parent.dataProvider.save();
      } else parent.save();
    }

    dataProvider.save();

    // if (parent != null)
    // if (!parent.record.getIdentity().equals(parentRid))
    // OLogManager.instance().error(this,
    // "[save]: Tree node %s has parentRid '%s' different by the rid of the assigned parent node:
    // %s", record.getIdentity(),
    // parentRid, parent.record.getIdentity());

    checkEntryStructure();

    if (pTree.searchNodeInCache(dataProvider.getIdentity()) != this) {
      // UPDATE THE CACHE
      pTree.addNodeInMemory(this);
    }

    return this;
  }
  /**
   * Delete all the nodes recursively. IF they are not loaded in memory, load all the tree.
   *
   * @throws IOException
   */
  public OMVRBTreeEntryPersistent<K, V> delete() throws IOException {
    if (dataProvider != null) {
      pTree.removeNodeFromMemory(this);
      pTree.removeEntry(dataProvider.getIdentity());

      // EARLY LOAD LEFT AND DELETE IT RECURSIVELY
      if (getLeft() != null) ((OMVRBTreeEntryPersistent<K, V>) getLeft()).delete();

      // EARLY LOAD RIGHT AND DELETE IT RECURSIVELY
      if (getRight() != null) ((OMVRBTreeEntryPersistent<K, V>) getRight()).delete();

      // DELETE MYSELF
      dataProvider.removeIdentityChangedListener(this);
      dataProvider.delete();
      clear();
    }

    return this;
  }
 private boolean canDisconnectFrom(OMVRBTreeEntryPersistent<K, V> entry) {
   return dataProvider == null
       || !dataProvider.getIdentity().isNew() && !entry.dataProvider.getIdentity().isNew();
 }
  /**
   * Disconnect the current node from others.
   *
   * @param iForceDirty Force disconnection also if the record it's dirty
   * @param iLevel
   * @return count of nodes that has been disconnected
   */
  protected int disconnect(final boolean iForceDirty, final int iLevel) {
    if (dataProvider == null)
      // DIRTY NODE, JUST REMOVE IT
      return 1;

    int totalDisconnected = 0;

    final ORID rid = dataProvider.getIdentity();

    boolean disconnectedFromParent = false;
    if (parent != null) {
      // DISCONNECT RECURSIVELY THE PARENT NODE
      if (canDisconnectFrom(parent) || iForceDirty) {
        if (parent.left == this) {
          parent.left = null;
        } else if (parent.right == this) {
          parent.right = null;
        } else
          OLogManager.instance()
              .warn(
                  this,
                  "Node "
                      + rid
                      + " has the parent ("
                      + parent
                      + ") unlinked to itself. It links to "
                      + parent);

        totalDisconnected += parent.disconnect(iForceDirty, iLevel + 1);
        parent = null;
        disconnectedFromParent = true;
      }
    } else {
      disconnectedFromParent = true;
    }

    boolean disconnectedFromLeft = false;
    if (left != null) {
      // DISCONNECT RECURSIVELY THE LEFT NODE
      if (canDisconnectFrom(left) || iForceDirty) {
        if (left.parent == this) left.parent = null;
        else
          OLogManager.instance()
              .warn(
                  this,
                  "Node "
                      + rid
                      + " has the left ("
                      + left
                      + ") unlinked to itself. It links to "
                      + left.parent);

        totalDisconnected += left.disconnect(iForceDirty, iLevel + 1);
        left = null;
        disconnectedFromLeft = true;
      }
    } else {
      disconnectedFromLeft = true;
    }

    boolean disconnectedFromRight = false;
    if (right != null) {
      // DISCONNECT RECURSIVELY THE RIGHT NODE
      if (canDisconnectFrom(right) || iForceDirty) {
        if (right.parent == this) right.parent = null;
        else
          OLogManager.instance()
              .warn(
                  this,
                  "Node "
                      + rid
                      + " has the right ("
                      + right
                      + ") unlinked to itself. It links to "
                      + right.parent);

        totalDisconnected += right.disconnect(iForceDirty, iLevel + 1);
        right = null;
        disconnectedFromRight = true;
      }
    } else {
      disconnectedFromLeft = true;
    }

    if (disconnectedFromParent && disconnectedFromLeft && disconnectedFromRight)
      if ((!dataProvider.isEntryDirty() && !dataProvider.getIdentity().isTemporary() || iForceDirty)
          && !pTree.isNodeEntryPoint(this)) {
        totalDisconnected++;
        pTree.removeNodeFromMemory(this);
        clear();
      }

    return totalDisconnected;
  }