/**
     * Update the flow on the given FlowView. By default, this causes all of the rows (child views)
     * to be rebuilt to match the given constraints for each row. This is called by a
     * FlowView.layout to update the child views in the flow.
     *
     * @param fv the view to reflow
     */
    public void layout(FlowView fv) {
      int p0 = fv.getStartOffset();
      int p1 = fv.getEndOffset();

      // we want to preserve all views from the logicalView from being
      // removed
      View lv = getLogicalView(fv);
      int n = lv.getViewCount();
      for (int i = 0; i < n; i++) {
        View v = lv.getView(i);
        v.setParent(lv);
      }
      fv.removeAll();
      for (int rowIndex = 0; p0 < p1; rowIndex++) {
        View row = fv.createRow();
        fv.append(row);

        // layout the row to the current span.  If nothing fits,
        // force something.
        int next = layoutRow(fv, rowIndex, p0);
        if (row.getViewCount() == 0) {
          row.append(createView(fv, p0, Integer.MAX_VALUE, rowIndex));
          next = row.getEndOffset();
        }
        if (next <= p0) {
          throw new StateInvariantError("infinite loop in formatting");
        } else {
          p0 = next;
        }
      }
    }
 /**
  * Gives notification from the document that attributes were changed in a location that this
  * view is responsible for.
  *
  * @param fv the <code>FlowView</code> containing the changes
  * @param e the <code>DocumentEvent</code> describing the changes done to the Document
  * @param alloc Bounds of the View
  * @see View#changedUpdate
  */
 public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
   if (alloc != null) {
     Component host = fv.getContainer();
     if (host != null) {
       host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
     }
   } else {
     fv.layoutChanged(View.X_AXIS);
     fv.layoutChanged(View.Y_AXIS);
   }
 }
    /**
     * Adjusts the given row if possible to fit within the layout span. By default this will try to
     * find the highest break weight possible nearest the end of the row. If a forced break is
     * encountered, the break will be positioned there.
     *
     * @param rowIndex the row to adjust to the current layout span.
     * @param desiredSpan the current layout span >= 0
     * @param x the location r starts at.
     */
    protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
      final int flowAxis = fv.getFlowAxis();
      View r = fv.getView(rowIndex);
      int n = r.getViewCount();
      int span = 0;
      int bestWeight = BadBreakWeight;
      int bestSpan = 0;
      int bestIndex = -1;
      int bestOffset = 0;
      View v;
      for (int i = 0; i < n; i++) {
        v = r.getView(i);
        int spanLeft = desiredSpan - span;

        int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
        if ((w >= bestWeight) && (w > BadBreakWeight)) {
          bestWeight = w;
          bestIndex = i;
          bestSpan = span;
          if (w >= ForcedBreakWeight) {
            // it's a forced break, so there is
            // no point in searching further.
            break;
          }
        }
        span += v.getPreferredSpan(flowAxis);
      }
      if (bestIndex < 0) {
        // there is nothing that can be broken, leave
        // it in it's current state.
        return;
      }

      // Break the best candidate view, and patch up the row.
      int spanLeft = desiredSpan - bestSpan;
      v = r.getView(bestIndex);
      v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);
      View[] va = new View[1];
      va[0] = v;
      View lv = getLogicalView(fv);
      for (int i = bestIndex; i < n; i++) {
        View tmpView = r.getView(i);
        if (contains(lv, tmpView)) {
          tmpView.setParent(lv);
        } else if (tmpView.getViewCount() > 0) {
          recursiveReparent(tmpView, lv);
        }
      }
      r.replace(bestIndex, n - bestIndex, va);
    }
    /**
     * Creates a row of views that will fit within the layout span of the row. This is called by the
     * layout method. This is implemented to fill the row by repeatedly calling the createView
     * method until the available span has been exhausted, a forced break was encountered, or the
     * createView method returned null. If the remaining span was exhaused, the adjustRow method
     * will be called to perform adjustments to the row to try and make it fit into the given span.
     *
     * @param rowIndex the index of the row to fill in with views. The row is assumed to be empty on
     *     entry.
     * @param pos The current position in the children of this views element from which to start.
     * @return the position to start the next row
     */
    protected int layoutRow(FlowView fv, int rowIndex, int pos) {
      View row = fv.getView(rowIndex);
      int x = fv.getFlowStart(rowIndex);
      int spanLeft = fv.getFlowSpan(rowIndex);
      int end = fv.getEndOffset();
      TabExpander te = (fv instanceof TabExpander) ? (TabExpander) fv : null;

      // Indentation.
      int preX = x;
      int availableSpan = spanLeft;
      preX = x;

      final int flowAxis = fv.getFlowAxis();
      boolean forcedBreak = false;
      while (pos < end && spanLeft > 0) {
        View v = createView(fv, pos, spanLeft, rowIndex);
        if (v == null) {
          break;
        }

        int chunkSpan;
        if ((flowAxis == X_AXIS) && (v instanceof TabableView)) {
          chunkSpan = (int) ((TabableView) v).getTabbedSpan(x, te);
        } else {
          chunkSpan = (int) v.getPreferredSpan(flowAxis);
        }

        // If a forced break is necessary, break
        if (v.getBreakWeight(flowAxis, pos, spanLeft) >= ForcedBreakWeight) {
          int n = row.getViewCount();
          if (n > 0) {
            /* If this is a forced break and it's not the only view
             * the view should be replaced with a call to breakView.
             * If it's it only view, it should be used directly.  In
             * either case no more children should be added beyond this
             * view.
             */
            v = v.breakView(flowAxis, pos, x, spanLeft);
            if (v != null) {
              if ((flowAxis == X_AXIS) && (v instanceof TabableView)) {
                chunkSpan = (int) ((TabableView) v).getTabbedSpan(x, te);
              } else {
                chunkSpan = (int) v.getPreferredSpan(flowAxis);
              }
            } else {
              chunkSpan = 0;
            }
          }
          forcedBreak = true;
        }

        spanLeft -= chunkSpan;
        x += chunkSpan;
        if (v != null) {
          row.append(v);
          pos = v.getEndOffset();
        }
        if (forcedBreak) {
          break;
        }
      }
      if (spanLeft < 0) {
        // This row is too long and needs to be adjusted.
        adjustRow(fv, rowIndex, availableSpan, preX);
      } else if (row.getViewCount() == 0) {
        // Impossible spec... put in whatever is left.
        View v = createView(fv, pos, Integer.MAX_VALUE, rowIndex);
        row.append(v);
      }
      return row.getEndOffset();
    }