public int getRowForPath(final TreePath path) {
    if (!isVisible(path)) {
      return -1;
    }

    VariableHeightStateNode correspondingNode = (VariableHeightStateNode) getStateNodeForPath(path);
    if (correspondingNode != null) {
      return correspondingNode.getRow();
    }

    VariableHeightStateNode parent =
        (VariableHeightStateNode) getStateNodeForPath(path.getParentPath());

    int modelIndex = parent.getModelIndexOfChild(path.getLastPathComponent());
    int rowIncrement = 0;
    for (int i = 0; i < modelIndex; i++) {
      rowIncrement++;

      Object modelNode = parent.getModelChildNode(i);
      StateNode childNode = parent.getChild(modelNode);
      if (childNode != null) {
        rowIncrement += childNode.getTotalChildrenCount();
      }
    }
    return parent.getRow() + rowIncrement + 1;
  }
  public TreePath getPathForRow(final int row) {
    if (row < 0 || row >= getRowCount()) {
      return null;
    }

    if (isRootVisible() && row == 0) {
      return getStateRoot().getModelPath();
    }

    VariableHeightStateNode parent = (VariableHeightStateNode) getStateRoot();

    while (true) {
      if (parent.getChildCount() == 0) {
        int modelIndex = row - parent.getRow() - 1;
        Object modelChild = parent.getModelChildNode(modelIndex);
        return parent.getModelPath().pathByAddingChild(modelChild);
      }

      for (int i = 0; i < parent.getChildCount(); i++) {
        VariableHeightStateNode childNode = (VariableHeightStateNode) parent.get(i);
        if (childNode.getRow() == row) {
          return childNode.getModelPath();
        }
        if (childNode.getRow() > row) {
          int modelChildIndex = parent.getModelIndexOfChild(childNode.getModelNode());
          int modelIndex = modelChildIndex - (childNode.getRow() - row);
          Object modelChild = parent.getModelChildNode(modelIndex);
          return parent.getModelPath().pathByAddingChild(modelChild);
        }
        if (childNode.getRow() < row
            && childNode.getRow() + childNode.getTotalChildrenCount() >= row) {
          parent = childNode;
          break;
        }
        if (i == parent.getChildCount() - 1) {
          int modelChildIndex = parent.getModelIndexOfChild(childNode.getModelNode());
          int modelIndex =
              modelChildIndex + row - (childNode.getRow() + childNode.getTotalChildrenCount());
          Object modelChild = parent.getModelChildNode(modelIndex);
          return parent.getModelPath().pathByAddingChild(modelChild);
        }
      }
    }
  }
  public Rectangle getBounds(final TreePath path, final Rectangle placeIn) {
    if (!isRoot(path) && !isVisible(path)) {
      return null;
    }

    if (isFixedRowHeight()) {
      return getFixedHeightBoundsImpl(path, placeIn);
    }

    if (getNodeDimensions() == null) {
      return new Rectangle();
    }

    if (isRoot(path)) {
      Rectangle result =
          getNodeDimensions(
              getStateRoot().getModelNode(),
              isRootVisible() ? 0 : -1,
              0,
              getStateRoot().isExpanded(),
              placeIn);
      result.y = 0;
      return result;
    }

    VariableHeightStateNode parentNode =
        (VariableHeightStateNode) getStateNodeForPath(path.getParentPath());
    StateNode node = parentNode.getChild(path.getLastPathComponent());
    Rectangle result =
        getNodeDimensions(
            path.getLastPathComponent(),
            getRowForPath(path),
            getPathDepth(path),
            node != null && node.isExpanded(),
            placeIn);
    result.y = 0;

    Object currentModelNode = path.getLastPathComponent();
    while (parentNode != null) {
      if (!parentNode.isRoot() || isRootVisible()) {
        result.y += parentNode.getHeight();
      }
      if (parentNode.isExpanded()) {
        int modelIndex = parentNode.getModelIndexOfChild(currentModelNode);
        for (int i = 0; i < modelIndex; i++) {
          result.y += parentNode.getChildrenHeights()[i];
        }
      }
      currentModelNode = parentNode.getModelNode();
      parentNode = (VariableHeightStateNode) parentNode.getParent();
    }

    return result;
  }