public int getPreferredHeight() {
    if (getStateRoot() == null) {
      return 0;
    }

    if (isFixedRowHeight()) {
      return getRowCount() * getRowHeight();
    }

    VariableHeightStateNode root = (VariableHeightStateNode) getStateRoot();
    return root.getTotalChildrenHeight() + (isRootVisible() ? root.getHeight() : 0);
  }
  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;
  }
  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;
  }
    protected void validateData() {
      super.validateData();
      if (getParent() == null) {
        row = isRootVisible() ? 0 : -1;
      }
      Rectangle bounds =
          getNodeDimensions(getModelNode(), row, getPathDepth(getModelPath()), isExpanded(), null);
      height = bounds != null ? bounds.height : 0;

      if (!isExpanded()) {
        totalChildrenCount = 0;
        totalChildrenHeight = 0;

        if (childrenHeights == null || childrenHeights.length != 0) {
          childrenHeights = new int[0];
        }
        return;
      }

      int modelChildCount = getModelChildCount();
      int childRow = row;

      totalChildrenCount = 0;
      totalChildrenHeight = 0;

      if (childrenHeights == null || childrenHeights.length != modelChildCount) {
        childrenHeights = new int[modelChildCount];
      }
      for (int i = 0; i < modelChildCount; i++) {
        Object modelChild = getModelChildNode(i);
        VariableHeightStateNode stateChild = (VariableHeightStateNode) getChild(modelChild);
        childRow++;
        if (stateChild != null) {
          stateChild.row = childRow;
          childrenHeights[i] = stateChild.getHeight() + stateChild.getTotalChildrenHeight();
          totalChildrenCount += stateChild.getTotalChildrenCount();

          childRow += stateChild.getTotalChildrenCount();
        } else {
          bounds =
              getNodeDimensions(
                  modelChild, childRow, getPathDepth(getModelPath()) + 1, false, bounds);
          childrenHeights[i] = bounds != null ? bounds.height : 0;
        }
        totalChildrenCount++;
        totalChildrenHeight += childrenHeights[i];
      }
    }
  public TreePath getPathClosestTo(final int x, final int y) {
    if (getStateRoot() == null) {
      return null;
    }

    if (isFixedRowHeight()) {
      return getFixedHeightPathClosestToImpl(x, y);
    }

    if (!getStateRoot().isExpanded()) {
      return isRootVisible() ? getStateRoot().getModelPath() : null;
    }

    int cummulativeHeight = 0;
    VariableHeightStateNode currentParent = (VariableHeightStateNode) getStateRoot();
    if (isRootVisible()) {
      if (currentParent.getHeight() > y) {
        return currentParent.getModelPath();
      }
      cummulativeHeight += currentParent.getHeight();
    }

    while (true) {
      int modelChildCount = currentParent.getModelChildCount();
      if (modelChildCount == 0) {
        return currentParent.getModelPath();
      }
      for (int i = 0; i < modelChildCount; i++) {
        if (cummulativeHeight + currentParent.getChildrenHeights()[i] > y
            || i + 1 == modelChildCount) {
          Object modelChild = currentParent.getModelChildNode(i);
          VariableHeightStateNode childNode =
              (VariableHeightStateNode) currentParent.getChild(modelChild);
          if (childNode != null) {
            currentParent = childNode;
            if (cummulativeHeight + currentParent.getHeight() > y || !currentParent.isExpanded()) {

              return currentParent.getModelPath();
            }
            cummulativeHeight += currentParent.getHeight();
            break;
          }
          return currentParent.getModelPath().pathByAddingChild(modelChild);
        }
        cummulativeHeight += currentParent.getChildrenHeights()[i];
      }
    }
  }
  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);
        }
      }
    }
  }