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

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

    Details

    • Subcomponent:
    • Introduced In Version:
      9
    • Resolved In Build:
      b23
    • CPU:
      x86
    • OS:
      other

      Description

      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).

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                psadhukhan Prasanta Sadhukhan
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: