/**
  * Provides a mapping from the document model coordinate space to the coordinate space of the view
  * mapped to it.
  *
  * @param pos the position to convert >= 0
  * @param a the allocated region to render into
  * @param b a bias value of either <code>Position.Bias.Forward</code> or <code>
  *     Position.Bias.Backward</code>
  * @return the bounding box of the given position
  * @exception BadLocationException if the given position does not represent a valid location in
  *     the associated document
  * @see View#modelToView
  */
 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
   boolean isBackward = (b == Position.Bias.Backward);
   int testPos = (isBackward) ? Math.max(0, pos - 1) : pos;
   if (isBackward && testPos < getStartOffset()) {
     return null;
   }
   int vIndex = getViewIndexAtPosition(testPos);
   if ((vIndex != -1) && (vIndex < getViewCount())) {
     View v = getView(vIndex);
     if (v != null && testPos >= v.getStartOffset() && testPos < v.getEndOffset()) {
       Shape childShape = getChildAllocation(vIndex, a);
       if (childShape == null) {
         // We are likely invalid, fail.
         return null;
       }
       Shape retShape = v.modelToView(pos, childShape, b);
       if (retShape == null && v.getEndOffset() == pos) {
         if (++vIndex < getViewCount()) {
           v = getView(vIndex);
           retShape = v.modelToView(pos, getChildAllocation(vIndex, a), b);
         }
       }
       return retShape;
     }
   }
   throw new BadLocationException("Position not represented by view", pos);
 }
    /**
     * paintLayer
     *
     * @param g Graphics
     * @param offs0 the beginning
     * @param offs1 the end
     * @param bounds the bounds
     * @param c the text component where to paint
     * @param view the view to use
     * @return the shape containg the highlighted text
     */
    public Shape paintLayer(
        Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
      try {
        Rectangle r =
            (Rectangle)
                view.modelToView(
                    offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
        g.setColor(color);

        switch (type) {
          case UNDERLINED:
            g.drawLine(r.x, r.y + r.height - 1, r.x + r.width - 1, r.y + r.height - 1);
            return r;
          case FRAMED:
            g.drawRect(r.x, r.y, r.width - 1, r.height - 1);
            return r;
          case FILLED:
          default:
            g.fillRect(r.x, r.y, r.width, r.height);
            return r;
        }
      } catch (BadLocationException e) {
        return null;
      }
    }
    /**
     * Paints a portion of a highlight.
     *
     * @param g the graphics context
     * @param offs0 the starting model offset &gt;= 0
     * @param offs1 the ending model offset &gt;= offs1
     * @param bounds the bounding box of the view, which is not necessarily the region to paint.
     * @param c the editor
     * @param view View painting for
     * @return region drawing occurred in
     */
    public Shape paintLayer(
        Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
      Color color = getColor();

      if (color == null) {
        g.setColor(c.getSelectionColor());
      } else {
        g.setColor(color);
      }

      Rectangle r;

      if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
        // Contained in view, can just use bounds.
        if (bounds instanceof Rectangle) {
          r = (Rectangle) bounds;
        } else {
          r = bounds.getBounds();
        }
      } else {
        // Should only render part of View.
        try {
          // --- determine locations ---
          Shape shape =
              view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
          r = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds();
        } catch (BadLocationException e) {
          // can't render
          r = null;
        }
      }

      if (r != null) {
        // If we are asked to highlight, we should draw something even
        // if the model-to-view projection is of zero width (6340106).
        r.width = Math.max(r.width, 1);

        g.fillRect(r.x, r.y, r.width, r.height);
      }

      return r;
    }
  /**
   * Provides a mapping from the document model coordinate space to the coordinate space of the view
   * mapped to it.
   *
   * @param p0 the position to convert &gt;= 0
   * @param b0 the bias toward the previous character or the next character represented by p0, in
   *     case the position is a boundary of two views; either <code>Position.Bias.Forward</code> or
   *     <code>Position.Bias.Backward</code>
   * @param p1 the position to convert &gt;= 0
   * @param b1 the bias toward the previous character or the next character represented by p1, in
   *     case the position is a boundary of two views
   * @param a the allocated region to render into
   * @return the bounding box of the given position is returned
   * @exception BadLocationException if the given position does not represent a valid location in
   *     the associated document
   * @exception IllegalArgumentException for an invalid bias argument
   * @see View#viewToModel
   */
  public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a)
      throws BadLocationException {
    if (p0 == getStartOffset() && p1 == getEndOffset()) {
      return a;
    }
    Rectangle alloc = getInsideAllocation(a);
    Rectangle r0 = new Rectangle(alloc);
    View v0 = getViewAtPosition((b0 == Position.Bias.Backward) ? Math.max(0, p0 - 1) : p0, r0);
    Rectangle r1 = new Rectangle(alloc);
    View v1 = getViewAtPosition((b1 == Position.Bias.Backward) ? Math.max(0, p1 - 1) : p1, r1);
    if (v0 == v1) {
      if (v0 == null) {
        return a;
      }
      // Range contained in one view
      return v0.modelToView(p0, b0, p1, b1, r0);
    }
    // Straddles some views.
    int viewCount = getViewCount();
    int counter = 0;
    while (counter < viewCount) {
      View v;
      // Views may not be in same order as model.
      // v0 or v1 may be null if there is a gap in the range this
      // view contains.
      if ((v = getView(counter)) == v0 || v == v1) {
        View endView;
        Rectangle retRect;
        Rectangle tempRect = new Rectangle();
        if (v == v0) {
          retRect =
              v0.modelToView(p0, b0, v0.getEndOffset(), Position.Bias.Backward, r0).getBounds();
          endView = v1;
        } else {
          retRect =
              v1.modelToView(v1.getStartOffset(), Position.Bias.Forward, p1, b1, r1).getBounds();
          endView = v0;
        }

        // Views entirely covered by range.
        while (++counter < viewCount && (v = getView(counter)) != endView) {
          tempRect.setBounds(alloc);
          childAllocation(counter, tempRect);
          retRect.add(tempRect);
        }

        // End view.
        if (endView != null) {
          Shape endShape;
          if (endView == v1) {
            endShape = v1.modelToView(v1.getStartOffset(), Position.Bias.Forward, p1, b1, r1);
          } else {
            endShape = v0.modelToView(p0, b0, v0.getEndOffset(), Position.Bias.Backward, r0);
          }
          if (endShape instanceof Rectangle) {
            retRect.add((Rectangle) endShape);
          } else {
            retRect.add(endShape.getBounds());
          }
        }
        return retRect;
      }
      counter++;
    }
    throw new BadLocationException("Position not represented by view", p0);
  }