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