FileDocCategorySizeDatePackage
TableView.javaAPI DocJava SE 6 API54614Tue Jun 10 00:27:00 BST 2008javax.swing.text.html

TableView

public class TableView extends BoxView implements ViewFactory
HTML table view.
author
Timothy Prinzing
version
1.41 05/12/06
see
View

Fields Summary
private AttributeSet
attr
private StyleSheet$BoxPainter
painter
private int
cellSpacing
private int
borderWidth
private int
captionIndex
The index of the caption view if there is a caption. This has a value of -1 if there is no caption. The caption lives in the inset area of the table, and is updated with each time the grid is recalculated.
private boolean
relativeCells
Do any of the table cells contain a relative size specification? This is updated with each call to updateGrid(). If this is true, the ColumnIterator will do extra work to calculate relative cell specifications.
private boolean
multiRowCells
Do any of the table cells span multiple rows? If true, the RowRequirementIterator will do additional work to adjust the requirements of rows spanned by a single table cell. This is updated with each call to updateGrid().
int[]
columnSpans
int[]
columnOffsets
SizeRequirements
totalColumnRequirements
SizeRequirements for all the columns.
SizeRequirements[]
columnRequirements
RowIterator
rowIterator
ColumnIterator
colIterator
Vector
rows
boolean
skipComments
boolean
gridValid
private static final BitSet
EMPTY
Constructors Summary
public TableView(Element elem)
Constructs a TableView for the given element.

param
elem the element that this view is responsible for

	super(elem, View.Y_AXIS);
	rows = new Vector();
	gridValid = false;
	captionIndex = -1;
        totalColumnRequirements = new SizeRequirements();
    
Methods Summary
voidaddFill(int row, int col)
Mark a grid location as filled in for a cells overflow.

	RowView rv = getRow(row);
	if (rv != null) {
	    rv.fillColumn(col);
	}
    
voidcalculateColumnRequirements(int axis)
Calculate the requirements for each column. The calculation is done as two passes over the table. The table cells that occupy a single column are scanned first to determine the maximum of minimum, preferred, and maximum spans along the give axis. Table cells that span multiple columns are excluded from the first pass. A second pass is made to determine if the cells that span multiple columns are satisfied. If the column requirements are not satisified, the needs of the multi-column cell is mixed into the existing column requirements. The calculation of the multi-column distribution is based upon the proportions of the existing column requirements and taking into consideration any constraining maximums.

        // clean columnRequirements
        for (SizeRequirements req : columnRequirements) {
            req.minimum = 0;
            req.preferred = 0;
            req.maximum = Integer.MAX_VALUE;
        }
	Container host = getContainer();
	if (host != null) {
	    if (host instanceof JTextComponent) {
		skipComments = !((JTextComponent)host).isEditable();
	    } else {
		skipComments = true;
	    }
	}
	// pass 1 - single column cells
	boolean hasMultiColumn = false;
	int nrows = getRowCount();
	for (int i = 0; i < nrows; i++) {
	    RowView row = getRow(i);
	    int col = 0;
	    int ncells = row.getViewCount();
	    for (int cell = 0; cell < ncells; cell++) {
		View cv = row.getView(cell);
		if (skipComments && !(cv instanceof CellView)) {
		    continue;
		}
		for (; row.isFilled(col); col++); // advance to a free column
		int rowSpan = getRowsOccupied(cv);
		int colSpan = getColumnsOccupied(cv);
		if (colSpan == 1) {
		    checkSingleColumnCell(axis, col, cv);
		} else {
		    hasMultiColumn = true;
		    col += colSpan - 1;
		}
		col++;
	    }
	}

	// pass 2 - multi-column cells
	if (hasMultiColumn) {
	    for (int i = 0; i < nrows; i++) {
		RowView row = getRow(i);
		int col = 0;
		int ncells = row.getViewCount();
		for (int cell = 0; cell < ncells; cell++) {
		    View cv = row.getView(cell);
		    if (skipComments && !(cv instanceof CellView)) {
			continue;
		    }
		    for (; row.isFilled(col); col++); // advance to a free column
		    int colSpan = getColumnsOccupied(cv);
		    if (colSpan > 1) {
			checkMultiColumnCell(axis, col, colSpan, cv);
			col += colSpan - 1;
		    }
		    col++;
		}
	    }
	}
    
protected javax.swing.SizeRequirementscalculateMajorAxisRequirements(int axis, javax.swing.SizeRequirements r)
Calculate the requirements for the major axis. This is called by the superclass whenever the requirements need to be updated (i.e. a preferenceChanged was messaged through this view).

This is implemented to provide the superclass behavior adjusted for multi-row table cells.

	updateInsets();
	rowIterator.updateAdjustments();
	r = CSS.calculateTiledRequirements(rowIterator, r);
	r.maximum = r.preferred;
	return r;
    
protected javax.swing.SizeRequirementscalculateMinorAxisRequirements(int axis, javax.swing.SizeRequirements r)
Calculate the requirements for the minor axis. This is called by the superclass whenever the requirements need to be updated (i.e. a preferenceChanged was messaged through this view).

This is implemented to calculate the requirements as the sum of the requirements of the columns and then adjust it if the CSS width or height attribute is specified and applicable to the axis.

	updateGrid();
	
	// calculate column requirements for each column
	calculateColumnRequirements(axis);


	// the requirements are the sum of the columns.
	if (r == null) {
	    r = new SizeRequirements();
	}
	long min = 0;
	long pref = 0;
	int n = columnRequirements.length;
	for (int i = 0; i < n; i++) {
	    SizeRequirements req = columnRequirements[i];
	    min += req.minimum;
	    pref += req.preferred;
	}
	int adjust = (n + 1) * cellSpacing + 2 * borderWidth;
	min += adjust;
	pref += adjust;
	r.minimum = (int) min;
	r.preferred = (int) pref;
	r.maximum = (int) pref;


	AttributeSet attr = getAttributes();
        CSS.LengthValue cssWidth = (CSS.LengthValue)attr.getAttribute(
                                                    CSS.Attribute.WIDTH);

	if (BlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
            if (r.minimum < (int)min) {
                // The user has requested a smaller size than is needed to
                // show the table, override it.
                r.maximum = r.minimum = r.preferred = (int) min;
            }
        }
        totalColumnRequirements.minimum = r.minimum;
        totalColumnRequirements.preferred = r.preferred;
        totalColumnRequirements.maximum = r.maximum;

	// set the alignment
	Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
	if (o != null) {
	    // set horizontal alignment
	    String ta = o.toString();
	    if (ta.equals("left")) {
		r.alignment = 0;
	    } else if (ta.equals("center")) {
		r.alignment = 0.5f;
	    } else if (ta.equals("right")) {
		r.alignment = 1;
	    } else {
		r.alignment = 0;
	    }
	} else {
	    r.alignment = 0;
	}
	
	return r;
    
public voidchangedUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)
Gives notification from the document that attributes were changed in a location that this view is responsible for. This replaces the ViewFactory with an implementation that calls through to the createTableRow and createTableCell methods. If the element given to the factory isn't a table row or cell, the request is delegated to the factory passed as an argument.

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
View#changedUpdate

	super.changedUpdate(e, a, this);
    
voidcheckMultiColumnCell(int axis, int col, int ncols, javax.swing.text.View v)
check the requirements of a table cell that spans multiple columns.

	// calculate the totals
	long min = 0;
	long pref = 0;
	long max = 0;
	for (int i = 0; i < ncols; i++) {
	    SizeRequirements req = columnRequirements[col + i];
	    min += req.minimum;
	    pref += req.preferred;
	    max += req.maximum;
	}

	// check if the minimum size needs adjustment.
	int cmin = (int) v.getMinimumSpan(axis); 
	if (cmin > min) {
	    /*
	     * the columns that this cell spans need adjustment to fit
	     * this table cell.... calculate the adjustments.
	     */
	    SizeRequirements[] reqs = new SizeRequirements[ncols];
	    for (int i = 0; i < ncols; i++) {
		reqs[i] = columnRequirements[col + i];
	    }
	    int[] spans = new int[ncols];
	    int[] offsets = new int[ncols];
	    SizeRequirements.calculateTiledPositions(cmin, null, reqs, 
						     offsets, spans);
	    // apply the adjustments
	    for (int i = 0; i < ncols; i++) {
		SizeRequirements req = reqs[i];
		req.minimum = Math.max(spans[i], req.minimum);
		req.preferred = Math.max(req.minimum, req.preferred);
		req.maximum = Math.max(req.preferred, req.maximum);
	    }
	}

	// check if the preferred size needs adjustment.
	int cpref = (int) v.getPreferredSpan(axis); 
	if (cpref > pref) {
	    /*
	     * the columns that this cell spans need adjustment to fit
	     * this table cell.... calculate the adjustments.
	     */
	    SizeRequirements[] reqs = new SizeRequirements[ncols];
	    for (int i = 0; i < ncols; i++) {
		reqs[i] = columnRequirements[col + i];
	    }
	    int[] spans = new int[ncols];
	    int[] offsets = new int[ncols];
	    SizeRequirements.calculateTiledPositions(cpref, null, reqs, 
						     offsets, spans);
	    // apply the adjustments
	    for (int i = 0; i < ncols; i++) {
		SizeRequirements req = reqs[i];
		req.preferred = Math.max(spans[i], req.preferred);
		req.maximum = Math.max(req.preferred, req.maximum);
	    }
	}

    
