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