Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JRE bug? JTable contains certain string -> ArrayIndexOutOfBoundsException

A user reported that a certain YouTube video causes our program to fail. Upon investigation it seems that adding a String containing the title of that video to a JTable causes an ArrayIndexOutOfBoundsException somewhere in the rendering code.

We have tested and confirmed this on both Java 7u45 and Java 8 (b121) on Windows XP, 7 and 8, and Ubuntu.

Here is a test program demonstrating the issue:

import javax.swing.*;
import java.awt.*;

public class TestJTableAIOOBE {
    private static final String TEXT = "\u0D38\u0D4D\u0D31\u0D4D\u0D31\u0D40\u0D32\u0D4D\u200D \u0D15\u0D31\u0D3F\u0D15\u0D4D\u0D15\u0D24\u0D4D\u0D24\u0D3F\u0D2F\u0D3F\u0D32\u0D4D\u200D \u0D2F\u0D47\u0D36\u0D41\u0D15\u0D4D\u0D30\u0D3F\u0D38\u0D4D\u0D24\u0D41\u0D35\u0D3F\u0D28\u0D4D\u0D31\u0D46 \u0D30\u0D42\u0D2A\u0D02 \u0D2A\u0D4D\u0D30\u0D24\u0D4D\u0D2F\u0D15\u0D4D\u0D37\u0D2A\u0D4D\u0D2A\u0D46\u0D1F\u0D4D\u0D1F\u0D41";

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                Font font = findFont();
                if (font == null) {
                    System.out.println("No suitable font found");
                    return;
                }
                System.out.println("Using font: " + font);
                JTable table = new JTable(new Object[][]{{TEXT}}, new Object[]{"title"});
                table.setFont(font);
                JFrame frame = new JFrame();
                frame.add(table);
                frame.pack();
                frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }

    private static Font findFont() {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        for (Font font : ge.getAllFonts()) {
            if (font.canDisplayUpTo(TEXT) == -1) {
                return font.deriveFont(0, 12);
            }
        }
        return null;
    }
}

This stack trace is printed to stderr every time Swing attempts to render the text:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 48
    at sun.font.ExtendedTextSourceLabel.createCharinfo(ExtendedTextSourceLabel.java:814)
    at sun.font.ExtendedTextSourceLabel.getCharinfo(ExtendedTextSourceLabel.java:548)
    at sun.font.ExtendedTextSourceLabel.getLineBreakIndex(ExtendedTextSourceLabel.java:480)
    at java.awt.font.TextMeasurer.calcLineBreak(TextMeasurer.java:330)
    at java.awt.font.TextMeasurer.getLineBreakIndex(TextMeasurer.java:566)
    at java.awt.font.LineBreakMeasurer.nextOffset(LineBreakMeasurer.java:359)
    at java.awt.font.LineBreakMeasurer.nextOffset(LineBreakMeasurer.java:328)
    at sun.swing.SwingUtilities2.clipString(SwingUtilities2.java:472)
    at javax.swing.SwingUtilities.layoutCompoundLabelImpl(SwingUtilities.java:1023)
    at javax.swing.SwingUtilities.layoutCompoundLabel(SwingUtilities.java:892)
    at javax.swing.plaf.basic.BasicLabelUI.layoutCL(BasicLabelUI.java:94)
    at javax.swing.plaf.basic.BasicLabelUI.layout(BasicLabelUI.java:201)
    at javax.swing.plaf.basic.BasicLabelUI.paint(BasicLabelUI.java:164)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:777)
    at javax.swing.JComponent.paint(JComponent.java:1053)
    at javax.swing.CellRendererPane.paintComponent(CellRendererPane.java:151)
    at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2115)
    at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2016)
    at javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1812)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:777)
    at javax.swing.JComponent.paint(JComponent.java:1053)
    at javax.swing.JComponent.paintChildren(JComponent.java:886)
    at javax.swing.JComponent.paint(JComponent.java:1062)
    at javax.swing.JComponent.paintChildren(JComponent.java:886)
    at javax.swing.JComponent.paint(JComponent.java:1062)
    at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
    at javax.swing.JComponent.paintChildren(JComponent.java:886)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5224)
    at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1532)
    at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1455)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1252)
    at javax.swing.JComponent.paint(JComponent.java:1039)
    at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
    at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
    at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
    at java.awt.Container.paint(Container.java:1973)
    at java.awt.Window.paint(Window.java:3901)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:822)
    at javax.swing.RepaintManager$3.run(RepaintManager.java:794)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:794)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:769)
    at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:718)
    at javax.swing.RepaintManager.access$1100(RepaintManager.java:62)
    at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1680)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:744)
    at java.awt.EventQueue.access$400(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:697)
    at java.awt.EventQueue$3.run(EventQueue.java:691)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:714)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Can you reproduce this? What peculiarity in the string actually causes this? Is it really a JRE bug? Should I report it to Oracle?