voidcheckSingleColumnCell(int axis, int col, javax.swing.text.View v)
check the requirements of a table cell that spans a single column.

	SizeRequirements req = columnRequirements[col];
	req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
	req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
    
public javax.swing.text.Viewcreate(javax.swing.text.Element elem)
The table itself acts as a factory for the various views that actually represent pieces of the table. All other factory activity is delegated to the factory returned by the parent of the table.

	Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
	if (o instanceof HTML.Tag) {
	    HTML.Tag kind = (HTML.Tag) o;
	    if (kind == HTML.Tag.TR) {
		return createTableRow(elem);
	    } else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
		return new CellView(elem);
	    } else if (kind == HTML.Tag.CAPTION) {
		return new javax.swing.text.html.ParagraphView(elem);
	    }
	}
	// default is to delegate to the normal factory
	View p = getParent();
	if (p != null) {
	    ViewFactory f = p.getViewFactory();
	    if (f != null) {
		return f.create(elem);
	    }
	}
	return null;
    
protected javax.swing.text.html.TableView$RowViewcreateTableRow(javax.swing.text.Element elem)
Creates a new table row.

param
elem an element
return
the row

	// PENDING(prinz) need to add support for some of the other
	// elements, but for now just ignore anything that is not
	// a TR.
	Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
	if (o == HTML.Tag.TR) {
	    return new RowView(elem);
	}
	return null;
    
protected voidforwardUpdate(javax.swing.event.DocumentEvent$ElementChange ec, javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)

	super.forwardUpdate(ec, e, a, f);
	// A change in any of the table cells usually effects the whole table,
	// so redraw it all!
	if (a != null) {
	    Component c = getContainer();
	    if (c != null) {
		Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
		                   a.getBounds();
		c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
	    }
	}
    
public javax.swing.text.AttributeSetgetAttributes()
Fetches the attributes to use when rendering. This is implemented to multiplex the attributes specified in the model with a StyleSheet.

	if (attr == null) {
	    StyleSheet sheet = getStyleSheet();
	    attr = sheet.getViewAttributes(this);
	}
	return attr;
    
public intgetColumnCount()
The number of columns in the table.

	return columnSpans.length;
    
public intgetColumnSpan(int col)
Fetches the span (width) of the given column. This is used by the nested cells to query the sizes of grid locations outside of themselves.

	if (col < columnSpans.length) {
	    return columnSpans[col];
	}
	return 0;
    
protected intgetColumnsOccupied(javax.swing.text.View v)
Determines the number of columns occupied by the table cell represented by given element.

	AttributeSet a = v.getElement().getAttributes();

        if (a.isDefined(HTML.Attribute.COLSPAN)) {
            String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
            if (s != null) {
                try {
                    return Integer.parseInt(s);
                } catch (NumberFormatException nfe) {
                    // fall through to one column
                }
            }
        }

	return 1;
    
public intgetMultiRowSpan(int row0, int row1)
Fetch the span of multiple rows. This includes the border area.

	RowView rv0 = getRow(row0);
	RowView rv1 = getRow(row1);
	if ((rv0 != null) && (rv1 != null)) {
	    int index0 = rv0.viewIndex;
	    int index1 = rv1.viewIndex;
	    int span = getOffset(Y_AXIS, index1) - getOffset(Y_AXIS, index0) + 
		getSpan(Y_AXIS, index1);
	    return span;
	}
	return 0;
    
javax.swing.text.html.TableView$RowViewgetRow(int row)

	if (row < rows.size()) {
	    return (RowView) rows.elementAt(row);
	}
	return null;
    
public intgetRowCount()
The number of rows in the table.

	return rows.size();
    
