< prev index next >

src/java.desktop/share/classes/java/awt/AWTKeyStroke.java

Print this page




  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.awt;
  26 
  27 import java.awt.event.KeyEvent;
  28 import sun.awt.AppContext;
  29 import java.awt.event.InputEvent;
  30 import java.util.Collections;
  31 import java.util.HashMap;
  32 import java.util.Map;
  33 import java.util.StringTokenizer;
  34 import java.io.Serializable;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 import java.lang.reflect.Constructor;
  38 import java.lang.reflect.InvocationTargetException;
  39 import java.lang.reflect.Modifier;
  40 import java.lang.reflect.Field;

  41 
  42 /**
  43  * An <code>AWTKeyStroke</code> represents a key action on the
  44  * keyboard, or equivalent input device. <code>AWTKeyStroke</code>s
  45  * can correspond to only a press or release of a
  46  * particular key, just as <code>KEY_PRESSED</code> and
  47  * <code>KEY_RELEASED</code> <code>KeyEvent</code>s do;
  48  * alternately, they can correspond to typing a specific Java character, just
  49  * as <code>KEY_TYPED</code> <code>KeyEvent</code>s do.
  50  * In all cases, <code>AWTKeyStroke</code>s can specify modifiers
  51  * (alt, shift, control, meta, altGraph, or a combination thereof) which must be present
  52  * during the action for an exact match.
  53  * <p>
  54  * <code>AWTKeyStrokes</code> are immutable, and are intended
  55  * to be unique. Client code should never create an
  56  * <code>AWTKeyStroke</code> on its own, but should instead use
  57  * a variant of <code>getAWTKeyStroke</code>. Client use of these factory
  58  * methods allows the <code>AWTKeyStroke</code> implementation
  59  * to cache and share instances efficiently.
  60  *


  63  * @author Arnaud Weber
  64  * @author David Mendenhall
  65  * @since 1.4
  66  */
  67 public class AWTKeyStroke implements Serializable {
  68     static final long serialVersionUID = -6430539691155161871L;
  69 
  70     private static Map<String, Integer> modifierKeywords;
  71     /**
  72      * Associates VK_XXX (as a String) with code (as Integer). This is
  73      * done to avoid the overhead of the reflective call to find the
  74      * constant.
  75      */
  76     private static VKCollection vks;
  77 
  78     //A key for the collection of AWTKeyStrokes within AppContext.
  79     private static Object APP_CONTEXT_CACHE_KEY = new Object();
  80     //A key withing the cache
  81     private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke();
  82 
  83     /*
  84      * Reads keystroke class from AppContext and if null, puts there the
  85      * AWTKeyStroke class.
  86      * Must be called under locked AWTKeyStroke
  87      */
  88     private static Class<AWTKeyStroke> getAWTKeyStrokeClass() {
  89         @SuppressWarnings("unchecked")
  90         Class<AWTKeyStroke> clazz = (Class<AWTKeyStroke>)AppContext.getAppContext().get(AWTKeyStroke.class);
  91         if (clazz == null) {
  92             clazz = AWTKeyStroke.class;
  93             AppContext.getAppContext().put(AWTKeyStroke.class, AWTKeyStroke.class);
  94         }
  95         return clazz;
  96     }
  97 
  98     private char keyChar = KeyEvent.CHAR_UNDEFINED;
  99     private int keyCode = KeyEvent.VK_UNDEFINED;
 100     private int modifiers;
 101     private boolean onKeyRelease;
 102 
 103     static {
 104         /* ensure that the necessary native libraries are loaded */
 105         Toolkit.loadLibraries();
 106     }
 107 
 108     /**
 109      * Constructs an <code>AWTKeyStroke</code> with default values.
 110      * The default values used are:
 111      * <table border="1" summary="AWTKeyStroke default values">
 112      * <tr><th>Property</th><th>Default Value</th></tr>
 113      * <tr>
 114      *    <td>Key Char</td>
 115      *    <td><code>KeyEvent.CHAR_UNDEFINED</code></td>
 116      * </tr>
 117      * <tr>


 142      * values. <code>AWTKeyStroke</code>s should not be constructed
 143      * by client code. Use a variant of <code>getAWTKeyStroke</code>
 144      * instead.
 145      *
 146      * @param keyChar the character value for a keyboard key
 147      * @param keyCode the key code for this <code>AWTKeyStroke</code>
 148      * @param modifiers a bitwise-ored combination of any modifiers
 149      * @param onKeyRelease <code>true</code> if this
 150      *        <code>AWTKeyStroke</code> corresponds
 151      *        to a key release; <code>false</code> otherwise
 152      * @see #getAWTKeyStroke
 153      */
 154     protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
 155                            boolean onKeyRelease) {
 156         this.keyChar = keyChar;
 157         this.keyCode = keyCode;
 158         this.modifiers = modifiers;
 159         this.onKeyRelease = onKeyRelease;
 160     }
 161 
 162     /**
 163      * Registers a new class which the factory methods in
 164      * <code>AWTKeyStroke</code> will use when generating new
 165      * instances of <code>AWTKeyStroke</code>s. After invoking this
 166      * method, the factory methods will return instances of the specified
 167      * Class. The specified Class must be either <code>AWTKeyStroke</code>
 168      * or derived from <code>AWTKeyStroke</code>, and it must have a
 169      * no-arg constructor. The constructor can be of any accessibility,
 170      * including <code>private</code>. This operation
 171      * flushes the current <code>AWTKeyStroke</code> cache.
 172      *
 173      * @param subclass the new Class of which the factory methods should create
 174      *        instances
 175      * @throws IllegalArgumentException if subclass is <code>null</code>,
 176      *         or if subclass does not have a no-arg constructor
 177      * @throws ClassCastException if subclass is not
 178      *         <code>AWTKeyStroke</code>, or a class derived from
 179      *         <code>AWTKeyStroke</code>
 180      */
 181     protected static void registerSubclass(Class<?> subclass) {
 182         if (subclass == null) {
 183             throw new IllegalArgumentException("subclass cannot be null");
 184         }
 185         synchronized (AWTKeyStroke.class) {
 186             @SuppressWarnings("unchecked")
 187             Class<AWTKeyStroke> keyStrokeClass = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
 188             if (keyStrokeClass != null && keyStrokeClass.equals(subclass)){
 189                 // Already registered
 190                 return;
 191             }
 192         }
 193         if (!AWTKeyStroke.class.isAssignableFrom(subclass)) {
 194             throw new ClassCastException("subclass is not derived from AWTKeyStroke");
 195         }
 196 
 197         Constructor<?> ctor = getCtor(subclass);
 198 
 199         String couldNotInstantiate = "subclass could not be instantiated";
 200 
 201         if (ctor == null) {
 202             throw new IllegalArgumentException(couldNotInstantiate);
 203         }
 204         try {
 205             AWTKeyStroke stroke = (AWTKeyStroke)ctor.newInstance((Object[]) null);
 206             if (stroke == null) {
 207                 throw new IllegalArgumentException(couldNotInstantiate);
 208             }
 209         } catch (NoSuchMethodError e) {
 210             throw new IllegalArgumentException(couldNotInstantiate);
 211         } catch (ExceptionInInitializerError e) {
 212             throw new IllegalArgumentException(couldNotInstantiate);
 213         } catch (InstantiationException e) {
 214             throw new IllegalArgumentException(couldNotInstantiate);
 215         } catch (IllegalAccessException e) {
 216             throw new IllegalArgumentException(couldNotInstantiate);
 217         } catch (InvocationTargetException e) {
 218             throw new IllegalArgumentException(couldNotInstantiate);
 219         }
 220 
 221         synchronized (AWTKeyStroke.class) {
 222             AppContext.getAppContext().put(AWTKeyStroke.class, subclass);
 223             AppContext.getAppContext().remove(APP_CONTEXT_CACHE_KEY);
 224             AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
 225         }
 226     }
 227 
 228     /* returns no-arg Constructor for class with accessible flag. No security
 229        threat as accessible flag is set only for this Constructor object,
 230        not for Class constructor.
 231      */
 232     private static Constructor<?> getCtor(final Class<?> clazz)
 233     {
 234         Constructor<?> ctor = AccessController.doPrivileged(new PrivilegedAction<Constructor<?>>() {
 235             public Constructor<?> run() {
 236                 try {
 237                     Constructor<?> ctor = clazz.getDeclaredConstructor((Class<?>[]) null);
 238                     if (ctor != null) {
 239                         ctor.setAccessible(true);
 240                     }
 241                     return ctor;
 242                 } catch (SecurityException e) {
 243                 } catch (NoSuchMethodException e) {
 244                 }
 245                 return null;
 246             }
 247         });
 248         return ctor;
 249     }
 250 
 251     private static synchronized AWTKeyStroke getCachedStroke
 252         (char keyChar, int keyCode, int modifiers, boolean onKeyRelease)
 253     {
 254         @SuppressWarnings("unchecked")
 255         Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);
 256         AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);
 257 
 258         if (cache == null) {
 259             cache = new HashMap<>();
 260             AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);
 261         }
 262 
 263         if (cacheKey == null) {
 264             try {
 265                 Class<AWTKeyStroke> clazz = getAWTKeyStrokeClass();
 266                 cacheKey = (AWTKeyStroke)getCtor(clazz).newInstance((Object[]) null);
 267                 AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);
 268             } catch (InstantiationException e) {
 269                 assert(false);
 270             } catch (IllegalAccessException e) {
 271                 assert(false);
 272             } catch (InvocationTargetException e) {
 273                 assert(false);
 274             }
 275         }

 276         cacheKey.keyChar = keyChar;
 277         cacheKey.keyCode = keyCode;
 278         cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
 279         cacheKey.onKeyRelease = onKeyRelease;
 280 
 281         AWTKeyStroke stroke = cache.get(cacheKey);
 282         if (stroke == null) {
 283             stroke = cacheKey;
 284             cache.put(stroke, stroke);
 285             AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
 286         }
 287         return stroke;
 288     }
 289 
 290     /**
 291      * Returns a shared instance of an <code>AWTKeyStroke</code>
 292      * that represents a <code>KEY_TYPED</code> event for the
 293      * specified character.
 294      *
 295      * @param keyChar the character value for a keyboard key


 789                     name = fields[i].getName();
 790                     vkCollect.put(name, key);
 791                     return name.substring(3);
 792                 }
 793             } catch (IllegalAccessException e) {
 794                 assert(false);
 795             }
 796         }
 797         return "UNKNOWN";
 798     }
 799 
 800     /**
 801      * Returns a cached instance of <code>AWTKeyStroke</code> (or a subclass of
 802      * <code>AWTKeyStroke</code>) which is equal to this instance.
 803      *
 804      * @return a cached instance which is equal to this instance
 805      * @throws java.io.ObjectStreamException if a serialization problem occurs
 806      */
 807     protected Object readResolve() throws java.io.ObjectStreamException {
 808         synchronized (AWTKeyStroke.class) {
 809             if (getClass().equals(getAWTKeyStrokeClass())) {
 810                 return  getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease);
 811             }
 812         }
 813         return this;
 814     }
 815 
 816     private static int mapOldModifiers(int modifiers) {
 817         if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
 818             modifiers |= InputEvent.SHIFT_DOWN_MASK;
 819         }
 820         if ((modifiers & InputEvent.ALT_MASK) != 0) {
 821             modifiers |= InputEvent.ALT_DOWN_MASK;
 822         }
 823         if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
 824             modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
 825         }
 826         if ((modifiers & InputEvent.CTRL_MASK) != 0) {
 827             modifiers |= InputEvent.CTRL_DOWN_MASK;
 828         }
 829         if ((modifiers & InputEvent.META_MASK) != 0) {
 830             modifiers |= InputEvent.META_DOWN_MASK;
 831         }
 832 
 833         modifiers &= InputEvent.SHIFT_DOWN_MASK
 834             | InputEvent.ALT_DOWN_MASK




  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.awt;
  26 
  27 import java.awt.event.KeyEvent;
  28 import sun.awt.AppContext;
  29 import java.awt.event.InputEvent;
  30 import java.util.Collections;
  31 import java.util.HashMap;
  32 import java.util.Map;
  33 import java.util.StringTokenizer;
  34 import java.io.Serializable;




  35 import java.lang.reflect.Modifier;
  36 import java.lang.reflect.Field;
  37 import sun.swing.SwingAccessor;
  38 
  39 /**
  40  * An <code>AWTKeyStroke</code> represents a key action on the
  41  * keyboard, or equivalent input device. <code>AWTKeyStroke</code>s
  42  * can correspond to only a press or release of a
  43  * particular key, just as <code>KEY_PRESSED</code> and
  44  * <code>KEY_RELEASED</code> <code>KeyEvent</code>s do;
  45  * alternately, they can correspond to typing a specific Java character, just
  46  * as <code>KEY_TYPED</code> <code>KeyEvent</code>s do.
  47  * In all cases, <code>AWTKeyStroke</code>s can specify modifiers
  48  * (alt, shift, control, meta, altGraph, or a combination thereof) which must be present
  49  * during the action for an exact match.
  50  * <p>
  51  * <code>AWTKeyStrokes</code> are immutable, and are intended
  52  * to be unique. Client code should never create an
  53  * <code>AWTKeyStroke</code> on its own, but should instead use
  54  * a variant of <code>getAWTKeyStroke</code>. Client use of these factory
  55  * methods allows the <code>AWTKeyStroke</code> implementation
  56  * to cache and share instances efficiently.
  57  *


  60  * @author Arnaud Weber
  61  * @author David Mendenhall
  62  * @since 1.4
  63  */
  64 public class AWTKeyStroke implements Serializable {
  65     static final long serialVersionUID = -6430539691155161871L;
  66 
  67     private static Map<String, Integer> modifierKeywords;
  68     /**
  69      * Associates VK_XXX (as a String) with code (as Integer). This is
  70      * done to avoid the overhead of the reflective call to find the
  71      * constant.
  72      */
  73     private static VKCollection vks;
  74 
  75     //A key for the collection of AWTKeyStrokes within AppContext.
  76     private static Object APP_CONTEXT_CACHE_KEY = new Object();
  77     //A key withing the cache
  78     private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke();
  79 















  80     private char keyChar = KeyEvent.CHAR_UNDEFINED;
  81     private int keyCode = KeyEvent.VK_UNDEFINED;
  82     private int modifiers;
  83     private boolean onKeyRelease;
  84 
  85     static {
  86         /* ensure that the necessary native libraries are loaded */
  87         Toolkit.loadLibraries();
  88     }
  89 
  90     /**
  91      * Constructs an <code>AWTKeyStroke</code> with default values.
  92      * The default values used are:
  93      * <table border="1" summary="AWTKeyStroke default values">
  94      * <tr><th>Property</th><th>Default Value</th></tr>
  95      * <tr>
  96      *    <td>Key Char</td>
  97      *    <td><code>KeyEvent.CHAR_UNDEFINED</code></td>
  98      * </tr>
  99      * <tr>


 124      * values. <code>AWTKeyStroke</code>s should not be constructed
 125      * by client code. Use a variant of <code>getAWTKeyStroke</code>
 126      * instead.
 127      *
 128      * @param keyChar the character value for a keyboard key
 129      * @param keyCode the key code for this <code>AWTKeyStroke</code>
 130      * @param modifiers a bitwise-ored combination of any modifiers
 131      * @param onKeyRelease <code>true</code> if this
 132      *        <code>AWTKeyStroke</code> corresponds
 133      *        to a key release; <code>false</code> otherwise
 134      * @see #getAWTKeyStroke
 135      */
 136     protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
 137                            boolean onKeyRelease) {
 138         this.keyChar = keyChar;
 139         this.keyCode = keyCode;
 140         this.modifiers = modifiers;
 141         this.onKeyRelease = onKeyRelease;
 142     }
 143 

























































































 144     private static synchronized AWTKeyStroke getCachedStroke
 145         (char keyChar, int keyCode, int modifiers, boolean onKeyRelease)
 146     {
 147         @SuppressWarnings("unchecked")
 148         Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);
 149         AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);
 150 
 151         if (cache == null) {
 152             cache = new HashMap<>();
 153             AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);
 154         }
 155 
 156         if (cacheKey == null) {
 157             cacheKey = SwingAccessor.getKeyStrokeAccessor().create();


 158             AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);







 159         }
 160 
 161         cacheKey.keyChar = keyChar;
 162         cacheKey.keyCode = keyCode;
 163         cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
 164         cacheKey.onKeyRelease = onKeyRelease;
 165 
 166         AWTKeyStroke stroke = cache.get(cacheKey);
 167         if (stroke == null) {
 168             stroke = cacheKey;
 169             cache.put(stroke, stroke);
 170             AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
 171         }
 172         return stroke;
 173     }
 174 
 175     /**
 176      * Returns a shared instance of an <code>AWTKeyStroke</code>
 177      * that represents a <code>KEY_TYPED</code> event for the
 178      * specified character.
 179      *
 180      * @param keyChar the character value for a keyboard key


 674                     name = fields[i].getName();
 675                     vkCollect.put(name, key);
 676                     return name.substring(3);
 677                 }
 678             } catch (IllegalAccessException e) {
 679                 assert(false);
 680             }
 681         }
 682         return "UNKNOWN";
 683     }
 684 
 685     /**
 686      * Returns a cached instance of <code>AWTKeyStroke</code> (or a subclass of
 687      * <code>AWTKeyStroke</code>) which is equal to this instance.
 688      *
 689      * @return a cached instance which is equal to this instance
 690      * @throws java.io.ObjectStreamException if a serialization problem occurs
 691      */
 692     protected Object readResolve() throws java.io.ObjectStreamException {
 693         synchronized (AWTKeyStroke.class) {
 694 
 695             return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease);
 696         }
 697     }


 698 
 699     private static int mapOldModifiers(int modifiers) {
 700         if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
 701             modifiers |= InputEvent.SHIFT_DOWN_MASK;
 702         }
 703         if ((modifiers & InputEvent.ALT_MASK) != 0) {
 704             modifiers |= InputEvent.ALT_DOWN_MASK;
 705         }
 706         if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
 707             modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
 708         }
 709         if ((modifiers & InputEvent.CTRL_MASK) != 0) {
 710             modifiers |= InputEvent.CTRL_DOWN_MASK;
 711         }
 712         if ((modifiers & InputEvent.META_MASK) != 0) {
 713             modifiers |= InputEvent.META_DOWN_MASK;
 714         }
 715 
 716         modifiers &= InputEvent.SHIFT_DOWN_MASK
 717             | InputEvent.ALT_DOWN_MASK


< prev index next >