like image 888
ntoskrnl Avatar asked Oct 21 '22 17:10

ntoskrnl


2 Answers

The bug was reported to Oracle in January 2014, and three months later it appeared in the public bug tracker as JDK-8041480. The bug was fixed for Java 9 in September 2016.

like image 149
ntoskrnl Avatar answered Oct 23 '22 11:10

ntoskrnl


We have tested and confirmed this on both Java 7u45 and Java 8 (b121) on Windows XP, 7 and 8, and Ubuntu.

and

JRE bug?

  • not just to remove, replace \u200D - ZERO WIDTH JOINER (hint 2times in String)

EDIT this is is Font issue (used) for JTable

.

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class Test {

    private String TEXT = "Jesus appear in Knife \u0D38\u0D4D\u0D31\u0D4D\u0D31\u0D40"
            + "\u0D32\u0D4D\u0D4D\u0D15\u0D31\u0D3F\u0D15\u0D4D\u0D15\u0D24\u0D4D\u0D24\u0D3F\u0D2F"
            + "\u0D3F\u0D32\u0D4D\u0D15\u0D2F\u0D47\u0D36\u0D41\u0D15\u0D4D\u0D30\u0D3F\u0D38\u0D4D"
            + "\u0D24\u0D41\u0D35\u0D3F\u0D28\u0D4D\u0D31\u0D46 \u0D30\u0D42\u0D2A\u0D02\u0D2A\u0D4D"
            + "\u0D30\u0D24\u0D4D\u0D2F\u0D15\u0D4D\u0D37\u0D2A\u0D4D\u0D2A\u0D46\u0D1F\u0D4D\u0D1F\u0D41";
    private String _TEXT = "Jesus appear in Knife \u0D38\u0D4D\u0D31\u0D4D\u0D31\u0D40\u0D32\u0D4D\u200D "
            + "\u0D15\u0D31\u0D3F\u0D15\u0D4D\u0D15\u0D24\u0D4D\u0D24\u0D3F\u0D2F\u0D3F\u0D32\u0D4D"
            + "\u200D \u0D2F\u0D47\u0D36\u0D41\u0D15\u0D4D\u0D30\u0D3F\u0D38\u0D4D\u0D24\u0D41\u0D35"
            + "\u0D3F\u0D28\u0D4D\u0D31\u0D46 \u0D30\u0D42\u0D2A\u0D02 \u0D2A\u0D4D\u0D30\u0D24\u0D4D"
            + "\u0D2F\u0D15\u0D4D\u0D37\u0D2A\u0D4D\u0D2A\u0D46\u0D1F\u0D4D\u0D1F\u0D41";
    private JTable table;
    DefaultTableModel model = new DefaultTableModel(
            new Object[][]{{_TEXT},},
            new String[]{"Col 1",});

    public Test() {
        table.setFont(new java.awt.Font("Tahoma", 1, 21));
        table = new JTable(model);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(table);
        frame.pack();
        frame.setVisible(true);
    }
    //http://stackoverflow.com/questions/21062484/jre-bug-jtable-contains-certain-string-arrayindexoutofboundsexception

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test();
            }
        });
    }
}

with generated exception

run:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at Bugs.Test.<init>(Test.java:28)
    at Bugs.Test$1.run(Test.java:42)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:694)
    at java.awt.EventQueue$3.run(EventQueue.java:692)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
BUILD SUCCESSFUL (total time: 1 second)

