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 * <modifiers>* (<typedID> | <pressedReleasedID>) 482 * 483 * modifiers := shift | control | ctrl | meta | alt | altGraph 484 * typedID := typed <typedKey> 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" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0); 493 * "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK); 494 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK); 495 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true); 496 * "typed a" => 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 }