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

Null pointer exception when adding more than 9 accelators to a JMenuBar

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P3
    • Resolution: Fixed
    • Affects Version/s: 7u9
    • Fix Version/s: 8
    • Component/s: client-libs
    • Labels:
    • Subcomponent:
    • Resolved In Build:
      b94
    • OS:
      windows
    • Verification:
      Verified

      Backports

        Description

        FULL PRODUCT VERSION :
        java version " 1.7.0_09 "
        Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
        Java HotSpot(TM) Client VM (build 23.5-b02, mixed mode, sharing)

        ADDITIONAL OS VERSION INFORMATION :
        Microsoft Windows [Version 6.1.7601]

        A DESCRIPTION OF THE PROBLEM :
        If one adds more than 10 key accelerators using the method getActionMap().put(), you will get NullPointerExceptions when the CTRL key is pressed on the keyboard.
        This is caused beause the KeyboardManager::fireKeyboardAction method has changed and a null keystroke is sent to the fireBinding function.

        At some point, the actions are searched using the ArrayTable::get but this class some optimization code that when you add more than ARRAY_BOUNDARY (=8), it uses a Hashtable to hold the values.

        When the Java code calls the method HashTable::get(null), a NullPointerException is thrown.

        REGRESSION. Last worked in version 7

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Create a JFrame with a JMenu and add 10 accelerators to the menu.
        Execute the application created.
        Press the CTRL key.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        CTRL key should NOT throw an exception.
        ACTUAL -
        NullPointerException is thrown

        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        Exception in thread " AWT-EventQueue-0 " java.lang.NullPointerException
        at java.util.Hashtable.hash(Hashtable.java:262)
        at java.util.Hashtable.get(Hashtable.java:459)
        at javax.swing.ArrayTable.get(ArrayTable.java:156)
        at javax.swing.InputMap.get(InputMap.java:120)
        at javax.swing.JComponent.processKeyBinding(JComponent.java:2876)
        at javax.swing.JMenuBar.processKeyBinding(JMenuBar.java:664)
        at ui.MyMenuBar.processKeyBinding(TestJMenu.java:100)
        at javax.swing.KeyboardManager.fireBinding(KeyboardManager.java:306)
        at javax.swing.KeyboardManager.fireKeyboardAction(KeyboardManager.java:289)
        at javax.swing.JComponent.processKeyBindingsForAllComponents(JComponent.java:2971)
        at javax.swing.SwingUtilities.processKeyBindings(SwingUtilities.java:1588)
        at javax.swing.UIManager$2.postProcessKeyEvent(UIManager.java:1476)
        at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:772)
        at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1027)
        at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:899)
        at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:727)
        at java.awt.Component.dispatchEventImpl(Component.java:4731)
        at java.awt.Container.dispatchEventImpl(Container.java:2287)
        at java.awt.Window.dispatchEventImpl(Window.java:2719)
        at java.awt.Component.dispatchEvent(Component.java:4687)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:723)
        at java.awt.EventQueue.access$200(EventQueue.java:103)
        at java.awt.EventQueue$3.run(EventQueue.java:682)
        at java.awt.EventQueue$3.run(EventQueue.java:680)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
        at java.awt.EventQueue$4.run(EventQueue.java:696)
        at java.awt.EventQueue$4.run(EventQueue.java:694)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:693)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)

        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        package ui;

        import java.awt.event.ActionEvent;
        import java.awt.event.KeyEvent;
        import java.beans.PropertyChangeListener;

        import javax.swing.Action;
        import javax.swing.InputMap;
        import javax.swing.JComponent;
        import javax.swing.JFrame;
        import javax.swing.JMenu;
        import javax.swing.JMenuBar;
        import javax.swing.JOptionPane;
        import javax.swing.KeyStroke;

        public class TestJMenu extends JFrame
        {
            public TestJMenu()
            {
                MyMenuBar mb = new MyMenuBar();
                JMenu menu = new JMenu( " Menu " );
                menu.add( " Item 1 " );
                mb.add(menu);
                setJMenuBar(mb);
                setSize(400,400);
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                InputMap im = mb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
                // We add exactly 10 actions because the ArrayTable is converted from a array to a hashtable when more than 8 values are added.
                for (int i=0;i<9;i++)
                {
                    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A+i, java.awt.event.InputEvent.CTRL_DOWN_MASK), " theAction " +i);
                    final int actId = i;
                    mb.getActionMap().put( " theAction " +i, new Action(){

                        @Override
                        public void actionPerformed(ActionEvent e)
                        {
                            JOptionPane.showMessageDialog(TestJMenu.this, " Action executed " + actId);
                        }

                        @Override
                        public Object getValue(String key)
                        {
                            return null;
                        }

                        @Override
                        public void putValue(String key, Object value)
                        {
                        }

                        @Override
                        public void setEnabled(boolean b)
                        {
                        }

                        @Override
                        public boolean isEnabled()
                        {
                            return true;
                        }

                        @Override
                        public void addPropertyChangeListener(
                            PropertyChangeListener listener)
                        {
                        }

                        @Override
                        public void removePropertyChangeListener(
                            PropertyChangeListener listener)
                        {
                        }});
                }
            }

            /**
             * @param args
             */
            public static void main(String[] args)
            {
                new TestJMenu().setVisible(true);
            }
        }

        class MyMenuBar extends JMenuBar
        {
            @Override
            protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
                int condition, boolean pressed)
            {
                if (ks==null)
                {
                    System.out.println( " Null KS is sent " );
                    // Remove comment below to fix the issue.
                    // return false;
                }

                return super.processKeyBinding(ks, e, condition, pressed);
            }
        }

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

        CUSTOMER SUBMITTED WORKAROUND :
        Inherit from JMenuBar and ignore the null key:

        class MyMenuBar extends JMenuBar
        {
            @Override
            protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
                int condition, boolean pressed)
            {
                if (ks==null)
                {
                        return false;
                }

                return super.processKeyBinding(ks, e, condition, pressed);
            }
        }

        SUPPORT :
        YES

          Attachments

            Issue Links

              Activity

                People

                Assignee:
                malenkov Sergey Malenkov (Inactive)
                Reporter:
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                  Dates

                  Created:
                  Updated:
                  Resolved: