1 /*
   2  * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.awt;
  26 
  27 import java.awt.peer.MenuItemPeer;
  28 import java.awt.event.*;
  29 import java.util.EventListener;
  30 import java.io.ObjectOutputStream;
  31 import java.io.ObjectInputStream;
  32 import java.io.IOException;
  33 import javax.accessibility.*;
  34 import sun.awt.AWTAccessor;
  35 
  36 /**
  37  * All items in a menu must belong to the class
  38  * <code>MenuItem</code>, or one of its subclasses.
  39  * <p>
  40  * The default <code>MenuItem</code> object embodies
  41  * a simple labeled menu item.
  42  * <p>
  43  * This picture of a menu bar shows five menu items:
  44  * <IMG SRC="doc-files/MenuBar-1.gif" alt="The following text describes this graphic."
  45  * style="float:center; margin: 7px 10px;">
  46  * <br style="clear:left;">
  47  * The first two items are simple menu items, labeled
  48  * <code>"Basic"</code> and <code>"Simple"</code>.
  49  * Following these two items is a separator, which is itself
  50  * a menu item, created with the label <code>"-"</code>.
  51  * Next is an instance of <code>CheckboxMenuItem</code>
  52  * labeled <code>"Check"</code>. The final menu item is a
  53  * submenu labeled <code>"More&nbsp;Examples"</code>,
  54  * and this submenu is an instance of <code>Menu</code>.
  55  * <p>
  56  * When a menu item is selected, AWT sends an action event to
  57  * the menu item. Since the event is an
  58  * instance of <code>ActionEvent</code>, the <code>processEvent</code>
  59  * method examines the event and passes it along to
  60  * <code>processActionEvent</code>. The latter method redirects the
  61  * event to any <code>ActionListener</code> objects that have
  62  * registered an interest in action events generated by this
  63  * menu item.
  64  * <P>
  65  * Note that the subclass <code>Menu</code> overrides this behavior and
  66  * does not send any event to the frame until one of its subitems is
  67  * selected.
  68  *
  69  * @author Sami Shaio
  70  */
  71 public class MenuItem extends MenuComponent implements Accessible {
  72 
  73     static {
  74         /* ensure that the necessary native libraries are loaded */
  75         Toolkit.loadLibraries();
  76         if (!GraphicsEnvironment.isHeadless()) {
  77             initIDs();
  78         }
  79 
  80         AWTAccessor.setMenuItemAccessor(
  81             new AWTAccessor.MenuItemAccessor() {
  82                 public boolean isEnabled(MenuItem item) {
  83                     return item.enabled;
  84                 }
  85 
  86                 public String getLabel(MenuItem item) {
  87                     return item.label;
  88                 }
  89 
  90                 public MenuShortcut getShortcut(MenuItem item) {
  91                     return item.shortcut;
  92                 }
  93 
  94                 public String getActionCommandImpl(MenuItem item) {
  95                     return item.getActionCommandImpl();
  96                 }
  97 
  98                 public boolean isItemEnabled(MenuItem item) {
  99                     return item.isItemEnabled();
 100                 }
 101             });
 102     }
 103 
 104     /**
 105      * A value to indicate whether a menu item is enabled
 106      * or not.  If it is enabled, <code>enabled</code> will
 107      * be set to true.  Else <code>enabled</code> will
 108      * be set to false.
 109      *
 110      * @serial
 111      * @see #isEnabled()
 112      * @see #setEnabled(boolean)
 113      */
 114     boolean enabled = true;
 115 
 116     /**
 117      * <code>label</code> is the label of a menu item.
 118      * It can be any string.
 119      *
 120      * @serial
 121      * @see #getLabel()
 122      * @see #setLabel(String)
 123      */
 124     String label;
 125 
 126     /**
 127      * This field indicates the command tha has been issued
 128      * by a  particular menu item.
 129      * By default the <code>actionCommand</code>
 130      * is the label of the menu item, unless it has been
 131      * set using setActionCommand.
 132      *
 133      * @serial
 134      * @see #setActionCommand(String)
 135      * @see #getActionCommand()
 136      */
 137     String actionCommand;
 138 
 139     /**
 140      * The eventMask is ONLY set by subclasses via enableEvents.
 141      * The mask should NOT be set when listeners are registered
 142      * so that we can distinguish the difference between when
 143      * listeners request events and subclasses request them.
 144      *
 145      * @serial
 146      */
 147     long eventMask;
 148 
 149     transient ActionListener actionListener;
 150 
 151     /**
 152      * A sequence of key stokes that ia associated with
 153      * a menu item.
 154      * Note :in 1.1.2 you must use setActionCommand()
 155      * on a menu item in order for its shortcut to
 156      * work.
 157      *
 158      * @serial
 159      * @see #getShortcut()
 160      * @see #setShortcut(MenuShortcut)
 161      * @see #deleteShortcut()
 162      */
 163     private MenuShortcut shortcut = null;
 164 
 165     private static final String base = "menuitem";
 166     private static int nameCounter = 0;
 167 
 168     /*
 169      * JDK 1.1 serialVersionUID
 170      */
 171     private static final long serialVersionUID = -21757335363267194L;
 172 
 173     /**
 174      * Constructs a new MenuItem with an empty label and no keyboard
 175      * shortcut.
 176      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 177      * returns true.
 178      * @see java.awt.GraphicsEnvironment#isHeadless
 179      * @since    1.1
 180      */
 181     public MenuItem() throws HeadlessException {
 182         this("", null);
 183     }
 184 
 185     /**
 186      * Constructs a new MenuItem with the specified label
 187      * and no keyboard shortcut. Note that use of "-" in
 188      * a label is reserved to indicate a separator between
 189      * menu items. By default, all menu items except for
 190      * separators are enabled.
 191      * @param       label the label for this menu item.
 192      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 193      * returns true.
 194      * @see java.awt.GraphicsEnvironment#isHeadless
 195      * @since       1.0
 196      */
 197     public MenuItem(String label) throws HeadlessException {
 198         this(label, null);
 199     }
 200 
 201     /**
 202      * Create a menu item with an associated keyboard shortcut.
 203      * Note that use of "-" in a label is reserved to indicate
 204      * a separator between menu items. By default, all menu
 205      * items except for separators are enabled.
 206      * @param       label the label for this menu item.
 207      * @param       s the instance of <code>MenuShortcut</code>
 208      *                       associated with this menu item.
 209      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 210      * returns true.
 211      * @see java.awt.GraphicsEnvironment#isHeadless
 212      * @since       1.1
 213      */
 214     public MenuItem(String label, MenuShortcut s) throws HeadlessException {
 215         this.label = label;
 216         this.shortcut = s;
 217     }
 218 
 219     /**
 220      * Construct a name for this MenuComponent.  Called by getName() when
 221      * the name is null.
 222      */
 223     String constructComponentName() {
 224         synchronized (MenuItem.class) {
 225             return base + nameCounter++;
 226         }
 227     }
 228 
 229     /**
 230      * Creates the menu item's peer.  The peer allows us to modify the
 231      * appearance of the menu item without changing its functionality.
 232      */
 233     public void addNotify() {
 234         synchronized (getTreeLock()) {
 235             if (peer == null)
 236                 peer = getComponentFactory().createMenuItem(this);
 237         }
 238     }
 239 
 240     /**
 241      * Gets the label for this menu item.
 242      * @return  the label of this menu item, or <code>null</code>
 243                        if this menu item has no label.
 244      * @see     java.awt.MenuItem#setLabel
 245      * @since   1.0
 246      */
 247     public String getLabel() {
 248         return label;
 249     }
 250 
 251     /**
 252      * Sets the label for this menu item to the specified label.
 253      * @param     label   the new label, or <code>null</code> for no label.
 254      * @see       java.awt.MenuItem#getLabel
 255      * @since     1.0
 256      */
 257     public synchronized void setLabel(String label) {
 258         this.label = label;
 259         MenuItemPeer peer = (MenuItemPeer)this.peer;
 260         if (peer != null) {
 261             peer.setLabel(label);
 262         }
 263     }
 264 
 265     /**
 266      * Checks whether this menu item is enabled.
 267      *
 268      * @return {@code true} if the item is enabled;
 269      *         otherwise {@code false}
 270      * @see        java.awt.MenuItem#setEnabled
 271      * @since      1.0
 272      */
 273     public boolean isEnabled() {
 274         return enabled;
 275     }
 276 
 277     /**
 278      * Sets whether or not this menu item can be chosen.
 279      * @param      b  if <code>true</code>, enables this menu item;
 280      *                       if <code>false</code>, disables it.
 281      * @see        java.awt.MenuItem#isEnabled
 282      * @since      1.1
 283      */
 284     public synchronized void setEnabled(boolean b) {
 285         enable(b);
 286     }
 287 
 288     /**
 289      * @deprecated As of JDK version 1.1,
 290      * replaced by <code>setEnabled(boolean)</code>.
 291      */
 292     @Deprecated
 293     public synchronized void enable() {
 294         enabled = true;
 295         MenuItemPeer peer = (MenuItemPeer)this.peer;
 296         if (peer != null) {
 297             peer.setEnabled(true);
 298         }
 299     }
 300 
 301     /**
 302      * Sets whether or not this menu item can be chosen.
 303      *
 304      * @param  b if {@code true}, enables this menu item;
 305      *           otherwise disables
 306      * @deprecated As of JDK version 1.1,
 307      * replaced by <code>setEnabled(boolean)</code>.
 308      */
 309     @Deprecated
 310     public void enable(boolean b) {
 311         if (b) {
 312             enable();
 313         } else {
 314             disable();
 315         }
 316     }
 317 
 318     /**
 319      * @deprecated As of JDK version 1.1,
 320      * replaced by <code>setEnabled(boolean)</code>.
 321      */
 322     @Deprecated
 323     public synchronized void disable() {
 324         enabled = false;
 325         MenuItemPeer peer = (MenuItemPeer)this.peer;
 326         if (peer != null) {
 327             peer.setEnabled(false);
 328         }
 329     }
 330 
 331     /**
 332      * Get the <code>MenuShortcut</code> object associated with this
 333      * menu item,
 334      * @return      the menu shortcut associated with this menu item,
 335      *                   or <code>null</code> if none has been specified.
 336      * @see         java.awt.MenuItem#setShortcut
 337      * @since       1.1
 338      */
 339     public MenuShortcut getShortcut() {
 340         return shortcut;
 341     }
 342 
 343     /**
 344      * Set the <code>MenuShortcut</code> object associated with this
 345      * menu item. If a menu shortcut is already associated with
 346      * this menu item, it is replaced.
 347      * @param       s  the menu shortcut to associate
 348      *                           with this menu item.
 349      * @see         java.awt.MenuItem#getShortcut
 350      * @since       1.1
 351      */
 352     public void setShortcut(MenuShortcut s) {
 353         shortcut = s;
 354         MenuItemPeer peer = (MenuItemPeer)this.peer;
 355         if (peer != null) {
 356             peer.setLabel(label);
 357         }
 358     }
 359 
 360     /**
 361      * Delete any <code>MenuShortcut</code> object associated
 362      * with this menu item.
 363      * @since      1.1
 364      */
 365     public void deleteShortcut() {
 366         shortcut = null;
 367         MenuItemPeer peer = (MenuItemPeer)this.peer;
 368         if (peer != null) {
 369             peer.setLabel(label);
 370         }
 371     }
 372 
 373     /*
 374      * Delete a matching MenuShortcut associated with this MenuItem.
 375      * Used when iterating Menus.
 376      */
 377     void deleteShortcut(MenuShortcut s) {
 378         if (s.equals(shortcut)) {
 379             shortcut = null;
 380             MenuItemPeer peer = (MenuItemPeer)this.peer;
 381             if (peer != null) {
 382                 peer.setLabel(label);
 383             }
 384         }
 385     }
 386 
 387     /*
 388      * The main goal of this method is to post an appropriate event
 389      * to the event queue when menu shortcut is pressed. However,
 390      * in subclasses this method may do more than just posting
 391      * an event.
 392      */
 393     void doMenuEvent(long when, int modifiers) {
 394         Toolkit.getEventQueue().postEvent(
 395             new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
 396                             getActionCommand(), when, modifiers));
 397     }
 398 
 399     /*
 400      * Returns true if the item and all its ancestors are
 401      * enabled, false otherwise
 402      */
 403     private final boolean isItemEnabled() {
 404         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 405         // should be disabled when the menu itself is disabled
 406         if (!isEnabled()) {
 407             return false;
 408         }
 409         MenuContainer container = getParent_NoClientCode();
 410         do {
 411             if (!(container instanceof Menu)) {
 412                 return true;
 413             }
 414             Menu menu = (Menu)container;
 415             if (!menu.isEnabled()) {
 416                 return false;
 417             }
 418             container = menu.getParent_NoClientCode();
 419         } while (container != null);
 420         return true;
 421     }
 422 
 423     /*
 424      * Post an ActionEvent to the target (on
 425      * keydown) and the item is enabled.
 426      * Returns true if there is an associated shortcut.
 427      */
 428     boolean handleShortcut(KeyEvent e) {
 429         MenuShortcut s = new MenuShortcut(e.getKeyCode(),
 430                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 431         MenuShortcut sE = new MenuShortcut(e.getExtendedKeyCode(),
 432                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 433         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 434         // should be disabled when the menu itself is disabled
 435         if ((s.equals(shortcut) || sE.equals(shortcut)) && isItemEnabled()) {
 436             // MenuShortcut match -- issue an event on keydown.
 437             if (e.getID() == KeyEvent.KEY_PRESSED) {
 438                 doMenuEvent(e.getWhen(), e.getModifiers());
 439             } else {
 440                 // silently eat key release.
 441             }
 442             return true;
 443         }
 444         return false;
 445     }
 446 
 447     MenuItem getShortcutMenuItem(MenuShortcut s) {
 448         return (s.equals(shortcut)) ? this : null;
 449     }
 450 
 451     /**
 452      * Enables event delivery to this menu item for events
 453      * to be defined by the specified event mask parameter
 454      * <p>
 455      * Since event types are automatically enabled when a listener for
 456      * that type is added to the menu item, this method only needs
 457      * to be invoked by subclasses of <code>MenuItem</code> which desire to
 458      * have the specified event types delivered to <code>processEvent</code>
 459      * regardless of whether a listener is registered.
 460      *
 461      * @param       eventsToEnable the event mask defining the event types
 462      * @see         java.awt.MenuItem#processEvent
 463      * @see         java.awt.MenuItem#disableEvents
 464      * @see         java.awt.Component#enableEvents
 465      * @since       1.1
 466      */
 467     protected final void enableEvents(long eventsToEnable) {
 468         eventMask |= eventsToEnable;
 469         newEventsOnly = true;
 470     }
 471 
 472     /**
 473      * Disables event delivery to this menu item for events
 474      * defined by the specified event mask parameter.
 475      *
 476      * @param       eventsToDisable the event mask defining the event types
 477      * @see         java.awt.MenuItem#processEvent
 478      * @see         java.awt.MenuItem#enableEvents
 479      * @see         java.awt.Component#disableEvents
 480      * @since       1.1
 481      */
 482     protected final void disableEvents(long eventsToDisable) {
 483         eventMask &= ~eventsToDisable;
 484     }
 485 
 486     /**
 487      * Sets the command name of the action event that is fired
 488      * by this menu item.
 489      * <p>
 490      * By default, the action command is set to the label of
 491      * the menu item.
 492      * @param       command   the action command to be set
 493      *                                for this menu item.
 494      * @see         java.awt.MenuItem#getActionCommand
 495      * @since       1.1
 496      */
 497     public void setActionCommand(String command) {
 498         actionCommand = command;
 499     }
 500 
 501     /**
 502      * Gets the command name of the action event that is fired
 503      * by this menu item.
 504      *
 505      * @return the action command name
 506      * @see java.awt.MenuItem#setActionCommand
 507      * @since 1.1
 508      */
 509     public String getActionCommand() {
 510         return getActionCommandImpl();
 511     }
 512 
 513     // This is final so it can be called on the Toolkit thread.
 514     final String getActionCommandImpl() {
 515         return (actionCommand == null? label : actionCommand);
 516     }
 517 
 518     /**
 519      * Adds the specified action listener to receive action events
 520      * from this menu item.
 521      * If l is null, no exception is thrown and no action is performed.
 522      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 523      * >AWT Threading Issues</a> for details on AWT's threading model.
 524      *
 525      * @param      l the action listener.
 526      * @see        #removeActionListener
 527      * @see        #getActionListeners
 528      * @see        java.awt.event.ActionEvent
 529      * @see        java.awt.event.ActionListener
 530      * @since      1.1
 531      */
 532     public synchronized void addActionListener(ActionListener l) {
 533         if (l == null) {
 534             return;
 535         }
 536         actionListener = AWTEventMulticaster.add(actionListener, l);
 537         newEventsOnly = true;
 538     }
 539 
 540     /**
 541      * Removes the specified action listener so it no longer receives
 542      * action events from this menu item.
 543      * If l is null, no exception is thrown and no action is performed.
 544      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 545      * >AWT Threading Issues</a> for details on AWT's threading model.
 546      *
 547      * @param      l the action listener.
 548      * @see        #addActionListener
 549      * @see        #getActionListeners
 550      * @see        java.awt.event.ActionEvent
 551      * @see        java.awt.event.ActionListener
 552      * @since      1.1
 553      */
 554     public synchronized void removeActionListener(ActionListener l) {
 555         if (l == null) {
 556             return;
 557         }
 558         actionListener = AWTEventMulticaster.remove(actionListener, l);
 559     }
 560 
 561     /**
 562      * Returns an array of all the action listeners
 563      * registered on this menu item.
 564      *
 565      * @return all of this menu item's <code>ActionListener</code>s
 566      *         or an empty array if no action
 567      *         listeners are currently registered
 568      *
 569      * @see        #addActionListener
 570      * @see        #removeActionListener
 571      * @see        java.awt.event.ActionEvent
 572      * @see        java.awt.event.ActionListener
 573      * @since 1.4
 574      */
 575     public synchronized ActionListener[] getActionListeners() {
 576         return getListeners(ActionListener.class);
 577     }
 578 
 579     /**
 580      * Returns an array of all the objects currently registered
 581      * as <code><em>Foo</em>Listener</code>s
 582      * upon this <code>MenuItem</code>.
 583      * <code><em>Foo</em>Listener</code>s are registered using the
 584      * <code>add<em>Foo</em>Listener</code> method.
 585      *
 586      * <p>
 587      * You can specify the <code>listenerType</code> argument
 588      * with a class literal, such as
 589      * <code><em>Foo</em>Listener.class</code>.
 590      * For example, you can query a
 591      * <code>MenuItem</code> <code>m</code>
 592      * for its action listeners with the following code:
 593      *
 594      * <pre>ActionListener[] als = (ActionListener[])(m.getListeners(ActionListener.class));</pre>
 595      *
 596      * If no such listeners exist, this method returns an empty array.
 597      *
 598      * @param listenerType the type of listeners requested; this parameter
 599      *          should specify an interface that descends from
 600      *          <code>java.util.EventListener</code>
 601      * @return an array of all objects registered as
 602      *          <code><em>Foo</em>Listener</code>s on this menu item,
 603      *          or an empty array if no such
 604      *          listeners have been added
 605      * @exception ClassCastException if <code>listenerType</code>
 606      *          doesn't specify a class or interface that implements
 607      *          <code>java.util.EventListener</code>
 608      *
 609      * @see #getActionListeners
 610      * @since 1.3
 611      */
 612     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 613         EventListener l = null;
 614         if  (listenerType == ActionListener.class) {
 615             l = actionListener;
 616         }
 617         return AWTEventMulticaster.getListeners(l, listenerType);
 618     }
 619 
 620     /**
 621      * Processes events on this menu item. If the event is an
 622      * instance of <code>ActionEvent</code>, it invokes
 623      * <code>processActionEvent</code>, another method
 624      * defined by <code>MenuItem</code>.
 625      * <p>
 626      * Currently, menu items only support action events.
 627      * <p>Note that if the event parameter is <code>null</code>
 628      * the behavior is unspecified and may result in an
 629      * exception.
 630      *
 631      * @param       e the event
 632      * @see         java.awt.MenuItem#processActionEvent
 633      * @since       1.1
 634      */
 635     protected void processEvent(AWTEvent e) {
 636         if (e instanceof ActionEvent) {
 637             processActionEvent((ActionEvent)e);
 638         }
 639     }
 640 
 641     // REMIND: remove when filtering is done at lower level
 642     boolean eventEnabled(AWTEvent e) {
 643         if (e.id == ActionEvent.ACTION_PERFORMED) {
 644             if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0 ||
 645                 actionListener != null) {
 646                 return true;
 647             }
 648             return false;
 649         }
 650         return super.eventEnabled(e);
 651     }
 652 
 653     /**
 654      * Processes action events occurring on this menu item,
 655      * by dispatching them to any registered
 656      * <code>ActionListener</code> objects.
 657      * This method is not called unless action events are
 658      * enabled for this component. Action events are enabled
 659      * when one of the following occurs:
 660      * <ul>
 661      * <li>An <code>ActionListener</code> object is registered
 662      * via <code>addActionListener</code>.
 663      * <li>Action events are enabled via <code>enableEvents</code>.
 664      * </ul>
 665      * <p>Note that if the event parameter is <code>null</code>
 666      * the behavior is unspecified and may result in an
 667      * exception.
 668      *
 669      * @param       e the action event
 670      * @see         java.awt.event.ActionEvent
 671      * @see         java.awt.event.ActionListener
 672      * @see         java.awt.MenuItem#enableEvents
 673      * @since       1.1
 674      */
 675     protected void processActionEvent(ActionEvent e) {
 676         ActionListener listener = actionListener;
 677         if (listener != null) {
 678             listener.actionPerformed(e);
 679         }
 680     }
 681 
 682     /**
 683      * Returns a string representing the state of this <code>MenuItem</code>.
 684      * This method is intended to be used only for debugging purposes, and the
 685      * content and format of the returned string may vary between
 686      * implementations. The returned string may be empty but may not be
 687      * <code>null</code>.
 688      *
 689      * @return the parameter string of this menu item
 690      */
 691     public String paramString() {
 692         String str = ",label=" + label;
 693         if (shortcut != null) {
 694             str += ",shortcut=" + shortcut;
 695         }
 696         return super.paramString() + str;
 697     }
 698 
 699 
 700     /* Serialization support.
 701      */
 702 
 703     /**
 704      * Menu item serialized data version.
 705      *
 706      * @serial
 707      */
 708     private int menuItemSerializedDataVersion = 1;
 709 
 710     /**
 711      * Writes default serializable fields to stream.  Writes
 712      * a list of serializable <code>ActionListeners</code>
 713      * as optional data. The non-serializable listeners are
 714      * detected and no attempt is made to serialize them.
 715      *
 716      * @param s the <code>ObjectOutputStream</code> to write
 717      * @serialData <code>null</code> terminated sequence of 0
 718      *   or more pairs; the pair consists of a <code>String</code>
 719      *   and an <code>Object</code>; the <code>String</code>
 720      *   indicates the type of object and is one of the following:
 721      *   <code>actionListenerK</code> indicating an
 722      *     <code>ActionListener</code> object
 723      *
 724      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
 725      * @see #readObject(ObjectInputStream)
 726      */
 727     private void writeObject(ObjectOutputStream s)
 728       throws IOException
 729     {
 730       s.defaultWriteObject();
 731 
 732       AWTEventMulticaster.save(s, actionListenerK, actionListener);
 733       s.writeObject(null);
 734     }
 735 
 736     /**
 737      * Reads the <code>ObjectInputStream</code> and if it
 738      * isn't <code>null</code> adds a listener to receive
 739      * action events fired by the <code>Menu</code> Item.
 740      * Unrecognized keys or values will be ignored.
 741      *
 742      * @param s the <code>ObjectInputStream</code> to read
 743      * @exception HeadlessException if
 744      *   <code>GraphicsEnvironment.isHeadless</code> returns
 745      *   <code>true</code>
 746      * @see #removeActionListener(ActionListener)
 747      * @see #addActionListener(ActionListener)
 748      * @see #writeObject(ObjectOutputStream)
 749      */
 750     private void readObject(ObjectInputStream s)
 751       throws ClassNotFoundException, IOException, HeadlessException
 752     {
 753       // HeadlessException will be thrown from MenuComponent's readObject
 754       s.defaultReadObject();
 755 
 756       Object keyOrNull;
 757       while(null != (keyOrNull = s.readObject())) {
 758         String key = ((String)keyOrNull).intern();
 759 
 760         if (actionListenerK == key)
 761           addActionListener((ActionListener)(s.readObject()));
 762 
 763         else // skip value for unrecognized key
 764           s.readObject();
 765       }
 766     }
 767 
 768     /**
 769      * Initialize JNI field and method IDs
 770      */
 771     private static native void initIDs();
 772 
 773 
 774 /////////////////
 775 // Accessibility support
 776 ////////////////
 777 
 778     /**
 779      * Gets the AccessibleContext associated with this MenuItem.
 780      * For menu items, the AccessibleContext takes the form of an
 781      * AccessibleAWTMenuItem.
 782      * A new AccessibleAWTMenuItem instance is created if necessary.
 783      *
 784      * @return an AccessibleAWTMenuItem that serves as the
 785      *         AccessibleContext of this MenuItem
 786      * @since 1.3
 787      */
 788     public AccessibleContext getAccessibleContext() {
 789         if (accessibleContext == null) {
 790             accessibleContext = new AccessibleAWTMenuItem();
 791         }
 792         return accessibleContext;
 793     }
 794 
 795     /**
 796      * Inner class of MenuItem used to provide default support for
 797      * accessibility.  This class is not meant to be used directly by
 798      * application developers, but is instead meant only to be
 799      * subclassed by menu component developers.
 800      * <p>
 801      * This class implements accessibility support for the
 802      * <code>MenuItem</code> class.  It provides an implementation of the
 803      * Java Accessibility API appropriate to menu item user-interface elements.
 804      * @since 1.3
 805      */
 806     protected class AccessibleAWTMenuItem extends AccessibleAWTMenuComponent
 807         implements AccessibleAction, AccessibleValue
 808     {
 809         /*
 810          * JDK 1.3 serialVersionUID
 811          */
 812         private static final long serialVersionUID = -217847831945965825L;
 813 
 814         /**
 815          * Get the accessible name of this object.
 816          *
 817          * @return the localized name of the object -- can be null if this
 818          * object does not have a name
 819          */
 820         public String getAccessibleName() {
 821             if (accessibleName != null) {
 822                 return accessibleName;
 823             } else {
 824                 if (getLabel() == null) {
 825                     return super.getAccessibleName();
 826                 } else {
 827                     return getLabel();
 828                 }
 829             }
 830         }
 831 
 832         /**
 833          * Get the role of this object.
 834          *
 835          * @return an instance of AccessibleRole describing the role of the
 836          * object
 837          */
 838         public AccessibleRole getAccessibleRole() {
 839             return AccessibleRole.MENU_ITEM;
 840         }
 841 
 842         /**
 843          * Get the AccessibleAction associated with this object.  In the
 844          * implementation of the Java Accessibility API for this class,
 845          * return this object, which is responsible for implementing the
 846          * AccessibleAction interface on behalf of itself.
 847          *
 848          * @return this object
 849          */
 850         public AccessibleAction getAccessibleAction() {
 851             return this;
 852         }
 853 
 854         /**
 855          * Get the AccessibleValue associated with this object.  In the
 856          * implementation of the Java Accessibility API for this class,
 857          * return this object, which is responsible for implementing the
 858          * AccessibleValue interface on behalf of itself.
 859          *
 860          * @return this object
 861          */
 862         public AccessibleValue getAccessibleValue() {
 863             return this;
 864         }
 865 
 866         /**
 867          * Returns the number of Actions available in this object.  The
 868          * default behavior of a menu item is to have one action.
 869          *
 870          * @return 1, the number of Actions in this object
 871          */
 872         public int getAccessibleActionCount() {
 873             return 1;
 874         }
 875 
 876         /**
 877          * Return a description of the specified action of the object.
 878          *
 879          * @param i zero-based index of the actions
 880          */
 881         public String getAccessibleActionDescription(int i) {
 882             if (i == 0) {
 883                 // [[[PENDING:  WDW -- need to provide a localized string]]]
 884                 return "click";
 885             } else {
 886                 return null;
 887             }
 888         }
 889 
 890         /**
 891          * Perform the specified Action on the object
 892          *
 893          * @param i zero-based index of actions
 894          * @return true if the action was performed; otherwise false.
 895          */
 896         public boolean doAccessibleAction(int i) {
 897             if (i == 0) {
 898                 // Simulate a button click
 899                 Toolkit.getEventQueue().postEvent(
 900                         new ActionEvent(MenuItem.this,
 901                                         ActionEvent.ACTION_PERFORMED,
 902                                         MenuItem.this.getActionCommand(),
 903                                         EventQueue.getMostRecentEventTime(),
 904                                         0));
 905                 return true;
 906             } else {
 907                 return false;
 908             }
 909         }
 910 
 911         /**
 912          * Get the value of this object as a Number.
 913          *
 914          * @return An Integer of 0 if this isn't selected or an Integer of 1 if
 915          * this is selected.
 916          * @see javax.swing.AbstractButton#isSelected()
 917          */
 918         public Number getCurrentAccessibleValue() {
 919             return Integer.valueOf(0);
 920         }
 921 
 922         /**
 923          * Set the value of this object as a Number.
 924          *
 925          * @return True if the value was set.
 926          */
 927         public boolean setCurrentAccessibleValue(Number n) {
 928             return false;
 929         }
 930 
 931         /**
 932          * Get the minimum value of this object as a Number.
 933          *
 934          * @return An Integer of 0.
 935          */
 936         public Number getMinimumAccessibleValue() {
 937             return Integer.valueOf(0);
 938         }
 939 
 940         /**
 941          * Get the maximum value of this object as a Number.
 942          *
 943          * @return An Integer of 0.
 944          */
 945         public Number getMaximumAccessibleValue() {
 946             return Integer.valueOf(0);
 947         }
 948 
 949     } // class AccessibleAWTMenuItem
 950 
 951 }