JDK-8065080 : Duplicating of Views when we insert these Views in an other View.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 8u25
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2014-11-01
  • Updated: 2014-11-17
  • Resolved: 2014-11-17
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows
Linux

A DESCRIPTION OF THE PROBLEM :
The table with JSE7 is well displayed.
The table with JSE8 is badly displayed.

REGRESSION.  Last worked in version 7u67

ADDITIONAL REGRESSION INFORMATION: 
It works fine with all Java SE7 releases.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test program first with JSE7 and then with JSE8.


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package CodeBug;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
public class CodeBug extends JFrame {

    JEditorPane edit = new JEditorPane();

    public CodeBug() {
        super("Code example for a TableView bug");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        edit.setEditorKit(new CodeBugEditorKit());
        initCodeBug();

        this.getContentPane().add(new JScrollPane(edit));
        this.setSize(300, 200);
        this.setLocationRelativeTo(null);

    }

    private void initCodeBug() {
        CodeBugDocument doc = (CodeBugDocument) edit.getDocument();
        try {
            doc.insertString(0, "TextB  TextE", null);
        } catch (BadLocationException ex) {
        }
        doc.insertTable(6, 4, 3);
        try {
            doc.insertString(7, "Cell11", null);
            doc.insertString(14, "Cell12", null);
            doc.insertString(21, "Cell13", null);
            doc.insertString(28, "Cell21", null);
            doc.insertString(35, "Cell22", null);
            doc.insertString(42, "Cell23", null);
            doc.insertString(49, "Cell31", null);
            doc.insertString(56, "Cell32", null);
            doc.insertString(63, "Cell33", null);
            doc.insertString(70, "Cell41", null);
            doc.insertString(77, "Cell42", null);
            doc.insertString(84, "Cell43", null);
        } catch (BadLocationException ex) {
        }
    }

    public static void main(String[] args) {
        CodeBug m = new CodeBug();
        m.setVisible(true);
    }
}

//-------------------------------------------------------------------------------
class CodeBugEditorKit extends StyledEditorKit {

    ViewFactory defaultFactory = new TableFactory();

    @Override
    public ViewFactory getViewFactory() {
        return defaultFactory;
    }

    @Override
    public Document createDefaultDocument() {
        return new CodeBugDocument();
    }
}
//-------------------------------------------------------------------------------

class TableFactory implements ViewFactory {

    @Override
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
                return new LabelView(elem);
            } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                return new ParagraphView(elem);
            } else if (kind.equals(AbstractDocument.SectionElementName)) {
                return new BoxView(elem, View.Y_AXIS);
            } else if (kind.equals(StyleConstants.ComponentElementName)) {
                return new ComponentView(elem);
            } else if (kind.equals(CodeBugDocument.ELEMENT_TABLE)) {
                return new tableView(elem);
            } else if (kind.equals(StyleConstants.IconElementName)) {
                return new IconView(elem);
            }
        }
        // default to text display
        return new LabelView(elem);

    }
}
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
/*
 * tableView n'est responsable que de l'affichage de ses ��l��ments (les lignes et
 * les cellules). Une ligne contient des cellules. Une cellule est juste un
 * conteneur, elle peut contenir d'autre paragraphe ou d'autres tableaux.
 */
class tableView extends tableview implements ViewFactory {

    public tableView(Element elem) {
        super(elem);
    }

    @Override
    public ViewFactory getViewFactory() {
        return this;
    }

    public float getMinimumSpan(int axis) {
        return getPreferredSpan(axis);
    }

    @Override
    public float getMaximumSpan(int axis) {
        return getPreferredSpan(axis);
    }

    @Override
    public float getAlignment(int axis) {
        return 0.5f;
    }

    @Override
    public void paint(Graphics g, Shape allocation) {
        super.paint(g, allocation);
        Rectangle alloc = allocation.getBounds();
        int lastY = alloc.y + alloc.height - 1;
        g.drawLine(alloc.x, lastY, alloc.x + alloc.width, lastY);
    }

    @Override
    protected void paintChild(Graphics g, Rectangle alloc, int index) {
        super.paintChild(g, alloc, index);
        g.setColor(Color.GRAY);
        int lastX = alloc.x + alloc.width;
        g.drawLine(alloc.x, alloc.y, lastX, alloc.y);
    }

