Use Tree Navigation
public abstract class

FlowView

extends BoxView
/*
 * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javax.swing.text;

import java.awt.*;
import java.util.Vector;
import javax.swing.event.*;
import javax.swing.SizeRequirements;

/**
 * A View that tries to flow it's children into some
 * partially constrained space.  This can be used to
 * build things like paragraphs, pages, etc.  The
 * flow is made up of the following pieces of functionality.
 * <ul>
 * <li>A logical set of child views, which as used as a
 * layout pool from which a physical view is formed.
 * <li>A strategy for translating the logical view to
 * a physical (flowed) view.
 * <li>Constraints for the strategy to work against.
 * <li>A physical structure, that represents the flow.
 * The children of this view are where the pieces of
 * of the logical views are placed to create the flow.
 * </ul>
 *
 * @author  Timothy Prinzing
 * @see     View
 * @since 1.3
 */

public abstract class FlowView extends BoxView {

   
/**
     * Constructs a FlowView for the given element.
     *
     * @param elem the element that this view is responsible for
     * @param axis may be either View.X_AXIS or View.Y_AXIS
     */

   
public FlowView(Element elem, int axis) {
       
super(elem, axis);
        layoutSpan
= Integer.MAX_VALUE;
        strategy
= new FlowStrategy();
   
}

   
/**
     * Fetches the axis along which views should be
     * flowed.  By default, this will be the axis
     * orthogonal to the axis along which the flow
     * rows are tiled (the axis of the default flow
     * rows themselves).  This is typically used
     * by the <code>FlowStrategy</code>.
     */

   
public int getFlowAxis() {
       
if (getAxis() == Y_AXIS) {
           
return X_AXIS;
       
}
       
return Y_AXIS;
   
}

   
/**
     * Fetch the constraining span to flow against for
     * the given child index.  This is called by the
     * FlowStrategy while it is updating the flow.
     * A flow can be shaped by providing different values
     * for the row constraints.  By default, the entire
     * span inside of the insets along the flow axis
     * is returned.
     *
     * @param index the index of the row being updated.
     *   This should be a value >= 0 and < getViewCount().
     * @see #getFlowStart
     */

   
public int getFlowSpan(int index) {
       
return layoutSpan;
   
}

   
/**
     * Fetch the location along the flow axis that the
     * flow span will start at.  This is called by the
     * FlowStrategy while it is updating the flow.
     * A flow can be shaped by providing different values
     * for the row constraints.

     * @param index the index of the row being updated.
     *   This should be a value >= 0 and < getViewCount().
     * @see #getFlowSpan
     */

   
public int getFlowStart(int index) {
       
return 0;
   
}

   
/**
     * Create a View that should be used to hold a
     * a rows worth of children in a flow.  This is
     * called by the FlowStrategy when new children
     * are added or removed (i.e. rows are added or
     * removed) in the process of updating the flow.
     */

   
protected abstract View createRow();

   
// ---- BoxView methods -------------------------------------

   
/**
     * Loads all of the children to initialize the view.
     * This is called by the <code>setParent</code> method.
     * This is reimplemented to not load any children directly
     * (as they are created in the process of formatting).
     * If the layoutPool variable is null, an instance of
     * LogicalView is created to represent the logical view
     * that is used in the process of formatting.
     *
     * @param f the view factory
     */

   
protected void loadChildren(ViewFactory f) {
       
if (layoutPool == null) {
            layoutPool
= new LogicalView(getElement());
       
}
        layoutPool
.setParent(this);

       
// This synthetic insertUpdate call gives the strategy a chance
       
// to initialize.
        strategy
.insertUpdate(this, null, null);
   
}

   
/**
     * Fetches the child view index representing the given position in
     * the model.
     *
     * @param pos the position >= 0
     * @return  index of the view representing the given position, or
     *   -1 if no view represents that position
     */

   
protected int getViewIndexAtPosition(int pos) {
       
if (pos >= getStartOffset() && (pos < getEndOffset())) {
           
for (int counter = 0; counter < getViewCount(); counter++) {
               
View v = getView(counter);
               
if(pos >= v.getStartOffset() &&
                   pos
< v.getEndOffset()) {
                   
return counter;
               
}
           
}
       
}
       
return -1;
   
}

   
/**
     * Lays out the children.  If the span along the flow
     * axis has changed, layout is marked as invalid which
     * which will cause the superclass behavior to recalculate
     * the layout along the box axis.  The FlowStrategy.layout
     * method will be called to rebuild the flow rows as
     * appropriate.  If the height of this view changes
     * (determined by the perferred size along the box axis),
     * a preferenceChanged is called.  Following all of that,
     * the normal box layout of the superclass is performed.
     *
     * @param width  the width to lay out against >= 0.  This is
     *   the width inside of the inset area.
     * @param height the height to lay out against >= 0 This
     *   is the height inside of the inset area.
     */

   
protected void layout(int width, int height) {
       
final int faxis = getFlowAxis();
       
int newSpan;
       
if (faxis == X_AXIS) {
            newSpan
= (int)width;
       
} else {
            newSpan
= (int)height;
       
}
       
if (layoutSpan != newSpan) {
            layoutChanged
(faxis);
            layoutChanged
(getAxis());
            layoutSpan
= newSpan;
       
}

       
// repair the flow if necessary
       
if (! isLayoutValid(faxis)) {
           
final int heightAxis = getAxis();
           
int oldFlowHeight = (int)((heightAxis == X_AXIS)? getWidth() : getHeight());
            strategy
.layout(this);
           
int newFlowHeight = (int) getPreferredSpan(heightAxis);
           
if (oldFlowHeight != newFlowHeight) {
               
View p = getParent();
               
if (p != null) {
                    p
.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));
               
}

               
// PENDING(shannonh)
               
// Temporary fix for 4250847
               
// Can be removed when TraversalContext is added
               
Component host = getContainer();
               
if (host != null) {
                   
//nb idk 12/12/2001 host should not be equal to null. We need to add assertion here
                    host
.repaint();
               
}
           
}
       
}

       
super.layout(width, height);
   
}

   
/**
     * Calculate equirements along the minor axis.  This
     * is implemented to forward the request to the logical
     * view by calling getMinimumSpan, getPreferredSpan, and
     * getMaximumSpan on it.
     */

   
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
       
if (r == null) {
            r
= new SizeRequirements();
       
}
       
float pref = layoutPool.getPreferredSpan(axis);
       
float min = layoutPool.getMinimumSpan(axis);
       
// Don't include insets, Box.getXXXSpan will include them.
        r
.minimum = (int)min;
        r
.preferred = Math.max(r.minimum, (int) pref);
        r
.maximum = Integer.MAX_VALUE;
        r
.alignment = 0.5f;
       
return r;
   
}

   
// ---- View methods ----------------------------------------------------

   
/**
     * Gives notification that something was inserted into the document
     * in a location that this view is responsible for.
     *
     * @param changes the change information from the associated document
     * @param a the current allocation of the view
     * @param f the factory to use to rebuild if the view has children
     * @see View#insertUpdate
     */

   
public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
        layoutPool
.insertUpdate(changes, a, f);
        strategy
.insertUpdate(this, changes, getInsideAllocation(a));
   
}

   
/**
     * Gives notification that something was removed from the document
     * in a location that this view is responsible for.
     *
     * @param changes the change information from the associated document
     * @param a the current allocation of the view
     * @param f the factory to use to rebuild if the view has children
     * @see View#removeUpdate
     */

   
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
        layoutPool
.removeUpdate(changes, a, f);
        strategy
.removeUpdate(this, changes, getInsideAllocation(a));
   
}

   
/**
     * Gives notification from the document that attributes were changed
     * in a location that this view is responsible for.
     *
     * @param changes the change information from the associated document
     * @param a the current allocation of the view
     * @param f the factory to use to rebuild if the view has children
     * @see View#changedUpdate
     */

   
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
        layoutPool
.changedUpdate(changes, a, f);
        strategy
.changedUpdate(this, changes, getInsideAllocation(a));
   
}

   
/** {@inheritDoc} */
   
public void setParent(View parent) {
       
super.setParent(parent);
       
if (parent == null
               
&& layoutPool != null ) {
            layoutPool
.setParent(null);
       
}
   
}

   
// --- variables -----------------------------------------------

   
/**
     * Default constraint against which the flow is
     * created against.
     */

   
protected int layoutSpan;

   
/**
     * These are the views that represent the child elements
     * of the element this view represents (The logical view
     * to translate to a physical view).  These are not
     * directly children of this view.  These are either
     * placed into the rows directly or used for the purpose
     * of breaking into smaller chunks, to form the physical
     * view.
     */

   
protected View layoutPool;

   
/**
     * The behavior for keeping the flow updated.  By
     * default this is a singleton shared by all instances
     * of FlowView (FlowStrategy is stateless).  Subclasses
     * can create an alternative strategy, which might keep
     * state.
     */

   