EDIT_2 other JComponents (reduced part of, pretty to ignore this chars sequence, but maybe I'm not used proper Font), but Renderers concept is used in this code example

enter image description here

import java.awt.*;
import java.awt.event.*;
import java.util.Locale;
import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.table.DefaultTableModel;

public class SystemFontDisplayer extends JFrame {

    private static final long serialVersionUID = 1L;
    private JFrame frame = new JFrame("Nimbus UIDeafaults and Font");
    private JComboBox fontsBox;
    private javax.swing.Timer timer = null;
    private String buggyConstalation = "Jesus appear in Knife \u0D38\u0D4D"
            + "\u0D31\u0D4D\u0D31\u0D40\u0D32\u0D4D\u200D\u0D15\u0D31\u0D3F"
            + "\u0D15\u0D4D\u0D15\u0D24\u0D4D\u0D24\u0D3F\u0D2F\u0D3F\u0D32"
            + "\u0D4D\u200D \u0D2F\u0D47\u0D36\u0D41\u0D15\u0D4D\u0D30\u0D3F"
            + "\u0D38\u0D4D\u0D24\u0D41\u0D35\u0D3F\u0D28\u0D4D\u0D31\u0D46"
            + "\u0D30\u0D42\u0D2A\u0D02 \u0D2A\u0D4D\u0D30\u0D24\u0D4D\u0D2F"
            + "\u0D15\u0D4D\u0D37\u0D2A\u0D4D\u0D2A\u0D46\u0D1F\u0D4D\u0D1F\u0D41";
    private JButton testButton = new JButton(buggyConstalation);
    private JTextField testTextField = new JTextField(buggyConstalation);
    private JLabel testLabel = new JLabel(buggyConstalation);//caused by \u200D
    private JTable table;
    private DefaultTableModel model;

    public SystemFontDisplayer() {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] fontFamilyNames = ge.getAvailableFontFamilyNames(Locale.getDefault());
        fontsBox = new JComboBox(fontFamilyNames);
        fontsBox.setSelectedItem("SansSerif");
        fontsBox.setRenderer(new ComboRenderer());
        fontsBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    final String fontName = fontsBox.getSelectedItem().toString();
                    fontsBox.setFont(new Font(fontName, Font.PLAIN, 16));
                    start();
                }
            }
        });
        fontsBox.setSelectedItem(0);
        fontsBox.getEditor().selectAll();
        model = new DefaultTableModel(
                new Object[][]{{buggyConstalation,buggyConstalation},}, 
                new String[]{"Col 1","Col 2",});
        table = new JTable(model);        
        frame.setLayout(new GridLayout(5, 0, 20, 20));
        frame.add(fontsBox);
        frame.add(testButton);
        frame.add(testTextField);
        frame.add(testLabel);
        frame.add(table);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocation(200, 105);
        frame.pack();
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                fontsBox.setPopupVisible(true);
                fontsBox.setPopupVisible(false);
            }
        });
        frame.setVisible(true);
    }

    private void start() {
        timer = new javax.swing.Timer(750, updateCol());
        timer.setRepeats(false);
        timer.start();
    }

    public Action updateCol() {
        return new AbstractAction("text load action") {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                final Font fnt = new Font(fontsBox.getSelectedItem().toString(), Font.PLAIN, 12);
                final FontUIResource res = new FontUIResource(fnt);
                UIManager.getLookAndFeelDefaults().put("Button.font", res);
                UIManager.getLookAndFeelDefaults().put("TextField.font", res);
                UIManager.getLookAndFeelDefaults().put("Label.font", res);
                UIManager.getLookAndFeelDefaults().put("Table.font", res);
                SwingUtilities.updateComponentTreeUI(frame);
            }
        };
    }

    public static void main(String arg[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                SystemFontDisplayer systemFontDisplayer = new SystemFontDisplayer();
            }
        });
    }

    private class ComboRenderer extends BasicComboBoxRenderer {

        private static final long serialVersionUID = 1L;

        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            final Object fntObj = value;
            final String fontFamilyName = (String) fntObj;
            setFont(new Font(fontFamilyName, Font.PLAIN, 16));
            return this;
        }
    }
}
like image 45
mKorbel Avatar answered Oct 23 '22 10:10

mKorbel