void register(@NotNull Disposable parent, @NotNull Disposable child) throws RuntimeException {
    if (parent == child) {
      throw new IllegalArgumentException("Cannot register to itself: "+parent);
    }
    synchronized (getTreeLock()) {
      if (isDisposed(parent)) {
        throw new IncorrectOperationException("Sorry but parent: " + parent + " (" + parent.getClass()+") has already been disposed " +
                                              "(see the cause for stacktrace) so the child: "+child+ " (" + child.getClass()+") will never be disposed",
                                              getDisposalTrace(parent));
      }

      myDisposedObjects.remove(child);
      if (child instanceof Disposer.CheckedDisposableImpl) {
        // if we dispose a child and then register it back, it means it's not disposed anymore
        ((Disposer.CheckedDisposableImpl)child).isDisposed = false;
      }

      ObjectNode parentNode = getParentNode(parent).findOrCreateChildNode(parent);
      ObjectNode childNode = getParentNode(child).moveChildNodeToOtherParent(child, parentNode);
      myObject2ParentNode.put(child, parentNode);

      assert childNode.getObject() == child;
      checkWasNotAddedAlreadyAsChild(parentNode, childNode);
    }
  }
void register(@NotNull Disposable parent, @NotNull Disposable child)