1 /*
   2  * Copyright (c) 2000, 2014, 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.event.KeyEvent;
  28 import sun.awt.AppContext;
  29 import java.awt.event.InputEvent;
  30 import java.util.Collections;
  31 import java.util.HashMap;
  32 import java.util.Map;
  33 import java.util.StringTokenizer;
  34 import java.io.Serializable;
  35 import java.lang.reflect.Modifier;
  36 import java.lang.reflect.Field;
  37 import sun.swing.SwingAccessor;
  38 
  39 /**
  40  * An {@code AWTKeyStroke} represents a key action on the
  41  * keyboard, or equivalent input device. {@code AWTKeyStroke}s
  42  * can correspond to only a press or release of a
  43  * particular key, just as {@code KEY_PRESSED} and
  44  * {@code KEY_RELEASED KeyEvent}s do;
  45  * alternately, they can correspond to typing a specific Java character, just
  46  * as {@code KEY_TYPED KeyEvent}s do.
  47  * In all cases, {@code AWTKeyStroke}s can specify modifiers
  48  * (alt, shift, control, meta, altGraph, or a combination thereof) which must be present
  49  * during the action for an exact match.
  50  * <p>
  51  * {@code AWTKeyStrokes} are immutable, and are intended
  52  * to be unique. Client code should never create an
  53  * {@code AWTKeyStroke} on its own, but should instead use
  54  * a variant of {@code getAWTKeyStroke}. Client use of these factory
  55  * methods allows the {@code AWTKeyStroke} implementation
  56  * to cache and share instances efficiently.
  57  *
  58  * @see #getAWTKeyStroke
  59  *
  60  * @author Arnaud Weber
  61  * @author David Mendenhall
  62  * @since 1.4
  63  */
  64 public class AWTKeyStroke implements Serializable {
  65     static final long serialVersionUID = -6430539691155161871L;
  66 
  67     private static Map<String, Integer> modifierKeywords;
  68     /**
  69      * Associates VK_XXX (as a String) with code (as Integer). This is
  70      * done to avoid the overhead of the reflective call to find the
  71      * constant.
  72      */
  73     private static VKCollection vks;
  74 
  75     //A key for the collection of AWTKeyStrokes within AppContext.
  76     private static Object APP_CONTEXT_CACHE_KEY = new Object();
  77     //A key withing the cache
  78     private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke();
  79 
  80     private char keyChar = KeyEvent.CHAR_UNDEFINED;
  81     private int keyCode = KeyEvent.VK_UNDEFINED;
  82     private int modifiers;
  83     private boolean onKeyRelease;
  84 
  85     static {
  86         /* ensure that the necessary native libraries are loaded */
  87         Toolkit.loadLibraries();
  88     }
  89 
  90     /**
  91      * Constructs an {@code AWTKeyStroke} with default values.
  92      * The default values used are:
  93      * <table border="1" summary="AWTKeyStroke default values">
  94      * <tr><th>Property</th><th>Default Value</th></tr>
  95      * <tr>
  96      *    <td>Key Char</td>
  97      *    <td>{@code KeyEvent.CHAR_UNDEFINED}</td>
  98      * </tr>
  99      * <tr>
 100      *    <td>Key Code</td>
 101      *    <td>{@code KeyEvent.VK_UNDEFINED}</td>
 102      * </tr>
 103      * <tr>
 104      *    <td>Modifiers</td>
 105      *    <td>none</td>
 106      * </tr>
 107      * <tr>
 108      *    <td>On key release?</td>
 109      *    <td>{@code false}</td>
 110      * </tr>
 111      * </table>
 112      *
 113      * {@code AWTKeyStroke}s should not be constructed
 114      * by client code. Use a variant of {@code getAWTKeyStroke}
 115      * instead.
 116      *
 117      * @see #getAWTKeyStroke
 118      */
 119     protected AWTKeyStroke() {
 120     }
 121 
 122     /**
 123      * Constructs an {@code AWTKeyStroke} with the specified
 124      * values. {@code AWTKeyStroke}s should not be constructed
 125      * by client code. Use a variant of {@code getAWTKeyStroke}
 126      * instead.
 127      *
 128      * @param keyChar the character value for a keyboard key
 129      * @param keyCode the key code for this {@code AWTKeyStroke}
 130      * @param modifiers a bitwise-ored combination of any modifiers
 131      * @param onKeyRelease {@code true} if this
 132      *        {@code AWTKeyStroke} corresponds
 133      *        to a key release; {@code false} otherwise
 134      * @see #getAWTKeyStroke
 135      */
 136     protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
 137                            boolean onKeyRelease) {
 138         this.keyChar = keyChar;
 139         this.keyCode = keyCode;
 140         this.modifiers = modifiers;
 141         this.onKeyRelease = onKeyRelease;
 142     }
 143 
 144     /**
 145      * The method has no effect and is only left present to avoid introducing
 146      * a binary incompatibility.
 147      *
 148      * @param subclass the new Class of which the factory methods should create
 149      *        instances
 150      * @deprecated
 151      */
 152     @Deprecated
 153     protected static void registerSubclass(Class<?> subclass) {
 154     }
 155 
 156     private static synchronized AWTKeyStroke getCachedStroke
 157         (char keyChar, int keyCode, int modifiers, boolean onKeyRelease)
 158     {
 159         @SuppressWarnings("unchecked")
 160         Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);
 161         AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);
 162 
 163         if (cache == null) {
 164             cache = new HashMap<>();
 165             AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);
 166         }
 167 
 168         if (cacheKey == null) {
 169             cacheKey = SwingAccessor.getKeyStrokeAccessor().create();
 170             AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);
 171         }
 172 
 173         cacheKey.keyChar = keyChar;
 174         cacheKey.keyCode = keyCode;
 175         cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
 176         cacheKey.onKeyRelease = onKeyRelease;
 177 
 178         AWTKeyStroke stroke = cache.get(cacheKey);
 179         if (stroke == null) {
 180             stroke = cacheKey;
 181             cache.put(stroke, stroke);
 182             AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
 183         }
 184         return stroke;
 185     }
 186 
 187     /**
 188      * Returns a shared instance of an {@code AWTKeyStroke}
 189      * that represents a {@code KEY_TYPED} event for the
 190      * specified character.
 191      *
 192      * @param keyChar the character value for a keyboard key
 193      * @return an {@code AWTKeyStroke} object for that key
 194      */
 195     public static AWTKeyStroke getAWTKeyStroke(char keyChar) {
 196         return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
 197     }
 198 
 199     /**
 200      * Returns a shared instance of an {@code AWTKeyStroke}
 201      * that represents a {@code KEY_TYPED} event for the
 202      * specified Character object and a set of modifiers. Note
 203      * that the first parameter is of type Character rather than
 204      * char. This is to avoid inadvertent clashes with
 205      * calls to {@code getAWTKeyStroke(int keyCode, int modifiers)}.
 206      *
 207      * The modifiers consist of any combination of following:<ul>
 208      * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
 209      * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
 210      * <li>java.awt.event.InputEvent.META_DOWN_MASK
 211      * <li>java.awt.event.InputEvent.ALT_DOWN_MASK
 212      * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
 213      * </ul>
 214      * The old modifiers listed below also can be used, but they are
 215      * mapped to _DOWN_ modifiers. <ul>
 216      * <li>java.awt.event.InputEvent.SHIFT_MASK
 217      * <li>java.awt.event.InputEvent.CTRL_MASK
 218      * <li>java.awt.event.InputEvent.META_MASK
 219      * <li>java.awt.event.InputEvent.ALT_MASK
 220      * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
 221      * </ul>
 222      * also can be used, but they are mapped to _DOWN_ modifiers.
 223      *
 224      * Since these numbers are all different powers of two, any combination of
 225      * them is an integer in which each bit represents a different modifier
 226      * key. Use 0 to specify no modifiers.
 227      *
 228      * @param keyChar the Character object for a keyboard character
 229      * @param modifiers a bitwise-ored combination of any modifiers
 230      * @return an {@code AWTKeyStroke} object for that key
 231      * @throws IllegalArgumentException if {@code keyChar} is
 232      *       {@code null}
 233      *
 234      * @see java.awt.event.InputEvent
 235      */
 236     public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
 237     {
 238         if (keyChar == null) {
 239             throw new IllegalArgumentException("keyChar cannot be null");
 240         }
 241         return getCachedStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
 242                                modifiers, false);
 243     }
 244 
 245     /**
 246      * Returns a shared instance of an {@code AWTKeyStroke},
 247      * given a numeric key code and a set of modifiers, specifying
 248      * whether the key is activated when it is pressed or released.
 249      * <p>
 250      * The "virtual key" constants defined in
 251      * {@code java.awt.event.KeyEvent} can be
 252      * used to specify the key code. For example:<ul>
 253      * <li>{@code java.awt.event.KeyEvent.VK_ENTER}
 254      * <li>{@code java.awt.event.KeyEvent.VK_TAB}
 255      * <li>{@code java.awt.event.KeyEvent.VK_SPACE}
 256      * </ul>
 257      * Alternatively, the key code may be obtained by calling
 258      * {@code java.awt.event.KeyEvent.getExtendedKeyCodeForChar}.
 259      *
 260      * The modifiers consist of any combination of:<ul>
 261      * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
 262      * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
 263      * <li>java.awt.event.InputEvent.META_DOWN_MASK
 264      * <li>java.awt.event.InputEvent.ALT_DOWN_MASK
 265      * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
 266      * </ul>
 267      * The old modifiers <ul>
 268      * <li>java.awt.event.InputEvent.SHIFT_MASK
 269      * <li>java.awt.event.InputEvent.CTRL_MASK
 270      * <li>java.awt.event.InputEvent.META_MASK
 271      * <li>java.awt.event.InputEvent.ALT_MASK
 272      * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
 273      * </ul>
 274      * also can be used, but they are mapped to _DOWN_ modifiers.
 275      *
 276      * Since these numbers are all different powers of two, any combination of
 277      * them is an integer in which each bit represents a different modifier
 278      * key. Use 0 to specify no modifiers.
 279      *
 280      * @param keyCode an int specifying the numeric code for a keyboard key
 281      * @param modifiers a bitwise-ored combination of any modifiers
 282      * @param onKeyRelease {@code true} if the {@code AWTKeyStroke}
 283      *        should represent a key release; {@code false} otherwise
 284      * @return an AWTKeyStroke object for that key
 285      *
 286      * @see java.awt.event.KeyEvent
 287      * @see java.awt.event.InputEvent
 288      */
 289     public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
 290                                                boolean onKeyRelease) {
 291         return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,
 292                                onKeyRelease);
 293     }
 294 
 295     /**
 296      * Returns a shared instance of an {@code AWTKeyStroke},
 297      * given a numeric key code and a set of modifiers. The returned
 298      * {@code AWTKeyStroke} will correspond to a key press.
 299      * <p>
 300      * The "virtual key" constants defined in
 301      * {@code java.awt.event.KeyEvent} can be
 302      * used to specify the key code. For example:<ul>
 303      * <li>{@code java.awt.event.KeyEvent.VK_ENTER}
 304      * <li>{@code java.awt.event.KeyEvent.VK_TAB}
 305      * <li>{@code java.awt.event.KeyEvent.VK_SPACE}
 306      * </ul>
 307      * The modifiers consist of any combination of:<ul>
 308      * <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK
 309      * <li>java.awt.event.InputEvent.CTRL_DOWN_MASK
 310      * <li>java.awt.event.InputEvent.META_DOWN_MASK
 311      * <li>java.awt.event.InputEvent.ALT_DOWN_MASK
 312      * <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK
 313      * </ul>
 314      * The old modifiers <ul>
 315      * <li>java.awt.event.InputEvent.SHIFT_MASK
 316      * <li>java.awt.event.InputEvent.CTRL_MASK
 317      * <li>java.awt.event.InputEvent.META_MASK
 318      * <li>java.awt.event.InputEvent.ALT_MASK
 319      * <li>java.awt.event.InputEvent.ALT_GRAPH_MASK
 320      * </ul>
 321      * also can be used, but they are mapped to _DOWN_ modifiers.
 322      *
 323      * Since these numbers are all different powers of two, any combination of
 324      * them is an integer in which each bit represents a different modifier
 325      * key. Use 0 to specify no modifiers.
 326      *
 327      * @param keyCode an int specifying the numeric code for a keyboard key
 328      * @param modifiers a bitwise-ored combination of any modifiers
 329      * @return an {@code AWTKeyStroke} object for that key
 330      *
 331      * @see java.awt.event.KeyEvent
 332      * @see java.awt.event.InputEvent
 333      */
 334     public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) {
 335         return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,
 336                                false);
 337     }
 338 
 339     /**
 340      * Returns an {@code AWTKeyStroke} which represents the
 341      * stroke which generated a given {@code KeyEvent}.
 342      * <p>
 343      * This method obtains the keyChar from a {@code KeyTyped}
 344      * event, and the keyCode from a {@code KeyPressed} or
 345      * {@code KeyReleased} event. The {@code KeyEvent} modifiers are
 346      * obtained for all three types of {@code KeyEvent}.
 347      *
 348      * @param anEvent the {@code KeyEvent} from which to
 349      *      obtain the {@code AWTKeyStroke}
 350      * @throws NullPointerException if {@code anEvent} is null
 351      * @return the {@code AWTKeyStroke} that precipitated the event
 352      */
 353     public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) {
 354         int id = anEvent.getID();
 355         switch(id) {
 356           case KeyEvent.KEY_PRESSED:
 357           case KeyEvent.KEY_RELEASED:
 358             return getCachedStroke(KeyEvent.CHAR_UNDEFINED,
 359                                    anEvent.getKeyCode(),
 360                                    anEvent.getModifiers(),
 361                                    (id == KeyEvent.KEY_RELEASED));
 362           case KeyEvent.KEY_TYPED:
 363             return getCachedStroke(anEvent.getKeyChar(),
 364                                    KeyEvent.VK_UNDEFINED,
 365                                    anEvent.getModifiers(),
 366                                    false);
 367           default:
 368             // Invalid ID for this KeyEvent
 369             return null;
 370         }
 371     }
 372 
 373     /**
 374      * Parses a string and returns an {@code AWTKeyStroke}.
 375      * The string must have the following syntax:
 376      * <pre>
 377      *    &lt;modifiers&gt;* (&lt;typedID&gt; | &lt;pressedReleasedID&gt;)
 378      *
 379      *    modifiers := shift | control | ctrl | meta | alt | altGraph
 380      *    typedID := typed &lt;typedKey&gt;
 381      *    typedKey := string of length 1 giving Unicode character.
 382      *    pressedReleasedID := (pressed | released) key
 383      *    key := KeyEvent key code name, i.e. the name following "VK_".
 384      * </pre>
 385      * If typed, pressed or released is not specified, pressed is assumed. Here
 386      * are some examples:
 387      * <pre>
 388      *     "INSERT" =&gt; getAWTKeyStroke(KeyEvent.VK_INSERT, 0);
 389      *     "control DELETE" =&gt; getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);
 390      *     "alt shift X" =&gt; getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);
 391      *     "alt shift released X" =&gt; getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);
 392      *     "typed a" =&gt; getAWTKeyStroke('a');
 393      * </pre>
 394      *
 395      * @param s a String formatted as described above
 396      * @return an {@code AWTKeyStroke} object for that String
 397      * @throws IllegalArgumentException if {@code s} is {@code null},
 398      *        or is formatted incorrectly
 399      */
 400     public static AWTKeyStroke getAWTKeyStroke(String s) {
 401         if (s == null) {
 402             throw new IllegalArgumentException("String cannot be null");
 403         }
 404 
 405         final String errmsg = "String formatted incorrectly";
 406 
 407         StringTokenizer st = new StringTokenizer(s, " ");
 408 
 409         int mask = 0;
 410         boolean released = false;
 411         boolean typed = false;
 412         boolean pressed = false;
 413 
 414         synchronized (AWTKeyStroke.class) {
 415             if (modifierKeywords == null) {
 416                 Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f);
 417                 uninitializedMap.put("shift",
 418                                      Integer.valueOf(InputEvent.SHIFT_DOWN_MASK
 419                                                      |InputEvent.SHIFT_MASK));
 420                 uninitializedMap.put("control",
 421                                      Integer.valueOf(InputEvent.CTRL_DOWN_MASK
 422                                                      |InputEvent.CTRL_MASK));
 423                 uninitializedMap.put("ctrl",
 424                                      Integer.valueOf(InputEvent.CTRL_DOWN_MASK
 425                                                      |InputEvent.CTRL_MASK));
 426                 uninitializedMap.put("meta",
 427                                      Integer.valueOf(InputEvent.META_DOWN_MASK
 428                                                      |InputEvent.META_MASK));
 429                 uninitializedMap.put("alt",
 430                                      Integer.valueOf(InputEvent.ALT_DOWN_MASK
 431                                                      |InputEvent.ALT_MASK));
 432                 uninitializedMap.put("altGraph",
 433                                      Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK
 434                                                      |InputEvent.ALT_GRAPH_MASK));
 435                 uninitializedMap.put("button1",
 436                                      Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK));
 437                 uninitializedMap.put("button2",
 438                                      Integer.valueOf(InputEvent.BUTTON2_DOWN_MASK));
 439                 uninitializedMap.put("button3",
 440                                      Integer.valueOf(InputEvent.BUTTON3_DOWN_MASK));
 441                 modifierKeywords =
 442                     Collections.synchronizedMap(uninitializedMap);
 443             }
 444         }
 445 
 446         int count = st.countTokens();
 447 
 448         for (int i = 1; i <= count; i++) {
 449             String token = st.nextToken();
 450 
 451             if (typed) {
 452                 if (token.length() != 1 || i != count) {
 453                     throw new IllegalArgumentException(errmsg);
 454                 }
 455                 return getCachedStroke(token.charAt(0), KeyEvent.VK_UNDEFINED,
 456                                        mask, false);
 457             }
 458 
 459             if (pressed || released || i == count) {
 460                 if (i != count) {
 461                     throw new IllegalArgumentException(errmsg);
 462                 }
 463 
 464                 String keyCodeName = "VK_" + token;
 465                 int keyCode = getVKValue(keyCodeName);
 466 
 467                 return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
 468                                        mask, released);
 469             }
 470 
 471             if (token.equals("released")) {
 472                 released = true;
 473                 continue;
 474             }
 475             if (token.equals("pressed")) {
 476                 pressed = true;
 477                 continue;
 478             }
 479             if (token.equals("typed")) {
 480                 typed = true;
 481                 continue;
 482             }
 483 
 484             Integer tokenMask = modifierKeywords.get(token);
 485             if (tokenMask != null) {
 486                 mask |= tokenMask.intValue();
 487             } else {
 488                 throw new IllegalArgumentException(errmsg);
 489             }
 490         }
 491 
 492         throw new IllegalArgumentException(errmsg);
 493     }
 494 
 495     private static VKCollection getVKCollection() {
 496         if (vks == null) {
 497             vks = new VKCollection();
 498         }
 499         return vks;
 500     }
 501     /**
 502      * Returns the integer constant for the KeyEvent.VK field named
 503      * {@code key}. This will throw an
 504      * {@code IllegalArgumentException} if {@code key} is
 505      * not a valid constant.
 506      */
 507     private static int getVKValue(String key) {
 508         VKCollection vkCollect = getVKCollection();
 509 
 510         Integer value = vkCollect.findCode(key);
 511 
 512         if (value == null) {
 513             int keyCode = 0;
 514             final String errmsg = "String formatted incorrectly";
 515 
 516             try {
 517                 keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class);
 518             } catch (NoSuchFieldException nsfe) {
 519                 throw new IllegalArgumentException(errmsg);
 520             } catch (IllegalAccessException iae) {
 521                 throw new IllegalArgumentException(errmsg);
 522             }
 523             value = Integer.valueOf(keyCode);
 524             vkCollect.put(key, value);
 525         }
 526         return value.intValue();
 527     }
 528 
 529     /**
 530      * Returns the character for this {@code AWTKeyStroke}.
 531      *
 532      * @return a char value
 533      * @see #getAWTKeyStroke(char)
 534      * @see KeyEvent#getKeyChar
 535      */
 536     public final char getKeyChar() {
 537         return keyChar;
 538     }
 539 
 540     /**
 541      * Returns the numeric key code for this {@code AWTKeyStroke}.
 542      *
 543      * @return an int containing the key code value
 544      * @see #getAWTKeyStroke(int,int)
 545      * @see KeyEvent#getKeyCode
 546      */
 547     public final int getKeyCode() {
 548         return keyCode;
 549     }
 550 
 551     /**
 552      * Returns the modifier keys for this {@code AWTKeyStroke}.
 553      *
 554      * @return an int containing the modifiers
 555      * @see #getAWTKeyStroke(int,int)
 556      */
 557     public final int getModifiers() {
 558         return modifiers;
 559     }
 560 
 561     /**
 562      * Returns whether this {@code AWTKeyStroke} represents a key release.
 563      *
 564      * @return {@code true} if this {@code AWTKeyStroke}
 565      *          represents a key release; {@code false} otherwise
 566      * @see #getAWTKeyStroke(int,int,boolean)
 567      */
 568     public final boolean isOnKeyRelease() {
 569         return onKeyRelease;
 570     }
 571 
 572     /**
 573      * Returns the type of {@code KeyEvent} which corresponds to
 574      * this {@code AWTKeyStroke}.
 575      *
 576      * @return {@code KeyEvent.KEY_PRESSED},
 577      *         {@code KeyEvent.KEY_TYPED},
 578      *         or {@code KeyEvent.KEY_RELEASED}
 579      * @see java.awt.event.KeyEvent
 580      */
 581     public final int getKeyEventType() {
 582         if (keyCode == KeyEvent.VK_UNDEFINED) {
 583             return KeyEvent.KEY_TYPED;
 584         } else {
 585             return (onKeyRelease)
 586                 ? KeyEvent.KEY_RELEASED
 587                 : KeyEvent.KEY_PRESSED;
 588         }
 589     }
 590 
 591     /**
 592      * Returns a numeric value for this object that is likely to be unique,
 593      * making it a good choice as the index value in a hash table.
 594      *
 595      * @return an int that represents this object
 596      */
 597     public int hashCode() {
 598         return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) +
 599             (onKeyRelease ? 1 : 2);
 600     }
 601 
 602     /**
 603      * Returns true if this object is identical to the specified object.
 604      *
 605      * @param anObject the Object to compare this object to
 606      * @return true if the objects are identical
 607      */
 608     public final boolean equals(Object anObject) {
 609         if (anObject instanceof AWTKeyStroke) {
 610             AWTKeyStroke ks = (AWTKeyStroke)anObject;
 611             return (ks.keyChar == keyChar && ks.keyCode == keyCode &&
 612                     ks.onKeyRelease == onKeyRelease &&
 613                     ks.modifiers == modifiers);
 614         }
 615         return false;
 616     }
 617 
 618     /**
 619      * Returns a string that displays and identifies this object's properties.
 620      * The {@code String} returned by this method can be passed
 621      * as a parameter to {@code getAWTKeyStroke(String)} to produce
 622      * a key stroke equal to this key stroke.
 623      *
 624      * @return a String representation of this object
 625      * @see #getAWTKeyStroke(String)
 626      */
 627     public String toString() {
 628         if (keyCode == KeyEvent.VK_UNDEFINED) {
 629             return getModifiersText(modifiers) + "typed " + keyChar;
 630         } else {
 631             return getModifiersText(modifiers) +
 632                 (onKeyRelease ? "released" : "pressed") + " " +
 633                 getVKText(keyCode);
 634         }
 635     }
 636 
 637     static String getModifiersText(int modifiers) {
 638         StringBuilder buf = new StringBuilder();
 639 
 640         if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) {
 641             buf.append("shift ");
 642         }
 643         if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0 ) {
 644             buf.append("ctrl ");
 645         }
 646         if ((modifiers & InputEvent.META_DOWN_MASK) != 0 ) {
 647             buf.append("meta ");
 648         }
 649         if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0 ) {
 650             buf.append("alt ");
 651         }
 652         if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0 ) {
 653             buf.append("altGraph ");
 654         }
 655         if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0 ) {
 656             buf.append("button1 ");
 657         }
 658         if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ) {
 659             buf.append("button2 ");
 660         }
 661         if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
 662             buf.append("button3 ");
 663         }
 664 
 665         return buf.toString();
 666     }
 667 
 668     static String getVKText(int keyCode) {
 669         VKCollection vkCollect = getVKCollection();
 670         Integer key = Integer.valueOf(keyCode);
 671         String name = vkCollect.findName(key);
 672         if (name != null) {
 673             return name.substring(3);
 674         }
 675         int expected_modifiers =
 676             (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
 677 
 678         Field[] fields = KeyEvent.class.getDeclaredFields();
 679         for (int i = 0; i < fields.length; i++) {
 680             try {
 681                 if (fields[i].getModifiers() == expected_modifiers
 682                     && fields[i].getType() == Integer.TYPE
 683                     && fields[i].getName().startsWith("VK_")
 684                     && fields[i].getInt(KeyEvent.class) == keyCode)
 685                 {
 686                     name = fields[i].getName();
 687                     vkCollect.put(name, key);
 688                     return name.substring(3);
 689                 }
 690             } catch (IllegalAccessException e) {
 691                 assert(false);
 692             }
 693         }
 694         return "UNKNOWN";
 695     }
 696 
 697     /**
 698      * Returns a cached instance of {@code AWTKeyStroke} (or a subclass of
 699      * {@code AWTKeyStroke}) which is equal to this instance.
 700      *
 701      * @return a cached instance which is equal to this instance
 702      * @throws java.io.ObjectStreamException if a serialization problem occurs
 703      */
 704     protected Object readResolve() throws java.io.ObjectStreamException {
 705         synchronized (AWTKeyStroke.class) {
 706 
 707             return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease);
 708         }
 709     }
 710 
 711     private static int mapOldModifiers(int modifiers) {
 712         if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
 713             modifiers |= InputEvent.SHIFT_DOWN_MASK;
 714         }
 715         if ((modifiers & InputEvent.ALT_MASK) != 0) {
 716             modifiers |= InputEvent.ALT_DOWN_MASK;
 717         }
 718         if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
 719             modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
 720         }
 721         if ((modifiers & InputEvent.CTRL_MASK) != 0) {
 722             modifiers |= InputEvent.CTRL_DOWN_MASK;
 723         }
 724         if ((modifiers & InputEvent.META_MASK) != 0) {
 725             modifiers |= InputEvent.META_DOWN_MASK;
 726         }
 727 
 728         modifiers &= InputEvent.SHIFT_DOWN_MASK
 729             | InputEvent.ALT_DOWN_MASK
 730             | InputEvent.ALT_GRAPH_DOWN_MASK
 731             | InputEvent.CTRL_DOWN_MASK
 732             | InputEvent.META_DOWN_MASK
 733             | InputEvent.BUTTON1_DOWN_MASK
 734             | InputEvent.BUTTON2_DOWN_MASK
 735             | InputEvent.BUTTON3_DOWN_MASK;
 736 
 737         return modifiers;
 738     }
 739 
 740     private static int mapNewModifiers(int modifiers) {
 741         if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {
 742             modifiers |= InputEvent.SHIFT_MASK;
 743         }
 744         if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
 745             modifiers |= InputEvent.ALT_MASK;
 746         }
 747         if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
 748             modifiers |= InputEvent.ALT_GRAPH_MASK;
 749         }
 750         if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
 751             modifiers |= InputEvent.CTRL_MASK;
 752         }
 753         if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
 754             modifiers |= InputEvent.META_MASK;
 755         }
 756 
 757         return modifiers;
 758     }
 759 
 760 }
 761 
 762 class VKCollection {
 763     Map<Integer, String> code2name;
 764     Map<String, Integer> name2code;
 765 
 766     public VKCollection() {
 767         code2name = new HashMap<>();
 768         name2code = new HashMap<>();
 769     }
 770 
 771     public synchronized void put(String name, Integer code) {
 772         assert((name != null) && (code != null));
 773         assert(findName(code) == null);
 774         assert(findCode(name) == null);
 775         code2name.put(code, name);
 776         name2code.put(name, code);
 777     }
 778 
 779     public synchronized Integer findCode(String name) {
 780         assert(name != null);
 781         return name2code.get(name);
 782     }
 783 
 784     public synchronized String findName(Integer code) {
 785         assert(code != null);
 786         return code2name.get(code);
 787     }
 788 }