code: protected OMVRBTreeEntry<K, V> removeNode(OMVRBTreeEntry<K, V> p) {
      modCount++;
      // If strictly internal, copy successor's element to p and then make p
      // point to successor.
      if (p.getLeft() != null && p.getRight() != null) {
        OMVRBTreeEntry<K, V> s = next(p);
        p.copyFrom(s);
        p = s;
      } // p has 2 children
  
      // Start fixup at replacement node, if it exists.
      final OMVRBTreeEntry<K, V> replacement = (p.getLeft() != null ? p.getLeft() : p.getRight());
  
      if (replacement != null) {
        // Link replacement to parent
        replacement.setParent(p.getParent());
        if (p.getParent() == null)
          setRoot(replacement);
        else if (p == p.getParent().getLeft())
          p.getParent().setLeft(replacement);
        else
          p.getParent().setRight(replacement);
  
        // Null out links so they are OK to use by fixAfterDeletion.
        p.setLeft(null);
        p.setRight(null);
        p.setParent(null);
  
        // Fix replacement
        if (p.getColor() == BLACK)
          fixAfterDeletion(replacement);
      } else if (p.getParent() == null) { // return if we are the only node.
        clear();
      } else { // No children. Use self as phantom replacement and unlink.
        if (p.getColor() == BLACK)
          fixAfterDeletion(p);
  
        if (p.getParent() != null) {
          if (p == p.getParent().getLeft())
            p.getParent().setLeft(null);
          else if (p == p.getParent().getRight())
            p.getParent().setRight(null);
          p.setParent(null);
        }
      }
  
      return p;
    }
docs: Remove a node from the tree.
  
  @param p
  Node to remove
  
  @return Node that was removed. Passed and removed nodes may be different in case node to remove contains two children. In this
  case node successor will be found and removed but it's content will be copied to the node that was passed in method.