    @Override
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(CodeBugDocument.ELEMENT_TR)) {
                return new trView(elem);
            } else if (kind.equals(CodeBugDocument.ELEMENT_TD)) {
                return new BoxView(elem, View.Y_AXIS);
            }
        }

        // default is to delegate to the parent factory
        View p = getParent();
        if (p != null) {
            ViewFactory f = p.getViewFactory();
            if (f != null) {
                return f.create(elem);
            }
        }

        return null;
    }

    public class trView extends TableRow {

        public trView(Element elem) {
            super(elem);
        }

        public float getMinimumSpan(int axis) {
            return getPreferredSpan(axis);
        }

        public float getMaximumSpan(int axis) {
            return getPreferredSpan(axis);
        }

        public float getAlignment(int axis) {
            return 0f;
        }

        @Override
        protected void paintChild(Graphics g, Rectangle alloc, int index) {
            super.paintChild(g, alloc, index);
            g.setColor(Color.GRAY);
            int lastY = alloc.y + alloc.height - 1;
            g.drawLine(alloc.x, alloc.y, alloc.x, lastY);
            int lastX = alloc.x + alloc.width;
            g.drawLine(lastX, alloc.y, lastX, lastY);
        }

        @Override
        // Surcharge provisoire. Tenir compte du fait qu'une cellule s'��tendre sur plusieurs lignes !
        protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
            int n = getViewCount();
            for (int i = 0; i < n; i++) {
                spans[i] = targetSpan;
            }
            int col = 0;
            int ncells = getViewCount();
            for (int cell = 0; cell < ncells; cell++, col++) {
                View cv = getView(cell);
                for (; isFilled(col); col++); // advance to a free column
                int colSpan = getColumnsOccupied(cv);
                int rowSpan = getRowsOccupied(cv);
                if (rowSpan > 1) {
                    for (int j = 1; j < rowSpan; j++) {
                        // test bounds of each row because it may not exist
                        // either because of error or because the table isn't
                        // fully loaded yet.
                        int row = getRow() + j;
                        if (row < tableView.this.getViewCount()) {
                            int span = tableView.this.getSpan(Y_AXIS, getRow() + j);
                            spans[cell] += span;
                        }
                    }
                }
                if (colSpan > 1) {
                    col += colSpan - 1;
                }
            }
        }
    }
}

//-------------------------------------------------------------------------------
class CodeBugDocument extends DefaultStyledDocument {

    public static final String ELEMENT_TABLE = "table";
    public static final String ELEMENT_TR = "table cells row";
    public static final String ELEMENT_TD = "table data cell";

    public CodeBugDocument() {
    }

    protected void insertTable(int offset, int rowCount, int colCount) {
        try {
            ArrayList Specs = new ArrayList();
            ElementSpec gapTag = new ElementSpec(new SimpleAttributeSet(), ElementSpec.ContentType, "\n".toCharArray(), 0, 1);
            Specs.add(gapTag);

            SimpleAttributeSet tableAttrs = new SimpleAttributeSet();
            tableAttrs.addAttribute(ElementNameAttribute, ELEMENT_TABLE);
            ElementSpec tableStart = new ElementSpec(tableAttrs, ElementSpec.StartTagType);
            Specs.add(tableStart); //start table tag


            fillRowSpecs(Specs, rowCount, colCount);

            ElementSpec[] spec = new ElementSpec[Specs.size()];
            Specs.toArray(spec);

            this.insert(offset, spec);
        } catch (BadLocationException ex) {
        }
    }

    protected void fillRowSpecs(ArrayList Specs, int rowCount, int colCount) {
        SimpleAttributeSet rowAttrs = new SimpleAttributeSet();
        rowAttrs.addAttribute(ElementNameAttribute, ELEMENT_TR);
        for (int i = 0; i < rowCount; i++) {
            ElementSpec rowStart = new ElementSpec(rowAttrs, ElementSpec.StartTagType);
            Specs.add(rowStart);

            fillCellSpecs(Specs, colCount);

            ElementSpec rowEnd = new ElementSpec(rowAttrs, ElementSpec.EndTagType);
            Specs.add(rowEnd);
        }

    }

