OSDN Git Service

* java/awt/AWTKeyStroke.java (vktable): Now package-private.
[pf3gnuchains/gcc-fork.git] / libjava / java / awt / AWTKeyStroke.java
1 /* AWTKeyStroke.java -- an immutable key stroke
2    Copyright (C) 2002, 2004, 2005  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.HashMap;
52 import java.util.LinkedHashMap;
53 import java.util.Map;
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.  This is package-private to
112    * avoid an accessor method.
113    *
114    * @see #getAWTKeyStroke(String)
115    */
116   static final HashMap vktable = new HashMap();
117   static
118   {
119     // Using reflection saves the hassle of keeping this in sync with KeyEvent,
120     // at the price of an expensive initialization.
121     AccessController.doPrivileged(new PrivilegedAction()
122       {
123         public Object run()
124         {
125           Field[] fields = KeyEvent.class.getFields();
126           int i = fields.length;
127           try
128             {
129               while (--i >= 0)
130                 {
131                   Field f = fields[i];
132                   String name = f.getName();
133                   if (name.startsWith("VK_"))
134                     vktable.put(name.substring(3), f.get(null));
135                 }
136             }
137           catch (Exception e)
138             {
139               throw (Error) new InternalError().initCause(e);
140             }
141           return null;
142         }
143       });
144   }
145
146   /**
147    * The typed character, or CHAR_UNDEFINED for key presses and releases.
148    *
149    * @serial the keyChar
150    */
151   private char keyChar;
152
153   /**
154    * The virtual key code, or VK_UNDEFINED for key typed. Package visible for
155    * use by Component.
156    *
157    * @serial the keyCode
158    */
159   int keyCode;
160
161   /**
162    * The modifiers in effect. To match Sun, this stores the old style masks
163    * for shift, control, alt, meta, and alt-graph (but not button1); as well
164    * as the new style of extended modifiers for all modifiers.
165    *
166    * @serial bitwise or of the *_DOWN_MASK modifiers
167    */
168   private int modifiers;
169
170   /**
171    * True if this is a key release; should only be true if keyChar is
172    * CHAR_UNDEFINED.
173    *
174    * @serial true to distinguish key pressed from key released
175    */
176   private boolean onKeyRelease;
177
178   /**
179    * Construct a keystroke with default values: it will be interpreted as a
180    * key typed event with an invalid character and no modifiers. Client code
181    * should use the factory methods instead.
182    *
183    * @see #getAWTKeyStroke(char)
184    * @see #getAWTKeyStroke(Character, int)
185    * @see #getAWTKeyStroke(int, int, boolean)
186    * @see #getAWTKeyStroke(int, int)
187    * @see #getAWTKeyStrokeForEvent(KeyEvent)
188    * @see #getAWTKeyStroke(String)
189    */
190   protected AWTKeyStroke()
191   {
192     keyChar = KeyEvent.CHAR_UNDEFINED;
193   }
194
195   /**
196    * Construct a keystroke with the given values. Client code should use the
197    * factory methods instead.
198    *
199    * @param keyChar the character entered, if this is a key typed
200    * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed
201    * @param modifiers the modifier keys for the keystroke, in old or new style
202    * @param onKeyRelease true if this is a key release instead of a press
203    * @see #getAWTKeyStroke(char)
204    * @see #getAWTKeyStroke(Character, int)
205    * @see #getAWTKeyStroke(int, int, boolean)
206    * @see #getAWTKeyStroke(int, int)
207    * @see #getAWTKeyStrokeForEvent(KeyEvent)
208    * @see #getAWTKeyStroke(String)
209    */
210   protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
211                          boolean onKeyRelease)
212   {
213     this.keyChar = keyChar;
214     this.keyCode = keyCode;
215     // No need to call extend(), as only trusted code calls this constructor.
216     this.modifiers = modifiers;
217     this.onKeyRelease = onKeyRelease;
218   }
219
220   /**
221    * Registers a new subclass as being the type of keystrokes to generate in
222    * the factory methods. This operation flushes the cache of stored keystrokes
223    * if the class differs from the current one. The new class must be
224    * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may
225    * be private).
226    *
227    * @param subclass the new runtime type of generated keystrokes
228    * @throws IllegalArgumentException subclass doesn't have no-arg constructor
229    * @throws ClassCastException subclass doesn't extend AWTKeyStroke
230    */
231   protected static void registerSubclass(final Class subclass)
232   {
233     if (subclass == null)
234       throw new IllegalArgumentException();
235     if (subclass.equals(ctor == null ? AWTKeyStroke.class
236                         : ctor.getDeclaringClass()))
237       return;
238     if (subclass.equals(AWTKeyStroke.class))
239        {
240          cache.clear();
241          recent = null;
242          ctor = null;
243          return;
244        }
245     try
246       {
247         ctor = (Constructor) AccessController.doPrivileged
248           (new PrivilegedExceptionAction()
249             {
250               public Object run()
251                 throws NoSuchMethodException, InstantiationException,
252                        IllegalAccessException, InvocationTargetException
253               {
254                 Constructor c = subclass.getDeclaredConstructor(null);
255                 c.setAccessible(true);
256                 // Create a new instance, to make sure that we can, and
257                 // to cause any ClassCastException.
258                 AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance(null);
259                 return c;
260               }
261             });
262       }
263     catch (PrivilegedActionException e)
264       {
265         // e.getCause() will not ever be ClassCastException; that should
266         // escape on its own.
267         throw (RuntimeException)
268           new IllegalArgumentException().initCause(e.getCause());
269       }
270     cache.clear();
271     recent = null;
272   }
273
274   /**
275    * Returns a keystroke representing a typed character.
276    *
277    * @param keyChar the typed character 
278    * @return the specified keystroke
279    */
280   public static AWTKeyStroke getAWTKeyStroke(char keyChar)
281   {
282     return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
283   }
284
285   /**
286    * Returns a keystroke representing a typed character with the given
287    * modifiers. Note that keyChar is a <code>Character</code> instead of a
288    * <code>char</code> to avoid accidental ambiguity with
289    * <code>getAWTKeyStroke(int, int)</code>. The modifiers are the bitwise
290    * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK)
291    * is preferred, but the old style will work.
292    *
293    * @param keyChar the typed character
294    * @param modifiers the modifiers, or 0
295    * @return the specified keystroke
296    * @throws IllegalArgumentException if keyChar is null
297    */
298   public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)
299   {
300     if (keyChar == null)
301       throw new IllegalArgumentException();
302     return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
303                            extend(modifiers), false);
304   }
305
306   /**
307    * Returns a keystroke representing a pressed or released key event, with
308    * the given modifiers. The "virtual key" should be one of the VK_*
309    * constants in {@link KeyEvent}. The modifiers are the bitwise or of the
310    * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is
311    * preferred, but the old style will work.
312    *
313    * @param keyCode the virtual key
314    * @param modifiers the modifiers, or 0
315    * @param release true if this is a key release instead of a key press
316    * @return the specified keystroke
317    */
318   public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
319                                              boolean release)
320   {
321     return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
322                            extend(modifiers), release);
323   }
324
325   /**
326    * Returns a keystroke representing a pressed key event, with the given
327    * modifiers. The "virtual key" should be one of the VK_* constants in
328    * {@link KeyEvent}. The modifiers are the bitwise or of the masks found
329    * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the
330    * old style will work.
331    *
332    * @param keyCode the virtual key
333    * @param modifiers the modifiers, or 0
334    * @return the specified keystroke
335    */
336   public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers)
337   {
338     return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode,
339                            extend(modifiers), false);
340   }
341
342   /**
343    * Returns a keystroke representing what caused the key event.
344    *
345    * @param event the key event to convert
346    * @return the specified keystroke, or null if the event is invalid
347    * @throws NullPointerException if event is null
348    */
349   public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event)
350   {
351     switch (event.id)
352       {
353       case KeyEvent.KEY_TYPED:
354         return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED,
355                                extend(event.getModifiersEx()), false);
356       case KeyEvent.KEY_PRESSED:
357         return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
358                                extend(event.getModifiersEx()), false);
359       case KeyEvent.KEY_RELEASED:
360         return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(),
361                                extend(event.getModifiersEx()), true);
362       default:
363         return null;
364       }
365   }
366
367   /**
368    * Parses a string and returns the keystroke that it represents. The syntax
369    * for keystrokes is listed below, with tokens separated by an arbitrary
370    * number of spaces:
371    * <pre>
372    * keyStroke := &lt;modifiers&gt;* ( &lt;typedID&gt; | &lt;codeID&gt; )
373    * modifiers := ( shift | control | ctrl | meta | alt
374    *                | button1 | button2 | button3 )
375    * typedID := typed &lt;single Unicode character&gt;
376    * codeID := ( pressed | released )? &lt;name&gt;
377    * name := &lt;the KeyEvent field name less the leading "VK_"&gt;
378    * </pre>
379    *
380    * <p>Note that the grammar is rather weak, and not all valid keystrokes
381    * can be generated in this manner (for example, a typed space, or anything
382    * with the alt-graph modifier!). The output of AWTKeyStroke.toString()
383    * will not meet the grammar. If pressed or released is not specified,
384    * pressed is assumed. Examples:<br>
385    * <code>
386    * "INSERT" =&gt; getAWTKeyStroke(KeyEvent.VK_INSERT, 0);<br>
387    * "control DELETE" =&gt;
388    *    getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);<br>
389    * "alt shift X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
390    *    InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);<br>
391    * "alt shift released X" =&gt; getAWTKeyStroke(KeyEvent.VK_X,
392    *    InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);<br>
393    * "typed a" =&gt; getAWTKeyStroke('a');
394    * </code>      
395    *
396    * @param s the string to parse
397    * @throws IllegalArgumentException if s is null or cannot be parsed
398    * @return the specified keystroke
399    */
400   public static AWTKeyStroke getAWTKeyStroke(String s)
401   {
402     if (s == null)
403       throw new IllegalArgumentException("null argument");
404     StringTokenizer t = new StringTokenizer(s, " ");
405     if (! t.hasMoreTokens())
406       throw new IllegalArgumentException("no tokens '" + s + "'");
407     int modifiers = 0;
408     boolean released = false;
409     String token = null;
410     do
411       {
412         token = t.nextToken();
413         if ("shift".equals(token))
414           modifiers |= KeyEvent.SHIFT_DOWN_MASK;
415         else if ("ctrl".equals(token) || "control".equals(token))
416           modifiers |= KeyEvent.CTRL_DOWN_MASK;
417         else if ("meta".equals(token))
418           modifiers |= KeyEvent.META_DOWN_MASK;
419         else if ("alt".equals(token))
420           modifiers |= KeyEvent.ALT_DOWN_MASK;
421         else if ("button1".equals(token))
422           modifiers |= KeyEvent.BUTTON1_DOWN_MASK;
423         else if ("button2".equals(token))
424           modifiers |= KeyEvent.BUTTON2_DOWN_MASK;
425         else if ("button3".equals(token))
426           modifiers |= KeyEvent.BUTTON3_DOWN_MASK;
427         else if ("typed".equals(token))
428           {
429             if (t.hasMoreTokens())
430               {
431                 token = t.nextToken();
432                 if (! t.hasMoreTokens() && token.length() == 1)
433                   return getAWTKeyStroke(token.charAt(0),
434                                          KeyEvent.VK_UNDEFINED, modifiers,
435                                          false);
436               }
437             throw new IllegalArgumentException("Invalid 'typed' argument '"
438                                                + s + "'");
439           }
440         else if ("pressed".equals(token))
441           {
442             if (t.hasMoreTokens())
443               token = t.nextToken();
444             break;
445           }
446         else if ("released".equals(token))
447           {
448             released = true;
449             if (t.hasMoreTokens())
450               token = t.nextToken();
451             break;
452           }
453         else
454           break;
455       }
456     while (t.hasMoreTokens());
457     // Now token contains the VK name we must parse.
458     Integer code = (Integer) vktable.get(token);
459     if (code == null)
460       throw new IllegalArgumentException("Unknown token '" + token
461                                          + "' in '" + s + "'");
462     if (t.hasMoreTokens())
463       throw new IllegalArgumentException("Too many tokens: " + s);
464     return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(),
465                            modifiers, released);
466   }
467
468   /**
469    * Returns the character of this keystroke, if it was typed.
470    *
471    * @return the character value, or CHAR_UNDEFINED
472    * @see #getAWTKeyStroke(char)
473    */
474   public final char getKeyChar()
475   {
476     return keyChar;
477   }
478
479   /**
480    * Returns the virtual key code of this keystroke, if it was pressed or
481    * released. This will be a VK_* constant from KeyEvent.
482    *
483    * @return the virtual key code value, or VK_UNDEFINED
484    * @see #getAWTKeyStroke(int, int)
485    */
486   public final int getKeyCode()
487   {
488     return keyCode;
489   }
490
491   /**
492    * Returns the modifiers for this keystroke. This will be a bitwise or of
493    * constants from InputEvent; it includes the old style masks for shift,
494    * control, alt, meta, and alt-graph (but not button1); as well as the new
495    * style of extended modifiers for all modifiers.
496    *
497    * @return the modifiers
498    * @see #getAWTKeyStroke(Character, int)
499    * @see #getAWTKeyStroke(int, int)
500    */
501   public final int getModifiers()
502   {
503     return modifiers;
504   }
505
506   /**
507    * Tests if this keystroke is a key release.
508    *
509    * @return true if this is a key release
510    * @see #getAWTKeyStroke(int, int, boolean)
511    */
512   public final boolean isOnKeyRelease()
513   {
514     return onKeyRelease;
515   }
516
517   /**
518    * Returns the AWT event type of this keystroke. This is one of
519    * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or
520    * {@link KeyEvent#KEY_RELEASED}.
521    *
522    * @return the key event type
523    */
524   public final int getKeyEventType()
525   {
526     return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED
527       : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED;
528   }
529
530   /**
531    * Returns a hashcode for this key event. It is not documented, but appears
532    * to be: <code>(getKeyChar() + 1) * (getKeyCode() + 1)
533    * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2)</code>.
534    *
535    * @return the hashcode
536    */
537   public int hashCode()
538   {
539     return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2
540       + (onKeyRelease ? 1 : 2);
541   }
542
543   /**
544    * Tests two keystrokes for equality.
545    *
546    * @param o the object to test
547    * @return true if it is equal
548    */
549   public final boolean equals(Object o)
550   {
551     if (! (o instanceof AWTKeyStroke))
552       return false;
553     AWTKeyStroke s = (AWTKeyStroke) o;
554     return this == o || (keyChar == s.keyChar && keyCode == s.keyCode
555                          && modifiers == s.modifiers
556                          && onKeyRelease == s.onKeyRelease);
557   }
558
559   /**
560    * Returns a string representation of this keystroke. For typed keystrokes,
561    * this is <code>"keyChar " + KeyEvent.getKeyModifiersText(getModifiers())
562    + getKeyChar()</code>; for pressed and released keystrokes, this is
563    * <code>"keyCode " + KeyEvent.getKeyModifiersText(getModifiers())
564    * + KeyEvent.getKeyText(getKeyCode())
565    * + (isOnKeyRelease() ? "-R" : "-P")</code>.
566    *
567    * @return a string representation
568    */
569   public String toString()
570   {
571     if (keyCode == KeyEvent.VK_UNDEFINED)
572       return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar;
573     return "keyCode " + KeyEvent.getKeyModifiersText(modifiers)
574       + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P");
575   }
576
577   /**
578    * Returns a cached version of the deserialized keystroke, if available.
579    *
580    * @return a cached replacement
581    * @throws ObjectStreamException if something goes wrong
582    */
583   protected Object readResolve() throws ObjectStreamException
584   {
585     AWTKeyStroke s = (AWTKeyStroke) cache.get(this);
586     if (s != null)
587       return s;
588     cache.put(this, this);
589     return this;
590   }
591
592   /**
593    * Gets the appropriate keystroke, creating one if necessary.
594    *
595    * @param keyChar the keyChar
596    * @param keyCode the keyCode
597    * @param modifiers the modifiers
598    * @param release true for key release
599    * @return the specified keystroke
600    */
601   private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
602                                               int modifiers, boolean release)
603   {
604     // Check level 0 cache.
605     AWTKeyStroke stroke = recent; // Avoid thread races.
606     if (stroke != null && stroke.keyChar == keyChar
607         && stroke.keyCode == keyCode && stroke.modifiers == modifiers
608         && stroke.onKeyRelease == release)
609       return stroke;
610     // Create a new object, on the assumption that if it has a match in the
611     // cache, the VM can easily garbage collect it as it is temporary.
612     Constructor c = ctor; // Avoid thread races.
613     if (c == null)
614       stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release);
615     else
616       try
617         {
618           stroke = (AWTKeyStroke) c.newInstance(null);
619           stroke.keyChar = keyChar;
620           stroke.keyCode = keyCode;
621           stroke.modifiers = modifiers;
622           stroke.onKeyRelease = release;
623         }
624       catch (Exception e)
625         {
626           throw (Error) new InternalError().initCause(e);
627         }
628     // Check level 1 cache.
629     AWTKeyStroke cached = (AWTKeyStroke) cache.get(stroke);
630     if (cached == null)
631       cache.put(stroke, stroke);
632     else
633       stroke = cached;
634     return recent = stroke;
635   }
636
637   /**
638    * Converts the modifiers to the appropriate format.
639    *
640    * @param mod the modifiers to convert
641    * @return the adjusted modifiers
642    */
643   private static int extend(int mod)
644   {
645     if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0)
646       mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK;
647     if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0)
648       mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK;
649     if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0)
650       mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK;
651     if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0)
652       mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK;
653     if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0)
654       mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK;
655     if ((mod & KeyEvent.BUTTON1_MASK) != 0)
656       mod |= KeyEvent.BUTTON1_DOWN_MASK;
657     return mod & MODIFIERS_MASK;
658   }
659 } // class AWTKeyStroke