protected FlowStrategy strategy;

   
/**
     * Strategy for maintaining the physical form
     * of the flow.  The default implementation is
     * completely stateless, and recalculates the
     * entire flow if the layout is invalid on the
     * given FlowView.  Alternative strategies can
     * be implemented by subclassing, and might
     * perform incrementatal repair to the layout
     * or alternative breaking behavior.
     * @since 1.3
     */

   
public static class FlowStrategy {
       
int damageStart = Integer.MAX_VALUE;
       
Vector<View> viewBuffer;

       
void addDamage(FlowView fv, int offset) {
           
if (offset >= fv.getStartOffset() && offset < fv.getEndOffset()) {
                damageStart
= Math.min(damageStart, offset);
           
}
       
}

       
void unsetDamage() {
            damageStart
= Integer.MAX_VALUE;
       
}

       
/**
         * Gives notification that something was inserted into the document
         * in a location that the given flow view is responsible for.  The
         * strategy should update the appropriate changed region (which
         * depends upon the strategy used for repair).
         *
         * @param e the change information from the associated document
         * @param alloc the current allocation of the view inside of the insets.
         *   This value will be null if the view has not yet been displayed.
         * @see View#insertUpdate
         */

       
public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
           
// FlowView.loadChildren() makes a synthetic call into this,
           
// passing null as e
           
if (e != null) {
                addDamage
(fv, e.getOffset());
           
}

           
if (alloc != null) {
               
Component host = fv.getContainer();
               
if (host != null) {
                    host
.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
               
}
           
} else {
                fv
.preferenceChanged(null, true, true);
           
}
       
}

       
/**
         * Gives notification that something was removed from the document
         * in a location that the given flow view is responsible for.
         *
         * @param e the change information from the associated document
         * @param alloc the current allocation of the view inside of the insets.
         * @see View#removeUpdate
         */

       
public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
            addDamage
(fv, e.getOffset());
           
if (alloc != null) {
               
Component host = fv.getContainer();
               
if (host != null) {
                    host
.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
               
}
           
} else {
                fv
.preferenceChanged(null, true, true);
           
}
       
}

       
/**
         * 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) {
            addDamage
(fv, e.getOffset());
           
if (alloc != null) {
               
Component host = fv.getContainer();
               
if (host != null) {
                    host
.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
               
}
           
} else {
                fv
.preferenceChanged(null, true, true);
           
}
       
}

       
/**
         * This method gives flow strategies access to the logical
         * view of the FlowView.
         */

       
