Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8182577

Exception when Tab key moves focus to a JCheckbox with a custom ButtonModel

XMLWordPrintable

    • 9
    • b23
    • x86
    • generic

        FULL PRODUCT VERSION :
        java version "9-ea"
        Java(TM) SE Runtime Environment (build 9-ea+174)
        Java HotSpot(TM) 64-Bit Server VM (build 9-ea+174, mixed mode)

        ADDITIONAL OS VERSION INFORMATION :
        Microsoft Windows [Version 10.0.10586]

        A DESCRIPTION OF THE PROBLEM :
        We use a customised "tri-state" checkbox. In JDK-9 a crash occurs (in LayoutFocusTraversalPolicy) when the keyboard focus moves to it by pressing the TAB key. The crash appears to have been introduced with additional logic added in JDK-9. I think the cast should be guarded with an instanceof check to match what is possible via the API of JToggleButton (and derivatives).

        The relevant code in LayoutFocusTraversalPolicy.accept is:

            } else if (aComponent instanceof JComponent) {
                if (SunToolkit.isInstanceOf(aComponent,
        "javax.swing.JToggleButton")) {
                    JToggleButton.ToggleButtonModel model =
                            (JToggleButton.ToggleButtonModel) ((JToggleButton)
                                    aComponent).getModel(); // <- Line 243
                    if (model != null) {
                        ButtonGroup group = model.getGroup(); // <- Only use of 'model'

        The Tri-state check button code was taken from an old online article:

        http://www.javaspecialists.eu/archive/Issue082.html

        It's model derives directly from ButtonModel, (then delegates all method calls to a real ToggleButtonModel).

        The solution I imagine is to perform an instanceof check for either DefaultButtonModel or ToggleButtonModel (depending on the desired behaviour). Note that in JToggeleButton.getGroupSelection we see similar code (also added in JDK-9?) using a check-and-cast to DefaultButtonModel.

        Also note that while JToggleButton creates a ToggleButtonModel by default, it does not enforce it via setModel().



        REGRESSION. Last worked in version 8u131

        ADDITIONAL REGRESSION INFORMATION:
        Seems related to focus logic changes with button-groups introduced in JDK-9. For example, see 8074883

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Execute the given code, press TAB twice and the crash should be seen


        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: java.desktop/javax.swing.DefaultButtonModel cannot be cast to java.desktop/javax.swing.JToggleButton$ToggleButtonModel
        at java.desktop/javax.swing.LayoutFocusTraversalPolicy.accept(LayoutFocusTraversalPolicy.java:243)
        at java.desktop/javax.swing.SortingFocusTraversalPolicy.getComponentAfter(SortingFocusTraversalPolicy.java:332)
        at java.desktop/javax.swing.LayoutFocusTraversalPolicy.getComponentAfter(LayoutFocusTraversalPolicy.java:107)
        at java.desktop/java.awt.Component.getNextFocusCandidate(Component.java:8155)
        at java.desktop/java.awt.Component.transferFocus(Component.java:8122)
        at java.desktop/java.awt.Component.nextFocus(Component.java:8115)
        at java.desktop/java.awt.Component.transferFocus(Component.java:8106)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.focusNextComponent(DefaultKeyboardFocusManager.java:1403)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.processKeyEvent(DefaultKeyboardFocusManager.java:1167)
        at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4877)
        at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2317)
        at java.desktop/java.awt.Component.dispatchEvent(Component.java:4793)
        at java.desktop/java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1950)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:827)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1096)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:966)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:792)
        at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4842)
        at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2317)
        at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2758)
        at java.desktop/java.awt.Component.dispatchEvent(Component.java:4793)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:761)
        at java.desktop/java.awt.EventQueue.access$500(EventQueue.java:97)
        at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:712)
        at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:706)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:89)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:99)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:734)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:732)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:89)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:731)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:199)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)
        at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:190)
        at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:235)
        at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:233)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:233)
        at java.desktop/java.awt.Dialog.show(Dialog.java:1070)
        at java.desktop/javax.swing.JOptionPane.showOptionDialog(JOptionPane.java:876)
        at java.desktop/javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:672)
        at java.desktop/javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:643)
        at java.desktop/javax.swing.JOptionPane.showMessageDialog(JOptionPane.java:614)
        at snippet.Snippet.go(Snippet.java:23)
        at snippet.Snippet.lambda$0(Snippet.java:8)
        at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:759)
        at java.desktop/java.awt.EventQueue.access$500(EventQueue.java:97)
        at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:712)
        at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:706)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:89)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:729)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:199)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)


        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        import java.awt.*;
        import javax.swing.*;

        public class Snippet {
            public static void main(String[] args) {
                SwingUtilities.invokeLater(() -> new Snippet().go());
            }

            private void go() {

                // Non-functional model for brevity. The original, implementing
                // interface ButtonModel directly, can be found at:
                // http://www.javaspecialists.eu/archive/Issue082.html
                ButtonModel model = new DefaultButtonModel();

                JCheckBox check = new JCheckBox("a bit broken");
                check.setModel(model);
                JPanel panel = new JPanel(new BorderLayout());
                panel.add(new JTextField("Press Tab (twice?)"), BorderLayout.NORTH);
                panel.add(check);
                JOptionPane.showMessageDialog(null, panel);
                System.exit(0);
            }
        }


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

        CUSTOMER SUBMITTED WORKAROUND :
        Users could click with the mouse instead of navigating via the keyboard.

        Programmers can move to a different tri-state checkbox implementation (from Jide, for example).

          1. Snippet.java
            0.8 kB
            Pardeep Sharma

              psadhukhan Prasanta Sadhukhan
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              9 Start watching this issue

                Created:
                Updated:
                Resolved: