Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / awt / AWTKeyStroke.java
blob977b3848d6d2b0724cd9d32188ce803b4ed99031
1 /* AWTKeyStroke.java -- an immutable key stroke
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation
4 This file is part of GNU Classpath.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
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.
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. */
39 package java.awt;
41 import java.awt.event.KeyEvent;
42 import java.io.ObjectStreamException;
43 import java.io.Serializable;
44 import java.lang.reflect.Constructor;
45 import java.lang.reflect.Field;
46 import java.lang.reflect.InvocationTargetException;
47 import java.security.AccessController;
48 import java.security.PrivilegedAction;
49 import java.security.PrivilegedActionException;
50 import java.security.PrivilegedExceptionAction;
51 import java.util.HashMap;
52 import java.util.LinkedHashMap;
53 import java.util.Map;
54 import java.util.StringTokenizer;
56 /**
57 * This class mirrors KeyEvents, representing both low-level key presses and
58 * key releases, and high level key typed inputs. However, this class forms
59 * immutable strokes, and can be efficiently reused via the factory methods
60 * for creating them.
62 * <p>For backwards compatibility with Swing, this supports a way to build
63 * instances of a subclass, using reflection, provided the subclass has a
64 * no-arg constructor (of any accessibility).
66 * @author Eric Blake (ebb9@email.byu.edu)
67 * @see #getAWTKeyStroke(char)
68 * @since 1.4
69 * @status updated to 1.4
71 public class AWTKeyStroke implements Serializable
73 /**
74 * Compatible with JDK 1.4+.
76 private static final long serialVersionUID = -6430539691155161871L;
78 /** The mask for modifiers. */
79 private static final int MODIFIERS_MASK = 0x3fef;
81 /**
82 * The cache of recently created keystrokes. This maps KeyStrokes to
83 * KeyStrokes in a cache which removes the least recently accessed entry,
84 * under the assumption that garbage collection of a new keystroke is
85 * easy when we find the old one that it matches in the cache.
87 private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true)
89 /** The largest the keystroke cache can grow. */
90 private static final int MAX_CACHE_SIZE = 2048;
92 /** Prune stale entries. */
93 protected boolean removeEldestEntry(Map.Entry eldest)
94 { // XXX - FIXME Use Map.Entry, not just Entry as gcj 3.1 workaround.
95 return size() > MAX_CACHE_SIZE;
99 /** The most recently generated keystroke, or null. */
100 private static AWTKeyStroke recent;
103 * The no-arg constructor of a subclass, or null to use AWTKeyStroke. Note
104 * that this will be left accessible, to get around private access; but
105 * it should not be a security risk as it is highly unlikely that creating
106 * protected instances of the subclass via reflection will do much damage.
108 private static Constructor ctor;
111 * A table of keyCode names to values.
113 * @see #getAWTKeyStroke(String)
115 private static final HashMap vktable = new HashMap();
116 static
118 // Using reflection saves the hassle of keeping this in sync with KeyEvent,
119 // at the price of an expensive initialization.
120 AccessController.doPrivileged(new PrivilegedAction()
122 public Object run()
124 Field[] fields = KeyEvent.class.getFields();
125 int i = fields.length;
128 while (--i >= 0)
130 Field f = fields[i];
131 String name = f.getName();
132 if (name.startsWith("VK_"))
133 vktable.put(name.substring(3), f.get(null));
136 catch (Exception e)
138 throw (Error) new InternalError().initCause(e);
140 return null;
146 * The typed character, or CHAR_UNDEFINED for key presses and releases.
148 * @serial the keyChar
150 private char keyChar;
153 * The virtual key code, or VK_UNDEFINED for key typed. Package visible for
154 * use by Component.
156 * @serial the keyCode
158 int keyCode;
161 * The modifiers in effect. To match Sun, this stores the old style masks
162 * for shift, control, alt, meta, and alt-graph (but not button1); as well
163 * as the new style of extended modifiers for all modifiers.
165 * @serial bitwise or of the *_DOWN_MASK modifiers
167 private int modifiers;
170 * True if this is a key release; should only be true if keyChar is
171 * CHAR_UNDEFINED.
173 * @serial true to distinguish key pressed from key released
175 private boolean onKeyRelease;
178 * Construct a keystroke with default values: it will be interpreted as a
179 * key typed event with an invalid character and no modifiers. Client code
180 * should use the factory methods instead.
182 * @see #getAWTKeyStroke(char)
183 * @see #getAWTKeyStroke(Character, int)
184 * @see #getAWTKeyStroke(int, int, boolean)
185 * @see #getAWTKeyStroke(int, int)
186 * @see #getAWTKeyStrokeForEvent(KeyEvent)
187 * @see #getAWTKeyStroke(String)
189 protected AWTKeyStroke()
191 keyChar = KeyEvent.CHAR_UNDEFINED;
195 * Construct a keystroke with the given values. Client code should use the
196 * factory methods instead.
198 * @param keyChar the character entered, if this is a key typed
199 * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed
200 * @param modifiers the modifier keys for the keystroke, in old or new style
201 * @param onKeyRelease true if this is a key release instead of a press
202 * @see #getAWTKeyStroke(char)
203 * @see #getAWTKeyStroke(Character, int)
204 * @see #getAWTKeyStroke(int, int, boolean)
205 * @see #getAWTKeyStroke(int, int)
206 * @see #getAWTKeyStrokeForEvent(KeyEvent)
207 * @see #getAWTKeyStroke(String)
209 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
210 boolean onKeyRelease)
212 this.keyChar = keyChar;
213 this.keyCode = keyCode;
214 // No need to call extend(), as only trusted code calls this constructor.
215 this.modifiers = modifiers;
216 this.onKeyRelease = onKeyRelease;
220 * Registers a new subclass as being the type of keystrokes to generate in
221 * the factory methods. This operation flushes the cache of stored keystrokes
222 * if the class differs from the current one. The new class must be
223 * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may
224 * be private).
226 * @param subclass the new runtime type of generated keystrokes
227 * @throws IllegalArgumentException subclass doesn't have no-arg constructor
228 * @throws ClassCastException subclass doesn't extend AWTKeyStroke
230 protected static void registerSubclass(final Class subclass)
232 if (subclass == null)
233 throw new IllegalArgumentException();
234 if (subclass.equals(ctor == null ? AWTKeyStroke.class
235 : ctor.getDeclaringClass()))
236 return;
237 if (subclass.equals(AWTKeyStroke.class))
239 cache.clear();
240 recent = null;
241 ctor = null;
242 return;
246 ctor = (Constructor) AccessController.doPrivileged
247 (new PrivilegedExceptionAction()
249 public Object run()
250 throws NoSuchMethodException, InstantiationException,
251 IllegalAccessException, InvocationTargetException
253 Constructor c = subclass.getDeclaredConstructor(null);
254 c.setAccessible(true);
255 // Create a new instance, to make sure that we can, and
256 // to cause any ClassCastException.
257 AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance(null);
258 return c;
262 catch (PrivilegedActionException e)
264 // e.getCause() will not ever be ClassCastException; that should
265 // escape on its own.
266 throw (RuntimeException)
267 new IllegalArgumentException().initCause(e.getCause());
269 cache.clear();
270 recent = null;
274 * Returns a keystroke representing a typed character.
276 * @param keyChar the typed character
277 * @return the specified keystroke
279 public static AWTKeyStroke getAWTKeyStroke(char keyChar)
281 return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
285 * Returns a keystroke representing a typed character with the given
286 * modifiers. Note that keyChar is a <code>Character</code> instead of a
287 * <code>char</code> to avoid accidental ambiguity with
288 * <code>getAWTKeyStroke(int, int)</code>. The modifiers are the bitwise
289 * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK)
290 * is preferred, but the old style will work.
292 * @param keyChar the typed character
293 * @param modifiers the modifiers, or 0
294 * @return the specified keystroke
295 * @throws IllegalArgumentException if keyChar is null
297 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
299 if (keyChar == null)
300 throw new IllegalArgumentException();
301 return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
302 extend(modifiers), false);
306 * Returns a keystroke representing a pressed or released key event, with
307 * the given modifiers. The "virtual key" should be one of the VK_*
308 * constants in {@link KeyEvent}. The modifiers are the bitwise or of the
309 * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is
310 * preferred, but the old style will work.
312 * @param keyCode the virtual key
313 * @param modifiers the modifiers, or 0
314 * @param release true if this is a key release instead of a key press
315 * @return the specified keystroke
317 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
318 boolean release)
320 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
321 extend(modifiers), release);
325 * Returns a keystroke representing a pressed key event, with the given
326 * modifiers. The "virtual key" should be one of the VK_* constants in
327 * {@link KeyEvent}. The modifiers are the bitwise or of the masks found
328 * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the
329 * old style will work.
331 * @param keyCode the virtual key
332 * @param modifiers the modifiers, or 0
333 * @return the specified keystroke
335 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers)
337 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
338 extend(modifiers), false);
342 * Returns a keystroke representing what caused the key event.
344 * @param event the key event to convert
345 * @return the specified keystroke, or null if the event is invalid
346 * @throws NullPointerException if event is null
348 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event)
350 switch (event.id)
352 case KeyEvent.KEY_TYPED:
353 return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED,
354 extend(event.getModifiersEx()), false);
355 case KeyEvent.KEY_PRESSED:
356 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
357 extend(event.getModifiersEx()), false);
358 case KeyEvent.KEY_RELEASED:
359 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
360 extend(event.getModifiersEx()), true);
361 default:
362 return null;
367 * Parses a string and returns the keystroke that it represents. The syntax
368 * for keystrokes is listed below, with tokens separated by an arbitrary
369 * number of spaces:
370 * <pre>
371 * keyStroke := &lt;modifiers&gt;* ( &lt;typedID&gt; | &lt;codeID&gt; )
372 * modifiers := ( shift | control | ctrl | meta | alt
373 * | button1 | button2 | button3 )
374 * typedID := typed &lt;single Unicode character&gt;
375 * codeID := ( pressed | released )? &lt;name&gt;
376 * name := &lt;the KeyEvent field name less the leading "VK_"&gt;
377 * </pre>
379 * <p>Note that the grammar is rather weak, and not all valid keystrokes
380 * can be generated in this manner (for example, a typed space, or anything
381 * with the alt-graph modifier!). The output of AWTKeyStroke.toString()
382 * will not meet the grammar. If pressed or released is not specified,
383 * pressed is assumed. Examples:<br>
384 * <code>
385 * "INSERT" =&gt; getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br>
386 * "control DELETE" =&gt;
387 * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br>
388 * "alt shift X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
389 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br>
390 * "alt shift released X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
391 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br>
392 * "typed a" =&gt; getAWTKeyStroke('a');
393 * </code>
395 * @param s the string to parse
396 * @throws IllegalArgumentException if s is null or cannot be parsed
397 * @return the specified keystroke
399 public static AWTKeyStroke getAWTKeyStroke(String s)
401 if (s == null)
402 throw new IllegalArgumentException("null argument");
403 StringTokenizer t = new StringTokenizer(s, " ");
404 if (! t.hasMoreTokens())
405 throw new IllegalArgumentException("no tokens '" + s + "'");
406 int modifiers = 0;
407 boolean released = false;
408 String token = null;
411 token = t.nextToken();
412 if ("shift".equals(token))
413 modifiers |= KeyEvent.SHIFT_DOWN_MASK;
414 else if ("ctrl".equals(token) || "control".equals(token))
415 modifiers |= KeyEvent.CTRL_DOWN_MASK;
416 else if ("meta".equals(token))
417 modifiers |= KeyEvent.META_DOWN_MASK;
418 else if ("alt".equals(token))
419 modifiers |= KeyEvent.ALT_DOWN_MASK;
420 else if ("button1".equals(token))
421 modifiers |= KeyEvent.BUTTON1_DOWN_MASK;
422 else if ("button2".equals(token))
423 modifiers |= KeyEvent.BUTTON2_DOWN_MASK;
424 else if ("button3".equals(token))
425 modifiers |= KeyEvent.BUTTON3_DOWN_MASK;
426 else if ("typed".equals(token))
428 if (t.hasMoreTokens())
430 token = t.nextToken();
431 if (! t.hasMoreTokens() && token.length() == 1)
432 return getAWTKeyStroke(token.charAt(0),
433 KeyEvent.VK_UNDEFINED, modifiers,
434 false);
436 throw new IllegalArgumentException("Invalid 'typed' argument '"
437 + s + "'");
439 else if ("pressed".equals(token))
441 if (t.hasMoreTokens())
442 token = t.nextToken();
443 break;
445 else if ("released".equals(token))
447 released = true;
448 if (t.hasMoreTokens())
449 token = t.nextToken();
450 break;
452 else
453 break;
455 while (t.hasMoreTokens());
456 // Now token contains the VK name we must parse.
457 Integer code = (Integer) vktable.get(token);
458 if (code == null)
459 throw new IllegalArgumentException("Unknown token '" + token
460 + "' in '" + s + "'");
461 if (t.hasMoreTokens())
462 throw new IllegalArgumentException("Too many tokens: " + s);
463 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(),
464 modifiers, released);
468 * Returns the character of this keystroke, if it was typed.
470 * @return the character value, or CHAR_UNDEFINED
471 * @see #getAWTKeyStroke(char)
473 public final char getKeyChar()
475 return keyChar;
479 * Returns the virtual key code of this keystroke, if it was pressed or
480 * released. This will be a VK_* constant from KeyEvent.
482 * @return the virtual key code value, or VK_UNDEFINED
483 * @see #getAWTKeyStroke(int, int)
485 public final int getKeyCode()
487 return keyCode;
491 * Returns the modifiers for this keystroke. This will be a bitwise or of
492 * constants from InputEvent; it includes the old style masks for shift,
493 * control, alt, meta, and alt-graph (but not button1); as well as the new
494 * style of extended modifiers for all modifiers.
496 * @return the modifiers
497 * @see #getAWTKeyStroke(Character, int)
498 * @see #getAWTKeyStroke(int, int)
500 public final int getModifiers()
502 return modifiers;
506 * Tests if this keystroke is a key release.
508 * @return true if this is a key release
509 * @see #getAWTKeyStroke(int, int, boolean)
511 public final boolean isOnKeyRelease()
513 return onKeyRelease;
517 * Returns the AWT event type of this keystroke. This is one of
518 * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or
519 * {@link KeyEvent#KEY_RELEASED}.
521 * @return the key event type
523 public final int getKeyEventType()
525 return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
526 : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
530 * Returns a hashcode for this key event. It is not documented, but appears
531 * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1)
532 * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>.
534 * @return the hashcode
536 public int hashCode()
538 return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
539 + (onKeyRelease ? 1 : 2);
543 * Tests two keystrokes for equality.
545 * @param o the object to test
546 * @return true if it is equal
548 public final boolean equals(Object o)
550 if (! (o instanceof AWTKeyStroke))
551 return false;
552 AWTKeyStroke s = (AWTKeyStroke) o;
553 return this == o || (keyChar == s.keyChar && keyCode == s.keyCode
554 && modifiers == s.modifiers
555 && onKeyRelease == s.onKeyRelease);
559 * Returns a string representation of this keystroke. For typed keystrokes,
560 * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers())
561 + getKeyChar()</code>; for pressed and released keystrokes, this is
562 * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers())
563 * + KeyEvent.getKeyText(getKeyCode())
564 * + (isOnKeyRelease() ? "-R" : "-P")</code>.
566 * @return a string representation
568 public String toString()
570 if (keyCode == KeyEvent.VK_UNDEFINED)
571 return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar;
572 return "keyCode " + KeyEvent.getKeyModifiersText(modifiers)
573 + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P");
577 * Returns a cached version of the deserialized keystroke, if available.
579 * @return a cached replacement
580 * @throws ObjectStreamException if something goes wrong
582 protected Object readResolve() throws ObjectStreamException
584 AWTKeyStroke s = (AWTKeyStroke) cache.get(this);
585 if (s != null)
586 return s;
587 cache.put(this, this);
588 return this;
592 * Gets the appropriate keystroke, creating one if necessary.
594 * @param keyChar the keyChar
595 * @param keyCode the keyCode
596 * @param modifiers the modifiers
597 * @param release true for key release
598 * @return the specified keystroke
600 private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
601 int modifiers, boolean release)
603 // Check level 0 cache.
604 AWTKeyStroke stroke = recent; // Avoid thread races.
605 if (stroke != null && stroke.keyChar == keyChar
606 && stroke.keyCode == keyCode && stroke.modifiers == modifiers
607 && stroke.onKeyRelease == release)
608 return stroke;
609 // Create a new object, on the assumption that if it has a match in the
610 // cache, the VM can easily garbage collect it as it is temporary.
611 Constructor c = ctor; // Avoid thread races.
612 if (c == null)
613 stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release);
614 else
617 stroke = (AWTKeyStroke) c.newInstance(null);
618 stroke.keyChar = keyChar;
619 stroke.keyCode = keyCode;
620 stroke.modifiers = modifiers;
621 stroke.onKeyRelease = release;
623 catch (Exception e)
625 throw (Error) new InternalError().initCause(e);
627 // Check level 1 cache.
628 AWTKeyStroke cached = (AWTKeyStroke) cache.get(stroke);
629 if (cached == null)
630 cache.put(stroke, stroke);
631 else
632 stroke = cached;
633 return recent = stroke;
637 * Converts the modifiers to the appropriate format.
639 * @param mod the modifiers to convert
640 * @return the adjusted modifiers
642 private static int extend(int mod)
644 if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0)
645 mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
646 if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0)
647 mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
648 if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0)
649 mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
650 if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0)
651 mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
652 if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0)
653 mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK;
654 if ((mod & KeyEvent.BUTTON1_MASK) != 0)
655 mod |= KeyEvent.BUTTON1_DOWN_MASK;
656 return mod & MODIFIERS_MASK;
658 } // class AWTKeyStroke