protected View getLogicalView(FlowView fv) {
           
return fv.layoutPool;
       
}

       
/**
         * 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) {
           
View pool = getLogicalView(fv);
           
int rowIndex, p0;
           
int p1 = fv.getEndOffset();

           
if (fv.majorAllocValid) {
               
if (damageStart == Integer.MAX_VALUE) {
                   
return;
               
}
               
// In some cases there's no view at position damageStart, so
               
// step back and search again. See 6452106 for details.
               
while ((rowIndex = fv.getViewIndexAtPosition(damageStart)) < 0) {
                    damageStart
--;
               
}
               
if (rowIndex > 0) {
                    rowIndex
--;
               
}
                p0
= fv.getView(rowIndex).getStartOffset();
           
} else {
                rowIndex
= 0;
                p0
= fv.getStartOffset();
           
}
            reparentViews
(pool, p0);

            viewBuffer
= new Vector<View>(10, 10);
           
int rowCount = fv.getViewCount();
           
while (p0 < p1) {
               
View row;
               
if (rowIndex >= rowCount) {
                    row
= fv.createRow();
                    fv
.append(row);
               
} else {
                    row
= fv.getView(rowIndex);
               
}
                p0
= layoutRow(fv, rowIndex, p0);
                rowIndex
++;
           
}
            viewBuffer
= null;

           
if (rowIndex < rowCount) {
                fv
.replace(rowIndex, rowCount - rowIndex, null);
           
}
            unsetDamage
();
       
}

       
/**
         * 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);
           
float x = fv.getFlowStart(rowIndex);
           
float spanLeft = fv.getFlowSpan(rowIndex);
           
int end = fv.getEndOffset();
           
TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;
           
final int flowAxis = fv.getFlowAxis();

           
int breakWeight = BadBreakWeight;
           
float breakX = 0f;
           
float breakSpan = 0f;
           
int breakIndex = -1;
           
int n = 0;

            viewBuffer
.clear();
           
while (pos < end && spanLeft >= 0) {
               
View v = createView(fv, pos, (int)spanLeft, rowIndex);
               
if (v == null) {
                   
break;
               
}

               
int bw = v.getBreakWeight(flowAxis, x, spanLeft);
               
if (bw >= ForcedBreakWeight) {
                   
View w = v.breakView(flowAxis, pos, x, spanLeft);
                   
if (w != null) {
                        viewBuffer
.add(w);
                   
} else if (n == 0) {
                       
// if the view does not break, and it is the only view
                       
// in a row, use the whole view
                        viewBuffer
.add(v);
                   
}
                   
break;
               
} else if (bw >= breakWeight && bw > BadBreakWeight) {
                    breakWeight
= bw;
                    breakX
= x;
                    breakSpan
= spanLeft;
                    breakIndex
= n;
               
}

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

               
if (chunkSpan > spanLeft && breakIndex >= 0) {
                   
// row is too long, and we may break
                   
if (breakIndex < n) {
                        v
= viewBuffer.get(breakIndex);
                   
}
                   
for (int i = n - 1; i >= breakIndex; i--) {
                        viewBuffer
.remove(i);
                   
}
                    v
= v.breakView(flowAxis, v.getStartOffset(), breakX, breakSpan);
               
}

                spanLeft
-= chunkSpan;
                x
+= chunkSpan;
                viewBuffer
.add(v);
                pos
= v.getEndOffset();
                n
++;
           
}

           
View[] views = new View[viewBuffer.size()];
            viewBuffer
.toArray(views);
            row
.replace(0, row.getViewCount(), views);
           
return (views.length > 0 ? row.getEndOffset() : pos);
       
}

       
/**
         * 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;
           
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);
           
int p0 = r.getView(bestIndex).getStartOffset();
           
int p1 = r.getEndOffset();
           
for (int i = 0; i < lv.getViewCount(); i++) {
               
View tmpView = lv.getView(i);
               
if (tmpView.getEndOffset() > p1) {
                   
break;
               
}
               
if (tmpView.getStartOffset() >= p0) {
                    tmpView
.setParent(lv);
               
}
           
}
            r
.replace(bestIndex, n - bestIndex, va);
       
}

       
void reparentViews(View pool, int startPos) {
           
int n = pool.getViewIndex(startPos, Position.Bias.Forward);
           
if (n >= 0) {
               
for (int i = n; i < pool.getViewCount(); i++) {
                    pool
.getView(i).setParent(pool);
               
}
           
}
       
}

       
/**
         * Creates a view that can be used to represent the current piece
         * of the flow.  This can be either an entire view from the
         * logical view, or a fragment of the logical view.
         *
         * @param fv the view holding the flow
         * @param startOffset the start location for the view being created
         * @param spanLeft the about of span left to fill in the row
         * @param rowIndex the row the view will be placed into
         */

       
protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
           
// Get the child view that contains the given starting position
           
View lv = getLogicalView(fv);
           
int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
           
View v = lv.getView(childIndex);
           
if (startOffset==v.getStartOffset()) {
               
// return the entire view
               
return v;
           
}

           
// return a fragment.
            v
= v.createFragment(startOffset, v.getEndOffset());
           
return v;
       
}
   
}

   
/**
     * This class can be used to represent a logical view for
     * a flow.  It keeps the children updated to reflect the state
     * of the model, gives the logical child views access to the
     * view hierarchy, and calculates a preferred span.  It doesn't
     * do any rendering, layout, or model/view translation.
     */

   
static class LogicalView extends CompositeView {

       
LogicalView(Element elem) {
           
super(elem);
       
}

       
protected int getViewIndexAtPosition(int pos) {
           
Element elem = getElement();
           
if (elem.isLeaf()) {
               
return 0;
           
}
           
return super.getViewIndexAtPosition(pos);
       
}

       
protected void loadChildren(ViewFactory f) {
           
Element elem = getElement();
           
if (elem.isLeaf()) {
               
View v = new LabelView(elem);
                append
(v);
           
} else {
               
super.loadChildren(f);
           
}
       
}

       
/**
         * Fetches the attributes to use when rendering.  This view
         * isn't directly responsible for an element so it returns
         * the outer classes attributes.
         */

       
public AttributeSet getAttributes() {
           
View p = getParent();
           
return (p != null) ? p.getAttributes() : null;
       
}

       
/**
         * Determines the preferred span for this view along an
         * axis.
         *
         * @param axis may be either View.X_AXIS or View.Y_AXIS
         * @return   the span the view would like to be rendered into.
         *           Typically the view is told to render into the span
         *           that is returned, although there is no guarantee.
         *           The parent may choose to resize or break the view.
         * @see View#getPreferredSpan
         */

       
public float getPreferredSpan(int axis) {
           
float maxpref = 0;
           
float pref = 0;
           
int n = getViewCount();
           
for (int i = 0; i < n; i++) {
               
View v = getView(i);
                pref
+= v.getPreferredSpan(axis);
               
if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) >= ForcedBreakWeight) {
                    maxpref
= Math.max(maxpref, pref);
                    pref
= 0;
               
}
           
}
            maxpref
= Math.max(maxpref, pref);
           
return maxpref;
       
}

       
/**
         * Determines the minimum span for this view along an
         * axis.  The is implemented to find the minimum unbreakable
         * span.
         *
         * @param axis may be either View.X_AXIS or View.Y_AXIS
         * @return  the span the view would like to be rendered into.
         *           Typically the view is told to render into the span
         *           that is returned, although there is no guarantee.
         *           The parent may choose to resize or break the view.
         * @see View#getPreferredSpan
         */

       
public float getMinimumSpan(int axis) {
           
float maxmin = 0;
           
float min = 0;
           
boolean nowrap = false;
           
int n = getViewCount();
           
for (int i = 0; i < n; i++) {
               
View v = getView(i);
               
if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) == BadBreakWeight) {
                    min
+= v.getPreferredSpan(axis);
                    nowrap
= true;
               
} else if (nowrap) {
                    maxmin
= Math.max(min, maxmin);
                    nowrap
= false;
                    min
= 0;
               
}
               
if (v instanceof ComponentView) {
                    maxmin
= Math.max(maxmin, v.getMinimumSpan(axis));
               
}
           
}
            maxmin
= Math.max(maxmin, min);
           
return maxmin;
       
}

       
/**
         * Forward the DocumentEvent to the given child view.  This
         * is implemented to reparent the child to the logical view
         * (the children may have been parented by a row in the flow
         * if they fit without breaking) and then execute the superclass
         * behavior.
         *
         * @param v the child view to forward the event to.
         * @param e the change information from the associated document
         * @param a the current allocation of the view
         * @param f the factory to use to rebuild if the view has children
         * @see #forwardUpdate
         * @since 1.3
         */

       
protected void forwardUpdateToView(View v, DocumentEvent e,
                                           
Shape a, ViewFactory f) {
           
View parent = v.getParent();
            v
.setParent(this);
           
super.forwardUpdateToView(v, e, a, f);
            v
.setParent(parent);
       
}

       
// The following methods don't do anything useful, they
       
// simply keep the class from being abstract.

       
/**
         * Renders using the given rendering surface and area on that
         * surface.  This is implemented to do nothing, the logical
         * view is never visible.
         *
         * @param g the rendering surface to use
         * @param allocation the allocated region to render into
         * @see View#paint
         */

       
public void paint(Graphics g, Shape allocation) {
       
}

       
/**
         * Tests whether a point lies before the rectangle range.
         * Implemented to return false, as hit detection is not
         * performed on the logical view.
         *
         * @param x the X coordinate >= 0
         * @param y the Y coordinate >= 0
         * @param alloc the rectangle
         * @return true if the point is before the specified range
         */

       
protected boolean isBefore(int x, int y, Rectangle alloc) {
           
return false;
       
}

       
/**
         * Tests whether a point lies after the rectangle range.
         * Implemented to return false, as hit detection is not
         * performed on the logical view.
         *
         * @param x the X coordinate >= 0
         * @param y the Y coordinate >= 0
         * @param alloc the rectangle
         * @return true if the point is after the specified range
         */

       
protected boolean isAfter(int x, int y, Rectangle alloc) {
           
return false;
       
}

       
/**
         * Fetches the child view at the given point.
         * Implemented to return null, as hit detection is not
         * performed on the logical view.
         *
         * @param x the X coordinate >= 0
         * @param y the Y coordinate >= 0
         * @param alloc the parent's allocation on entry, which should
         *   be changed to the child's allocation on exit
         * @return the child view
         */

       
protected View getViewAtPoint(int x, int y, Rectangle alloc) {
           
return null;
       
}

       
/**
         * Returns the allocation for a given child.
         * Implemented to do nothing, as the logical view doesn't
         * perform layout on the children.
         *
         * @param index the index of the child, >= 0 && < getViewCount()
         * @param a  the allocation to the interior of the box on entry,
         *   and the allocation of the child view at the index on exit.
         */

       
protected void childAllocation(int index, Rectangle a) {
       
}
   
}


}