changeset 16847:103f15c4564d

8158209: Editing in TableView breaks the layout, when the document is I18n Reviewed-by: serb, alexsch Contributed-by: Abossolo Foh Guy <guy.abossolo.foh@scientificware.com>
author serb
date Mon, 06 Mar 2017 22:52:45 +0300
parents 7bcfcb00a069
children ca56040ffe44
files src/java.desktop/share/classes/javax/swing/text/TableView.java test/javax/swing/text/TableView/I18nLayoutTest.java test/javax/swing/text/TableView/TableViewLayoutTest.java
diffstat 3 files changed, 531 insertions(+), 350 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/share/classes/javax/swing/text/TableView.java	Mon Mar 06 10:35:14 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/text/TableView.java	Mon Mar 06 22:52:45 2017 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -78,6 +78,7 @@
         super(elem, View.Y_AXIS);
         rows = new Vector<TableRow>();
         gridValid = false;
+        totalColumnRequirements = new SizeRequirements();
     }
 
     /**
@@ -377,6 +378,11 @@
         r.preferred = (int) pref;
         r.maximum = (int) max;
         r.alignment = 0;
+
+        totalColumnRequirements.minimum = r.minimum;
+        totalColumnRequirements.preferred = r.preferred;
+        totalColumnRequirements.maximum = r.maximum;
+
         return r;
     }
 
@@ -406,6 +412,13 @@
      * into consideration any constraining maximums.
      */
     void calculateColumnRequirements(int axis) {
+
+        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();
@@ -576,6 +589,9 @@
 
     int[] columnSpans;
     int[] columnOffsets;
+
+    SizeRequirements totalColumnRequirements;
+
     SizeRequirements[] columnRequirements;
     Vector<TableRow> rows;
     boolean gridValid;
@@ -646,6 +662,53 @@
             invalidateGrid();
         }
 
