2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / awt / AWTKeyStroke.java
blob9848d43ef635a44823948358623597026740c510
1 /* AWTKeyStroke.java -- an immutable key stroke
2 Copyright (C) 2002 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.Map;
52 import java.util.HashMap;
53 import java.util.LinkedHashMap;
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 * @return the specified keystroke
397 * @throws NullPointerException if s is null
398 * @throws IllegalArgumentException if s cannot be parsed
400 public static AWTKeyStroke getAWTKeyStroke(String s)
402 StringTokenizer t = new StringTokenizer(s, " ");
403 if (! t.hasMoreTokens())
404 throw new IllegalArgumentException();
405 int modifiers = 0;
406 boolean released = false;
407 String token = null;
410 token = t.nextToken();
411 if ("shift".equals(token))
412 modifiers |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
413 else if ("ctrl".equals(token) || "control".equals(token))
414 modifiers |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
415 else if ("meta".equals(token))
416 modifiers |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
417 else if ("alt".equals(token))
418 modifiers |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
419 else if ("button1".equals(token))
420 modifiers |= KeyEvent.BUTTON1_DOWN_MASK;
421 else if ("button2".equals(token))
422 modifiers |= KeyEvent.BUTTON2_DOWN_MASK;
423 else if ("button3".equals(token))
424 modifiers |= KeyEvent.BUTTON3_DOWN_MASK;
425 else if ("typed".equals(token))
427 if (t.hasMoreTokens())
429 token = t.nextToken();
430 if (! t.hasMoreTokens() && token.length() == 1)
431 return getAWTKeyStroke(token.charAt(0),
432 KeyEvent.VK_UNDEFINED, modifiers,
433 false);
435 throw new IllegalArgumentException();
437 else if ("pressed".equals(token))
439 if (t.hasMoreTokens())
440 token = t.nextToken();
441 break;
443 else if ("released".equals(token))
445 released = true;
446 if (t.hasMoreTokens())
447 token = t.nextToken();
448 break;
450 else
451 break;
453 while (t.hasMoreTokens());
454 // Now token contains the VK name we must parse.
455 Integer code = (Integer) vktable.get(token);
456 if (code == null || t.hasMoreTokens())
457 throw new IllegalArgumentException();
458 return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(),
459 modifiers, released);
463 * Returns the character of this keystroke, if it was typed.
465 * @return the character value, or CHAR_UNDEFINED
466 * @see #getAWTKeyStroke(char)
468 public final char getKeyChar()
470 return keyChar;
474 * Returns the virtual key code of this keystroke, if it was pressed or
475 * released. This will be a VK_* constant from KeyEvent.
477 * @return the virtual key code value, or VK_UNDEFINED
478 * @see #getAWTKeyStroke(int, int)
480 public final int getKeyCode()
482 return keyCode;
486 * Returns the modifiers for this keystroke. This will be a bitwise or of
487 * constants from InputEvent; it includes the old style masks for shift,
488 * control, alt, meta, and alt-graph (but not button1); as well as the new
489 * style of extended modifiers for all modifiers.
491 * @return the modifiers
492 * @see #getAWTKeyStroke(Character, int)
493 * @see #getAWTKeyStroke(int, int)
495 public final int getModifiers()
497 return modifiers;
501 * Tests if this keystroke is a key release.
503 * @return true if this is a key release
504 * @see #getAWTKeyStroke(int, int, boolean)
506 public final boolean isOnKeyRelease()
508 return onKeyRelease;
512 * Returns the AWT event type of this keystroke. This is one of
513 * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or
514 * {@link KeyEvent#KEY_RELEASED}.
516 * @return the key event type
518 public final int getKeyEventType()
520 return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
521 : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
525 * Returns a hashcode for this key event. It is not documented, but appears
526 * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1)
527 * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>.
529 * @return the hashcode
531 public int hashCode()
533 return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
534 + (onKeyRelease ? 1 : 2);
538 * Tests two keystrokes for equality.
540 * @param o the object to test
541 * @return true if it is equal
543 public final boolean equals(Object o)
545 if (! (o instanceof AWTKeyStroke))
546 return false;
547 AWTKeyStroke s = (AWTKeyStroke) o;
548 return this == o || (keyChar == s.keyChar && keyCode == s.keyCode
549 && modifiers == s.modifiers
550 && onKeyRelease == s.onKeyRelease);
554 * Returns a string representation of this keystroke. For typed keystrokes,
555 * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers())
556 + getKeyChar()</code>; for pressed and released keystrokes, this is
557 * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers())
558 * + KeyEvent.getKeyText(getKeyCode())
559 * + (isOnKeyRelease() ? "-R" : "-P")</code>.
561 * @return a string representation
563 public String toString()
565 if (keyCode == KeyEvent.VK_UNDEFINED)
566 return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar;
567 return "keyCode " + KeyEvent.getKeyModifiersText(modifiers)
568 + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P");
572 * Returns a cached version of the deserialized keystroke, if available.
574 * @return a cached replacement
575 * @throws ObjectStreamException if something goes wrong
577 protected Object readResolve() throws ObjectStreamException
579 AWTKeyStroke s = (AWTKeyStroke) cache.get(this);
580 if (s != null)
581 return s;
582 cache.put(this, this);
583 return this;
587 * Gets the appropriate keystroke, creating one if necessary.
589 * @param keyChar the keyChar
590 * @param keyCode the keyCode
591 * @param modifiers the modifiers
592 * @param release true for key release
593 * @return the specified keystroke
595 private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
596 int modifiers, boolean release)
598 // Check level 0 cache.
599 AWTKeyStroke stroke = recent; // Avoid thread races.
600 if (stroke != null && stroke.keyChar == keyChar
601 && stroke.keyCode == keyCode && stroke.modifiers == modifiers
602 && stroke.onKeyRelease == release)
603 return stroke;
604 // Create a new object, on the assumption that if it has a match in the
605 // cache, the VM can easily garbage collect it as it is temporary.
606 Constructor c = ctor; // Avoid thread races.
607 if (c == null)
608 stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release);
609 else
612 stroke = (AWTKeyStroke) c.newInstance(null);
613 stroke.keyChar = keyChar;
614 stroke.keyCode = keyCode;
615 stroke.modifiers = modifiers;
616 stroke.onKeyRelease = release;
618 catch (Exception e)
620 throw (Error) new InternalError().initCause(e);
622 // Check level 1 cache.
623 AWTKeyStroke cached = (AWTKeyStroke) cache.get(stroke);
624 if (cached == null)
625 cache.put(stroke, stroke);
626 else
627 stroke = cached;
628 return recent = stroke;
632 * Converts the modifiers to the appropriate format.
634 * @param mod the modifiers to convert
635 * @return the adjusted modifiers
637 private static int extend(int mod)
639 if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0)
640 mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
641 if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0)
642 mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
643 if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0)
644 mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
645 if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0)
646 mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
647 if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0)
648 mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK;
649 if ((mod & KeyEvent.BUTTON1_MASK) != 0)
650 mod |= KeyEvent.BUTTON1_DOWN_MASK;
651 return mod & MODIFIERS_MASK;
653 } // class AWTKeyStroke