1/*
2 * Copyright (c) 1997, 2017, 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 */
25package javax.swing.text;
26
27import java.awt.*;
28import java.util.BitSet;
29import java.util.Vector;
30import javax.swing.SizeRequirements;
31import javax.swing.event.DocumentEvent;
32
33import javax.swing.text.html.HTML;
34
35/**
36 * <p>
37 * Implements View interface for a table, that is composed of an
38 * element structure where the child elements of the element
39 * this view is responsible for represent rows and the child
40 * elements of the row elements are cells.  The cell elements can
41 * have an arbitrary element structure under them, which will
42 * be built with the ViewFactory returned by the getViewFactory
43 * method.
44 * <pre>
45 *
46 * &nbsp;  TABLE
47 * &nbsp;    ROW
48 * &nbsp;      CELL
49 * &nbsp;      CELL
50 * &nbsp;    ROW
51 * &nbsp;      CELL
52 * &nbsp;      CELL
53 *
54 * </pre>
55 * <p>
56 * This is implemented as a hierarchy of boxes, the table itself
57 * is a vertical box, the rows are horizontal boxes, and the cells
58 * are vertical boxes.  The cells are allowed to span multiple
59 * columns and rows.  By default, the table can be thought of as
60 * being formed over a grid (i.e. somewhat like one would find in
61 * gridbag layout), where table cells can request to span more
62 * than one grid cell.  The default horizontal span of table cells
63 * will be based upon this grid, but can be changed by reimplementing
64 * the requested span of the cell (i.e. table cells can have independent
65 * spans if desired).
66 *
67 * @author  Timothy Prinzing
68 * @see     View
69 */
70public abstract class TableView extends BoxView {
71
72    /**
73     * Constructs a TableView for the given element.
74     *
75     * @param elem the element that this view is responsible for
76     */
77    public TableView(Element elem) {
78        super(elem, View.Y_AXIS);
79        rows = new Vector<TableRow>();
80        gridValid = false;
81        totalColumnRequirements = new SizeRequirements();
82    }
83
84    /**
85     * Creates a new table row.
86     *
87     * @param elem an element
88     * @return the row
89     */
90    protected TableRow createTableRow(Element elem) {
91        return new TableRow(elem);
92    }
93
94    /**
95     * @deprecated Table cells can now be any arbitrary
96     * View implementation and should be produced by the
97     * ViewFactory rather than the table.
98     *
99     * @param elem an element
100     * @return the cell
101     */
102    @Deprecated
103    protected TableCell createTableCell(Element elem) {
104        return new TableCell(elem);
105    }
106
107    /**
108     * The number of columns in the table.
109     */
110    int getColumnCount() {
111        return columnSpans.length;
112    }
113
114    /**
115     * Fetches the span (width) of the given column.
116     * This is used by the nested cells to query the
117     * sizes of grid locations outside of themselves.
118     */
119    int getColumnSpan(int col) {
120        return columnSpans[col];
121    }
122
123    /**
124     * The number of rows in the table.
125     */
126    int getRowCount() {
127        return rows.size();
128    }
129
130    /**
131     * Fetches the span (height) of the given row.
132     */
133    int getRowSpan(int row) {
134        View rv = getRow(row);
135        if (rv != null) {
136            return (int) rv.getPreferredSpan(Y_AXIS);
137        }
138        return 0;
139    }
140
141    TableRow getRow(int row) {
142        if (row < rows.size()) {
143            return rows.elementAt(row);
144        }
145        return null;
146    }
147
148    /**
149     * Determines the number of columns occupied by
150     * the table cell represented by given element.
151     */
152    /*protected*/ int getColumnsOccupied(View v) {
153        // PENDING(prinz) this code should be in the html
154        // paragraph, but we can't add api to enable it.
155        AttributeSet a = v.getElement().getAttributes();
156        String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
157        if (s != null) {
158            try {
159                return Integer.parseInt(s);
160            } catch (NumberFormatException nfe) {
161                // fall through to one column
162            }
163        }
164
165        return 1;
166    }
167
168    /**
169     * Determines the number of rows occupied by
170     * the table cell represented by given element.
171     */
172    /*protected*/ int getRowsOccupied(View v) {
173        // PENDING(prinz) this code should be in the html
174        // paragraph, but we can't add api to enable it.
175        AttributeSet a = v.getElement().getAttributes();
176        String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
177        if (s != null) {
178            try {
179                return Integer.parseInt(s);
180            } catch (NumberFormatException nfe) {
181                // fall through to one row
182            }
183        }
184
185        return 1;
186    }
187
188    /*protected*/ void invalidateGrid() {
189        gridValid = false;
190    }
191
192    protected void forwardUpdate(DocumentEvent.ElementChange ec,
193                                     DocumentEvent e, Shape a, ViewFactory f) {
194        super.forwardUpdate(ec, e, a, f);
195        // A change in any of the table cells usually effects the whole table,
196        // so redraw it all!
197        if (a != null) {
198            Component c = getContainer();
199            if (c != null) {
200                Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
201                                   a.getBounds();
202                c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
203            }
204        }
205    }
206
207    /**
208     * Change the child views.  This is implemented to
209     * provide the superclass behavior and invalidate the
210     * grid so that rows and columns will be recalculated.
211     */
212    public void replace(int offset, int length, View[] views) {
213        super.replace(offset, length, views);
214        invalidateGrid();
215    }
216
217    /**
218     * Fill in the grid locations that are placeholders
219     * for multi-column, multi-row, and missing grid
220     * locations.
221     */
222    void updateGrid() {
223        if (! gridValid) {
224            // determine which views are table rows and clear out
225            // grid points marked filled.
226            rows.removeAllElements();
227            int n = getViewCount();
228            for (int i = 0; i < n; i++) {
229                View v = getView(i);
230                if (v instanceof TableRow) {
231                    rows.addElement((TableRow) v);
232                    TableRow rv = (TableRow) v;
233                    rv.clearFilledColumns();
234                    rv.setRow(i);
235                }
236            }
237
238            int maxColumns = 0;
239            int nrows = rows.size();
240            for (int row = 0; row < nrows; row++) {
241                TableRow rv = getRow(row);
242                int col = 0;
243                for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
244                    View cv = rv.getView(cell);
245                    // advance to a free column
246                    for (; rv.isFilled(col); col++);
247                    int rowSpan = getRowsOccupied(cv);
248                    int colSpan = getColumnsOccupied(cv);
249                    if ((colSpan > 1) || (rowSpan > 1)) {
250                        // fill in the overflow entries for this cell
251                        int rowLimit = row + rowSpan;
252                        int colLimit = col + colSpan;
253                        for (int i = row; i < rowLimit; i++) {
254                            for (int j = col; j < colLimit; j++) {
255                                if (i != row || j != col) {
256                                    addFill(i, j);
257                                }
258                            }
259                        }
260                        if (colSpan > 1) {
261                            col += colSpan - 1;
262                        }
263                    }
264                }
265                maxColumns = Math.max(maxColumns, col);
266            }
267
268            // setup the column layout/requirements
269            columnSpans = new int[maxColumns];
270            columnOffsets = new int[maxColumns];
271            columnRequirements = new SizeRequirements[maxColumns];
272            for (int i = 0; i < maxColumns; i++) {
273                columnRequirements[i] = new SizeRequirements();
274            }
275            gridValid = true;
276        }
277    }
278
279    /**
280     * Mark a grid location as filled in for a cells overflow.
281     */
282    void addFill(int row, int col) {
283        TableRow rv = getRow(row);
284        if (rv != null) {
285            rv.fillColumn(col);
286        }
287    }
288
289    /**
290     * Lays out the columns to fit within the given target span.
291     * Returns the results through {@code offsets} and {@code spans}.
292     *
293     * @param targetSpan the given span for total of all the table
294     *  columns
295     * @param reqs the requirements desired for each column.  This
296     *  is the column maximum of the cells minimum, preferred, and
297     *  maximum requested span
298     * @param spans the return value of how much to allocated to
299     *  each column
300     * @param offsets the return value of the offset from the
301     *  origin for each column
302     */
303    protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
304                                 SizeRequirements[] reqs) {
305        // allocate using the convenience method on SizeRequirements
306        SizeRequirements.calculateTiledPositions(targetSpan, null, reqs,
307                                                 offsets, spans);
308    }
309
310    /**
311     * Perform layout for the minor axis of the box (i.e. the
312     * axis orthogonal to the axis that it represents).  The results
313     * of the layout should be placed in the given arrays which represent
314     * the allocations to the children along the minor axis.  This
315     * is called by the superclass whenever the layout needs to be
316     * updated along the minor axis.
317     * <p>
318     * This is implemented to call the
319     * {@link #layoutColumns layoutColumns} method, and then
320     * forward to the superclass to actually carry out the layout
321     * of the tables rows.
322     *
323     * @param targetSpan the total span given to the view, which
324     *  would be used to layout the children.
325     * @param axis the axis being layed out.
326     * @param offsets the offsets from the origin of the view for
327     *  each of the child views.  This is a return value and is
328     *  filled in by the implementation of this method.
329     * @param spans the span of each child view.  This is a return
330     *  value and is filled in by the implementation of this method.
331     */
332    protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
333        // make grid is properly represented
334        updateGrid();
335
336        // all of the row layouts are invalid, so mark them that way
337        int n = getRowCount();
338        for (int i = 0; i < n; i++) {
339            TableRow row = getRow(i);
340            row.layoutChanged(axis);
341        }
342
343        // calculate column spans
344        layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
345
346        // continue normal layout
347        super.layoutMinorAxis(targetSpan, axis, offsets, spans);
348    }
349
350    /**
351     * Calculate the requirements for the minor axis.  This is called by
352     * the superclass whenever the requirements need to be updated (i.e.
353     * a preferenceChanged was messaged through this view).
354     * <p>
355     * This is implemented to calculate the requirements as the sum of the
356     * requirements of the columns.
357     */
358    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
359        updateGrid();
360
361        // calculate column requirements for each column
362        calculateColumnRequirements(axis);
363
364
365        // the requirements are the sum of the columns.
366        if (r == null) {
367            r = new SizeRequirements();
368        }
369        long min = 0;
370        long pref = 0;
371        long max = 0;
372        for (SizeRequirements req : columnRequirements) {
373            min += req.minimum;
374            pref += req.preferred;
375            max += req.maximum;
376        }
377        r.minimum = (int) min;
378        r.preferred = (int) pref;
379        r.maximum = (int) max;
380        r.alignment = 0;
381
382        totalColumnRequirements.minimum = r.minimum;
383        totalColumnRequirements.preferred = r.preferred;
384        totalColumnRequirements.maximum = r.maximum;
385
386        return r;
387    }
388
389    /*
390    boolean shouldTrace() {
391        AttributeSet a = getElement().getAttributes();
392        Object o = a.getAttribute(HTML.Attribute.ID);
393        if ((o != null) && o.equals("debug")) {
394            return true;
395        }
396        return false;
397    }
398    */
399
400    /**
401     * Calculate the requirements for each column.  The calculation
402     * is done as two passes over the table.  The table cells that
403     * occupy a single column are scanned first to determine the
404     * maximum of minimum, preferred, and maximum spans along the
405     * give axis.  Table cells that span multiple columns are excluded
406     * from the first pass.  A second pass is made to determine if
407     * the cells that span multiple columns are satisfied.  If the
408     * column requirements are not satisified, the needs of the
409     * multi-column cell is mixed into the existing column requirements.
410     * The calculation of the multi-column distribution is based upon
411     * the proportions of the existing column requirements and taking
412     * into consideration any constraining maximums.
413     */
414    void calculateColumnRequirements(int axis) {
415
416        for (SizeRequirements req : columnRequirements) {
417            req.minimum = 0;
418            req.preferred = 0;
419            req.maximum = Integer.MAX_VALUE;
420        }
421
422        // pass 1 - single column cells
423        boolean hasMultiColumn = false;
424        int nrows = getRowCount();
425        for (int i = 0; i < nrows; i++) {
426            TableRow row = getRow(i);
427            int col = 0;
428            int ncells = row.getViewCount();
429            for (int cell = 0; cell < ncells; cell++, col++) {
430                View cv = row.getView(cell);
431                for (; row.isFilled(col); col++); // advance to a free column
432                int rowSpan = getRowsOccupied(cv);
433                int colSpan = getColumnsOccupied(cv);
434                if (colSpan == 1) {
435                    checkSingleColumnCell(axis, col, cv);
436                } else {
437                    hasMultiColumn = true;
438                    col += colSpan - 1;
439                }
440            }
441        }
442
443        // pass 2 - multi-column cells
444        if (hasMultiColumn) {
445            for (int i = 0; i < nrows; i++) {
446                TableRow row = getRow(i);
447                int col = 0;
448                int ncells = row.getViewCount();
449                for (int cell = 0; cell < ncells; cell++, col++) {
450                    View cv = row.getView(cell);
451                    for (; row.isFilled(col); col++); // advance to a free column
452                    int colSpan = getColumnsOccupied(cv);
453                    if (colSpan > 1) {
454                        checkMultiColumnCell(axis, col, colSpan, cv);
455                        col += colSpan - 1;
456                    }
457                }
458            }
459        }
460
461        /*
462        if (shouldTrace()) {
463            System.err.println("calc:");
464            for (int i = 0; i < columnRequirements.length; i++) {
465                System.err.println(" " + i + ": " + columnRequirements[i]);
466            }
467        }
468        */
469    }
470
471    /**
472     * check the requirements of a table cell that spans a single column.
473     */
474    void checkSingleColumnCell(int axis, int col, View v) {
475        SizeRequirements req = columnRequirements[col];
476        req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
477        req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
478        req.maximum = Math.max((int) v.getMaximumSpan(axis), req.maximum);
479    }
480
481    /**
482     * check the requirements of a table cell that spans multiple
483     * columns.
484     */
485    void checkMultiColumnCell(int axis, int col, int ncols, View v) {
486        // calculate the totals
487        long min = 0;
488        long pref = 0;
489        long max = 0;
490        for (int i = 0; i < ncols; i++) {
491            SizeRequirements req = columnRequirements[col + i];
492            min += req.minimum;
493            pref += req.preferred;
494            max += req.maximum;
495        }
496
497        // check if the minimum size needs adjustment.
498        int cmin = (int) v.getMinimumSpan(axis);
499        if (cmin > min) {
500            /*
501             * the columns that this cell spans need adjustment to fit
502             * this table cell.... calculate the adjustments.  The
503             * maximum for each cell is the maximum of the existing
504             * maximum or the amount needed by the cell.
505             */
506            SizeRequirements[] reqs = new SizeRequirements[ncols];
507            for (int i = 0; i < ncols; i++) {
508                SizeRequirements r = reqs[i] = columnRequirements[col + i];
509                r.maximum = Math.max(r.maximum, (int) v.getMaximumSpan(axis));
510            }
511            int[] spans = new int[ncols];
512            int[] offsets = new int[ncols];
513            SizeRequirements.calculateTiledPositions(cmin, null, reqs,
514                                                     offsets, spans);
515            // apply the adjustments
516            for (int i = 0; i < ncols; i++) {
517                SizeRequirements req = reqs[i];
518                req.minimum = Math.max(spans[i], req.minimum);
519                req.preferred = Math.max(req.minimum, req.preferred);
520                req.maximum = Math.max(req.preferred, req.maximum);
521            }
522        }
523
524        // check if the preferred size needs adjustment.
525        int cpref = (int) v.getPreferredSpan(axis);
526        if (cpref > pref) {
527            /*
528             * the columns that this cell spans need adjustment to fit
529             * this table cell.... calculate the adjustments.  The
530             * maximum for each cell is the maximum of the existing
531             * maximum or the amount needed by the cell.
532             */
533            SizeRequirements[] reqs = new SizeRequirements[ncols];
534            for (int i = 0; i < ncols; i++) {
535                SizeRequirements r = reqs[i] = columnRequirements[col + i];
536            }
537            int[] spans = new int[ncols];
538            int[] offsets = new int[ncols];
539            SizeRequirements.calculateTiledPositions(cpref, null, reqs,
540                                                     offsets, spans);
541            // apply the adjustments
542            for (int i = 0; i < ncols; i++) {
543                SizeRequirements req = reqs[i];
544                req.preferred = Math.max(spans[i], req.preferred);
545                req.maximum = Math.max(req.preferred, req.maximum);
546            }
547        }
548
549    }
550
551    /**
552     * Fetches the child view that represents the given position in
553     * the model.  This is implemented to walk through the children
554     * looking for a range that contains the given position.  In this
555     * view the children do not necessarily have a one to one mapping
556     * with the child elements.
557     *
558     * @param pos  the search position &gt;= 0
559     * @param a  the allocation to the table on entry, and the
560     *   allocation of the view containing the position on exit
561     * @return  the view representing the given position, or
562     *   <code>null</code> if there isn't one
563     */
564    protected View getViewAtPosition(int pos, Rectangle a) {
565        int n = getViewCount();
566        for (int i = 0; i < n; i++) {
567            View v = getView(i);
568            int p0 = v.getStartOffset();
569            int p1 = v.getEndOffset();
570            if ((pos >= p0) && (pos < p1)) {
571                // it's in this view.
572                if (a != null) {
573                    childAllocation(i, a);
574                }
575                return v;
576            }
577        }
578        if (pos == getEndOffset()) {
579            View v = getView(n - 1);
580            if (a != null) {
581                this.childAllocation(n - 1, a);
582            }
583            return v;
584        }
585        return null;
586    }
587
588    // ---- variables ----------------------------------------------------
589
590    int[] columnSpans;
591    int[] columnOffsets;
592
593    SizeRequirements totalColumnRequirements;
594
595    SizeRequirements[] columnRequirements;
596    Vector<TableRow> rows;
597    boolean gridValid;
598    private static final BitSet EMPTY = new BitSet();
599
600    /**
601     * View of a row in a row-centric table.
602     */
603    public class TableRow extends BoxView {
604
605        /**
606         * Constructs a TableView for the given element.
607         *
608         * @param elem the element that this view is responsible for
609         * @since 1.4
610         */
611        public TableRow(Element elem) {
612            super(elem, View.X_AXIS);
613            fillColumns = new BitSet();
614        }
615
616        void clearFilledColumns() {
617            fillColumns.and(EMPTY);
618        }
619
620        void fillColumn(int col) {
621            fillColumns.set(col);
622        }
623
624        boolean isFilled(int col) {
625            return fillColumns.get(col);
626        }
627
628        /** get location in the overall set of rows */
629        int getRow() {
630            return row;
631        }
632
633        /**
634         * set location in the overall set of rows, this is
635         * set by the TableView.updateGrid() method.
636         */
637        void setRow(int row) {
638            this.row = row;
639        }
640
641        /**
642         * The number of columns present in this row.
643         */
644        int getColumnCount() {
645            int nfill = 0;
646            int n = fillColumns.size();
647            for (int i = 0; i < n; i++) {
648                if (fillColumns.get(i)) {
649                    nfill ++;
650                }
651            }
652            return getViewCount() + nfill;
653        }
654
655        /**
656         * Change the child views.  This is implemented to
657         * provide the superclass behavior and invalidate the
658         * grid so that rows and columns will be recalculated.
659         */
660        public void replace(int offset, int length, View[] views) {
661            super.replace(offset, length, views);
662            invalidateGrid();
663        }
664
665        @Override
666        protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
667            SizeRequirements req = new SizeRequirements();
668            req.minimum = totalColumnRequirements.minimum;
669            req.maximum = totalColumnRequirements.maximum;
670            req.preferred = totalColumnRequirements.preferred;
671            req.alignment = 0f;
672            return req;
673        }
674
675        @Override
676        public float getMinimumSpan(int axis) {
677            float value;
678
679            if (axis == View.X_AXIS) {
680                value = totalColumnRequirements.minimum + getLeftInset() + getRightInset();
681            } else {
682                value = super.getMinimumSpan(axis);
683            }
684            return value;
685        }
686
687        @Override
688        public float getMaximumSpan(int axis) {
689            float value;
690
691            if (axis == View.X_AXIS) {
692                // We're flexible.
693                value = (float) Integer.MAX_VALUE;
694            } else {
695                value = super.getMaximumSpan(axis);
696            }
697            return value;
698        }
699
700        @Override
701        public float getPreferredSpan(int axis) {
702            float value;
703
704            if (axis == View.X_AXIS) {
705                value = totalColumnRequirements.preferred + getLeftInset() + getRightInset();
706            } else {
707                value = super.getPreferredSpan(axis);
708            }
709            return value;
710        }
711
712        /**
713         * Perform layout for the major axis of the box (i.e. the
714         * axis that it represents).  The results of the layout should
715         * be placed in the given arrays which represent the allocations
716         * to the children along the major axis.
717         * <p>
718         * This is re-implemented to give each child the span of the column
719         * width for the table, and to give cells that span multiple columns
720         * the multi-column span.
721         *
722         * @param targetSpan the total span given to the view, which
723         *  would be used to layout the children.
724         * @param axis the axis being layed out.
725         * @param offsets the offsets from the origin of the view for
726         *  each of the child views.  This is a return value and is
727         *  filled in by the implementation of this method.
728         * @param spans the span of each child view.  This is a return
729         *  value and is filled in by the implementation of this method.
730         */
731        protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
732            int col = 0;
733            int ncells = getViewCount();
734            for (int cell = 0; cell < ncells; cell++, col++) {
735                View cv = getView(cell);
736                for (; isFilled(col); col++); // advance to a free column
737                int colSpan = getColumnsOccupied(cv);
738                spans[cell] = columnSpans[col];
739                offsets[cell] = columnOffsets[col];
740                if (colSpan > 1) {
741                    int n = columnSpans.length;
742                    for (int j = 1; j < colSpan; j++) {
743                        // Because the table may be only partially formed, some
744                        // of the columns may not yet exist.  Therefore we check
745                        // the bounds.
746                        if ((col+j) < n) {
747                            spans[cell] += columnSpans[col+j];
748                        }
749                    }
750                    col += colSpan - 1;
751                }
752            }
753        }
754
755        /**
756         * Perform layout for the minor axis of the box (i.e. the
757         * axis orthogonal to the axis that it represents).  The results
758         * of the layout should be placed in the given arrays which represent
759         * the allocations to the children along the minor axis.  This
760         * is called by the superclass whenever the layout needs to be
761         * updated along the minor axis.
762         * <p>
763         * This is implemented to delegate to the superclass, then adjust
764         * the span for any cell that spans multiple rows.
765         *
766         * @param targetSpan the total span given to the view, which
767         *  would be used to layout the children.
768         * @param axis the axis being layed out.
769         * @param offsets the offsets from the origin of the view for
770         *  each of the child views.  This is a return value and is
771         *  filled in by the implementation of this method.
772         * @param spans the span of each child view.  This is a return
773         *  value and is filled in by the implementation of this method.
774         */
775        protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
776            super.layoutMinorAxis(targetSpan, axis, offsets, spans);
777            int col = 0;
778            int ncells = getViewCount();
779            for (int cell = 0; cell < ncells; cell++, col++) {
780                View cv = getView(cell);
781                for (; isFilled(col); col++); // advance to a free column
782                int colSpan = getColumnsOccupied(cv);
783                int rowSpan = getRowsOccupied(cv);
784                if (rowSpan > 1) {
785                    for (int j = 1; j < rowSpan; j++) {
786                        // test bounds of each row because it may not exist
787                        // either because of error or because the table isn't
788                        // fully loaded yet.
789                        int row = getRow() + j;
790                        if (row < TableView.this.getViewCount()) {
791                            int span = TableView.this.getSpan(Y_AXIS, getRow()+j);
792                            spans[cell] += span;
793                        }
794                    }
795                }
796                if (colSpan > 1) {
797                    col += colSpan - 1;
798                }
799            }
800        }
801
802        /**
803         * Determines the resizability of the view along the
804         * given axis.  A value of 0 or less is not resizable.
805         *
806         * @param axis may be either View.X_AXIS or View.Y_AXIS
807         * @return the resize weight
808         * @exception IllegalArgumentException for an invalid axis
809         */
810        public int getResizeWeight(int axis) {
811            return 1;
812        }
813
814        /**
815         * Fetches the child view that represents the given position in
816         * the model.  This is implemented to walk through the children
817         * looking for a range that contains the given position.  In this
818         * view the children do not necessarily have a one to one mapping
819         * with the child elements.
820         *
821         * @param pos  the search position &gt;= 0
822         * @param a  the allocation to the table on entry, and the
823         *   allocation of the view containing the position on exit
824         * @return  the view representing the given position, or
825         *   <code>null</code> if there isn't one
826         */
827        protected View getViewAtPosition(int pos, Rectangle a) {
828            int n = getViewCount();
829            for (int i = 0; i < n; i++) {
830                View v = getView(i);
831                int p0 = v.getStartOffset();
832                int p1 = v.getEndOffset();
833                if ((pos >= p0) && (pos < p1)) {
834                    // it's in this view.
835                    if (a != null) {
836                        childAllocation(i, a);
837                    }
838                    return v;
839                }
840            }
841            if (pos == getEndOffset()) {
842                View v = getView(n - 1);
843                if (a != null) {
844                    this.childAllocation(n - 1, a);
845                }
846                return v;
847            }
848            return null;
849        }
850
851        /** columns filled by multi-column or multi-row cells */
852        BitSet fillColumns;
853        /** the row within the overall grid */
854        int row;
855    }
856
857    /**
858     * @deprecated  A table cell can now be any View implementation.
859     */
860    @Deprecated
861    public class TableCell extends BoxView implements GridCell {
862
863        /**
864         * Constructs a TableCell for the given element.
865         *
866         * @param elem the element that this view is responsible for
867         * @since 1.4
868         */
869        public TableCell(Element elem) {
870            super(elem, View.Y_AXIS);
871        }
872
873        // --- GridCell methods -------------------------------------
874
875        /**
876         * Gets the number of columns this cell spans (e.g. the
877         * grid width).
878         *
879         * @return the number of columns
880         */
881        public int getColumnCount() {
882            return 1;
883        }
884
885        /**
886         * Gets the number of rows this cell spans (that is, the
887         * grid height).
888         *
889         * @return the number of rows
890         */
891        public int getRowCount() {
892            return 1;
893        }
894
895
896        /**
897         * Sets the grid location.
898         *
899         * @param row the row &gt;= 0
900         * @param col the column &gt;= 0
901         */
902        public void setGridLocation(int row, int col) {
903            this.row = row;
904            this.col = col;
905        }
906
907        /**
908         * Gets the row of the grid location
909         */
910        public int getGridRow() {
911            return row;
912        }
913
914        /**
915         * Gets the column of the grid location
916         */
917        public int getGridColumn() {
918            return col;
919        }
920
921        int row;
922        int col;
923    }
924
925    /**
926     * <em>
927     * THIS IS NO LONGER USED, AND WILL BE REMOVED IN THE
928     * NEXT RELEASE.  THE JCK SIGNATURE TEST THINKS THIS INTERFACE
929     * SHOULD EXIST
930     * </em>
931     */
932    interface GridCell {
933
934        /**
935         * Sets the grid location.
936         *
937         * @param row the row &gt;= 0
938         * @param col the column &gt;= 0
939         */
940        public void setGridLocation(int row, int col);
941
942        /**
943         * Gets the row of the grid location
944         */
945        public int getGridRow();
946
947        /**
948         * Gets the column of the grid location
949         */
950        public int getGridColumn();
951
952        /**
953         * Gets the number of columns this cell spans (e.g. the
954         * grid width).
955         *
956         * @return the number of columns
957         */
958        public int getColumnCount();
959
960        /**
961         * Gets the number of rows this cell spans (that is, the
962         * grid height).
963         *
964         * @return the number of rows
965         */
966        public int getRowCount();
967
968    }
969
970}
971