    protected void fillCellSpecs(ArrayList Specs, int colCount) {
        for (int i = 0; i < colCount; i++) {
            SimpleAttributeSet cellAttrs = new SimpleAttributeSet();
            cellAttrs.addAttribute(ElementNameAttribute, ELEMENT_TD);

            ElementSpec cellStart = new ElementSpec(cellAttrs, ElementSpec.StartTagType);
            Specs.add(cellStart);

            ElementSpec parStart = new ElementSpec(new SimpleAttributeSet(), ElementSpec.StartTagType);
            Specs.add(parStart);
            ElementSpec parContent = new ElementSpec(new SimpleAttributeSet(), ElementSpec.ContentType, "\n".toCharArray(), 0, 1);
            Specs.add(parContent);
            ElementSpec parEnd = new ElementSpec(new SimpleAttributeSet(), ElementSpec.EndTagType);
            Specs.add(parEnd);
            ElementSpec cellEnd = new ElementSpec(cellAttrs, ElementSpec.EndTagType);
            Specs.add(cellEnd);
        }

    }
}

---------------------------------------------------------------------------------------------------------------------------------------

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package CodeBug;

import java.awt.*;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Vector;

import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;

import javax.swing.text.AttributeSet;
import javax.swing.text.BoxView;
import javax.swing.text.Element;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;

/**
 * <p> Implements View interface for a table, that is composed of an element
 * structure where the child elements of the element this view is responsible
 * for represent rows and the child elements of the row elements are cells. The
 * cell elements can have an arbitrary element structure under them, which will
 * be built with the ViewFactory returned by the getViewFactory method.
 * <pre>
 *
 * &nbsp;  TABLE
 * &nbsp;    ROW
 * &nbsp;      CELL
 * &nbsp;      CELL
 * &nbsp;    ROW
 * &nbsp;      CELL
 * &nbsp;      CELL
 *
 * </pre> <p> This is implemented as a hierarchy of boxes, the table itself is a
 * vertical box, the rows are horizontal boxes, and the cells are vertical
 * boxes. The cells are allowed to span multiple columns and rows. By default,
 * the table can be thought of as being formed over a grid (i.e. somewhat like
 * one would find in gridbag layout), where table cells can request to span more
 * than one grid cell. The default horizontal span of table cells will be based
 * upon this grid, but can be changed by reimplementing the requested span of
 * the cell (i.e. table cells can have independant spans if desired).
 *
 * @author Timothy Prinzing
 * @see View
 */
public abstract class tableview extends BoxView {

    /**
     * Constructs a TableView for the given element.
     *
     * @param elem the element that this view is responsible for
     */
    public tableview(Element elem) {
        super(elem, View.Y_AXIS);
        rows = new Vector<TableRow>();
        gridValid = false;
        // Rajout
        totalColumnRequirements = new SizeRequirements();
    }

    /**
     * Creates a new table row.
     *
     * @param elem an element
     * @return the row
     */
    protected TableRow createTableRow(Element elem) {
        return new TableRow(elem);
    }

    /**
     * @deprecated Table cells can now be any arbitrary View implementation and
     * should be produced by the ViewFactory rather than the table.
     *
     * @param elem an element
     * @return the cell
     */
    @Deprecated
    protected TableCell createTableCell(Element elem) {
        return new TableCell(elem);
    }

    /**
     * The number of columns in the table.
     */
    int getColumnCount() {
        return columnSpans.length;
    }

    /**
     * Fetches the span (width) of the given column. This is used by the nested
     * cells to query the sizes of grid locations outside of themselves.
     */
    int getColumnSpan(int col) {
        return columnSpans[col];
    }

    /**
     * The number of rows in the table.
     */
    int getRowCount() {
        return rows.size();
    }

    /**
     * Fetches the span (height) of the given row.
     */
    int getRowSpan(int row) {
        View rv = getRow(row);
        if (rv != null) {
            return (int) rv.getPreferredSpan(Y_AXIS);
        }
        return 0;
    }

    TableRow getRow(int row) {
        if (row < rows.size()) {
            return rows.elementAt(row);
        }
        return null;
    }

    /**
     * Determines the number of columns occupied by the table cell represented
     * by given element.
     */
    /*
     * protected
     */ int getColumnsOccupied(View v) {
        // PENDING(prinz) this code should be in the html
        // paragraph, but we can't add api to enable it.
        AttributeSet a = v.getElement().getAttributes();
        String s = (String) a.getAttribute(HTML.Attribute.COLSPAN);
        if (s != null) {
            try {
                return Integer.parseInt(s);
            } catch (NumberFormatException nfe) {
                // fall through to one column
            }
        }

        return 1;
    }

    /**
     * Determines the number of rows occupied by the table cell represented by
     * given element.
     */
    /*
     * protected
     */ int getRowsOccupied(View v) {
        // PENDING(prinz) this code should be in the html
        // paragraph, but we can't add api to enable it.
        AttributeSet a = v.getElement().getAttributes();
        String s = (String) a.getAttribute(HTML.Attribute.ROWSPAN);
        if (s != null) {
            try {
                return Integer.parseInt(s);
            } catch (NumberFormatException nfe) {
                // fall through to one row
            }
        }

        return 1;
    }

    /*
     * protected
     */ void invalidateGrid() {
        gridValid = false;
    }

    protected void forwardUpdate(DocumentEvent.ElementChange ec,
            DocumentEvent e, Shape a, ViewFactory f) {
        super.forwardUpdate(ec, e, a, f);
        // A change in any of the table cells usually effects the whole table,
        // so redraw it all!
        if (a != null) {
            Component c = getContainer();
            if (c != null) {
                Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a
                        : a.getBounds();
                c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
            }
        }
    }

    /**
     * Change the child views. This is implemented to provide the superclass
     * behavior and invalidate the grid so that rows and columns will be
     * recalculated.
     */
    public void replace(int offset, int length, View[] views) {
        super.replace(offset, length, views);
        invalidateGrid();
    }

    /**
     * Fill in the grid locations that are placeholders for multi-column,
     * multi-row, and missing grid locations.
     */
    void updateGrid() {
        if (!gridValid) {
            // determine which views are table rows and clear out
            // grid points marked filled.
            rows.removeAllElements();
            int n = getViewCount();
            for (int i = 0; i < n; i++) {
                View v = getView(i);
                if (v instanceof TableRow) {
                    rows.addElement((TableRow) v);
                    TableRow rv = (TableRow) v;
                    rv.clearFilledColumns();
                    rv.setRow(i);
                }
            }

            int maxColumns = 0;
            int nrows = rows.size();
            for (int row = 0; row < nrows; row++) {
                TableRow rv = getRow(row);
                int col = 0;
                for (int cell = 0; cell < rv.getViewCount(); cell++, col++) {
                    View cv = rv.getView(cell);
                    // advance to a free column
                    for (; rv.isFilled(col); col++);
                    int rowSpan = getRowsOccupied(cv);
                    int colSpan = getColumnsOccupied(cv);
                    if ((colSpan > 1) || (rowSpan > 1)) {
                        // fill in the overflow entries for this cell
                        int rowLimit = row + rowSpan;
                        int colLimit = col + colSpan;
                        for (int i = row; i < rowLimit; i++) {
                            for (int j = col; j < colLimit; j++) {
                                if (i != row || j != col) {
                                    addFill(i, j);
                                }
                            }
                        }
                        if (colSpan > 1) {
                            col += colSpan - 1;
                        }
                    }
                }
                maxColumns = Math.max(maxColumns, col);
            }

            // setup the column layout/requirements
            columnSpans = new int[maxColumns];
            columnOffsets = new int[maxColumns];
            columnRequirements = new SizeRequirements[maxColumns];
            for (int i = 0; i < maxColumns; i++) {
                columnRequirements[i] = new SizeRequirements();
            }
            gridValid = true;
        }
    }

    /**
     * Mark a grid location as filled in for a cells overflow.
     */
    void addFill(int row, int col) {
        TableRow rv = getRow(row);
        if (rv != null) {
            rv.fillColumn(col);
        }
    }

    /**
     * Lays out the columns to fit within the given target span. Returns the
     * results through {@code offsets} and {@code spans}.
     *
     * @param targetSpan the given span for total of all the table columns
     * @param reqs the requirements desired for each column. This is the column
     * maximum of the cells minimum, preferred, and maximum requested span
     * @param spans the return value of how much to allocated to each column
     * @param offsets the return value of the offset from the origin for each
     * column
     */
    protected void layoutColumns(int targetSpan, int[] offsets, int[] spans,
            SizeRequirements[] reqs) {
        // allocate using the convenience method on SizeRequirements
        SizeRequirements.calculateTiledPositions(targetSpan, null, reqs,
                offsets, spans);
    }

