1 /* AWTKeyStroke.java -- an immutable key stroke
2    Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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.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;
55 
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.
61  *
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).
65  *
66  * @author Eric Blake <ebb9@email.byu.edu>
67  * @see #getAWTKeyStroke(char)
68  * @since 1.4
69  * @status updated to 1.4
70  */
71 public class AWTKeyStroke implements Serializable
72 {
73   /**
74    * Compatible with JDK 1.4+.
75    */
76   private static final long serialVersionUID = -6430539691155161871L;
77 
78   /** The mask for modifiers. */
79   private static final int MODIFIERS_MASK = 0x3fef;
80 
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.
86    */
87   private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true)
88   {
89     /** The largest the keystroke cache can grow. */
90     private static final int MAX_CACHE_SIZE = 2048;
91 
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;
96     }
97   };
98 
99   /** The most recently generated keystroke, or null. */
100   private static AWTKeyStroke recent;
101 
102   /**
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.
107    */
108   private static Constructor ctor;
109 
110   /**
111    * A table of keyCode names to values.
112    *
113    * @see #getAWTKeyStroke(String)
114    */
115   private static final HashMap vktable = new HashMap();
116   static
117   {
118     // Using reflection saves the hassle of keeping this in sync with KeyEvent,
119     // 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; } })120     AccessController.doPrivileged(new PrivilegedAction()
121       {
122         public Object run()
123         {
124           Field[] fields = KeyEvent.class.getFields();
125           int i = fields.length;
126           try
127             {
128               while (--i >= 0)
129                 {
130                   Field f = fields[i];
131                   String name = f.getName();
132                   if (name.startsWith("VK_"))
133                     vktable.put(name.substring(3), f.get(null));
134                 }
135             }
136           catch (Exception e)
137             {
138               throw (Error) new InternalError().initCause(e);
139             }
140           return null;
141         }
142       });
143   }
144 
145   /**
146    * The typed character, or CHAR_UNDEFINED for key presses and releases.
147    *
148    * @serial the keyChar
149    */
150   private char keyChar;
151 
152   /**
153    * The virtual key code, or VK_UNDEFINED for key typed. Package visible for
154    * use by Component.
155    *
156    * @serial the keyCode
157    */
158   int keyCode;
159 
160   /**
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.
164    *
165    * @serial bitwise or of the *_DOWN_MASK modifiers
166    */
167   private int modifiers;
168 
169   /**
170    * True if this is a key release; should only be true if keyChar is
171    * CHAR_UNDEFINED.
172    *
173    * @serial true to distinguish key pressed from key released
174    */
175   private boolean onKeyRelease;
176 
177   /**
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.
181    *
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)
188    */
AWTKeyStroke()189   protected AWTKeyStroke()
190   {
191     keyChar = KeyEvent.CHAR_UNDEFINED;
192   }
193 
194   /**
195    * Construct a keystroke with the given values. Client code should use the
196    * factory methods instead.
197    *
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)
208    */
AWTKeyStroke(char keyChar, int keyCode, int modifiers, boolean onKeyRelease)209   protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
210                          boolean onKeyRelease)
211   {
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;
217   }
218 
219   /**
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).
225    *
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
229    */
registerSubclass(final Class subclass)230   protected static void registerSubclass(final Class subclass)
231   {
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))
238        {
239          cache.clear();
240          recent = null;
241          ctor = null;
242          return;
243        }
244     try
245       {
246         ctor = (Constructor) AccessController.doPrivileged
247           (new PrivilegedExceptionAction()
248             {
249               public Object run()
250                 throws NoSuchMethodException, InstantiationException,
251                        IllegalAccessException, InvocationTargetException
252               {
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;
259               }
260             });
261       }
262     catch (PrivilegedActionException e)
263       {
264         // e.getCause() will not ever be ClassCastException; that should
265         // escape on its own.
266         throw (RuntimeException)
267           new IllegalArgumentException().initCause(e.getCause());
268       }
269     cache.clear();
270     recent = null;
271   }
272 
273   /**
274    * Returns a keystroke representing a typed character.
275    *
276    * @param keyChar the typed character
277    * @return the specified keystroke
278    */
getAWTKeyStroke(char keyChar)279   public static AWTKeyStroke getAWTKeyStroke(char keyChar)
280   {
281     return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
282   }
283 
284   /**
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.
291    *
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
296    */
getAWTKeyStroke(Character keyChar, int modifiers)297   public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
298   {
299     if (keyChar == null)
300       throw new IllegalArgumentException();
301     return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
302                            extend(modifiers), false);
303   }
304 
305   /**
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.
311    *
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
316    */
getAWTKeyStroke(int keyCode, int modifiers, boolean release)317   public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
318                                              boolean release)
319   {
320     return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
321                            extend(modifiers), release);
322   }
323 
324   /**
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.
330    *
331    * @param keyCode the virtual key
332    * @param modifiers the modifiers, or 0
333    * @return the specified keystroke
334    */
getAWTKeyStroke(int keyCode, int modifiers)335   public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers)
336   {
337     return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
338                            extend(modifiers), false);
339   }
340 
341   /**
342    * Returns a keystroke representing what caused the key event.
343    *
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
347    */
getAWTKeyStrokeForEvent(KeyEvent event)348   public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event)
349   {
350     switch (event.id)
351       {
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;
363       }
364   }
365 
366   /**
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>
378    *
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>
394    *
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
399    */
getAWTKeyStroke(String s)400   public static AWTKeyStroke getAWTKeyStroke(String s)
401   {
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;
408     do
409       {
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))
426           {
427             if (t.hasMoreTokens())
428               {
429                 token = t.nextToken();
430                 if (! t.hasMoreTokens() && token.length() == 1)
431                   return getAWTKeyStroke(token.charAt(0),
432                                          KeyEvent.VK_UNDEFINED, modifiers,
433                                          false);
434               }
435             throw new IllegalArgumentException();
436           }
437         else if ("pressed".equals(token))
438           {
439             if (t.hasMoreTokens())
440               token = t.nextToken();
441             break;
442           }
443         else if ("released".equals(token))
444           {
445             released = true;
446             if (t.hasMoreTokens())
447               token = t.nextToken();
448             break;
449           }
450         else
451           break;
452       }
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);
460   }
461 
462   /**
463    * Returns the character of this keystroke, if it was typed.
464    *
465    * @return the character value, or CHAR_UNDEFINED
466    * @see #getAWTKeyStroke(char)
467    */
getKeyChar()468   public final char getKeyChar()
469   {
470     return keyChar;
471   }
472 
473   /**
474    * Returns the virtual key code of this keystroke, if it was pressed or
475    * released. This will be a VK_* constant from KeyEvent.
476    *
477    * @return the virtual key code value, or VK_UNDEFINED
478    * @see #getAWTKeyStroke(int, int)
479    */
getKeyCode()480   public final int getKeyCode()
481   {
482     return keyCode;
483   }
484 
485   /**
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.
490    *
491    * @return the modifiers
492    * @see #getAWTKeyStroke(Character, int)
493    * @see #getAWTKeyStroke(int, int)
494    */
getModifiers()495   public final int getModifiers()
496   {
497     return modifiers;
498   }
499 
500   /**
501    * Tests if this keystroke is a key release.
502    *
503    * @return true if this is a key release
504    * @see #getAWTKeyStroke(int, int, boolean)
505    */
isOnKeyRelease()506   public final boolean isOnKeyRelease()
507   {
508     return onKeyRelease;
509   }
510 
511   /**
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}.
515    *
516    * @return the key event type
517    */
getKeyEventType()518   public final int getKeyEventType()
519   {
520     return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
521       : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
522   }
523 
524   /**
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>.
528    *
529    * @return the hashcode
530    */
hashCode()531   public int hashCode()
532   {
533     return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
534       + (onKeyRelease ? 1 : 2);
535   }
536 
537   /**
538    * Tests two keystrokes for equality.
539    *
540    * @param o the object to test
541    * @return true if it is equal
542    */
equals(Object o)543   public final boolean equals(Object o)
544   {
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);
551   }
552 
553   /**
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>.
560    *
561    * @return a string representation
562    */
toString()563   public String toString()
564   {
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");
569   }
570 
571   /**
572    * Returns a cached version of the deserialized keystroke, if available.
573    *
574    * @return a cached replacement
575    * @throws ObjectStreamException if something goes wrong
576    */
readResolve()577   protected Object readResolve() throws ObjectStreamException
578   {
579     AWTKeyStroke s = (AWTKeyStroke) cache.get(this);
580     if (s != null)
581       return s;
582     cache.put(this, this);
583     return this;
584   }
585 
586   /**
587    * Gets the appropriate keystroke, creating one if necessary.
588    *
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
594    */
getAWTKeyStroke(char keyChar, int keyCode, int modifiers, boolean release)595   private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
596                                               int modifiers, boolean release)
597   {
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
610       try
611         {
612           stroke = (AWTKeyStroke) c.newInstance(null);
613           stroke.keyChar = keyChar;
614           stroke.keyCode = keyCode;
615           stroke.modifiers = modifiers;
616           stroke.onKeyRelease = release;
617         }
618       catch (Exception e)
619         {
620           throw (Error) new InternalError().initCause(e);
621         }
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;
629   }
630 
631   /**
632    * Converts the modifiers to the appropriate format.
633    *
634    * @param mod the modifiers to convert
635    * @return the adjusted modifiers
636    */
extend(int mod)637   private static int extend(int mod)
638   {
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;
652   }
653 } // class AWTKeyStroke
654