1 /* AWTKeyStroke.java -- an immutable key stroke 2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package java.awt; 40 41 import java.awt.event.InputEvent; 42 import java.awt.event.KeyEvent; 43 import java.io.ObjectStreamException; 44 import java.io.Serializable; 45 import java.lang.reflect.Constructor; 46 import java.lang.reflect.Field; 47 import java.lang.reflect.InvocationTargetException; 48 import java.security.AccessController; 49 import java.security.PrivilegedAction; 50 import java.security.PrivilegedActionException; 51 import java.security.PrivilegedExceptionAction; 52 import java.util.HashMap; 53 import java.util.LinkedHashMap; 54 import java.util.Map; 55 import java.util.StringTokenizer; 56 57 /** 58 * This class mirrors KeyEvents, representing both low-level key presses and 59 * key releases, and high level key typed inputs. However, this class forms 60 * immutable strokes, and can be efficiently reused via the factory methods 61 * for creating them. 62 * 63 * <p>For backwards compatibility with Swing, this supports a way to build 64 * instances of a subclass, using reflection, provided the subclass has a 65 * no-arg constructor (of any accessibility). 66 * 67 * @author Eric Blake (ebb9@email.byu.edu) 68 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 69 * @see #getAWTKeyStroke(char) 70 * @since 1.4 71 * @status updated to 1.4 72 */ 73 public class AWTKeyStroke implements Serializable 74 { 75 /** 76 * Compatible with JDK 1.4+. 77 */ 78 private static final long serialVersionUID = -6430539691155161871L; 79 80 /** The mask for modifiers. */ 81 private static final int MODIFIERS_MASK = 0x3fef; 82 83 /** 84 * The cache of recently created keystrokes. This maps KeyStrokes to 85 * KeyStrokes in a cache which removes the least recently accessed entry, 86 * under the assumption that garbage collection of a new keystroke is 87 * easy when we find the old one that it matches in the cache. 88 */ 89 private static final LinkedHashMap<AWTKeyStroke,AWTKeyStroke> cache = 90 new LinkedHashMap<AWTKeyStroke,AWTKeyStroke>(11, 0.75f, true) 91 { 92 /** The largest the keystroke cache can grow. */ 93 private static final int MAX_CACHE_SIZE = 2048; 94 95 /** Prune stale entries. */ 96 protected boolean removeEldestEntry(Map.Entry<AWTKeyStroke,AWTKeyStroke> 97 eldest) 98 { 99 return size() > MAX_CACHE_SIZE; 100 } 101 }; 102 103 /** The most recently generated keystroke, or null. */ 104 private static AWTKeyStroke recent; 105 106 /** 107 * The no-arg constructor of a subclass, or null to use AWTKeyStroke. Note 108 * that this will be left accessible, to get around private access; but 109 * it should not be a security risk as it is highly unlikely that creating 110 * protected instances of the subclass via reflection will do much damage. 111 */ 112 private static Constructor ctor; 113 114 /** 115 * A table of keyCode names to values. This is package-private to 116 * avoid an accessor method. 117 * 118 * @see #getAWTKeyStroke(String) 119 */ 120 static final HashMap<String,Object> vktable = new HashMap<String,Object>(); 121 static 122 { 123 // Using reflection saves the hassle of keeping this in sync with KeyEvent, 124 // at the price of an expensive initialization. AccessController.doPrivileged(new PrivilegedAction() { public Object run() { Field[] fields = KeyEvent.class.getFields(); int i = fields.length; try { while (--i >= 0) { Field f = fields[i]; String name = f.getName(); if (name.startsWith(R)) vktable.put(name.substring(3), f.get(null)); } } catch (Exception e) { throw (Error) new InternalError().initCause(e); } return null; } })125 AccessController.doPrivileged(new PrivilegedAction() 126 { 127 public Object run() 128 { 129 Field[] fields = KeyEvent.class.getFields(); 130 int i = fields.length; 131 try 132 { 133 while (--i >= 0) 134 { 135 Field f = fields[i]; 136 String name = f.getName(); 137 if (name.startsWith("VK_")) 138 vktable.put(name.substring(3), f.get(null)); 139 } 140 } 141 catch (Exception e) 142 { 143 throw (Error) new InternalError().initCause(e); 144 } 145 return null; 146 } 147 }); 148 } 149 150 /** 151 * The typed character, or CHAR_UNDEFINED for key presses and releases. 152 * 153 * @serial the keyChar 154 */ 155 private char keyChar; 156 157 /** 158 * The virtual key code, or VK_UNDEFINED for key typed. Package visible for 159 * use by Component. 160 * 161 * @serial the keyCode 162 */ 163 int keyCode; 164 165 /** 166 * The modifiers in effect. To match Sun, this stores the old style masks 167 * for shift, control, alt, meta, and alt-graph (but not button1); as well 168 * as the new style of extended modifiers for all modifiers. 169 * 170 * @serial bitwise or of the *_DOWN_MASK modifiers 171 */ 172 private int modifiers; 173 174 /** 175 * True if this is a key release; should only be true if keyChar is 176 * CHAR_UNDEFINED. 177 * 178 * @serial true to distinguish key pressed from key released 179 */ 180 private boolean onKeyRelease; 181 182 /** 183 * Construct a keystroke with default values: it will be interpreted as a 184 * key typed event with an invalid character and no modifiers. Client code 185 * should use the factory methods instead. 186 * 187 * @see #getAWTKeyStroke(char) 188 * @see #getAWTKeyStroke(Character, int) 189 * @see #getAWTKeyStroke(int, int, boolean) 190 * @see #getAWTKeyStroke(int, int) 191 * @see #getAWTKeyStrokeForEvent(KeyEvent) 192 * @see #getAWTKeyStroke(String) 193 */ AWTKeyStroke()194 protected AWTKeyStroke() 195 { 196 keyChar = KeyEvent.CHAR_UNDEFINED; 197 } 198 199 /** 200 * Construct a keystroke with the given values. Client code should use the 201 * factory methods instead. 202 * 203 * @param keyChar the character entered, if this is a key typed 204 * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed 205 * @param modifiers the modifier keys for the keystroke, in old or new style 206 * @param onKeyRelease true if this is a key release instead of a press 207 * @see #getAWTKeyStroke(char) 208 * @see #getAWTKeyStroke(Character, int) 209 * @see #getAWTKeyStroke(int, int, boolean) 210 * @see #getAWTKeyStroke(int, int) 211 * @see #getAWTKeyStrokeForEvent(KeyEvent) 212 * @see #getAWTKeyStroke(String) 213 */ AWTKeyStroke(char keyChar, int keyCode, int modifiers, boolean onKeyRelease)214 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, 215 boolean onKeyRelease) 216 { 217 this.keyChar = keyChar; 218 this.keyCode = keyCode; 219 // No need to call extend(), as only trusted code calls this constructor. 220 this.modifiers = modifiers; 221 this.onKeyRelease = onKeyRelease; 222 } 223 224 /** 225 * Registers a new subclass as being the type of keystrokes to generate in 226 * the factory methods. This operation flushes the cache of stored keystrokes 227 * if the class differs from the current one. The new class must be 228 * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may 229 * be private). 230 * 231 * @param subclass the new runtime type of generated keystrokes 232 * @throws IllegalArgumentException subclass doesn't have no-arg constructor 233 * @throws ClassCastException subclass doesn't extend AWTKeyStroke 234 */ registerSubclass(final Class<?> subclass)235 protected static void registerSubclass(final Class<?> subclass) 236 { 237 if (subclass == null) 238 throw new IllegalArgumentException(); 239 if (subclass.equals(ctor == null ? AWTKeyStroke.class 240 : ctor.getDeclaringClass())) 241 return; 242 if (subclass.equals(AWTKeyStroke.class)) 243 { 244 cache.clear(); 245 recent = null; 246 ctor = null; 247 return; 248 } 249 try 250 { 251 ctor = (Constructor) AccessController.doPrivileged 252 (new PrivilegedExceptionAction() 253 { 254 public Object run() 255 throws NoSuchMethodException, InstantiationException, 256 IllegalAccessException, InvocationTargetException 257 { 258 Constructor<?> c = 259 subclass.getDeclaredConstructor((Class<?>[])null); 260 c.setAccessible(true); 261 // Create a new instance, to make sure that we can, and 262 // to cause any ClassCastException. 263 AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance(); 264 return c; 265 } 266 }); 267 } 268 catch (PrivilegedActionException e) 269 { 270 // e.getCause() will not ever be ClassCastException; that should 271 // escape on its own. 272 throw (RuntimeException) 273 new IllegalArgumentException().initCause(e.getCause()); 274 } 275 cache.clear(); 276 recent = null; 277 } 278 279 /** 280 * Returns a keystroke representing a typed character. 281 * 282 * @param keyChar the typed character 283 * @return the specified keystroke 284 */ getAWTKeyStroke(char keyChar)285 public static AWTKeyStroke getAWTKeyStroke(char keyChar) 286 { 287 return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); 288 } 289 290 /** 291 * Returns a keystroke representing a typed character with the given 292 * modifiers. Note that keyChar is a <code>Character</code> instead of a 293 * <code>char</code> to avoid accidental ambiguity with 294 * <code>getAWTKeyStroke(int, int)</code>. The modifiers are the bitwise 295 * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK) 296 * is preferred, but the old style will work. 297 * 298 * @param keyChar the typed character 299 * @param modifiers the modifiers, or 0 300 * @return the specified keystroke 301 * @throws IllegalArgumentException if keyChar is null 302 */ getAWTKeyStroke(Character keyChar, int modifiers)303 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) 304 { 305 if (keyChar == null) 306 throw new IllegalArgumentException(); 307 return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, 308 extend(modifiers), false); 309 } 310 311 /** 312 * Returns a keystroke representing a pressed or released key event, with 313 * the given modifiers. The "virtual key" should be one of the VK_* 314 * constants in {@link KeyEvent}. The modifiers are the bitwise or of the 315 * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is 316 * preferred, but the old style will work. 317 * 318 * @param keyCode the virtual key 319 * @param modifiers the modifiers, or 0 320 * @param release true if this is a key release instead of a key press 321 * @return the specified keystroke 322 */ getAWTKeyStroke(int keyCode, int modifiers, boolean release)323 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, 324 boolean release) 325 { 326 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, 327 extend(modifiers), release); 328 } 329 330 /** 331 * Returns a keystroke representing a pressed key event, with the given 332 * modifiers. The "virtual key" should be one of the VK_* constants in 333 * {@link KeyEvent}. The modifiers are the bitwise or of the masks found 334 * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the 335 * old style will work. 336 * 337 * @param keyCode the virtual key 338 * @param modifiers the modifiers, or 0 339 * @return the specified keystroke 340 */ getAWTKeyStroke(int keyCode, int modifiers)341 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) 342 { 343 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, 344 extend(modifiers), false); 345 } 346 347 /** 348 * Returns a keystroke representing what caused the key event. 349 * 350 * @param event the key event to convert 351 * @return the specified keystroke, or null if the event is invalid 352 * @throws NullPointerException if event is null 353 */ getAWTKeyStrokeForEvent(KeyEvent event)354 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event) 355 { 356 switch (event.id) 357 { 358 case KeyEvent.KEY_TYPED: 359 return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED, 360 extend(event.getModifiersEx()), false); 361 case KeyEvent.KEY_PRESSED: 362 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(), 363 extend(event.getModifiersEx()), false); 364 case KeyEvent.KEY_RELEASED: 365 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(), 366 extend(event.getModifiersEx()), true); 367 default: 368 return null; 369 } 370 } 371 372 /** 373 * Parses a string and returns the keystroke that it represents. The syntax 374 * for keystrokes is listed below, with tokens separated by an arbitrary 375 * number of spaces: 376 * <pre> 377 * keyStroke := <modifiers>* ( <typedID> | <codeID> ) 378 * modifiers := ( shift | control | ctrl | meta | alt 379 * | button1 | button2 | button3 ) 380 * typedID := typed <single Unicode character> 381 * codeID := ( pressed | released )? <name> 382 * name := <the KeyEvent field name less the leading "VK_"> 383 * </pre> 384 * 385 * <p>Note that the grammar is rather weak, and not all valid keystrokes 386 * can be generated in this manner (for example, a typed space, or anything 387 * with the alt-graph modifier!). The output of AWTKeyStroke.toString() 388 * will not meet the grammar. If pressed or released is not specified, 389 * pressed is assumed. Examples:<br> 390 * <code> 391 * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br> 392 * "control DELETE" => 393 * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br> 394 * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, 395 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br> 396 * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, 397 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br> 398 * "typed a" => getAWTKeyStroke('a'); 399 * </code> 400 * 401 * @param s the string to parse 402 * @throws IllegalArgumentException if s is null or cannot be parsed 403 * @return the specified keystroke 404 */ getAWTKeyStroke(String s)405 public static AWTKeyStroke getAWTKeyStroke(String s) 406 { 407 if (s == null) 408 throw new IllegalArgumentException("null argument"); 409 StringTokenizer t = new StringTokenizer(s, " "); 410 if (! t.hasMoreTokens()) 411 throw new IllegalArgumentException("no tokens '" + s + "'"); 412 int modifiers = 0; 413 boolean released = false; 414 String token = null; 415 do 416 { 417 token = t.nextToken(); 418 if ("shift".equals(token)) 419 { 420 modifiers |= KeyEvent.SHIFT_MASK; 421 modifiers |= KeyEvent.SHIFT_DOWN_MASK; 422 } 423 else if ("ctrl".equals(token) || "control".equals(token)) 424 { 425 modifiers |= KeyEvent.CTRL_MASK; 426 modifiers |= KeyEvent.CTRL_DOWN_MASK; 427 } 428 else if ("meta".equals(token)) 429 { 430 modifiers |= KeyEvent.META_MASK; 431 modifiers |= KeyEvent.META_DOWN_MASK; 432 } 433 else if ("alt".equals(token)) 434 { 435 modifiers |= KeyEvent.ALT_MASK; 436 modifiers |= KeyEvent.ALT_DOWN_MASK; 437 } 438 else if ("button1".equals(token)) 439 modifiers |= KeyEvent.BUTTON1_DOWN_MASK; 440 else if ("button2".equals(token)) 441 modifiers |= KeyEvent.BUTTON2_DOWN_MASK; 442 else if ("button3".equals(token)) 443 modifiers |= KeyEvent.BUTTON3_DOWN_MASK; 444 else if ("typed".equals(token)) 445 { 446 if (t.hasMoreTokens()) 447 { 448 token = t.nextToken(); 449 if (! t.hasMoreTokens() && token.length() == 1) 450 return getAWTKeyStroke(token.charAt(0), 451 KeyEvent.VK_UNDEFINED, modifiers, 452 false); 453 } 454 throw new IllegalArgumentException("Invalid 'typed' argument '" 455 + s + "'"); 456 } 457 else if ("pressed".equals(token)) 458 { 459 if (t.hasMoreTokens()) 460 token = t.nextToken(); 461 break; 462 } 463 else if ("released".equals(token)) 464 { 465 released = true; 466 if (t.hasMoreTokens()) 467 token = t.nextToken(); 468 break; 469 } 470 else 471 break; 472 } 473 while (t.hasMoreTokens()); 474 // Now token contains the VK name we must parse. 475 Integer code = (Integer) vktable.get(token); 476 if (code == null) 477 throw new IllegalArgumentException("Unknown token '" + token 478 + "' in '" + s + "'"); 479 if (t.hasMoreTokens()) 480 throw new IllegalArgumentException("Too many tokens: " + s); 481 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(), 482 modifiers, released); 483 } 484 485 /** 486 * Returns the character of this keystroke, if it was typed. 487 * 488 * @return the character value, or CHAR_UNDEFINED 489 * @see #getAWTKeyStroke(char) 490 */ getKeyChar()491 public final char getKeyChar() 492 { 493 return keyChar; 494 } 495 496 /** 497 * Returns the virtual key code of this keystroke, if it was pressed or 498 * released. This will be a VK_* constant from KeyEvent. 499 * 500 * @return the virtual key code value, or VK_UNDEFINED 501 * @see #getAWTKeyStroke(int, int) 502 */ getKeyCode()503 public final int getKeyCode() 504 { 505 return keyCode; 506 } 507 508 /** 509 * Returns the modifiers for this keystroke. This will be a bitwise or of 510 * constants from InputEvent; it includes the old style masks for shift, 511 * control, alt, meta, and alt-graph (but not button1); as well as the new 512 * style of extended modifiers for all modifiers. 513 * 514 * @return the modifiers 515 * @see #getAWTKeyStroke(Character, int) 516 * @see #getAWTKeyStroke(int, int) 517 */ getModifiers()518 public final int getModifiers() 519 { 520 return modifiers; 521 } 522 523 /** 524 * Tests if this keystroke is a key release. 525 * 526 * @return true if this is a key release 527 * @see #getAWTKeyStroke(int, int, boolean) 528 */ isOnKeyRelease()529 public final boolean isOnKeyRelease() 530 { 531 return onKeyRelease; 532 } 533 534 /** 535 * Returns the AWT event type of this keystroke. This is one of 536 * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or 537 * {@link KeyEvent#KEY_RELEASED}. 538 * 539 * @return the key event type 540 */ getKeyEventType()541 public final int getKeyEventType() 542 { 543 return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED 544 : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED; 545 } 546 547 /** 548 * Returns a hashcode for this key event. It is not documented, but appears 549 * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1) 550 * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>. 551 * 552 * @return the hashcode 553 */ hashCode()554 public int hashCode() 555 { 556 return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2 557 + (onKeyRelease ? 1 : 2); 558 } 559 560 /** 561 * Tests two keystrokes for equality. 562 * 563 * @param o the object to test 564 * @return true if it is equal 565 */ equals(Object o)566 public final boolean equals(Object o) 567 { 568 if (! (o instanceof AWTKeyStroke)) 569 return false; 570 AWTKeyStroke s = (AWTKeyStroke) o; 571 return this == o || (keyChar == s.keyChar && keyCode == s.keyCode 572 && modifiers == s.modifiers 573 && onKeyRelease == s.onKeyRelease); 574 } 575 576 /** 577 * Returns a string representation of this keystroke. For typed keystrokes, 578 * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers()) 579 + getKeyChar()</code>; for pressed and released keystrokes, this is 580 * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers()) 581 * + KeyEvent.getKeyText(getKeyCode()) 582 * + (isOnKeyRelease() ? "-R" : "-P")</code>. 583 * 584 * @return a string representation 585 */ toString()586 public String toString() 587 { 588 if (keyCode == KeyEvent.VK_UNDEFINED) 589 return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar; 590 return "keyCode " + KeyEvent.getKeyModifiersText(modifiers) 591 + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P"); 592 } 593 594 /** 595 * Returns a cached version of the deserialized keystroke, if available. 596 * 597 * @return a cached replacement 598 * @throws ObjectStreamException if something goes wrong 599 */ readResolve()600 protected Object readResolve() throws ObjectStreamException 601 { 602 AWTKeyStroke s = cache.get(this); 603 if (s != null) 604 return s; 605 cache.put(this, this); 606 return this; 607 } 608 609 /** 610 * Gets the appropriate keystroke, creating one if necessary. 611 * 612 * @param keyChar the keyChar 613 * @param keyCode the keyCode 614 * @param modifiers the modifiers 615 * @param release true for key release 616 * @return the specified keystroke 617 */ getAWTKeyStroke(char keyChar, int keyCode, int modifiers, boolean release)618 private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode, 619 int modifiers, boolean release) 620 { 621 // Check level 0 cache. 622 AWTKeyStroke stroke = recent; // Avoid thread races. 623 if (stroke != null && stroke.keyChar == keyChar 624 && stroke.keyCode == keyCode && stroke.modifiers == modifiers 625 && stroke.onKeyRelease == release) 626 return stroke; 627 // Create a new object, on the assumption that if it has a match in the 628 // cache, the VM can easily garbage collect it as it is temporary. 629 Constructor c = ctor; // Avoid thread races. 630 if (c == null) 631 stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release); 632 else 633 try 634 { 635 stroke = (AWTKeyStroke) c.newInstance(); 636 stroke.keyChar = keyChar; 637 stroke.keyCode = keyCode; 638 stroke.modifiers = modifiers; 639 stroke.onKeyRelease = release; 640 } 641 catch (Exception e) 642 { 643 throw (Error) new InternalError().initCause(e); 644 } 645 // Check level 1 cache. 646 AWTKeyStroke cached = cache.get(stroke); 647 if (cached == null) 648 cache.put(stroke, stroke); 649 else 650 stroke = cached; 651 return recent = stroke; 652 } 653 654 /** 655 * Converts the modifiers to the appropriate format. 656 * 657 * @param mod the modifiers to convert 658 * @return the adjusted modifiers 659 */ extend(int mod)660 private static int extend(int mod) 661 { 662 if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0) 663 mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK; 664 if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0) 665 mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK; 666 if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0) 667 mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK; 668 if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0) 669 mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK; 670 if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0) 671 mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK; 672 if ((mod & KeyEvent.BUTTON1_MASK) != 0) 673 mod |= KeyEvent.BUTTON1_DOWN_MASK; 674 return mod & MODIFIERS_MASK; 675 } 676 } // class AWTKeyStroke 677