    /**
     * Perform layout for the minor axis of the box (i.e. the axis orthoginal to
     * the axis that it represents). The results of the layout should be placed
     * in the given arrays which represent the allocations to the children along
     * the minor axis. This is called by the superclass whenever the layout
     * needs to be updated along the minor axis. <p> This is implemented to call
     * the
     * {@link #layoutColumns layoutColumns} method, and then forward to the
     * superclass to actually carry out the layout of the tables rows.
     *
     * @param targetSpan the total span given to the view, which whould be used
     * to layout the children.
     * @param axis the axis being layed out.
     * @param offsets the offsets from the origin of the view for each of the
     * child views. This is a return value and is filled in by the
     * implementation of this method.
     * @param spans the span of each child view. This is a return value and is
     * filled in by the implementation of this method.
     */
    protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
        // make grid is properly represented
        updateGrid();

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

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

        // continue normal layout
        super.layoutMinorAxis(targetSpan, axis, offsets, spans);
    }

    @Override
    public void preferenceChanged(View child, boolean width, boolean height) {
        // Rajout (instead of clean columnRequirements in calculateColumnRequirements)
        //invalidateGrid();
        super.preferenceChanged(child, width, height);
    }

    /**
     * Calculate the requirements for the minor axis. This is called by the
     * superclass whenever the requirements need to be updated (i.e. a
     * preferenceChanged was messaged through this view). <p> This is
     * implemented to calculate the requirements as the sum of the requirements
     * of the columns.
     */
    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
        updateGrid();

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


        // the requirements are the sum of the columns.
        if (r == null) {
            r = new SizeRequirements();
        }
        long min = 0;
        long pref = 0;
        long max = 0;
        for (SizeRequirements req : columnRequirements) {
            min += req.minimum;
            pref += req.preferred;
            max += req.maximum;
        }
        r.minimum = (int) min;
        r.preferred = (int) pref;
        r.maximum = (int) max;
        r.alignment = 0;

        // Rajout
        totalColumnRequirements.minimum = r.minimum;
        totalColumnRequirements.preferred = r.preferred;
        totalColumnRequirements.maximum = r.maximum;

        return r;
    }

    /*
     * boolean shouldTrace() { AttributeSet a = getElement().getAttributes();
     * Object o = a.getAttribute(HTML.Attribute.ID); if ((o != null) &&
     * o.equals("debug")) { return true; } return false; }
     */
    /**
     * Calculate the requirements for each column. The calculation is done as
     * two passes over the table. The table cells that occupy a single column
     * are scanned first to determine the maximum of minimum, preferred, and
     * maximum spans along the give axis. Table cells that span multiple columns
     * are excluded from the first pass. A second pass is made to determine if
     * the cells that span multiple columns are satisfied. If the column
     * requirements are not satisified, the needs of the multi-column cell is
     * mixed into the existing column requirements. The calculation of the
     * multi-column distribution is based upon the proportions of the existing
     * column requirements and taking into consideration any constraining
     * maximums.
     */
    void calculateColumnRequirements(int axis) {
        // Rajout (see preferenceChanged for another solution)
        // clean columnRequirements
        for (SizeRequirements req : columnRequirements) {
            req.minimum = 0;
            req.preferred = 0;
            req.maximum = Integer.MAX_VALUE;
        }

        // pass 1 - single column cells
        boolean hasMultiColumn = false;
        int nrows = getRowCount();
        for (int i = 0; i < nrows; i++) {
            TableRow row = getRow(i);
            int col = 0;
            int ncells = row.getViewCount();
            for (int cell = 0; cell < ncells; cell++, col++) {
                View cv = row.getView(cell);
                for (; row.isFilled(col); col++); // advance to a free column
                int rowSpan = getRowsOccupied(cv);
                int colSpan = getColumnsOccupied(cv);
                if (colSpan == 1) {
                    checkSingleColumnCell(axis, col, cv);
                } else {
                    hasMultiColumn = true;
                    col += colSpan - 1;
                }
            }
        }

        // pass 2 - multi-column cells
        if (hasMultiColumn) {
            for (int i = 0; i < nrows; i++) {
                TableRow row = getRow(i);
                int col = 0;
                int ncells = row.getViewCount();
                for (int cell = 0; cell < ncells; cell++, col++) {
                    View cv = row.getView(cell);
                    for (; row.isFilled(col); col++); // advance to a free column
                    int colSpan = getColumnsOccupied(cv);
                    if (colSpan > 1) {
                        checkMultiColumnCell(axis, col, colSpan, cv);
                        col += colSpan - 1;
                    }
                }
            }
        }

        /*
         * if (shouldTrace()) { System.err.println("calc:"); for (int i = 0; i <
         * columnRequirements.length; i++) { System.err.println(" " + i + ": " +
         * columnRequirements[i]); } }
         */
    }

    /**
     * check the requirements of a table cell that spans a single column.
     */
    void checkSingleColumnCell(int axis, int col, View v) {
        SizeRequirements req = columnRequirements[col];
        req.minimum = Math.max((int) v.getMinimumSpan(axis), req.minimum);
        req.preferred = Math.max((int) v.getPreferredSpan(axis), req.preferred);
        req.maximum = Math.max((int) v.getMaximumSpan(axis), req.maximum);
    }

    /**
     * check the requirements of a table cell that spans multiple columns.
     */
    void checkMultiColumnCell(int axis, int col, int ncols, View v) {
        // calculate the totals
        long min = 0;
        long pref = 0;
        long max = 0;
        for (int i = 0; i < ncols; i++) {
            SizeRequirements req = columnRequirements[col + i];
            min += req.minimum;
            pref += req.preferred;
            max += req.maximum;
        }

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

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

    }

    /**
     * Fetches the child view that represents the given position in the model.
     * This is implemented to walk through the children looking for a range that
     * contains the given position. In this view the children do not necessarily
     * have a one to one mapping with the child elements.
     *
     * @param pos the search position >= 0
     * @param a the allocation to the table on entry, and the allocation of the
     * view containing the position on exit
     * @return the view representing the given position, or
     * <code>null</code> if there isn't one
     */
    protected View getViewAtPosition(int pos, Rectangle a) {
        int n = getViewCount();
        for (int i = 0; i < n; i++) {
            View v = getView(i);
            int p0 = v.getStartOffset();
            int p1 = v.getEndOffset();
            if ((pos >= p0) && (pos < p1)) {
                // it's in this view.
                if (a != null) {
                    childAllocation(i, a);
                }
                return v;
            }
        }
        if (pos == getEndOffset()) {
            View v = getView(n - 1);
            if (a != null) {
                this.childAllocation(n - 1, a);
            }
            return v;
        }
        return null;
    }
    // ---- variables ----------------------------------------------------
    int[] columnSpans;
    int[] columnOffsets;
    // Rajout
    SizeRequirements totalColumnRequirements;
    SizeRequirements[] columnRequirements;
    Vector<TableRow> rows;
    boolean gridValid;
    static final private BitSet EMPTY = new BitSet();

    /**
     * View of a row in a row-centric table.
     */
    public class TableRow extends BoxView {

        /**
         * Constructs a TableView for the given element.
         *
         * @param elem the element that this view is responsible for
         * @since 1.4
         */
        public TableRow(Element elem) {
            super(elem, View.X_AXIS);
            fillColumns = new BitSet();
        }

        void clearFilledColumns() {
            fillColumns.and(EMPTY);
        }

        void fillColumn(int col) {
            fillColumns.set(col);
        }

        boolean isFilled(int col) {
            return fillColumns.get(col);
        }

        /**
         * get location in the overall set of rows
         */
        int getRow() {
            return row;
        }

        /**
         * set location in the overall set of rows, this is set by the
         * TableView.updateGrid() method.
         */
        void setRow(int row) {
            this.row = row;
        }

        /**
         * The number of columns present in this row.
         */
        int getColumnCount() {
            int nfill = 0;
            int n = fillColumns.size();
            for (int i = 0; i < n; i++) {
                if (fillColumns.get(i)) {
                    nfill++;
                }
            }
            return getViewCount() + nfill;
        }

        /**
         * Change the child views. This is implemented to provide the superclass
         * behavior and invalidate the grid so that rows and columns will be
         * recalculated.
         */
        public void replace(int offset, int length, View[] views) {
            super.replace(offset, length, views);
            invalidateGrid();
        }

        // Rajout
        // The major axis requirements for a row are dictated by the column
        // requirements. These methods use the value calculated by
        // TableView.
        protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
            SizeRequirements req = new SizeRequirements();
            req.minimum = totalColumnRequirements.minimum;
            req.maximum = totalColumnRequirements.maximum;
            req.preferred = totalColumnRequirements.preferred;
            req.alignment = 0f;
            return req;
        }

        // Rajout
        public float getMinimumSpan(int axis) {
            float value;

            if (axis == View.X_AXIS) {
                value = totalColumnRequirements.minimum + getLeftInset() + getRightInset();
            } else {
                value = super.getMinimumSpan(axis);
            }
            return value;
        }
        
        // Rajout
        public float getMaximumSpan(int axis) {
            float value;

            if (axis == View.X_AXIS) {
                // We're flexible.
                value = (float) Integer.MAX_VALUE;
            } else {
                value = super.getMaximumSpan(axis);
            }
            return value;
        }
        
        // Rajout
        public float getPreferredSpan(int axis) {
            float value;

            if (axis == View.X_AXIS) {
                value = totalColumnRequirements.preferred + getLeftInset() + getRightInset();
            } else {
                value = super.getPreferredSpan(axis);
            }
            return value;
        }

        /**
         * Perform layout for the major axis of the box (i.e. the axis that it
         * represents). The results of the layout should be placed in the given
         * arrays which represent the allocations to the children along the
         * major axis. <p> This is re-implemented to give each child the span of
         * the column width for the table, and to give cells that span multiple
         * columns the multi-column span.
         *
         * @param targetSpan the total span given to the view, which whould be
         * used to layout the children.
         * @param axis the axis being layed out.
         * @param offsets the offsets from the origin of the view for each of
         * the child views. This is a return value and is filled in by the
         * implementation of this method.
         * @param spans the span of each child view. This is a return value and
         * is filled in by the implementation of this method.
         */
        protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
            int col = 0;
            int ncells = getViewCount();
            for (int cell = 0; cell < ncells; cell++, col++) {
                View cv = getView(cell);
                for (; isFilled(col); col++); // advance to a free column
                int colSpan = getColumnsOccupied(cv);
                spans[cell] = columnSpans[col];
                offsets[cell] = columnOffsets[col];
                if (colSpan > 1) {
                    int n = columnSpans.length;
                    for (int j = 1; j < colSpan; j++) {
                        // Because the table may be only partially formed, some
                        // of the columns may not yet exist.  Therefore we check
                        // the bounds.
                        if ((col + j) < n) {
                            spans[cell] += columnSpans[col + j];
                        }
                    }
                    col += colSpan - 1;
                }
            }
        }

        /**
         * Perform layout for the minor axis of the box (i.e. the axis
         * orthoginal to the axis that it represents). The results of the layout
         * should be placed in the given arrays which represent the allocations
         * to the children along the minor axis. This is called by the
         * superclass whenever the layout needs to be updated along the minor
         * axis. <p> This is implemented to delegate to the superclass, then
         * adjust the span for any cell that spans multiple rows.
         *
         * @param targetSpan the total span given to the view, which whould be
         * used to layout the children.
         * @param axis the axis being layed out.
         * @param offsets the offsets from the origin of the view for each of
         * the child views. This is a return value and is filled in by the
         * implementation of this method.
         * @param spans the span of each child view. This is a return value and
         * is filled in by the implementation of this method.
         */
        protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
            super.layoutMinorAxis(targetSpan, axis, offsets, spans);
            int col = 0;
            int ncells = getViewCount();
            for (int cell = 0; cell < ncells; cell++, col++) {
                View cv = getView(cell);
                for (; isFilled(col); col++); // advance to a free column
                int colSpan = getColumnsOccupied(cv);
                int rowSpan = getRowsOccupied(cv);
                if (rowSpan > 1) {
                    for (int j = 1; j < rowSpan; j++) {
                        // test bounds of each row because it may not exist
                        // either because of error or because the table isn't
                        // fully loaded yet.
                        int row = getRow() + j;
                        if (row < tableview.this.getViewCount()) {
                            int span = tableview.this.getSpan(Y_AXIS, getRow() + j);
                            spans[cell] += span;
                        }
                    }
                }
                if (colSpan > 1) {
                    col += colSpan - 1;
                }
            }
        }

        /**
         * Determines the resizability of the view along the given axis. A value
         * of 0 or less is not resizable.
         *
         * @param axis may be either View.X_AXIS or View.Y_AXIS
         * @return the resize weight
         * @exception IllegalArgumentException for an invalid axis
         */
        public int getResizeWeight(int axis) {
            return 1;
        }

        /**
         * Fetches the child view that represents the given position in the
         * model. This is implemented to walk through the children looking for a
         * range that contains the given position. In this view the children do
         * not necessarily have a one to one mapping with the child elements.
         *
         * @param pos the search position >= 0
         * @param a the allocation to the table on entry, and the allocation of
         * the view containing the position on exit
         * @return the view representing the given position, or
         * <code>null</code> if there isn't one
         */
        protected View getViewAtPosition(int pos, Rectangle a) {
            int n = getViewCount();
            for (int i = 0; i < n; i++) {
                View v = getView(i);
                int p0 = v.getStartOffset();
                int p1 = v.getEndOffset();
                if ((pos >= p0) && (pos < p1)) {
                    // it's in this view.
                    if (a != null) {
                        childAllocation(i, a);
                    }
                    return v;
                }
            }
            if (pos == getEndOffset()) {
                View v = getView(n - 1);
                if (a != null) {
                    this.childAllocation(n - 1, a);
                }
                return v;
            }
            return null;
        }
        /**
         * columns filled by multi-column or multi-row cells
         */
        BitSet fillColumns;
        /**
         * the row within the overall grid
         */
        int row;
    }

    /**
     * @deprecated A table cell can now be any View implementation.
     */
    @Deprecated
    public class TableCell extends BoxView implements GridCell {

        /**
         * Constructs a TableCell for the given element.
         *
         * @param elem the element that this view is responsible for
         * @since 1.4
         */
        public TableCell(Element elem) {
            super(elem, View.Y_AXIS);
        }

        // --- GridCell methods -------------------------------------
        /**
         * Gets the number of columns this cell spans (e.g. the grid width).
         *
         * @return the number of columns
         */
        public int getColumnCount() {
            return 1;
        }

        /**
         * Gets the number of rows this cell spans (that is, the grid height).
         *
         * @return the number of rows
         */
        public int getRowCount() {
            return 1;
        }

        /**
         * Sets the grid location.
         *
         * @param row the row >= 0
         * @param col the column >= 0
         */
        public void setGridLocation(int row, int col) {
            this.row = row;
            this.col = col;
        }

        /**
         * Gets the row of the grid location
         */
        public int getGridRow() {
            return row;
        }

        /**
         * Gets the column of the grid location
         */
        public int getGridColumn() {
            return col;
        }
        int row;
        int col;
    }

    /**
     * <em> THIS IS NO LONGER USED, AND WILL BE REMOVED IN THE NEXT RELEASE. THE
     * JCK SIGNATURE TEST THINKS THIS INTERFACE SHOULD EXIST </em>
     */
    interface GridCell {

        /**
         * Sets the grid location.
         *
         * @param row the row >= 0
         * @param col the column >= 0
         */
        public void setGridLocation(int row, int col);

        /**
         * Gets the row of the grid location
         */
        public int getGridRow();

        /**
         * Gets the column of the grid location
         */
        public int getGridColumn();

        /**
         * Gets the number of columns this cell spans (e.g. the grid width).
         *
         * @return the number of columns
         */
        public int getColumnCount();

        /**
         * Gets the number of rows this cell spans (that is, the grid height).
         *
         * @return the number of rows
         */
        public int getRowCount();
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
It seems that, in JSE8, the "protected void forwardUpdate" method in javax.swing.text.FlowView has been rewrited.
And it sends another new update to the new insterted Views.

In the JSE7, Update was send to all view except the new ones (placed between hole0 and hole1).


Comments
This is the duplicate of the issue JDK-8024395.
2014-11-17

is it reproducible on 8 GA?
2014-11-17

This is a duplicate of closed bug JDK-7169915 per response from the submitter.
2014-11-17

Main sent to submitter with following: "Able to compile your program with JDK 7 and 8. However, it fails to run. is there any missing information or steps that need to be included. If possible, can you send me the whole test case as a zip file".
2014-11-17