1 /*
   2  * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.text.html;
  26 
  27 import java.awt.*;
  28 import java.util.BitSet;
  29 import java.util.Vector;
  30 import java.util.Arrays;
  31 import javax.swing.SizeRequirements;
  32 import javax.swing.event.DocumentEvent;
  33 
  34 import javax.swing.text.*;
  35 
  36 /**
  37  * HTML table view.
  38  *
  39  * @author  Timothy Prinzing
  40  * @see     View
  41  */
  42 /*public*/ class TableView extends BoxView implements ViewFactory {
  43 
  44     /**
  45      * Constructs a TableView for the given element.
  46      *
  47      * @param elem the element that this view is responsible for
  48      */
  49     public TableView(Element elem) {
  50         super(elem, View.Y_AXIS);
  51         rows = new Vector<RowView>();
  52         gridValid = false;
  53         captionIndex = -1;
  54         totalColumnRequirements = new SizeRequirements();
  55     }
  56 
  57     /**
  58      * Creates a new table row.
  59      *
  60      * @param elem an element
  61      * @return the row
  62      */
  63     protected RowView createTableRow(Element elem) {
  64         // PENDING(prinz) need to add support for some of the other
  65         // elements, but for now just ignore anything that is not
  66         // a TR.
  67         Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
  68         if (o == HTML.Tag.TR) {
  69             return new RowView(elem);
  70         }
  71         return null;
  72     }
  73 
  74     /**
  75      * The number of columns in the table.
  76      */
  77     public int getColumnCount() {
  78         return columnSpans.length;
  79     }
  80 
  81     /**
  82      * Fetches the span (width) of the given column.
  83      * This is used by the nested cells to query the
  84      * sizes of grid locations outside of themselves.
  85      */
  86     public int getColumnSpan(int col) {
  87         if (col < columnSpans.length) {
  88             return columnSpans[col];
  89         }
  90         return 0;
  91     }
  92 
  93     /**
  94      * The number of rows in the table.
  95      */
  96     public int getRowCount() {
  97         return rows.size();
  98     }
  99 
 100     /**
 101      * Fetch the span of multiple rows.  This includes
 102      * the border area.
 103      */
 104     public int getMultiRowSpan(int row0, int row1) {
 105         RowView rv0 = getRow(row0);
 106         RowView rv1 = getRow(row1);
 107         if ((rv0 != null) && (rv1 != null)) {
 108             int index0 = rv0.viewIndex;
 109             int index1 = rv1.viewIndex;
 110             int span = getOffset(Y_AXIS, index1) - getOffset(Y_AXIS, index0) +
 111                 getSpan(Y_AXIS, index1);
 112             return span;
 113         }
 114         return 0;
 115     }
 116 
 117     /**
 118      * Fetches the span (height) of the given row.
 119      */
 120     public int getRowSpan(int row) {
 121         RowView rv = getRow(row);
 122         if (rv != null) {
 123             return getSpan(Y_AXIS, rv.viewIndex);
 124         }
 125         return 0;
 126     }
 127 
 128     RowView getRow(int row) {
 129         if (row < rows.size()) {
 130             return rows.elementAt(row);
 131         }
 132         return null;
 133     }
 134 
 135     protected View getViewAtPoint(int x, int y, Rectangle alloc) {
 136         int n = getViewCount();
 137         View v;
 138         Rectangle allocation = new Rectangle();
 139         for (int i = 0; i < n; i++) {
 140             allocation.setBounds(alloc);
 141             childAllocation(i, allocation);
 142             v = getView(i);
 143             if (v instanceof RowView) {
 144                 v = ((RowView)v).findViewAtPoint(x, y, allocation);
 145                 if (v != null) {
 146                     alloc.setBounds(allocation);
 147                     return v;
 148                 }
 149             }
 150         }
 151         return super.getViewAtPoint(x, y, alloc);
 152     }
 153 
 154     /**
 155      * Determines the number of columns occupied by
 156      * the table cell represented by given element.
 157      */
 158     protected int getColumnsOccupied(View v) {
 159         AttributeSet a = v.getElement().getAttributes();
 160 
 161         if (a.isDefined(HTML.Attribute.COLSPAN)) {
 162             String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
 163             if (s != null) {
 164                 try {
 165                     return Integer.parseInt(s);
 166                 } catch (NumberFormatException nfe) {
 167                     // fall through to one column
 168                 }
 169             }
 170         }
 171 
 172         return 1;
 173     }
 174 
 175     /**
 176      * Determines the number of rows occupied by
 177      * the table cell represented by given element.
 178      */
 179     protected int getRowsOccupied(View v) {
 180         AttributeSet a = v.getElement().getAttributes();
 181 
 182         if (a.isDefined(HTML.Attribute.ROWSPAN)) {
 183             String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
 184             if (s != null) {
 185                 try {
 186                     return Integer.parseInt(s);
 187                 } catch (NumberFormatException nfe) {
 188                     // fall through to one row
 189                 }
 190             }
 191         }
 192 
 193         return 1;
 194     }
 195 
 196     protected void invalidateGrid() {
 197         gridValid = false;
 198     }
 199 
 200     protected StyleSheet getStyleSheet() {
 201         HTMLDocument doc = (HTMLDocument) getDocument();
 202         return doc.getStyleSheet();
 203     }
 204 
 205     /**
 206      * Update the insets, which contain the caption if there
 207      * is a caption.
 208      */
 209     void updateInsets() {
 210         short top = (short) painter.getInset(TOP, this);
 211         short bottom = (short) painter.getInset(BOTTOM, this);
 212         if (captionIndex != -1) {
 213             View caption = getView(captionIndex);
 214             short h = (short) caption.getPreferredSpan(Y_AXIS);
 215             AttributeSet a = caption.getAttributes();
 216             Object align = a.getAttribute(CSS.Attribute.CAPTION_SIDE);
 217             if ((align != null) && (align.equals("bottom"))) {
 218                 bottom += h;
 219             } else {
 220                 top += h;
 221             }
 222         }
 223         setInsets(top, (short) painter.getInset(LEFT, this),
 224                   bottom, (short) painter.getInset(RIGHT, this));
 225     }
 226 
 227     /**
 228      * Update any cached values that come from attributes.
 229      */
 230     protected void setPropertiesFromAttributes() {
 231         StyleSheet sheet = getStyleSheet();
 232         attr = sheet.getViewAttributes(this);
 233         painter = sheet.getBoxPainter(attr);
 234         if (attr != null) {
 235             setInsets((short) painter.getInset(TOP, this),
 236                       (short) painter.getInset(LEFT, this),
 237                           (short) painter.getInset(BOTTOM, this),
 238                       (short) painter.getInset(RIGHT, this));
 239 
 240             CSS.LengthValue lv = (CSS.LengthValue)
 241                 attr.getAttribute(CSS.Attribute.BORDER_SPACING);
 242             if (lv != null) {
 243                 cellSpacing = (int) lv.getValue();
 244             } else {
 245                 // Default cell spacing equals 2
 246                 cellSpacing = 2;
 247             }
 248             lv = (CSS.LengthValue)
 249                     attr.getAttribute(CSS.Attribute.BORDER_TOP_WIDTH);
 250             if (lv != null) {
 251                     borderWidth = (int) lv.getValue();
 252             } else {
 253                     borderWidth = 0;
 254             }
 255         }
 256     }
 257 
 258     /**
 259      * Fill in the grid locations that are placeholders
 260      * for multi-column, multi-row, and missing grid
 261      * locations.
 262      */
 263     void updateGrid() {
 264         if (! gridValid) {
 265             relativeCells = false;
 266             multiRowCells = false;
 267 
 268             // determine which views are table rows and clear out
 269             // grid points marked filled.
 270             captionIndex = -1;
 271             rows.removeAllElements();
 272             int n = getViewCount();
 273             for (int i = 0; i < n; i++) {
 274                 View v = getView(i);
 275                 if (v instanceof RowView) {
 276                     rows.addElement((RowView) v);
 277                     RowView rv = (RowView) v;
 278                     rv.clearFilledColumns();
 279                     rv.rowIndex = rows.size() - 1;
 280                     rv.viewIndex = i;
 281                 } else {
 282                     Object o = v.getElement().getAttributes().getAttribute(StyleConstants.NameAttribute);
 283                     if (o instanceof HTML.Tag) {
 284                         HTML.Tag kind = (HTML.Tag) o;
 285                         if (kind == HTML.Tag.CAPTION) {
 286                             captionIndex = i;
 287                         }
 288                     }
 289                 }
 290             }
 291 
 292             int maxColumns = 0;
 293             int nrows = rows.size();
 294             for (int row = 0; row < nrows; row++) {
 295                 RowView rv = getRow(row);
 296                 int col = 0;
 297                 for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
 298                     View cv = rv.getView(cell);
 299                     if (! relativeCells) {
 300                         AttributeSet a = cv.getAttributes();
 301                         CSS.LengthValue lv = (CSS.LengthValue)
 302                             a.getAttribute(CSS.Attribute.WIDTH);
 303                         if ((lv != null) && (lv.isPercentage())) {
 304                             relativeCells = true;
 305                         }
 306                     }
 307                     // advance to a free column
 308                     for (; rv.isFilled(col); col++);
 309                     int rowSpan = getRowsOccupied(cv);
 310                     if (rowSpan > 1) {
 311                         multiRowCells = true;
 312                     }
 313                     int colSpan = getColumnsOccupied(cv);
 314                     if ((colSpan > 1) || (rowSpan > 1)) {
 315                         // fill in the overflow entries for this cell
 316                         int rowLimit = row + rowSpan;
 317                         int colLimit = col + colSpan;
 318                         for (int i = row; i < rowLimit; i++) {
 319                             for (int j = col; j < colLimit; j++) {
 320                                 if (i != row || j != col) {
 321                                     addFill(i, j);
 322                                 }
 323                             }
 324                         }
 325                         if (colSpan > 1) {
 326                             col += colSpan - 1;
 327                         }
 328                     }
 329                 }
 330                 maxColumns = Math.max(maxColumns, col);
 331             }
 332 
 333             // setup the column layout/requirements
 334             columnSpans = new int[maxColumns];
 335             columnOffsets = new int[maxColumns];
 336             columnRequirements = new SizeRequirements[maxColumns];
 337             for (int i = 0; i < maxColumns; i++) {
 338                 columnRequirements[i] = new SizeRequirements();
 339                 columnRequirements[i].maximum = Integer.MAX_VALUE;
 340             }
 341             gridValid = true;
 342         }
 343     }
 344 
 345     /**
 346      * Mark a grid location as filled in for a cells overflow.
 347      */
 348     void addFill(int row, int col) {
 349         RowView rv = getRow(row);
 350         if (rv != null) {
 351             rv.fillColumn(col);
 352         }
 353     }
 354 
 355     /**
 356      * Layout the columns to fit within the given target span.
 357      *
 358      * @param targetSpan the given span for total of all the table
 359      *  columns
 360      * @param reqs the requirements desired for each column.  This
 361      *  is the column maximum of the cells minimum, preferred, and
 362      *  maximum requested span
 363      * @param spans the return value of how much to allocated to
 364      *  each column
 365      * @param offsets the return value of the offset from the
 366      *  origin for each column
 367      * @return the offset from the origin and the span for each column
 368      *  in the offsets and spans parameters
 369      */
 370     protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
 371                                  SizeRequirements[] reqs) {
 372         //clean offsets and spans
 373         Arrays.fill(offsets, 0);
 374         Arrays.fill(spans, 0);
 375         colIterator.setLayoutArrays(offsets, spans, targetSpan);
 376         CSS.calculateTiledLayout(colIterator, targetSpan);
 377     }
 378 
 379     /**
 380      * Calculate the requirements for each column.  The calculation
 381      * is done as two passes over the table.  The table cells that
 382      * occupy a single column are scanned first to determine the
 383      * maximum of minimum, preferred, and maximum spans along the
 384      * give axis.  Table cells that span multiple columns are excluded
 385      * from the first pass.  A second pass is made to determine if
 386      * the cells that span multiple columns are satisfied.  If the
 387      * column requirements are not satisified, the needs of the
 388      * multi-column cell is mixed into the existing column requirements.
 389      * The calculation of the multi-column distribution is based upon
 390      * the proportions of the existing column requirements and taking
 391      * into consideration any constraining maximums.
 392      */
 393     void calculateColumnRequirements(int axis) {
 394         // clean columnRequirements
 395         for (SizeRequirements req : columnRequirements) {
 396             req.minimum = 0;
 397             req.preferred = 0;
 398             req.maximum = Integer.MAX_VALUE;
 399         }
 400         Container host = getContainer();
 401         if (host != null) {
 402             if (host instanceof JTextComponent) {
 403                 skipComments = !((JTextComponent)host).isEditable();
 404             } else {
 405                 skipComments = true;
 406             }
 407         }
 408         // pass 1 - single column cells
 409         boolean hasMultiColumn = false;
 410         int nrows = getRowCount();
 411         for (int i = 0; i < nrows; i++) {
 412             RowView row = getRow(i);
 413             int col = 0;
 414             int ncells = row.getViewCount();
 415             for (int cell = 0; cell < ncells; cell++) {
 416                 View cv = row.getView(cell);
 417                 if (skipComments && !(cv instanceof CellView)) {
 418                     continue;
 419                 }
 420                 for (; row.isFilled(col); col++); // advance to a free column
 421                 int rowSpan = getRowsOccupied(cv);
 422                 int colSpan = getColumnsOccupied(cv);
 423                 if (colSpan == 1) {
 424                     checkSingleColumnCell(axis, col, cv);
 425                 } else {
 426                     hasMultiColumn = true;
 427                     col += colSpan - 1;
 428                 }
 429                 col++;
 430             }
 431         }
 432 
 433         // pass 2 - multi-column cells
 434         if (hasMultiColumn) {
 435             for (int i = 0; i < nrows; i++) {
 436                 RowView row = getRow(i);
 437                 int col = 0;
 438                 int ncells = row.getViewCount();
 439                 for (int cell = 0; cell < ncells; cell++) {
 440                     View cv = row.getView(cell);
 441                     if (skipComments && !(cv instanceof CellView)) {
 442                         continue;
 443                     }
 444                     for (; row.isFilled(col); col++); // advance to a free column
 445                     int colSpan = getColumnsOccupied(cv);
 446                     if (colSpan > 1) {
 447                         checkMultiColumnCell(axis, col, colSpan, cv);
 448                         col += colSpan - 1;
 449                     }
 450                     col++;
 451                 }
 452             }
 453         }
 454     }
 455 
 456     /**
 457      * check the requirements of a table cell that spans a single column.
 458      */
 459     void checkSingleColumnCell(int axis, int col, View v) {
 460         SizeRequirements req = columnRequirements[col];
 461         req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
 462         req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
 463     }
 464 
 465     /**
 466      * check the requirements of a table cell that spans multiple
 467      * columns.
 468      */
 469     void checkMultiColumnCell(int axis, int col, int ncols, View v) {
 470         // calculate the totals
 471         long min = 0;
 472         long pref = 0;
 473         long max = 0;
 474         for (int i = 0; i < ncols; i++) {
 475             SizeRequirements req = columnRequirements[col + i];
 476             min += req.minimum;
 477             pref += req.preferred;
 478             max += req.maximum;
 479         }
 480 
 481         // check if the minimum size needs adjustment.
 482         int cmin = (int) v.getMinimumSpan(axis);
 483         if (cmin > min) {
 484             /*
 485              * the columns that this cell spans need adjustment to fit
 486              * this table cell.... calculate the adjustments.
 487              */
 488             SizeRequirements[] reqs = new SizeRequirements[ncols];
 489             for (int i = 0; i < ncols; i++) {
 490                 reqs[i] = columnRequirements[col + i];
 491             }
 492             int[] spans = new int[ncols];
 493             int[] offsets = new int[ncols];
 494             SizeRequirements.calculateTiledPositions(cmin, null, reqs,
 495                                                      offsets, spans);
 496             // apply the adjustments
 497             for (int i = 0; i < ncols; i++) {
 498                 SizeRequirements req = reqs[i];
 499                 req.minimum = Math.max(spans[i], req.minimum);
 500                 req.preferred = Math.max(req.minimum, req.preferred);
 501                 req.maximum = Math.max(req.preferred, req.maximum);
 502             }
 503         }
 504 
 505         // check if the preferred size needs adjustment.
 506         int cpref = (int) v.getPreferredSpan(axis);
 507         if (cpref > pref) {
 508             /*
 509              * the columns that this cell spans need adjustment to fit
 510              * this table cell.... calculate the adjustments.
 511              */
 512             SizeRequirements[] reqs = new SizeRequirements[ncols];
 513             for (int i = 0; i < ncols; i++) {
 514                 reqs[i] = columnRequirements[col + i];
 515             }
 516             int[] spans = new int[ncols];
 517             int[] offsets = new int[ncols];
 518             SizeRequirements.calculateTiledPositions(cpref, null, reqs,
 519                                                      offsets, spans);
 520             // apply the adjustments
 521             for (int i = 0; i < ncols; i++) {
 522                 SizeRequirements req = reqs[i];
 523                 req.preferred = Math.max(spans[i], req.preferred);
 524                 req.maximum = Math.max(req.preferred, req.maximum);
 525             }
 526         }
 527 
 528     }
 529 
 530     // --- BoxView methods -----------------------------------------
 531 
 532     /**
 533      * Calculate the requirements for the minor axis.  This is called by
 534      * the superclass whenever the requirements need to be updated (i.e.
 535      * a preferenceChanged was messaged through this view).
 536      * <p>
 537      * This is implemented to calculate the requirements as the sum of the
 538      * requirements of the columns and then adjust it if the
 539      * CSS width or height attribute is specified and applicable to
 540      * the axis.
 541      */
 542     protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
 543         updateGrid();
 544 
 545         // calculate column requirements for each column
 546         calculateColumnRequirements(axis);
 547 
 548 
 549         // the requirements are the sum of the columns.
 550         if (r == null) {
 551             r = new SizeRequirements();
 552         }
 553         long min = 0;
 554         long pref = 0;
 555         int n = columnRequirements.length;
 556         for (int i = 0; i < n; i++) {
 557             SizeRequirements req = columnRequirements[i];
 558             min += req.minimum;
 559             pref += req.preferred;
 560         }
 561         int adjust = (n + 1) * cellSpacing + 2 * borderWidth;
 562         min += adjust;
 563         pref += adjust;
 564         r.minimum = (int) min;
 565         r.preferred = (int) pref;
 566         r.maximum = (int) pref;
 567 
 568 
 569         AttributeSet attr = getAttributes();
 570         CSS.LengthValue cssWidth = (CSS.LengthValue)attr.getAttribute(
 571                                                     CSS.Attribute.WIDTH);
 572 
 573         if (BlockView.spanSetFromAttributes(axis, r, cssWidth, null)) {
 574             if (r.minimum < (int)min) {
 575                 // The user has requested a smaller size than is needed to
 576                 // show the table, override it.
 577                 r.maximum = r.minimum = r.preferred = (int) min;
 578             }
 579         }
 580         totalColumnRequirements.minimum = r.minimum;
 581         totalColumnRequirements.preferred = r.preferred;
 582         totalColumnRequirements.maximum = r.maximum;
 583 
 584         // set the alignment
 585         Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
 586         if (o != null) {
 587             // set horizontal alignment
 588             String ta = o.toString();
 589             if (ta.equals("left")) {
 590                 r.alignment = 0;
 591             } else if (ta.equals("center")) {
 592                 r.alignment = 0.5f;
 593             } else if (ta.equals("right")) {
 594                 r.alignment = 1;
 595             } else {
 596                 r.alignment = 0;
 597             }
 598         } else {
 599             r.alignment = 0;
 600         }
 601 
 602         return r;
 603     }
 604 
 605     /**
 606      * Calculate the requirements for the major axis.  This is called by
 607      * the superclass whenever the requirements need to be updated (i.e.
 608      * a preferenceChanged was messaged through this view).
 609      * <p>
 610      * This is implemented to provide the superclass behavior adjusted for
 611      * multi-row table cells.
 612      */
 613     protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
 614         updateInsets();
 615         rowIterator.updateAdjustments();
 616         r = CSS.calculateTiledRequirements(rowIterator, r);
 617         r.maximum = r.preferred;
 618         return r;
 619     }
 620 
 621     /**
 622      * Perform layout for the minor axis of the box (i.e. the
 623      * axis orthogonal to the axis that it represents).  The results
 624      * of the layout should be placed in the given arrays which represent
 625      * the allocations to the children along the minor axis.  This
 626      * is called by the superclass whenever the layout needs to be
 627      * updated along the minor axis.
 628      * <p>
 629      * This is implemented to call the
 630      * <a href="#layoutColumns">layoutColumns</a> method, and then
 631      * forward to the superclass to actually carry out the layout
 632      * of the tables rows.
 633      *
 634      * @param targetSpan the total span given to the view, which
 635      *  would be used to layout the children
 636      * @param axis the axis being layed out
 637      * @param offsets the offsets from the origin of the view for
 638      *  each of the child views.  This is a return value and is
 639      *  filled in by the implementation of this method
 640      * @param spans the span of each child view;  this is a return
 641      *  value and is filled in by the implementation of this method
 642      * @return the offset and span for each child view in the
 643      *  offsets and spans parameters
 644      */
 645     protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
 646         // make grid is properly represented
 647         updateGrid();
 648 
 649         // all of the row layouts are invalid, so mark them that way
 650         int n = getRowCount();
 651         for (int i = 0; i < n; i++) {
 652             RowView row = getRow(i);
 653             row.layoutChanged(axis);
 654         }
 655 
 656         // calculate column spans
 657         layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
 658 
 659         // continue normal layout
 660         super.layoutMinorAxis(targetSpan, axis, offsets, spans);
 661     }
 662 
 663 
 664     /**
 665      * Perform layout for the major axis of the box (i.e. the
 666      * axis that it represents).  The results
 667      * of the layout should be placed in the given arrays which represent
 668      * the allocations to the children along the minor axis.  This
 669      * is called by the superclass whenever the layout needs to be
 670      * updated along the minor axis.
 671      * <p>
 672      * This method is where the layout of the table rows within the
 673      * table takes place.  This method is implemented to call the use
 674      * the RowIterator and the CSS collapsing tile to layout
 675      * with border spacing and border collapsing capabilities.
 676      *
 677      * @param targetSpan the total span given to the view, which
 678      *  would be used to layout the children
 679      * @param axis the axis being layed out
 680      * @param offsets the offsets from the origin of the view for
 681      *  each of the child views; this is a return value and is
 682      *  filled in by the implementation of this method
 683      * @param spans the span of each child view; this is a return
 684      *  value and is filled in by the implementation of this method
 685      * @return the offset and span for each child view in the
 686      *  offsets and spans parameters
 687      */
 688     protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
 689         rowIterator.setLayoutArrays(offsets, spans);
 690         CSS.calculateTiledLayout(rowIterator, targetSpan);
 691 
 692         if (captionIndex != -1) {
 693             // place the caption
 694             View caption = getView(captionIndex);
 695             int h = (int) caption.getPreferredSpan(Y_AXIS);
 696             spans[captionIndex] = h;
 697             short boxBottom = (short) painter.getInset(BOTTOM, this);
 698             if (boxBottom != getBottomInset()) {
 699                 offsets[captionIndex] = targetSpan + boxBottom;
 700             } else {
 701                 offsets[captionIndex] = - getTopInset();
 702             }
 703         }
 704     }
 705 
 706     /**
 707      * Fetches the child view that represents the given position in
 708      * the model.  This is implemented to walk through the children
 709      * looking for a range that contains the given position.  In this
 710      * view the children do not necessarily have a one to one mapping
 711      * with the child elements.
 712      *
 713      * @param pos  the search position >= 0
 714      * @param a  the allocation to the table on entry, and the
 715      *   allocation of the view containing the position on exit
 716      * @return  the view representing the given position, or
 717      *   null if there isn't one
 718      */
 719     protected View getViewAtPosition(int pos, Rectangle a) {
 720         int n = getViewCount();
 721         for (int i = 0; i < n; i++) {
 722             View v = getView(i);
 723             int p0 = v.getStartOffset();
 724             int p1 = v.getEndOffset();
 725             if ((pos >= p0) && (pos < p1)) {
 726                 // it's in this view.
 727                 if (a != null) {
 728                     childAllocation(i, a);
 729                 }
 730                 return v;
 731             }
 732         }
 733         if (pos == getEndOffset()) {
 734             View v = getView(n - 1);
 735             if (a != null) {
 736                 this.childAllocation(n - 1, a);
 737             }
 738             return v;
 739         }
 740         return null;
 741     }
 742 
 743     // --- View methods ---------------------------------------------
 744 
 745     /**
 746      * Fetches the attributes to use when rendering.  This is
 747      * implemented to multiplex the attributes specified in the
 748      * model with a StyleSheet.
 749      */
 750     public AttributeSet getAttributes() {
 751         if (attr == null) {
 752             StyleSheet sheet = getStyleSheet();
 753             attr = sheet.getViewAttributes(this);
 754         }
 755         return attr;
 756     }
 757 
 758     /**
 759      * Renders using the given rendering surface and area on that
 760      * surface.  This is implemented to delegate to the css box
 761      * painter to paint the border and background prior to the
 762      * interior.  The superclass culls rendering the children
 763      * that don't directly intersect the clip and the row may
 764      * have cells hanging from a row above in it.  The table
 765      * does not use the superclass rendering behavior and instead
 766      * paints all of the rows and lets the rows cull those
 767      * cells not intersecting the clip region.
 768      *
 769      * @param g the rendering surface to use
 770      * @param allocation the allocated region to render into
 771      * @see View#paint
 772      */
 773     public void paint(Graphics g, Shape allocation) {
 774         // paint the border
 775         Rectangle a = allocation.getBounds();
 776         setSize(a.width, a.height);
 777         if (captionIndex != -1) {
 778             // adjust the border for the caption
 779             short top = (short) painter.getInset(TOP, this);
 780             short bottom = (short) painter.getInset(BOTTOM, this);
 781             if (top != getTopInset()) {
 782                 int h = getTopInset() - top;
 783                 a.y += h;
 784                 a.height -= h;
 785             } else {
 786                 a.height -= getBottomInset() - bottom;
 787             }
 788         }
 789         painter.paint(g, a.x, a.y, a.width, a.height, this);
 790         // paint interior
 791         int n = getViewCount();
 792         for (int i = 0; i < n; i++) {
 793             View v = getView(i);
 794             v.paint(g, getChildAllocation(i, allocation));
 795         }
 796         //super.paint(g, a);
 797     }
 798 
 799     /**
 800      * Establishes the parent view for this view.  This is
 801      * guaranteed to be called before any other methods if the
 802      * parent view is functioning properly.
 803      * <p>
 804      * This is implemented
 805      * to forward to the superclass as well as call the
 806      * <a href="#setPropertiesFromAttributes">setPropertiesFromAttributes</a>
 807      * method to set the paragraph properties from the css
 808      * attributes.  The call is made at this time to ensure
 809      * the ability to resolve upward through the parents
 810      * view attributes.
 811      *
 812      * @param parent the new parent, or null if the view is
 813      *  being removed from a parent it was previously added
 814      *  to
 815      */
 816     public void setParent(View parent) {
 817         super.setParent(parent);
 818         if (parent != null) {
 819             setPropertiesFromAttributes();
 820         }
 821     }
 822 
 823     /**
 824      * Fetches the ViewFactory implementation that is feeding
 825      * the view hierarchy.
 826      * This replaces the ViewFactory with an implementation that
 827      * calls through to the createTableRow and createTableCell
 828      * methods.   If the element given to the factory isn't a
 829      * table row or cell, the request is delegated to the factory
 830      * produced by the superclass behavior.
 831      *
 832      * @return the factory, null if none
 833      */
 834     public ViewFactory getViewFactory() {
 835         return this;
 836     }
 837 
 838     /**
 839      * Gives notification that something was inserted into
 840      * the document in a location that this view is responsible for.
 841      * This replaces the ViewFactory with an implementation that
 842      * calls through to the createTableRow and createTableCell
 843      * methods.   If the element given to the factory isn't a
 844      * table row or cell, the request is delegated to the factory
 845      * passed as an argument.
 846      *
 847      * @param e the change information from the associated document
 848      * @param a the current allocation of the view
 849      * @param f the factory to use to rebuild if the view has children
 850      * @see View#insertUpdate
 851      */
 852     public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
 853         super.insertUpdate(e, a, this);
 854     }
 855 
 856     /**
 857      * Gives notification that something was removed from the document
 858      * in a location that this view is responsible for.
 859      * This replaces the ViewFactory with an implementation that
 860      * calls through to the createTableRow and createTableCell
 861      * methods.   If the element given to the factory isn't a
 862      * table row or cell, the request is delegated to the factory
 863      * passed as an argument.
 864      *
 865      * @param e the change information from the associated document
 866      * @param a the current allocation of the view
 867      * @param f the factory to use to rebuild if the view has children
 868      * @see View#removeUpdate
 869      */
 870     public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
 871         super.removeUpdate(e, a, this);
 872     }
 873 
 874     /**
 875      * Gives notification from the document that attributes were changed
 876      * in a location that this view is responsible for.
 877      * This replaces the ViewFactory with an implementation that
 878      * calls through to the createTableRow and createTableCell
 879      * methods.   If the element given to the factory isn't a
 880      * table row or cell, the request is delegated to the factory
 881      * passed as an argument.
 882      *
 883      * @param e the change information from the associated document
 884      * @param a the current allocation of the view
 885      * @param f the factory to use to rebuild if the view has children
 886      * @see View#changedUpdate
 887      */
 888     public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
 889         super.changedUpdate(e, a, this);
 890     }
 891 
 892     protected void forwardUpdate(DocumentEvent.ElementChange ec,
 893                                  DocumentEvent e, Shape a, ViewFactory f) {
 894         super.forwardUpdate(ec, e, a, f);
 895         // A change in any of the table cells usually effects the whole table,
 896         // so redraw it all!
 897         if (a != null) {
 898             Component c = getContainer();
 899             if (c != null) {
 900                 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
 901                                    a.getBounds();
 902                 c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
 903             }
 904         }
 905     }
 906 
 907     /**
 908      * Change the child views.  This is implemented to
 909      * provide the superclass behavior and invalidate the
 910      * grid so that rows and columns will be recalculated.
 911      */
 912     public void replace(int offset, int length, View[] views) {
 913         super.replace(offset, length, views);
 914         invalidateGrid();
 915     }
 916 
 917     // --- ViewFactory methods ------------------------------------------
 918 
 919     /**
 920      * The table itself acts as a factory for the various
 921      * views that actually represent pieces of the table.
 922      * All other factory activity is delegated to the factory
 923      * returned by the parent of the table.
 924      */
 925     public View create(Element elem) {
 926         Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
 927         if (o instanceof HTML.Tag) {
 928             HTML.Tag kind = (HTML.Tag) o;
 929             if (kind == HTML.Tag.TR) {
 930                 return createTableRow(elem);
 931             } else if ((kind == HTML.Tag.TD) || (kind == HTML.Tag.TH)) {
 932                 return new CellView(elem);
 933             } else if (kind == HTML.Tag.CAPTION) {
 934                 return new javax.swing.text.html.ParagraphView(elem);
 935             }
 936         }
 937         // default is to delegate to the normal factory
 938         View p = getParent();
 939         if (p != null) {
 940             ViewFactory f = p.getViewFactory();
 941             if (f != null) {
 942                 return f.create(elem);
 943             }
 944         }
 945         return null;
 946     }
 947 
 948     // ---- variables ----------------------------------------------------
 949 
 950     private AttributeSet attr;
 951     private StyleSheet.BoxPainter painter;
 952 
 953     private int cellSpacing;
 954     private int borderWidth;
 955 
 956     /**
 957      * The index of the caption view if there is a caption.
 958      * This has a value of -1 if there is no caption.  The
 959      * caption lives in the inset area of the table, and is
 960      * updated with each time the grid is recalculated.
 961      */
 962     private int captionIndex;
 963 
 964     /**
 965      * Do any of the table cells contain a relative size
 966      * specification?  This is updated with each call to
 967      * updateGrid().  If this is true, the ColumnIterator
 968      * will do extra work to calculate relative cell
 969      * specifications.
 970      */
 971     private boolean relativeCells;
 972 
 973     /**
 974      * Do any of the table cells span multiple rows?  If
 975      * true, the RowRequirementIterator will do additional
 976      * work to adjust the requirements of rows spanned by
 977      * a single table cell.  This is updated with each call to
 978      * updateGrid().
 979      */
 980     private boolean multiRowCells;
 981 
 982     int[] columnSpans;
 983     int[] columnOffsets;
 984     /**
 985      * SizeRequirements for all the columns.
 986      */
 987     SizeRequirements totalColumnRequirements;
 988     SizeRequirements[] columnRequirements;
 989 
 990     RowIterator rowIterator = new RowIterator();
 991     ColumnIterator colIterator = new ColumnIterator();
 992 
 993     Vector<RowView> rows;
 994 
 995     // whether to display comments inside table or not.
 996     boolean skipComments = false;
 997 
 998     boolean gridValid;
 999     private static final BitSet EMPTY = new BitSet();
