Import GNU Classpath (libgcj-import-20070727).
[official-gcc.git] / libjava / classpath / java / awt / AWTKeyStroke.java
blob0fc44287a16c83f362231fa017f1f06f45e3f57c
1 /* AWTKeyStroke.java -- an immutable key stroke
2 Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 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.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;
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.
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).
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
73 public class AWTKeyStroke implements Serializable
75 /**
76 * Compatible with JDK 1.4+.
78 private static final long serialVersionUID = -6430539691155161871L;
80 /** The mask for modifiers. */
81 private static final int MODIFIERS_MASK = 0x3fef;
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.
89 private static final LinkedHashMap<AWTKeyStroke,AWTKeyStroke> cache =
90 new LinkedHashMap<AWTKeyStroke,AWTKeyStroke>(11, 0.75f, true)
92 /** The largest the keystroke cache can grow. */
93 private static final int MAX_CACHE_SIZE = 2048;
95 /** Prune stale entries. */
96 protected boolean removeEldestEntry(Map.Entry<AWTKeyStroke,AWTKeyStroke>
97 eldest)
99 return size() > MAX_CACHE_SIZE;
103 /** The most recently generated keystroke, or null. */
104 private static AWTKeyStroke recent;
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.
112 private static Constructor ctor;
115 * A table of keyCode names to values. This is package-private to
116 * avoid an accessor method.
118 * @see #getAWTKeyStroke(String)
120 static final HashMap<String,Object> vktable = new HashMap<String,Object>();
121 static
123 // Using reflection saves the hassle of keeping this in sync with KeyEvent,
124 // at the price of an expensive initialization.
125 AccessController.doPrivileged(new PrivilegedAction()
127 public Object run()
129 Field[] fields = KeyEvent.class.getFields();
130 int i = fields.length;
133 while (--i >= 0)
135 Field f = fields[i];
136 String name = f.getName();
137 if (name.startsWith("VK_"))
138 vktable.put(name.substring(3), f.get(null));
141 catch (Exception e)
143 throw (Error) new InternalError().initCause(e);
145 return null;
151 * The typed character, or CHAR_UNDEFINED for key presses and releases.
153 * @serial the keyChar
155 private char keyChar;
158 * The virtual key code, or VK_UNDEFINED for key typed. Package visible for
159 * use by Component.
161 * @serial the keyCode
163 int keyCode;
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.
170 * @serial bitwise or of the *_DOWN_MASK modifiers
172 private int modifiers;
175 * True if this is a key release; should only be true if keyChar is
176 * CHAR_UNDEFINED.
178 * @serial true to distinguish key pressed from key released
180 private boolean onKeyRelease;
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.
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)
194 protected AWTKeyStroke()
196 keyChar = KeyEvent.CHAR_UNDEFINED;
200 * Construct a keystroke with the given values. Client code should use the
201 * factory methods instead.
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)
214 protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
215 boolean onKeyRelease)
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;
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).
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
235 protected static void registerSubclass(final Class<?> subclass)
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))
244 cache.clear();
245 recent = null;
246 ctor = null;
247 return;
251 ctor = (Constructor) AccessController.doPrivileged
252 (new PrivilegedExceptionAction()
254 public Object run()
255 throws NoSuchMethodException, InstantiationException,
256 IllegalAccessException, InvocationTargetException
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(null);
264 return c;
268 catch (PrivilegedActionException e)
270 // e.getCause() will not ever be ClassCastException; that should
271 // escape on its own.
272 throw (RuntimeException)
273 new IllegalArgumentException().initCause(e.getCause());
275 cache.clear();
276 recent = null;
280 * Returns a keystroke representing a typed character.
282 * @param keyChar the typed character
283 * @return the specified keystroke
285 public static AWTKeyStroke getAWTKeyStroke(char keyChar)
287 return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
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.
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
303 public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
305 if (keyChar == null)
306 throw new IllegalArgumentException();
307 return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
308 extend(modifiers), false);
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.
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
323 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
324 boolean release)
326 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
327 extend(modifiers), release);
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.
337 * @param keyCode the virtual key
338 * @param modifiers the modifiers, or 0
339 * @return the specified keystroke
341 public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers)
343 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
344 extend(modifiers), false);
348 * Returns a keystroke representing what caused the key event.
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
354 public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event)
356 switch (event.id)
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;
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 := &lt;modifiers&gt;* ( &lt;typedID&gt; | &lt;codeID&gt; )
378 * modifiers := ( shift | control | ctrl | meta | alt
379 * | button1 | button2 | button3 )
380 * typedID := typed &lt;single Unicode character&gt;
381 * codeID := ( pressed | released )? &lt;name&gt;
382 * name := &lt;the KeyEvent field name less the leading "VK_"&gt;
383 * </pre>
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" =&gt; getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br>
392 * "control DELETE" =&gt;
393 * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br>
394 * "alt shift X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
395 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br>
396 * "alt shift released X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
397 * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br>
398 * "typed a" =&gt; getAWTKeyStroke('a');
399 * </code>
401 * @param s the string to parse
402 * @throws IllegalArgumentException if s is null or cannot be parsed
403 * @return the specified keystroke
405 public static AWTKeyStroke getAWTKeyStroke(String s)
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;
417 token = t.nextToken();
418 if ("shift".equals(token))
420 modifiers |= KeyEvent.SHIFT_MASK;
421 modifiers |= KeyEvent.SHIFT_DOWN_MASK;
423 else if ("ctrl".equals(token) || "control".equals(token))
425 modifiers |= KeyEvent.CTRL_MASK;
426 modifiers |= KeyEvent.CTRL_DOWN_MASK;
428 else if ("meta".equals(token))
430 modifiers |= KeyEvent.META_MASK;
431 modifiers |= KeyEvent.META_DOWN_MASK;
433 else if ("alt".equals(token))
435 modifiers |= KeyEvent.ALT_MASK;
436 modifiers |= KeyEvent.ALT_DOWN_MASK;
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))
446 if (t.hasMoreTokens())
448 token = t.nextToken();
449 if (! t.hasMoreTokens() && token.length() == 1)
450 return getAWTKeyStroke(token.charAt(0),
451 KeyEvent.VK_UNDEFINED, modifiers,
452 false);
454 throw new IllegalArgumentException("Invalid 'typed' argument '"
455 + s + "'");
457 else if ("pressed".equals(token))
459 if (t.hasMoreTokens())
460 token = t.nextToken();
461 break;
463 else if ("released".equals(token))
465 released = true;
466 if (t.hasMoreTokens())
467 token = t.nextToken();
468 break;
470 else
471 break;
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);
486 * Returns the character of this keystroke, if it was typed.
488 * @return the character value, or CHAR_UNDEFINED
489 * @see #getAWTKeyStroke(char)
491 public final char getKeyChar()
493 return keyChar;
497 * Returns the virtual key code of this keystroke, if it was pressed or
498 * released. This will be a VK_* constant from KeyEvent.
500 * @return the virtual key code value, or VK_UNDEFINED
501 * @see #getAWTKeyStroke(int, int)
503 public final int getKeyCode()
505 return keyCode;
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.
514 * @return the modifiers
515 * @see #getAWTKeyStroke(Character, int)
516 * @see #getAWTKeyStroke(int, int)
518 public final int getModifiers()
520 return modifiers;
524 * Tests if this keystroke is a key release.
526 * @return true if this is a key release
527 * @see #getAWTKeyStroke(int, int, boolean)
529 public final boolean isOnKeyRelease()
531 return onKeyRelease;
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}.
539 * @return the key event type
541 public final int getKeyEventType()
543 return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
544 : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
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>.
552 * @return the hashcode
554 public int hashCode()
556 return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
557 + (onKeyRelease ? 1 : 2);
561 * Tests two keystrokes for equality.
563 * @param o the object to test
564 * @return true if it is equal
566 public final boolean equals(Object o)
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);
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>.
584 * @return a string representation
586 public String toString()
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");
595 * Returns a cached version of the deserialized keystroke, if available.
597 * @return a cached replacement
598 * @throws ObjectStreamException if something goes wrong
600 protected Object readResolve() throws ObjectStreamException
602 AWTKeyStroke s = cache.get(this);
603 if (s != null)
604 return s;
605 cache.put(this, this);
606 return this;
610 * Gets the appropriate keystroke, creating one if necessary.
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
618 private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
619 int modifiers, boolean release)
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
635 stroke = (AWTKeyStroke) c.newInstance(null);
636 stroke.keyChar = keyChar;
637 stroke.keyCode = keyCode;
638 stroke.modifiers = modifiers;
639 stroke.onKeyRelease = release;
641 catch (Exception e)
643 throw (Error) new InternalError().initCause(e);
645 // Check level 1 cache.
646 AWTKeyStroke cached = (AWTKeyStroke) cache.get(stroke);
647 if (cached == null)
648 cache.put(stroke, stroke);
649 else
650 stroke = cached;
651 return recent = stroke;
655 * Converts the modifiers to the appropriate format.
657 * @param mod the modifiers to convert
658 * @return the adjusted modifiers
660 private static int extend(int mod)
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;
676 } // class AWTKeyStroke