AWTKeyStroke.java 22 KB

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