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