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