public intgetRowSpan(int row)
Fetches the span (height) of the given row.

	RowView rv = getRow(row);
	if (rv != null) {
	    return getSpan(Y_AXIS, rv.viewIndex);
	}
	return 0;
    
protected intgetRowsOccupied(javax.swing.text.View v)
Determines the number of rows occupied by the table cell represented by given element.

	AttributeSet a = v.getElement().getAttributes();

        if (a.isDefined(HTML.Attribute.ROWSPAN)) {
            String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
            if (s != null) {
                try {
                    return Integer.parseInt(s);
                } catch (NumberFormatException nfe) {
                    // fall through to one row
                }
            }
        }

	return 1;
    
protected javax.swing.text.html.StyleSheetgetStyleSheet()

	HTMLDocument doc = (HTMLDocument) getDocument();
	return doc.getStyleSheet();
    
protected javax.swing.text.ViewgetViewAtPoint(int x, int y, java.awt.Rectangle alloc)

	int n = getViewCount();
	View v = null;
	Rectangle allocation = new Rectangle();
	for (int i = 0; i < n; i++) {
	    allocation.setBounds(alloc);
	    childAllocation(i, allocation);
	    v = getView(i);
	    if (v instanceof RowView) {
		v = ((RowView)v).findViewAtPoint(x, y, allocation);
		if (v != null) {
		    alloc.setBounds(allocation);
		    return v;
		}
	    }
	}
        return super.getViewAtPoint(x, y, alloc);
    
protected javax.swing.text.ViewgetViewAtPosition(int pos, java.awt.Rectangle a)
Fetches the child view that represents the given position in the model. This is implemented to walk through the children looking for a range that contains the given position. In this view the children do not necessarily have a one to one mapping with the child elements.

param
pos the search position >= 0
param
a the allocation to the table on entry, and the allocation of the view containing the position on exit
return
the view representing the given position, or null if there isn't one

        int n = getViewCount();
        for (int i = 0; i < n; i++) {
            View v = getView(i);
            int p0 = v.getStartOffset();
            int p1 = v.getEndOffset();
            if ((pos >= p0) && (pos < p1)) {
                // it's in this view.
		if (a != null) {
		    childAllocation(i, a);
		}
                return v;
            }
        }
	if (pos == getEndOffset()) {
	    View v = getView(n - 1);
	    if (a != null) {
		this.childAllocation(n - 1, a);
	    }
	    return v;
	}
        return null;
    
public javax.swing.text.ViewFactorygetViewFactory()
Fetches the ViewFactory implementation that is feeding the view hierarchy. This replaces the ViewFactory with an implementation that calls through to the createTableRow and createTableCell methods. If the element given to the factory isn't a table row or cell, the request is delegated to the factory produced by the superclass behavior.

return
the factory, null if none

	return this;
    
public voidinsertUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)
Gives notification that something was inserted into the document in a location that this view is responsible for. This replaces the ViewFactory with an implementation that calls through to the createTableRow and createTableCell methods. If the element given to the factory isn't a table row or cell, the request is delegated to the factory passed as an argument.

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
View#insertUpdate

	super.insertUpdate(e, a, this);
    
protected voidinvalidateGrid()

	gridValid = false;
    
protected voidlayoutColumns(int targetSpan, int[] offsets, int[] spans, javax.swing.SizeRequirements[] reqs)
Layout the columns to fit within the given target span.

param
targetSpan the given span for total of all the table columns
param
reqs the requirements desired for each column. This is the column maximum of the cells minimum, preferred, and maximum requested span
param
spans the return value of how much to allocated to each column
param
offsets the return value of the offset from the origin for each column
return
the offset from the origin and the span for each column in the offsets and spans parameters

        //clean offsets and spans
        Arrays.fill(offsets, 0);
        Arrays.fill(spans, 0);
	colIterator.setLayoutArrays(offsets, spans, targetSpan);
	CSS.calculateTiledLayout(colIterator, targetSpan);
    
protected voidlayoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans)
Perform layout for the major axis of the box (i.e. the axis that it represents). The results of the layout should be placed in the given arrays which represent the allocations to the children along the minor axis. This is called by the superclass whenever the layout needs to be updated along the minor axis.

This method is where the layout of the table rows within the table takes place. This method is implemented to call the use the RowIterator and the CSS collapsing tile to layout with border spacing and border collapsing capabilities.

param
targetSpan the total span given to the view, which whould be used to layout the children
param
axis the axis being layed out
param
offsets the offsets from the origin of the view for each of the child views; this is a return value and is filled in by the implementation of this method
param
spans the span of each child view; this is a return value and is filled in by the implementation of this method
return
the offset and span for each child view in the offsets and spans parameters

	rowIterator.setLayoutArrays(offsets, spans);
	CSS.calculateTiledLayout(rowIterator, targetSpan);

	if (captionIndex != -1) {
	    // place the caption
	    View caption = getView(captionIndex);
	    int h = (int) caption.getPreferredSpan(Y_AXIS);
	    spans[captionIndex] = h;
	    short boxBottom = (short) painter.getInset(BOTTOM, this);
	    if (boxBottom != getBottomInset()) {
		offsets[captionIndex] = targetSpan + boxBottom;
	    } else {
		offsets[captionIndex] = - getTopInset();
	    }
	}
    
protected voidlayoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)
Perform layout for the minor axis of the box (i.e. the axis orthoginal to the axis that it represents). The results of the layout should be placed in the given arrays which represent the allocations to the children along the minor axis. This is called by the superclass whenever the layout needs to be updated along the minor axis.

This is implemented to call the layoutColumns method, and then forward to the superclass to actually carry out the layout of the tables rows.

param
targetSpan the total span given to the view, which whould be used to layout the children
param
axis the axis being layed out
param
offsets the offsets from the origin of the view for each of the child views. This is a return value and is filled in by the implementation of this method
param
spans the span of each child view; this is a return value and is filled in by the implementation of this method
return
the offset and span for each child view in the offsets and spans parameters

	// make grid is properly represented
	updateGrid();

	// all of the row layouts are invalid, so mark them that way
	int n = getRowCount();
	for (int i = 0; i < n; i++) {
	    RowView row = getRow(i);
	    row.layoutChanged(axis);
	}

	// calculate column spans
	layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);

	// continue normal layout
	super.layoutMinorAxis(targetSpan, axis, offsets, spans);
    
public voidpaint(java.awt.Graphics g, java.awt.Shape allocation)
Renders using the given rendering surface and area on that surface. This is implemented to delegate to the css box painter to paint the border and background prior to the interior. The superclass culls rendering the children that don't directly intersect the clip and the row may have cells hanging from a row above in it. The table does not use the superclass rendering behavior and instead paints all of the rows and lets the rows cull those cells not intersecting the clip region.

param
g the rendering surface to use
param
allocation the allocated region to render into
see
View#paint

	// paint the border 
	Rectangle a = allocation.getBounds();
	setSize(a.width, a.height);
	if (captionIndex != -1) {
	    // adjust the border for the caption
	    short top = (short) painter.getInset(TOP, this);
	    short bottom = (short) painter.getInset(BOTTOM, this);
	    if (top != getTopInset()) {
		int h = getTopInset() - top;
		a.y += h;
		a.height -= h;
	    } else {
		a.height -= getBottomInset() - bottom;
	    }
	}
        painter.paint(g, a.x, a.y, a.width, a.height, this); 
	// paint interior
	int n = getViewCount();
	for (int i = 0; i < n; i++) {
	    View v = getView(i);
	    v.paint(g, getChildAllocation(i, allocation));
	}
	//super.paint(g, a);
    
public voidremoveUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)
Gives notification that something was removed from the document in a location that this view is responsible for. This replaces the ViewFactory with an implementation that calls through to the createTableRow and createTableCell methods. If the element given to the factory isn't a table row or cell, the request is delegated to the factory passed as an argument.

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
View#removeUpdate

	super.removeUpdate(e, a, this);
    
public voidreplace(int offset, int length, javax.swing.text.View[] views)
Change the child views. This is implemented to provide the superclass behavior and invalidate the grid so that rows and columns will be recalculated.

	super.replace(offset, length, views);
	invalidateGrid();
    
public voidsetParent(javax.swing.text.View parent)
Establishes the parent view for this view. This is guaranteed to be called before any other methods if the parent view is functioning properly.

This is implemented to forward to the superclass as well as call the setPropertiesFromAttributes method to set the paragraph properties from the css attributes. The call is made at this time to ensure the ability to resolve upward through the parents view attributes.

param
parent the new parent, or null if the view is being removed from a parent it was previously added to

	super.setParent(parent);
        if (parent != null) {
	    setPropertiesFromAttributes();
        }
    
protected voidsetPropertiesFromAttributes()
Update any cached values that come from attributes.

	StyleSheet sheet = getStyleSheet();
	attr = sheet.getViewAttributes(this);
	painter = sheet.getBoxPainter(attr);
	if (attr != null) {
	    setInsets((short) painter.getInset(TOP, this), 
		      (short) painter.getInset(LEFT, this), 
			  (short) painter.getInset(BOTTOM, this), 
		      (short) painter.getInset(RIGHT, this));

	    CSS.LengthValue lv = (CSS.LengthValue) 
		attr.getAttribute(CSS.Attribute.BORDER_SPACING);
	    if (lv != null) {
		cellSpacing = (int) lv.getValue();
	    } else {
		cellSpacing = 0;
	    }
	    lv = (CSS.LengthValue)                                
		    attr.getAttribute(CSS.Attribute.BORDER_TOP_WIDTH);
	    if (lv != null) {                                     
		    borderWidth = (int) lv.getValue();                
	    } else {                                              
		    borderWidth = 0;                                  
	    }                                                     

		}
    
voidupdateGrid()
Fill in the grid locations that are placeholders for multi-column, multi-row, and missing grid locations.

	if (! gridValid) {
	    relativeCells = false;
	    multiRowCells = false;

	    // determine which views are table rows and clear out
	    // grid points marked filled.
	    captionIndex = -1;
	    rows.removeAllElements();
	    int n = getViewCount();
	    for (int i = 0; i < n; i++) {
		View v = getView(i);
		if (v instanceof RowView) {
		    rows.addElement(v);
		    RowView rv = (RowView) v;
		    rv.clearFilledColumns();
		    rv.rowIndex = rows.size() - 1;
		    rv.viewIndex = i;
		} else {
		    Object o = v.getElement().getAttributes().getAttribute(StyleConstants.NameAttribute);
		    if (o instanceof HTML.Tag) {
			HTML.Tag kind = (HTML.Tag) o;
			if (kind == HTML.Tag.CAPTION) {
			    captionIndex = i;
			}
		    }
		}
	    }

	    int maxColumns = 0;
	    int nrows = rows.size();
	    for (int row = 0; row < nrows; row++) {
		RowView rv = getRow(row);
		int col = 0;
		for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
		    View cv = rv.getView(cell);
		    if (! relativeCells) {
			AttributeSet a = cv.getAttributes();
			CSS.LengthValue lv = (CSS.LengthValue) 
			    a.getAttribute(CSS.Attribute.WIDTH);
			if ((lv != null) && (lv.isPercentage())) {
			    relativeCells = true;
			}
		    }
		    // advance to a free column
		    for (; rv.isFilled(col); col++);
		    int rowSpan = getRowsOccupied(cv);
		    if (rowSpan > 1) {
			multiRowCells = true;
		    }
		    int colSpan = getColumnsOccupied(cv);
		    if ((colSpan > 1) || (rowSpan > 1)) {
			// fill in the overflow entries for this cell
			int rowLimit = row + rowSpan;
			int colLimit = col + colSpan;
			for (int i = row; i < rowLimit; i++) {
			    for (int j = col; j < colLimit; j++) {
				if (i != row || j != col) {
				    addFill(i, j);
				}
			    }
			}
			if (colSpan > 1) {
			    col += colSpan - 1;
			}
		    }
		}
		maxColumns = Math.max(maxColumns, col);
	    }

	    // setup the column layout/requirements
	    columnSpans = new int[maxColumns];
	    columnOffsets = new int[maxColumns];
	    columnRequirements = new SizeRequirements[maxColumns];
	    for (int i = 0; i < maxColumns; i++) {
		columnRequirements[i] = new SizeRequirements();
                columnRequirements[i].maximum = Integer.MAX_VALUE;
	    }
	    gridValid = true;
	}
    
voidupdateInsets()
Update the insets, which contain the caption if there is a caption.

	short top = (short) painter.getInset(TOP, this);
	short bottom = (short) painter.getInset(BOTTOM, this);
	if (captionIndex != -1) {
	    View caption = getView(captionIndex);
	    short h = (short) caption.getPreferredSpan(Y_AXIS);
	    AttributeSet a = caption.getAttributes();
	    Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
	    if ((align != null) && (align.equals("bottom"))) {
		bottom += h;
	    } else {
		top += h;
	    }
	}
	setInsets(top, (short) painter.getInset(LEFT, this), 
		  bottom, (short) painter.getInset(RIGHT, this));