+        @Override
+        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;
+        }
+
+        @Override
+        public float getMinimumSpan(int axis) {
+            float value;
+
+            if (axis == View.X_AXIS) {
+                value = totalColumnRequirements.minimum + getLeftInset() + getRightInset();
+            } else {
+                value = super.getMinimumSpan(axis);
+            }
+            return value;
+        }
+
+        @Override
+        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;
+        }
+
+        @Override
+        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
--- a/test/javax/swing/text/TableView/I18nLayoutTest.java	Mon Mar 06 10:35:14 2017 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,349 +0,0 @@
-/*
- * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @key headful
- * @bug 8133864
- * @summary  Wrong display, when the document I18n properties is true.
- * @author Semyon Sadetsky
- * @run main I18nLayoutTest
- */
-
-import javax.swing.*;
-import javax.swing.text.*;
-import java.awt.*;
-import java.util.ArrayList;
-
-public class I18nLayoutTest extends JFrame {
-
-    private static int height;
-    JEditorPane edit = new JEditorPane();
-    private static I18nLayoutTest frame;
-
-    public I18nLayoutTest() {
-        super("Code example for a TableView bug");
-        setUndecorated(true);
-        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-        edit.setEditorKit(new CodeBugEditorKit());
-        initCodeBug();
-        this.getContentPane().add(new JScrollPane(edit));
-        this.pack();
-        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) throws Exception {
-        SwingUtilities.invokeAndWait(new Runnable() {
-            @Override
-            public void run() {
-                frame = new I18nLayoutTest();
-                frame.setVisible(true);
-            }
-        });
-        Robot robot = new Robot();
-        robot.delay(200);
-        robot.waitForIdle();
-        SwingUtilities.invokeAndWait(new Runnable() {
-            @Override
-            public void run() {
-                height = frame.getHeight();
-            }
-        });
-        SwingUtilities.invokeAndWait(new Runnable() {
-            @Override
-            public void run() {
-                frame.dispose();
-            }
-        });
-        if (height < 32) {
-            throw new RuntimeException(
-                    "TableView layout height is wrong " + height);
-        }
-        System.out.println("ok");
-    }
-}
-
-//------------------------------------------------------------------------------
-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);
-
-    }
-}
-//------------------------------------------------------------------------------
-
-//------------------------------------------------------------------------------
-class tableView extends TableView implements ViewFactory {
-
-    public tableView(Element elem) {
-        super(elem);
-    }
-
-    @Override
-    public void setParent(View parent) {
-        super.setParent(parent);
-    }
-
-    @Override
-    public void setSize(float width, float height) {
-        super.setSize(width, height);
-    }
-
-    @Override
-    public ViewFactory getViewFactory() {
-        return this;
-    }
-
-    @Override
-    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 float getPreferredSpan(int axis) {
-        if (axis == 0) return super.getPreferredSpan(0);
-        float preferredSpan = super.getPreferredSpan(axis);
-        return preferredSpan;
-    }
-
-    @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);
-        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 normal 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 {
-        @Override
-        public void setParent(View parent) {
-            super.setParent(parent);
-        }
-
-        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);
-            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);
-        }
-    }
-
-    ;
-}
-
-//------------------------------------------------------------------------------
-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() {
-        putProperty("i18n", Boolean.TRUE);
-    }
-
-
-    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);
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/text/TableView/TableViewLayoutTest.java	Mon Mar 06 22:52:45 2017 +0300
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @key headful
+ * @bug 8133864 8158209
+ * @summary  Wrong display, when the document I18n properties is true.
+ */
+import javax.swing.*;
+import javax.swing.text.*;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+
+public class TableViewLayoutTest extends JFrame {
+
+    private static double yCaret;
+    private static double xCaret;
+
+    // Number of iteration to verify the stability of the test with different robot delays :
+    // Work well with robot.delay(50) in hitKey method.
+    // But if the robot delay is too low, the test is not stable.
+    // Put this to 100, and reduce robot delay sometimes answers may be different.
+    private static int tn = 2;
+
+    // The four caret positions to test.
+    private static double yCarFLTab;
+    private static double yCarLLTab;
+    private static double xCarBTab;
+    private static double xCarETab;
+
+    // The caret coordonate differences along axis after the insertion and the removing cycle.
+    // 0 if the table layout is right.
+    private static double dyCarFLTab;
+    private static double dyCarLLTab;
+    private static double dxCarBTab;
+    private static double dxCarETab;
+
+    private static JEditorPane edit = new JEditorPane();
+    private static TableViewLayoutTest frame;
+
+    private static String Prop = "\n";
+    private static boolean isTabWrong = Boolean.FALSE;
+
+    private static Boolean isI18n = false;
+
+    public TableViewLayoutTest() {
+
+        super("Code example for a TableView bug");
+        setUndecorated(true);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        edit.setEditorKit(new CodeBugEditorKit());
+        initCodeBug();
+        this.getContentPane().add(new JScrollPane(edit));
+        this.pack();
+        this.setLocationRelativeTo(null);
+
+        edit.addCaretListener(new CaretListener() {
+            public void caretUpdate(CaretEvent e) {
+                JTextComponent textComp = (JTextComponent) e.getSource();
+                try {
+                    Rectangle rect = textComp.getUI().modelToView(textComp, e.getDot());
+                    yCaret = rect.getY();
+                    xCaret = rect.getX();
+                } catch (BadLocationException ex) {
+                    throw new RuntimeException("Failed to get pixel position of caret", ex);
+                }
+            }
+        });
+    }
+
+    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) throws Exception {
+
+        for (int i = 0; i < tn; i++) {
+            Robot rob = new Robot();
+
+            SwingUtilities.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    frame = new TableViewLayoutTest();
+                    frame.setVisible(true);
+                }
+            });
+
+            SwingUtilities.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    //Enable or disable i18n.
+                    isI18n = !isI18n;
+                    edit.getDocument().putProperty("i18n", isI18n);
+
+                    //Made a change to update table layout.
+                    //Without any change the table i18n property change is not take in account.
+                    edit.select(11, 12);
+                    edit.replaceSelection("1");
+
+                    //Catch the four caret positions to test before insertions.
+                    edit.setCaretPosition(6);
+                    xCarBTab = xCaret;
+                    edit.setCaretPosition(91);
+                    xCarETab = xCaret;
+
+                    edit.setCaretPosition(74);
+                    yCarLLTab = yCaret;
+                    edit.setCaretPosition(11);
+                    yCarFLTab = yCaret;
+                }
+            });
+
+            hitKey(rob, KeyEvent.VK_T);
+            hitKey(rob, KeyEvent.VK_E);
+            hitKey(rob, KeyEvent.VK_S);
+            hitKey(rob, KeyEvent.VK_T);
+            hitKey(rob, KeyEvent.VK_BACK_SPACE);
+            hitKey(rob, KeyEvent.VK_BACK_SPACE);
+            hitKey(rob, KeyEvent.VK_BACK_SPACE);
+            hitKey(rob, KeyEvent.VK_BACK_SPACE);
+
+            rob.waitForIdle();
+
+            SwingUtilities.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    //Calculate caret coordinate differences and catch caret positions after insertions.
+                    edit.setCaretPosition(6);
+                    dxCarBTab = Math.abs(xCarBTab - xCaret);
+                    edit.setCaretPosition(91);
+                    dxCarETab = Math.abs(xCarETab - xCaret);
+
+                    edit.setCaretPosition(74);
+                    dyCarLLTab = Math.abs(yCarLLTab - yCaret);
+                    edit.setCaretPosition(11);
+                    dyCarFLTab = Math.abs(yCarFLTab - yCaret);
+
+                    edit.setCaretPosition(74);
+                    yCarLLTab = yCaret;
+                    edit.setCaretPosition(11);
+                    yCarFLTab = yCaret;
+                }
+            });
+
+            Object dp = edit.getDocument().getProperty("i18n");
+            Boolean isI18n = dp instanceof Boolean ? (Boolean) dp : Boolean.FALSE;
+            String i18n = isI18n ? "\nWhen i18n enable, " : "\nWhen i18n disable, ";
+
+            if (Math.abs(yCarFLTab - yCarLLTab) < 10) {
+                isTabWrong = Boolean.TRUE;
+                Prop = Prop + i18n + "test can't be completed : TableView layout wrong, lines overlap, see JDK-8133864.";
+            } else {
+                if (dyCarFLTab != 0 || dyCarLLTab != 0) {
+                    isTabWrong = Boolean.TRUE;
+                    Prop = Prop + i18n + "TableView layout wrong : Table high change when inserts and removes caracters, bug never reported yet. First Line dy=" + dyCarFLTab + " Last Line dy=" + dyCarLLTab;
+                }
+                if (dxCarBTab != 0 || dxCarETab != 0) {
+                    isTabWrong = Boolean.TRUE;
+                    Prop = Prop + i18n + "TableView layout wrong : Table width change when inserts and removes caracters, see JDK-8158209 and JDK-7169915. Before Table dx=" + dxCarBTab + " After Table dx=" + dxCarETab;
+                }
+            }
+            rob.waitForIdle();
+
+            SwingUtilities.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    frame.dispose();
+                }
+            });
+        }
+        if (isTabWrong) {
+            throw new RuntimeException(Prop);
+        }
+
+        System.out.println("ok");
+    }
+
+    private static void hitKey(Robot robot, int k) throws Exception {
+        robot.delay(50);
+        robot.keyPress(k);
+        robot.keyRelease(k);
+        robot.delay(50);
+    }
+}
+
+//------------------------------------------------------------------------------
+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);
+
+    }
+}
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+class tableView extends TableView implements ViewFactory {
+
+    public tableView(Element elem) {
+        super(elem);
+    }
+
+    @Override
+    public void setParent(View parent) {
+        super.setParent(parent);
+    }
+
+    @Override
+    public void setSize(float width, float height) {
+        super.setSize(width, height);
+    }
+
+    @Override
+    public ViewFactory getViewFactory() {
+        return this;
+    }
+
+    @Override
+    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 float getPreferredSpan(int axis) {
+        if (axis == 0) {
+            return super.getPreferredSpan(0);
+        }
+        float preferredSpan = super.getPreferredSpan(axis);
+        return preferredSpan;
+    }
+
+    @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);
+        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 normal 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 {
+
+        @Override
+        public void setParent(View parent) {
+            super.setParent(parent);
+        }
+
+        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);
+            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);
+        }
+    };
+}
+
+//------------------------------------------------------------------------------
+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() {
+        //putProperty("i18n", Boolean.TRUE);
+    }
+
+    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);
+        }
+    }
+}