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