/**
   * A utility method for determining the height of a text block.
   *
   * @param block the text block.
   * @param position the label position.
   * @param g2 the graphics device.
   * @return The height.
   */
  protected double calculateTextBlockHeight(
      TextBlock block, CategoryLabelPosition position, Graphics2D g2) {

    RectangleInsets insets = getTickLabelInsets();
    Size2D size = block.calculateDimensions(g2);
    Rectangle2D box = new Rectangle2D.Double(0.0, 0.0, size.getWidth(), size.getHeight());
    Shape rotatedBox = ShapeUtilities.rotateShape(box, position.getAngle(), 0.0f, 0.0f);
    double h = rotatedBox.getBounds2D().getHeight() + insets.getTop() + insets.getBottom();
    return h;
  }
  /**
   * Draws the category labels and returns the updated axis state.
   *
   * @param g2 the graphics device (<code>null</code> not permitted).
   * @param plotArea the plot area (<code>null</code> not permitted).
   * @param dataArea the area inside the axes (<code>null</code> not permitted).
   * @param edge the axis location (<code>null</code> not permitted).
   * @param state the axis state (<code>null</code> not permitted).
   * @param plotState collects information about the plot (<code>null</code> permitted).
   * @return The updated axis state (never <code>null</code>).
   */
  protected AxisState drawCategoryLabels(
      Graphics2D g2,
      Rectangle2D plotArea,
      Rectangle2D dataArea,
      RectangleEdge edge,
      AxisState state,
      PlotRenderingInfo plotState) {

    ParamChecks.nullNotPermitted(state, "state");
    if (!isTickLabelsVisible()) {
      return state;
    }
    List<CategoryTick> ticks = refreshTicks(g2, state, plotArea, edge);
    // state.setTicks(ticks);        //FIXME MMC had to remove this as the types don't match

    int categoryIndex = 0;
    for (CategoryTick tick : ticks) {

      g2.setFont(getTickLabelFont(tick.getCategory()));
      g2.setPaint(getTickLabelPaint(tick.getCategory()));

      CategoryLabelPosition position = this.categoryLabelPositions.getLabelPosition(edge);
      double x0 = 0.0;
      double x1 = 0.0;
      double y0 = 0.0;
      double y1 = 0.0;
      if (edge == RectangleEdge.TOP) {
        x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        y1 = state.getCursor() - this.categoryLabelPositionOffset;
        y0 = y1 - state.getMax();
      } else if (edge == RectangleEdge.BOTTOM) {
        x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        y0 = state.getCursor() + this.categoryLabelPositionOffset;
        y1 = y0 + state.getMax();
      } else if (edge == RectangleEdge.LEFT) {
        y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        x1 = state.getCursor() - this.categoryLabelPositionOffset;
        x0 = x1 - state.getMax();
      } else if (edge == RectangleEdge.RIGHT) {
        y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge);
        y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge);
        x0 = state.getCursor() + this.categoryLabelPositionOffset;
        x1 = x0 - state.getMax();
      }
      Rectangle2D area = new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
      Point2D anchorPoint = RectangleAnchor.coordinates(area, position.getCategoryAnchor());
      TextBlock block = tick.getLabel();
      block.draw(
          g2,
          (float) anchorPoint.getX(),
          (float) anchorPoint.getY(),
          position.getLabelAnchor(),
          (float) anchorPoint.getX(),
          (float) anchorPoint.getY(),
          position.getAngle());
      Shape bounds =
          block.calculateBounds(
              g2,
              (float) anchorPoint.getX(),
              (float) anchorPoint.getY(),
              position.getLabelAnchor(),
              (float) anchorPoint.getX(),
              (float) anchorPoint.getY(),
              position.getAngle());
      if (plotState != null && plotState.getOwner() != null) {
        EntityCollection entities = plotState.getOwner().getEntityCollection();
        if (entities != null) {
          String tooltip = getCategoryLabelToolTip(tick.getCategory());
          String url = getCategoryLabelURL(tick.getCategory());
          entities.add(new CategoryLabelEntity(tick.getCategory(), bounds, tooltip, url));
        }
      }
      categoryIndex++;
    }

    if (edge.equals(RectangleEdge.TOP)) {
      double h = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorUp(h);
    } else if (edge.equals(RectangleEdge.BOTTOM)) {
      double h = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorDown(h);
    } else if (edge == RectangleEdge.LEFT) {
      double w = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorLeft(w);
    } else if (edge == RectangleEdge.RIGHT) {
      double w = state.getMax() + this.categoryLabelPositionOffset;
      state.cursorRight(w);
    }
    return state;
  }