1 /*
   2  * Copyright (c) 1995, 2013, 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    JDK1.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       JDK1.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       JDK1.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 = Toolkit.getDefaultToolkit().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   JDK1.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     JDK1.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      * @see        java.awt.MenuItem#setEnabled
 268      * @since      JDK1.0
 269      */
 270     public boolean isEnabled() {
 271         return enabled;
 272     }
 273 
 274     /**
 275      * Sets whether or not this menu item can be chosen.
 276      * @param      b  if <code>true</code>, enables this menu item;
 277      *                       if <code>false</code>, disables it.
 278      * @see        java.awt.MenuItem#isEnabled
 279      * @since      JDK1.1
 280      */
 281     public synchronized void setEnabled(boolean b) {
 282         enable(b);
 283     }
 284 
 285     /**
 286      * @deprecated As of JDK version 1.1,
 287      * replaced by <code>setEnabled(boolean)</code>.
 288      */
 289     @Deprecated
 290     public synchronized void enable() {
 291         enabled = true;
 292         MenuItemPeer peer = (MenuItemPeer)this.peer;
 293         if (peer != null) {
 294             peer.setEnabled(true);
 295         }
 296     }
 297 
 298     /**
 299      * @deprecated As of JDK version 1.1,
 300      * replaced by <code>setEnabled(boolean)</code>.
 301      */
 302     @Deprecated
 303     public void enable(boolean b) {
 304         if (b) {
 305             enable();
 306         } else {
 307             disable();
 308         }
 309     }
 310 
 311     /**
 312      * @deprecated As of JDK version 1.1,
 313      * replaced by <code>setEnabled(boolean)</code>.
 314      */
 315     @Deprecated
 316     public synchronized void disable() {
 317         enabled = false;
 318         MenuItemPeer peer = (MenuItemPeer)this.peer;
 319         if (peer != null) {
 320             peer.setEnabled(false);
 321         }
 322     }
 323 
 324     /**
 325      * Get the <code>MenuShortcut</code> object associated with this
 326      * menu item,
 327      * @return      the menu shortcut associated with this menu item,
 328      *                   or <code>null</code> if none has been specified.
 329      * @see         java.awt.MenuItem#setShortcut
 330      * @since       JDK1.1
 331      */
 332     public MenuShortcut getShortcut() {
 333         return shortcut;
 334     }
 335 
 336     /**
 337      * Set the <code>MenuShortcut</code> object associated with this
 338      * menu item. If a menu shortcut is already associated with
 339      * this menu item, it is replaced.
 340      * @param       s  the menu shortcut to associate
 341      *                           with this menu item.
 342      * @see         java.awt.MenuItem#getShortcut
 343      * @since       JDK1.1
 344      */
 345     public void setShortcut(MenuShortcut s) {
 346         shortcut = s;
 347         MenuItemPeer peer = (MenuItemPeer)this.peer;
 348         if (peer != null) {
 349             peer.setLabel(label);
 350         }
 351     }
 352 
 353     /**
 354      * Delete any <code>MenuShortcut</code> object associated
 355      * with this menu item.
 356      * @since      JDK1.1
 357      */
 358     public void deleteShortcut() {
 359         shortcut = null;
 360         MenuItemPeer peer = (MenuItemPeer)this.peer;
 361         if (peer != null) {
 362             peer.setLabel(label);
 363         }
 364     }
 365 
 366     /*
 367      * Delete a matching MenuShortcut associated with this MenuItem.
 368      * Used when iterating Menus.
 369      */
 370     void deleteShortcut(MenuShortcut s) {
 371         if (s.equals(shortcut)) {
 372             shortcut = null;
 373             MenuItemPeer peer = (MenuItemPeer)this.peer;
 374             if (peer != null) {
 375                 peer.setLabel(label);
 376             }
 377         }
 378     }
 379 
 380     /*
 381      * The main goal of this method is to post an appropriate event
 382      * to the event queue when menu shortcut is pressed. However,
 383      * in subclasses this method may do more than just posting
 384      * an event.
 385      */
 386     void doMenuEvent(long when, int modifiers) {
 387         Toolkit.getEventQueue().postEvent(
 388             new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
 389                             getActionCommand(), when, modifiers));
 390     }
 391 
 392     /*
 393      * Returns true if the item and all its ancestors are
 394      * enabled, false otherwise
 395      */
 396     private final boolean isItemEnabled() {
 397         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 398         // should be disabled when the menu itself is disabled
 399         if (!isEnabled()) {
 400             return false;
 401         }
 402         MenuContainer container = getParent_NoClientCode();
 403         do {
 404             if (!(container instanceof Menu)) {
 405                 return true;
 406             }
 407             Menu menu = (Menu)container;
 408             if (!menu.isEnabled()) {
 409                 return false;
 410             }
 411             container = menu.getParent_NoClientCode();
 412         } while (container != null);
 413         return true;
 414     }
 415 
 416     /*
 417      * Post an ActionEvent to the target (on
 418      * keydown) and the item is enabled.
 419      * Returns true if there is an associated shortcut.
 420      */
 421     boolean handleShortcut(KeyEvent e) {
 422         MenuShortcut s = new MenuShortcut(e.getKeyCode(),
 423                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 424         MenuShortcut sE = new MenuShortcut(e.getExtendedKeyCode(),
 425                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 426         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 427         // should be disabled when the menu itself is disabled
 428         if ((s.equals(shortcut) || sE.equals(shortcut)) && isItemEnabled()) {
 429             // MenuShortcut match -- issue an event on keydown.
 430             if (e.getID() == KeyEvent.KEY_PRESSED) {
 431                 doMenuEvent(e.getWhen(), e.getModifiers());
 432             } else {
 433                 // silently eat key release.
 434             }
 435             return true;
 436         }
 437         return false;
 438     }
 439 
 440     MenuItem getShortcutMenuItem(MenuShortcut s) {
 441         return (s.equals(shortcut)) ? this : null;
 442     }
 443 
 444     /**
 445      * Enables event delivery to this menu item for events
 446      * to be defined by the specified event mask parameter
 447      * <p>
 448      * Since event types are automatically enabled when a listener for
 449      * that type is added to the menu item, this method only needs
 450      * to be invoked by subclasses of <code>MenuItem</code> which desire to
 451      * have the specified event types delivered to <code>processEvent</code>
 452      * regardless of whether a listener is registered.
 453      *
 454      * @param       eventsToEnable the event mask defining the event types
 455      * @see         java.awt.MenuItem#processEvent
 456      * @see         java.awt.MenuItem#disableEvents
 457      * @see         java.awt.Component#enableEvents
 458      * @since       JDK1.1
 459      */
 460     protected final void enableEvents(long eventsToEnable) {
 461         eventMask |= eventsToEnable;
 462         newEventsOnly = true;
 463     }
 464 
 465     /**
 466      * Disables event delivery to this menu item for events
 467      * defined by the specified event mask parameter.
 468      *
 469      * @param       eventsToDisable the event mask defining the event types
 470      * @see         java.awt.MenuItem#processEvent
 471      * @see         java.awt.MenuItem#enableEvents
 472      * @see         java.awt.Component#disableEvents
 473      * @since       JDK1.1
 474      */
 475     protected final void disableEvents(long eventsToDisable) {
 476         eventMask &= ~eventsToDisable;
 477     }
 478 
 479     /**
 480      * Sets the command name of the action event that is fired
 481      * by this menu item.
 482      * <p>
 483      * By default, the action command is set to the label of
 484      * the menu item.
 485      * @param       command   the action command to be set
 486      *                                for this menu item.
 487      * @see         java.awt.MenuItem#getActionCommand
 488      * @since       JDK1.1
 489      */
 490     public void setActionCommand(String command) {
 491         actionCommand = command;
 492     }
 493 
 494     /**
 495      * Gets the command name of the action event that is fired
 496      * by this menu item.
 497      * @see         java.awt.MenuItem#setActionCommand
 498      * @since       JDK1.1
 499      */
 500     public String getActionCommand() {
 501         return getActionCommandImpl();
 502     }
 503 
 504     // This is final so it can be called on the Toolkit thread.
 505     final String getActionCommandImpl() {
 506         return (actionCommand == null? label : actionCommand);
 507     }
 508 
 509     /**
 510      * Adds the specified action listener to receive action events
 511      * from this menu item.
 512      * If l is null, no exception is thrown and no action is performed.
 513      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 514      * >AWT Threading Issues</a> for details on AWT's threading model.
 515      *
 516      * @param      l the action listener.
 517      * @see        #removeActionListener
 518      * @see        #getActionListeners
 519      * @see        java.awt.event.ActionEvent
 520      * @see        java.awt.event.ActionListener
 521      * @since      JDK1.1
 522      */
 523     public synchronized void addActionListener(ActionListener l) {
 524         if (l == null) {
 525             return;
 526         }
 527         actionListener = AWTEventMulticaster.add(actionListener, l);
 528         newEventsOnly = true;
 529     }
 530 
 531     /**
 532      * Removes the specified action listener so it no longer receives
 533      * action events from this menu item.
 534      * If l is null, no exception is thrown and no action is performed.
 535      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 536      * >AWT Threading Issues</a> for details on AWT's threading model.
 537      *
 538      * @param      l the action listener.
 539      * @see        #addActionListener
 540      * @see        #getActionListeners
 541      * @see        java.awt.event.ActionEvent
 542      * @see        java.awt.event.ActionListener
 543      * @since      JDK1.1
 544      */
 545     public synchronized void removeActionListener(ActionListener l) {
 546         if (l == null) {
 547             return;
 548         }
 549         actionListener = AWTEventMulticaster.remove(actionListener, l);
 550     }
 551 
 552     /**
 553      * Returns an array of all the action listeners
 554      * registered on this menu item.
 555      *
 556      * @return all of this menu item's <code>ActionListener</code>s
 557      *         or an empty array if no action
 558      *         listeners are currently registered
 559      *
 560      * @see        #addActionListener
 561      * @see        #removeActionListener
 562      * @see        java.awt.event.ActionEvent
 563      * @see        java.awt.event.ActionListener
 564      * @since 1.4
 565      */
 566     public synchronized ActionListener[] getActionListeners() {
 567         return getListeners(ActionListener.class);
 568     }
 569 
 570     /**
 571      * Returns an array of all the objects currently registered
 572      * as <code><em>Foo</em>Listener</code>s
 573      * upon this <code>MenuItem</code>.
 574      * <code><em>Foo</em>Listener</code>s are registered using the
 575      * <code>add<em>Foo</em>Listener</code> method.
 576      *
 577      * <p>
 578      * You can specify the <code>listenerType</code> argument
 579      * with a class literal, such as
 580      * <code><em>Foo</em>Listener.class</code>.
 581      * For example, you can query a
 582      * <code>MenuItem</code> <code>m</code>
 583      * for its action listeners with the following code:
 584      *
 585      * <pre>ActionListener[] als = (ActionListener[])(m.getListeners(ActionListener.class));</pre>
 586      *
 587      * If no such listeners exist, this method returns an empty array.
 588      *
 589      * @param listenerType the type of listeners requested; this parameter
 590      *          should specify an interface that descends from
 591      *          <code>java.util.EventListener</code>
 592      * @return an array of all objects registered as
 593      *          <code><em>Foo</em>Listener</code>s on this menu item,
 594      *          or an empty array if no such
 595      *          listeners have been added
 596      * @exception ClassCastException if <code>listenerType</code>
 597      *          doesn't specify a class or interface that implements
 598      *          <code>java.util.EventListener</code>
 599      *
 600      * @see #getActionListeners
 601      * @since 1.3
 602      */
 603     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 604         EventListener l = null;
 605         if  (listenerType == ActionListener.class) {
 606             l = actionListener;
 607         }
 608         return AWTEventMulticaster.getListeners(l, listenerType);
 609     }
 610 
 611     /**
 612      * Processes events on this menu item. If the event is an
 613      * instance of <code>ActionEvent</code>, it invokes
 614      * <code>processActionEvent</code>, another method
 615      * defined by <code>MenuItem</code>.
 616      * <p>
 617      * Currently, menu items only support action events.
 618      * <p>Note that if the event parameter is <code>null</code>
 619      * the behavior is unspecified and may result in an
 620      * exception.
 621      *
 622      * @param       e the event
 623      * @see         java.awt.MenuItem#processActionEvent
 624      * @since       JDK1.1
 625      */
 626     protected void processEvent(AWTEvent e) {
 627         if (e instanceof ActionEvent) {
 628             processActionEvent((ActionEvent)e);
 629         }
 630     }
 631 
 632     // REMIND: remove when filtering is done at lower level
 633     boolean eventEnabled(AWTEvent e) {
 634         if (e.id == ActionEvent.ACTION_PERFORMED) {
 635             if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0 ||
 636                 actionListener != null) {
 637                 return true;
 638             }
 639             return false;
 640         }
 641         return super.eventEnabled(e);
 642     }
 643 
 644     /**
 645      * Processes action events occurring on this menu item,
 646      * by dispatching them to any registered
 647      * <code>ActionListener</code> objects.
 648      * This method is not called unless action events are
 649      * enabled for this component. Action events are enabled
 650      * when one of the following occurs:
 651      * <ul>
 652      * <li>An <code>ActionListener</code> object is registered
 653      * via <code>addActionListener</code>.
 654      * <li>Action events are enabled via <code>enableEvents</code>.
 655      * </ul>
 656      * <p>Note that if the event parameter is <code>null</code>
 657      * the behavior is unspecified and may result in an
 658      * exception.
 659      *
 660      * @param       e the action event
 661      * @see         java.awt.event.ActionEvent
 662      * @see         java.awt.event.ActionListener
 663      * @see         java.awt.MenuItem#enableEvents
 664      * @since       JDK1.1
 665      */
 666     protected void processActionEvent(ActionEvent e) {
 667         ActionListener listener = actionListener;
 668         if (listener != null) {
 669             listener.actionPerformed(e);
 670         }
 671     }
 672 
 673     /**
 674      * Returns a string representing the state of this <code>MenuItem</code>.
 675      * This method is intended to be used only for debugging purposes, and the
 676      * content and format of the returned string may vary between
 677      * implementations. The returned string may be empty but may not be
 678      * <code>null</code>.
 679      *
 680      * @return the parameter string of this menu item
 681      */
 682     public String paramString() {
 683         String str = ",label=" + label;
 684         if (shortcut != null) {
 685             str += ",shortcut=" + shortcut;
 686         }
 687         return super.paramString() + str;
 688     }
 689 
 690 
 691     /* Serialization support.
 692      */
 693 
 694     /**
 695      * Menu item serialized data version.
 696      *
 697      * @serial
 698      */
 699     private int menuItemSerializedDataVersion = 1;
 700 
 701     /**
 702      * Writes default serializable fields to stream.  Writes
 703      * a list of serializable <code>ActionListeners</code>
 704      * as optional data. The non-serializable listeners are
 705      * detected and no attempt is made to serialize them.
 706      *
 707      * @param s the <code>ObjectOutputStream</code> to write
 708      * @serialData <code>null</code> terminated sequence of 0
 709      *   or more pairs; the pair consists of a <code>String</code>
 710      *   and an <code>Object</code>; the <code>String</code>
 711      *   indicates the type of object and is one of the following:
 712      *   <code>actionListenerK</code> indicating an
 713      *     <code>ActionListener</code> object
 714      *
 715      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
 716      * @see #readObject(ObjectInputStream)
 717      */
 718     private void writeObject(ObjectOutputStream s)
 719       throws IOException
 720     {
 721       s.defaultWriteObject();
 722 
 723       AWTEventMulticaster.save(s, actionListenerK, actionListener);
 724       s.writeObject(null);
 725     }
 726 
 727     /**
 728      * Reads the <code>ObjectInputStream</code> and if it
 729      * isn't <code>null</code> adds a listener to receive
 730      * action events fired by the <code>Menu</code> Item.
 731      * Unrecognized keys or values will be ignored.
 732      *
 733      * @param s the <code>ObjectInputStream</code> to read
 734      * @exception HeadlessException if
 735      *   <code>GraphicsEnvironment.isHeadless</code> returns
 736      *   <code>true</code>
 737      * @see #removeActionListener(ActionListener)
 738      * @see #addActionListener(ActionListener)
 739      * @see #writeObject(ObjectOutputStream)
 740      */
 741     private void readObject(ObjectInputStream s)
 742       throws ClassNotFoundException, IOException, HeadlessException
 743     {
 744       // HeadlessException will be thrown from MenuComponent's readObject
 745       s.defaultReadObject();
 746 
 747       Object keyOrNull;
 748       while(null != (keyOrNull = s.readObject())) {
 749         String key = ((String)keyOrNull).intern();
 750 
 751         if (actionListenerK == key)
 752           addActionListener((ActionListener)(s.readObject()));
 753 
 754         else // skip value for unrecognized key
 755           s.readObject();
 756       }
 757     }
 758 
 759     /**
 760      * Initialize JNI field and method IDs
 761      */
 762     private static native void initIDs();
 763 
 764 
 765 /////////////////
 766 // Accessibility support
 767 ////////////////
 768 
 769     /**
 770      * Gets the AccessibleContext associated with this MenuItem.
 771      * For menu items, the AccessibleContext takes the form of an
 772      * AccessibleAWTMenuItem.
 773      * A new AccessibleAWTMenuItem instance is created if necessary.
 774      *
 775      * @return an AccessibleAWTMenuItem that serves as the
 776      *         AccessibleContext of this MenuItem
 777      * @since 1.3
 778      */
 779     public AccessibleContext getAccessibleContext() {
 780         if (accessibleContext == null) {
 781             accessibleContext = new AccessibleAWTMenuItem();
 782         }
 783         return accessibleContext;
 784     }
 785 
 786     /**
 787      * Inner class of MenuItem used to provide default support for
 788      * accessibility.  This class is not meant to be used directly by
 789      * application developers, but is instead meant only to be
 790      * subclassed by menu component developers.
 791      * <p>
 792      * This class implements accessibility support for the
 793      * <code>MenuItem</code> class.  It provides an implementation of the
 794      * Java Accessibility API appropriate to menu item user-interface elements.
 795      * @since 1.3
 796      */
 797     protected class AccessibleAWTMenuItem extends AccessibleAWTMenuComponent
 798         implements AccessibleAction, AccessibleValue
 799     {
 800         /*
 801          * JDK 1.3 serialVersionUID
 802          */
 803         private static final long serialVersionUID = -217847831945965825L;
 804 
 805         /**
 806          * Get the accessible name of this object.
 807          *
 808          * @return the localized name of the object -- can be null if this
 809          * object does not have a name
 810          */
 811         public String getAccessibleName() {
 812             if (accessibleName != null) {
 813                 return accessibleName;
 814             } else {
 815                 if (getLabel() == null) {
 816                     return super.getAccessibleName();
 817                 } else {
 818                     return getLabel();
 819                 }
 820             }
 821         }
 822 
 823         /**
 824          * Get the role of this object.
 825          *
 826          * @return an instance of AccessibleRole describing the role of the
 827          * object
 828          */
 829         public AccessibleRole getAccessibleRole() {
 830             return AccessibleRole.MENU_ITEM;
 831         }
 832 
 833         /**
 834          * Get the AccessibleAction associated with this object.  In the
 835          * implementation of the Java Accessibility API for this class,
 836          * return this object, which is responsible for implementing the
 837          * AccessibleAction interface on behalf of itself.
 838          *
 839          * @return this object
 840          */
 841         public AccessibleAction getAccessibleAction() {
 842             return this;
 843         }
 844 
 845         /**
 846          * Get the AccessibleValue associated with this object.  In the
 847          * implementation of the Java Accessibility API for this class,
 848          * return this object, which is responsible for implementing the
 849          * AccessibleValue interface on behalf of itself.
 850          *
 851          * @return this object
 852          */
 853         public AccessibleValue getAccessibleValue() {
 854             return this;
 855         }
 856 
 857         /**
 858          * Returns the number of Actions available in this object.  The
 859          * default behavior of a menu item is to have one action.
 860          *
 861          * @return 1, the number of Actions in this object
 862          */
 863         public int getAccessibleActionCount() {
 864             return 1;
 865         }
 866 
 867         /**
 868          * Return a description of the specified action of the object.
 869          *
 870          * @param i zero-based index of the actions
 871          */
 872         public String getAccessibleActionDescription(int i) {
 873             if (i == 0) {
 874                 // [[[PENDING:  WDW -- need to provide a localized string]]]
 875                 return "click";
 876             } else {
 877                 return null;
 878             }
 879         }
 880 
 881         /**
 882          * Perform the specified Action on the object
 883          *
 884          * @param i zero-based index of actions
 885          * @return true if the action was performed; otherwise false.
 886          */
 887         public boolean doAccessibleAction(int i) {
 888             if (i == 0) {
 889                 // Simulate a button click
 890                 Toolkit.getEventQueue().postEvent(
 891                         new ActionEvent(MenuItem.this,
 892                                         ActionEvent.ACTION_PERFORMED,
 893                                         MenuItem.this.getActionCommand(),
 894                                         EventQueue.getMostRecentEventTime(),
 895                                         0));
 896                 return true;
 897             } else {
 898                 return false;
 899             }
 900         }
 901 
 902         /**
 903          * Get the value of this object as a Number.
 904          *
 905          * @return An Integer of 0 if this isn't selected or an Integer of 1 if
 906          * this is selected.
 907          * @see javax.swing.AbstractButton#isSelected()
 908          */
 909         public Number getCurrentAccessibleValue() {
 910             return Integer.valueOf(0);
 911         }
 912 
 913         /**
 914          * Set the value of this object as a Number.
 915          *
 916          * @return True if the value was set.
 917          */
 918         public boolean setCurrentAccessibleValue(Number n) {
 919             return false;
 920         }
 921 
 922         /**
 923          * Get the minimum value of this object as a Number.
 924          *
 925          * @return An Integer of 0.
 926          */
 927         public Number getMinimumAccessibleValue() {
 928             return Integer.valueOf(0);
 929         }
 930 
 931         /**
 932          * Get the maximum value of this object as a Number.
 933          *
 934          * @return An Integer of 0.
 935          */
 936         public Number getMaximumAccessibleValue() {
 937             return Integer.valueOf(0);
 938         }
 939 
 940     } // class AccessibleAWTMenuItem
 941 
 942 }