1000 
1001     class ColumnIterator implements CSS.LayoutIterator {
1002 
1003         /**
1004          * Disable percentage adjustments which should only apply
1005          * when calculating layout, not requirements.
1006          */
1007         void disablePercentages() {
1008             percentages = null;
1009         }
1010 
1011         /**
1012          * Update percentage adjustments if they are needed.
1013          */
1014         private void updatePercentagesAndAdjustmentWeights(int span) {
1015             adjustmentWeights = new int[columnRequirements.length];
1016             for (int i = 0; i < columnRequirements.length; i++) {
1017                 adjustmentWeights[i] = 0;
1018             }
1019             if (relativeCells) {
1020                 percentages = new int[columnRequirements.length];
1021             } else {
1022                 percentages = null;
1023             }
1024             int nrows = getRowCount();
1025             for (int rowIndex = 0; rowIndex < nrows; rowIndex++) {
1026                 RowView row = getRow(rowIndex);
1027                 int col = 0;
1028                 int ncells = row.getViewCount();
1029                 for (int cell = 0; cell < ncells; cell++, col++) {
1030                     View cv = row.getView(cell);
1031                     for (; row.isFilled(col); col++); // advance to a free column
1032                     int rowSpan = getRowsOccupied(cv);
1033                     int colSpan = getColumnsOccupied(cv);
1034                     AttributeSet a = cv.getAttributes();
1035                     CSS.LengthValue lv = (CSS.LengthValue)
1036                         a.getAttribute(CSS.Attribute.WIDTH);
1037                     if ( lv != null ) {
1038                         int len = (int) (lv.getValue(span) / colSpan + 0.5f);
1039                         for (int i = 0; i < colSpan; i++) {
1040                             if (lv.isPercentage()) {
1041                                 // add a percentage requirement
1042                                 percentages[col+i] = Math.max(percentages[col+i], len);
1043                                 adjustmentWeights[col + i] = Math.max(adjustmentWeights[col + i], WorstAdjustmentWeight);
1044                             } else {
1045                                 adjustmentWeights[col + i] = Math.max(adjustmentWeights[col + i], WorstAdjustmentWeight - 1);
1046                             }
1047                         }
1048                     }
1049                     col += colSpan - 1;
1050                 }
1051             }
1052         }
1053 
1054         /**
1055          * Set the layout arrays to use for holding layout results
1056          */
1057         public void setLayoutArrays(int offsets[], int spans[], int targetSpan) {
1058             this.offsets = offsets;
1059             this.spans = spans;
1060             updatePercentagesAndAdjustmentWeights(targetSpan);
1061         }
1062 
1063         // --- RequirementIterator methods -------------------
1064 
1065         public int getCount() {
1066             return columnRequirements.length;
1067         }
1068 
1069         public void setIndex(int i) {
1070             col = i;
1071         }
1072 
1073         public void setOffset(int offs) {
1074             offsets[col] = offs;
1075         }
1076 
1077         public int getOffset() {
1078             return offsets[col];
1079         }
1080 
1081         public void setSpan(int span) {
1082             spans[col] = span;
1083         }
1084 
1085         public int getSpan() {
1086             return spans[col];
1087         }
1088 
1089         public float getMinimumSpan(float parentSpan) {
1090             // do not care for percentages, since min span can't
1091             // be less than columnRequirements[col].minimum,
1092             // but can be less than percentage value.
1093             return columnRequirements[col].minimum;
1094         }
1095 
1096         public float getPreferredSpan(float parentSpan) {
1097             if ((percentages != null) && (percentages[col] != 0)) {
1098                 return Math.max(percentages[col], columnRequirements[col].minimum);
1099             }
1100             return columnRequirements[col].preferred;
1101         }
1102 
1103         public float getMaximumSpan(float parentSpan) {
1104             return columnRequirements[col].maximum;
1105         }
1106 
1107         public float getBorderWidth() {
1108             return borderWidth;
1109         }
1110 
1111 
1112         public float getLeadingCollapseSpan() {
1113             return cellSpacing;
1114         }
1115 
1116         public float getTrailingCollapseSpan() {
1117             return cellSpacing;
1118         }
1119 
1120         public int getAdjustmentWeight() {
1121             return adjustmentWeights[col];
1122         }
1123 
1124         /**
1125          * Current column index
1126          */
1127         private int col;
1128 
1129         /**
1130          * percentage values (may be null since there
1131          * might not be any).
1132          */
1133         private int[] percentages;
1134 
1135         private int[] adjustmentWeights;
1136 
1137         private int[] offsets;
1138         private int[] spans;
1139     }
1140 
1141     class RowIterator implements CSS.LayoutIterator {
1142 
1143         RowIterator() {
1144         }
1145 
1146         void updateAdjustments() {
1147             int axis = Y_AXIS;
1148             if (multiRowCells) {
1149                 // adjust requirements of multi-row cells
1150                 int n = getRowCount();
1151                 adjustments = new int[n];
1152                 for (int i = 0; i < n; i++) {
1153                     RowView rv = getRow(i);
1154                     if (rv.multiRowCells == true) {
1155                         int ncells = rv.getViewCount();
1156                         for (int j = 0; j < ncells; j++) {
1157                             View v = rv.getView(j);
1158                             int nrows = getRowsOccupied(v);
1159                             if (nrows > 1) {
1160                                 int spanNeeded = (int) v.getPreferredSpan(axis);
1161                                 adjustMultiRowSpan(spanNeeded, nrows, i);
1162                             }
1163                         }
1164                     }
1165                 }
1166             } else {
1167                 adjustments = null;
1168             }
1169         }
1170 
1171         /**
1172          * Fixup preferences to accommodate a multi-row table cell
1173          * if not already covered by existing preferences.  This is
1174          * a no-op if not all of the rows needed (to do this check/fixup)
1175          * have arrived yet.
1176          */
1177         void adjustMultiRowSpan(int spanNeeded, int nrows, int rowIndex) {
1178             if ((rowIndex + nrows) > getCount()) {
1179                 // rows are missing (could be a bad rowspan specification)
1180                 // or not all the rows have arrived.  Do the best we can with
1181                 // the current set of rows.
1182                 nrows = getCount() - rowIndex;
1183                 if (nrows < 1) {
1184                     return;
1185                 }
1186             }
1187             int span = 0;
1188             for (int i = 0; i < nrows; i++) {
1189                 RowView rv = getRow(rowIndex + i);
1190                 span += rv.getPreferredSpan(Y_AXIS);
1191             }
1192             if (spanNeeded > span) {
1193                 int adjust = (spanNeeded - span);
1194                 int rowAdjust = adjust / nrows;
1195                 int firstAdjust = rowAdjust + (adjust - (rowAdjust * nrows));
1196                 RowView rv = getRow(rowIndex);
1197                 adjustments[rowIndex] = Math.max(adjustments[rowIndex],
1198                                                  firstAdjust);
1199                 for (int i = 1; i < nrows; i++) {
1200                     adjustments[rowIndex + i] = Math.max(
1201                         adjustments[rowIndex + i], rowAdjust);
1202                 }
1203             }
1204         }
1205 
1206         void setLayoutArrays(int[] offsets, int[] spans) {
1207             this.offsets = offsets;
1208             this.spans = spans;
1209         }
1210 
1211         // --- RequirementIterator methods -------------------
1212 
1213         public void setOffset(int offs) {
1214             RowView rv = getRow(row);
1215             if (rv != null) {
1216                 offsets[rv.viewIndex] = offs;
1217             }
1218         }
1219 
1220         public int getOffset() {
1221             RowView rv = getRow(row);
1222             if (rv != null) {
1223                 return offsets[rv.viewIndex];
1224             }
1225             return 0;
1226         }
1227 
1228         public void setSpan(int span) {
1229             RowView rv = getRow(row);
1230             if (rv != null) {
1231                 spans[rv.viewIndex] = span;
1232             }
1233         }
1234 
1235         public int getSpan() {
1236             RowView rv = getRow(row);
1237             if (rv != null) {
1238                 return spans[rv.viewIndex];
1239             }
1240             return 0;
1241         }
1242 
1243         public int getCount() {
1244             return rows.size();
1245         }
1246 
1247         public void setIndex(int i) {
1248             row = i;
1249         }
1250 
1251         public float getMinimumSpan(float parentSpan) {
1252             return getPreferredSpan(parentSpan);
1253         }
1254 
1255         public float getPreferredSpan(float parentSpan) {
1256             RowView rv = getRow(row);
1257             if (rv != null) {
1258                 int adjust = (adjustments != null) ? adjustments[row] : 0;
1259                 return rv.getPreferredSpan(TableView.this.getAxis()) + adjust;
1260             }
1261             return 0;
1262         }
1263 
1264         public float getMaximumSpan(float parentSpan) {
1265             return getPreferredSpan(parentSpan);
1266         }
1267 
1268         public float getBorderWidth() {
1269             return borderWidth;
1270         }
1271 
1272         public float getLeadingCollapseSpan() {
1273             return cellSpacing;
1274         }
1275 
1276         public float getTrailingCollapseSpan() {
1277             return cellSpacing;
1278         }
1279 
1280         public int getAdjustmentWeight() {
1281             return 0;
1282         }
1283 
1284         /**
1285          * Current row index
1286          */
1287         private int row;
1288 
1289         /**
1290          * Adjustments to the row requirements to handle multi-row
1291          * table cells.
1292          */
1293         private int[] adjustments;
1294 
1295         private int[] offsets;
1296         private int[] spans;
1297     }
1298 
1299     /**
1300      * View of a row in a row-centric table.
1301      */
1302     public class RowView extends BoxView {
1303 
1304         /**
1305          * Constructs a TableView for the given element.
1306          *
1307          * @param elem the element that this view is responsible for
1308          */
1309         public RowView(Element elem) {
1310             super(elem, View.X_AXIS);
1311             fillColumns = new BitSet();
1312             RowView.this.setPropertiesFromAttributes();
1313         }
1314 
1315         void clearFilledColumns() {
1316             fillColumns.and(EMPTY);
1317         }
1318 
1319         void fillColumn(int col) {
1320             fillColumns.set(col);
1321         }
1322 
1323         boolean isFilled(int col) {
1324             return fillColumns.get(col);
1325         }
1326 
1327         /**
1328          * The number of columns present in this row.
1329          */
1330         int getColumnCount() {
1331             int nfill = 0;
1332             int n = fillColumns.size();
1333             for (int i = 0; i < n; i++) {
1334                 if (fillColumns.get(i)) {
1335                     nfill ++;
1336                 }
1337             }
1338             return getViewCount() + nfill;
1339         }
1340 
1341         /**
1342          * Fetches the attributes to use when rendering.  This is
1343          * implemented to multiplex the attributes specified in the
1344          * model with a StyleSheet.
1345          */
1346         public AttributeSet getAttributes() {
1347             return attr;
1348         }
1349 
1350         View findViewAtPoint(int x, int y, Rectangle alloc) {
1351             int n = getViewCount();
1352             for (int i = 0; i < n; i++) {
1353                 if (getChildAllocation(i, alloc).contains(x, y)) {
1354                     childAllocation(i, alloc);
1355                     return getView(i);
1356                 }
1357             }
1358             return null;
1359         }
1360 
1361         protected StyleSheet getStyleSheet() {
1362             HTMLDocument doc = (HTMLDocument) getDocument();
1363             return doc.getStyleSheet();
1364         }
1365 
1366         /**
1367          * This is called by a child to indicate its
1368          * preferred span has changed.  This is implemented to
1369          * execute the superclass behavior and well as try to
1370          * determine if a row with a multi-row cell hangs across
1371          * this row.  If a multi-row cell covers this row it also
1372          * needs to propagate a preferenceChanged so that it will
1373          * recalculate the multi-row cell.
1374          *
1375          * @param child the child view
1376          * @param width true if the width preference should change
1377          * @param height true if the height preference should change
1378          */
1379         public void preferenceChanged(View child, boolean width, boolean height) {
1380             super.preferenceChanged(child, width, height);
1381             if (TableView.this.multiRowCells && height) {
1382                 for (int i = rowIndex  - 1; i >= 0; i--) {
1383                     RowView rv = TableView.this.getRow(i);
1384                     if (rv.multiRowCells) {
1385                         rv.preferenceChanged(null, false, true);
1386                         break;
1387                     }
1388                 }
1389             }
1390         }
1391 
1392         // The major axis requirements for a row are dictated by the column
1393         // requirements. These methods use the value calculated by
1394         // TableView.
1395         protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
1396             SizeRequirements req = new SizeRequirements();
1397             req.minimum = totalColumnRequirements.minimum;
1398             req.maximum = totalColumnRequirements.maximum;
1399             req.preferred = totalColumnRequirements.preferred;
1400             req.alignment = 0f;
1401             return req;
1402         }
1403 
1404         public float getMinimumSpan(int axis) {
1405             float value;
1406 
1407             if (axis == View.X_AXIS) {
1408                 value = totalColumnRequirements.minimum + getLeftInset() +
1409                         getRightInset();
1410             }
1411             else {
1412                 value = super.getMinimumSpan(axis);
1413             }
1414             return value;
1415         }
1416 
1417         public float getMaximumSpan(int axis) {
1418             float value;
1419 
1420             if (axis == View.X_AXIS) {
1421                 // We're flexible.
1422                 value = (float)Integer.MAX_VALUE;
1423             }
1424             else {
1425                 value = super.getMaximumSpan(axis);
1426             }
1427             return value;
1428         }
1429 
1430         public float getPreferredSpan(int axis) {
1431             float value;
1432 
1433             if (axis == View.X_AXIS) {
1434                 value = totalColumnRequirements.preferred + getLeftInset() +
1435                         getRightInset();
1436             }
1437             else {
1438                 value = super.getPreferredSpan(axis);
1439             }
1440             return value;
1441         }
1442 
1443         public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
1444             super.changedUpdate(e, a, f);
1445             int pos = e.getOffset();
1446             if (pos <= getStartOffset() && (pos + e.getLength()) >=
1447                 getEndOffset()) {
1448                 RowView.this.setPropertiesFromAttributes();
1449             }
1450         }
1451 
1452         /**
1453          * Renders using the given rendering surface and area on that
1454          * surface.  This is implemented to delegate to the css box
1455          * painter to paint the border and background prior to the
1456          * interior.
1457          *
1458          * @param g the rendering surface to use
1459          * @param allocation the allocated region to render into
1460          * @see View#paint
1461          */
1462         public void paint(Graphics g, Shape allocation) {
1463             Rectangle a = (Rectangle) allocation;
1464             painter.paint(g, a.x, a.y, a.width, a.height, this);
1465             super.paint(g, a);
1466         }
1467 
1468         /**
1469          * Change the child views.  This is implemented to
1470          * provide the superclass behavior and invalidate the
1471          * grid so that rows and columns will be recalculated.
1472          */
1473         public void replace(int offset, int length, View[] views) {
1474             super.replace(offset, length, views);
1475             invalidateGrid();
1476         }
1477 
1478         /**
1479          * Calculate the height requirements of the table row.  The
1480          * requirements of multi-row cells are not considered for this
1481          * calculation.  The table itself will check and adjust the row
1482          * requirements for all the rows that have multi-row cells spanning
1483          * them.  This method updates the multi-row flag that indicates that
1484          * this row and rows below need additional consideration.
1485          */
1486         protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
1487 //          return super.calculateMinorAxisRequirements(axis, r);
1488             long min = 0;
1489             long pref = 0;
1490             long max = 0;
1491             multiRowCells = false;
1492             int n = getViewCount();
1493             for (int i = 0; i < n; i++) {
1494                 View v = getView(i);
1495                 if (getRowsOccupied(v) > 1) {
1496                     multiRowCells = true;
1497                     max = Math.max((int) v.getMaximumSpan(axis), max);
1498                 } else {
1499                     min = Math.max((int) v.getMinimumSpan(axis), min);
1500                     pref = Math.max((int) v.getPreferredSpan(axis), pref);
1501                     max = Math.max((int) v.getMaximumSpan(axis), max);
1502                 }
1503             }
1504 
1505             if (r == null) {
1506                 r = new SizeRequirements();
1507                 r.alignment = 0.5f;
1508             }
1509             r.preferred = (int) pref;
1510             r.minimum = (int) min;
1511             r.maximum = (int) max;
1512             return r;
1513         }
1514 
1515         /**
1516          * Perform layout for the major axis of the box (i.e. the
1517          * axis that it represents).  The results of the layout should
1518          * be placed in the given arrays which represent the allocations
1519          * to the children along the major axis.
1520          * <p>
1521          * This is re-implemented to give each child the span of the column
1522          * width for the table, and to give cells that span multiple columns
1523          * the multi-column span.
1524          *
1525          * @param targetSpan the total span given to the view, which
1526          *  would be used to layout the children
1527          * @param axis the axis being layed out
1528          * @param offsets the offsets from the origin of the view for
1529          *  each of the child views; this is a return value and is
1530          *  filled in by the implementation of this method
1531          * @param spans the span of each child view; this is a return
1532          *  value and is filled in by the implementation of this method
1533          * @return the offset and span for each child view in the
1534          *  offsets and spans parameters
1535          */
1536         protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
1537             int col = 0;
1538             int ncells = getViewCount();
1539             for (int cell = 0; cell < ncells; cell++) {
1540                 View cv = getView(cell);
1541                 if (skipComments && !(cv instanceof CellView)) {
1542                     continue;
1543                 }
1544                 for (; isFilled(col); col++); // advance to a free column
1545                 int colSpan = getColumnsOccupied(cv);
1546                 spans[cell] = columnSpans[col];
1547                 offsets[cell] = columnOffsets[col];
1548                 if (colSpan > 1) {
1549                     int n = columnSpans.length;
1550                     for (int j = 1; j < colSpan; j++) {
1551                         // Because the table may be only partially formed, some
1552                         // of the columns may not yet exist.  Therefore we check
1553                         // the bounds.
1554                         if ((col+j) < n) {
1555                             spans[cell] += columnSpans[col+j];
1556                             spans[cell] += cellSpacing;
1557                         }
1558                     }
1559                     col += colSpan - 1;
1560                 }
1561                 col++;
1562             }
1563         }
1564 
1565         /**
1566          * Perform layout for the minor axis of the box (i.e. the
1567          * axis orthogonal to the axis that it represents).  The results
1568          * of the layout should be placed in the given arrays which represent
1569          * the allocations to the children along the minor axis.  This
1570          * is called by the superclass whenever the layout needs to be
1571          * updated along the minor axis.
1572          * <p>
1573          * This is implemented to delegate to the superclass, then adjust
1574          * the span for any cell that spans multiple rows.
1575          *
1576          * @param targetSpan the total span given to the view, which
1577          *  would be used to layout the children
1578          * @param axis the axis being layed out
1579          * @param offsets the offsets from the origin of the view for
1580          *  each of the child views; this is a return value and is
1581          *  filled in by the implementation of this method
1582          * @param spans the span of each child view; this is a return
1583          *  value and is filled in by the implementation of this method
1584          * @return the offset and span for each child view in the
1585          *  offsets and spans parameters
1586          */
1587         protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
1588             super.layoutMinorAxis(targetSpan, axis, offsets, spans);
1589             int col = 0;
1590             int ncells = getViewCount();
1591             for (int cell = 0; cell < ncells; cell++, col++) {
1592                 View cv = getView(cell);
1593                 for (; isFilled(col); col++); // advance to a free column
1594                 int colSpan = getColumnsOccupied(cv);
1595                 int rowSpan = getRowsOccupied(cv);
1596                 if (rowSpan > 1) {
1597 
1598                     int row0 = rowIndex;
1599                     int row1 = Math.min(rowIndex + rowSpan - 1, getRowCount()-1);
1600                     spans[cell] = getMultiRowSpan(row0, row1);
1601                 }
1602                 if (colSpan > 1) {
1603                     col += colSpan - 1;
1604                 }
1605             }
1606         }
1607 
1608         /**
1609          * Determines the resizability of the view along the
1610          * given axis.  A value of 0 or less is not resizable.
1611          *
1612          * @param axis may be either View.X_AXIS or View.Y_AXIS
1613          * @return the resize weight
1614          * @exception IllegalArgumentException for an invalid axis
1615          */
1616         public int getResizeWeight(int axis) {
1617             return 1;
1618         }
1619 
1620         /**
1621          * Fetches the child view that represents the given position in
1622          * the model.  This is implemented to walk through the children
1623          * looking for a range that contains the given position.  In this
1624          * view the children do not necessarily have a one to one mapping
1625          * with the child elements.
1626          *
1627          * @param pos  the search position >= 0
1628          * @param a  the allocation to the table on entry, and the
1629          *   allocation of the view containing the position on exit
1630          * @return  the view representing the given position, or
1631          *   null if there isn't one
1632          */
1633         protected View getViewAtPosition(int pos, Rectangle a) {
1634             int n = getViewCount();
1635             for (int i = 0; i < n; i++) {
1636                 View v = getView(i);
1637                 int p0 = v.getStartOffset();
1638                 int p1 = v.getEndOffset();
1639                 if ((pos >= p0) && (pos < p1)) {
1640                     // it's in this view.
1641                     if (a != null) {
1642                         childAllocation(i, a);
1643                     }
1644                     return v;
1645                 }
1646             }
1647             if (pos == getEndOffset()) {
1648                 View v = getView(n - 1);
1649                 if (a != null) {
1650                     this.childAllocation(n - 1, a);
1651                 }
1652                 return v;
1653             }
1654             return null;
1655         }
1656 
1657         /**
1658          * Update any cached values that come from attributes.
1659          */
1660         void setPropertiesFromAttributes() {
1661             StyleSheet sheet = getStyleSheet();
1662             attr = sheet.getViewAttributes(this);
1663             painter = sheet.getBoxPainter(attr);
1664         }
1665 
1666         private StyleSheet.BoxPainter painter;
1667         private AttributeSet attr;
1668 
1669         /** columns filled by multi-column or multi-row cells */
1670         BitSet fillColumns;
1671 
1672         /**
1673          * The row index within the overall grid
1674          */
1675         int rowIndex;
1676 
1677         /**
1678          * The view index (for row index to view index conversion).
1679          * This is set by the updateGrid method.
1680          */
1681         int viewIndex;
1682 
1683         /**
1684          * Does this table row have cells that span multiple rows?
1685          */
1686         boolean multiRowCells;
1687 
1688     }
1689 
1690     /**
1691      * Default view of an html table cell.  This needs to be moved
1692      * somewhere else.
1693      */
1694     class CellView extends BlockView {
1695 
1696         /**
1697          * Constructs a TableCell for the given element.
1698          *
1699          * @param elem the element that this view is responsible for
1700          */
1701         public CellView(Element elem) {
1702             super(elem, Y_AXIS);
1703         }
1704 
1705         /**
1706          * Perform layout for the major axis of the box (i.e. the
1707          * axis that it represents).  The results of the layout should
1708          * be placed in the given arrays which represent the allocations
1709          * to the children along the major axis.  This is called by the
1710          * superclass to recalculate the positions of the child views
1711          * when the layout might have changed.
1712          * <p>
1713          * This is implemented to delegate to the superclass to
1714          * tile the children.  If the target span is greater than
1715          * was needed, the offsets are adjusted to align the children
1716          * (i.e. position according to the html valign attribute).
1717          *
1718          * @param targetSpan the total span given to the view, which
1719          *  would be used to layout the children
1720          * @param axis the axis being layed out
1721          * @param offsets the offsets from the origin of the view for
1722          *  each of the child views; this is a return value and is
1723          *  filled in by the implementation of this method
1724          * @param spans the span of each child view; this is a return
1725          *  value and is filled in by the implementation of this method
1726          * @return the offset and span for each child view in the
1727          *  offsets and spans parameters
1728          */
1729         protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
1730             super.layoutMajorAxis(targetSpan, axis, offsets, spans);
1731             // calculate usage
1732             int used = 0;
1733             int n = spans.length;
1734             for (int i = 0; i < n; i++) {
1735                 used += spans[i];
1736             }
1737 
1738             // calculate adjustments
1739             int adjust = 0;
1740             if (used < targetSpan) {
1741                 // PENDING(prinz) change to use the css alignment.
1742                 String valign = (String) getElement().getAttributes().getAttribute(
1743                     HTML.Attribute.VALIGN);
1744                 if (valign == null) {
1745                     AttributeSet rowAttr = getElement().getParentElement().getAttributes();
1746                     valign = (String) rowAttr.getAttribute(HTML.Attribute.VALIGN);
1747                 }
1748                 if ((valign == null) || valign.equals("middle")) {
1749                     adjust = (targetSpan - used) / 2;
1750                 } else if (valign.equals("bottom")) {
1751                     adjust = targetSpan - used;
1752                 }
1753             }
1754 
1755             // make adjustments.
1756             if (adjust != 0) {
1757                 for (int i = 0; i < n; i++) {
1758                     offsets[i] += adjust;
1759                 }
1760             }
1761         }
1762 
1763         /**
1764          * Calculate the requirements needed along the major axis.
1765          * This is called by the superclass whenever the requirements
1766          * need to be updated (i.e. a preferenceChanged was messaged
1767          * through this view).
1768          * <p>
1769          * This is implemented to delegate to the superclass, but
1770          * indicate the maximum size is very large (i.e. the cell
1771          * is willing to expend to occupy the full height of the row).
1772          *
1773          * @param axis the axis being layed out.
1774          * @param r the requirements to fill in.  If null, a new one
1775          *  should be allocated.
1776          */
1777         protected SizeRequirements calculateMajorAxisRequirements(int axis,
1778                                                                   SizeRequirements r) {
1779             SizeRequirements req = super.calculateMajorAxisRequirements(axis, r);
1780             req.maximum = Integer.MAX_VALUE;
1781             return req;
1782         }
1783 
1784         @Override
1785         protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
1786             SizeRequirements rv = super.calculateMinorAxisRequirements(axis, r);
1787             //for the cell the minimum should be derived from the child views
1788             //the parent behaviour is to use CSS for that
1789             int n = getViewCount();
1790             int min = 0;
1791             for (int i = 0; i < n; i++) {
1792                 View v = getView(i);
1793                 min = Math.max((int) v.getMinimumSpan(axis), min);
1794             }
1795             rv.minimum = Math.min(rv.minimum, min);
1796             return rv;
1797         }
1798     }
1799 
1800 
1801 }