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 *
61 * @see #getAWTKeyStroke
62 *
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 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 AWTKeyStro
87 */
88 private static Class getAWTKeyStrokeClass() {
89 Class clazz = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
90 if (clazz == null) {
91 clazz = AWTKeyStroke.class;
92 AppContext.getAppContext().put(AWTKeyStroke.class, AWTKeyStroke.class);
93 }
94 return clazz;
95 }
96
97 private char keyChar = KeyEvent.CHAR_UNDEFINED;
98 private int keyCode = KeyEvent.VK_UNDEFINED;
99 private int modifiers;
100 private boolean onKeyRelease;
101
102 static {
103 /* ensure that the necessary native libraries are loaded */
104 Toolkit.loadLibraries();
105 }
106
107 /**
108 * Constructs an <code>AWTKeyStroke</code> with default values.
109 * The default values used are:
165 * method, the factory methods will return instances of the specified
166 * Class. The specified Class must be either <code>AWTKeyStroke</code>
167 * or derived from <code>AWTKeyStroke</code>, and it must have a
168 * no-arg constructor. The constructor can be of any accessibility,
169 * including <code>private</code>. This operation
170 * flushes the current <code>AWTKeyStroke</code> cache.
171 *
172 * @param subclass the new Class of which the factory methods should create
173 * instances
174 * @throws IllegalArgumentException if subclass is <code>null</code>,
175 * or if subclass does not have a no-arg constructor
176 * @throws ClassCastException if subclass is not
177 * <code>AWTKeyStroke</code>, or a class derived from
178 * <code>AWTKeyStroke</code>
179 */
180 protected static void registerSubclass(Class<?> subclass) {
181 if (subclass == null) {
182 throw new IllegalArgumentException("subclass cannot be null");
183 }
184 synchronized (AWTKeyStroke.class) {
185 Class keyStrokeClass = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
186 if (keyStrokeClass != null && keyStrokeClass.equals(subclass)){
187 // Already registered
188 return;
189 }
190 }
191 if (!AWTKeyStroke.class.isAssignableFrom(subclass)) {
192 throw new ClassCastException("subclass is not derived from AWTKeyStroke");
193 }
194
195 Constructor ctor = getCtor(subclass);
196
197 String couldNotInstantiate = "subclass could not be instantiated";
198
199 if (ctor == null) {
200 throw new IllegalArgumentException(couldNotInstantiate);
201 }
202 try {
203 AWTKeyStroke stroke = (AWTKeyStroke)ctor.newInstance((Object[]) null);
204 if (stroke == null) {
205 throw new IllegalArgumentException(couldNotInstantiate);
212 throw new IllegalArgumentException(couldNotInstantiate);
213 } catch (IllegalAccessException e) {
214 throw new IllegalArgumentException(couldNotInstantiate);
215 } catch (InvocationTargetException e) {
216 throw new IllegalArgumentException(couldNotInstantiate);
217 }
218
219 synchronized (AWTKeyStroke.class) {
220 AppContext.getAppContext().put(AWTKeyStroke.class, subclass);
221 AppContext.getAppContext().remove(APP_CONTEXT_CACHE_KEY);
222 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
223 }
224 }
225
226 /* returns noarg Constructor for class with accessible flag. No security
227 threat as accessible flag is set only for this Constructor object,
228 not for Class constructor.
229 */
230 private static Constructor getCtor(final Class clazz)
231 {
232 Object ctor = AccessController.doPrivileged(new PrivilegedAction() {
233 public Object run() {
234 try {
235 Constructor ctor = clazz.getDeclaredConstructor((Class[]) null);
236 if (ctor != null) {
237 ctor.setAccessible(true);
238 }
239 return ctor;
240 } catch (SecurityException e) {
241 } catch (NoSuchMethodException e) {
242 }
243 return null;
244 }
245 });
246 return (Constructor)ctor;
247 }
248
249 private static synchronized AWTKeyStroke getCachedStroke
250 (char keyChar, int keyCode, int modifiers, boolean onKeyRelease)
251 {
252 Map cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);
253 AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);
254
255 if (cache == null) {
256 cache = new HashMap();
257 AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);
258 }
259
260 if (cacheKey == null) {
261 try {
262 Class clazz = getAWTKeyStrokeClass();
263 cacheKey = (AWTKeyStroke)getCtor(clazz).newInstance((Object[]) null);
264 AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);
265 } catch (InstantiationException e) {
266 assert(false);
267 } catch (IllegalAccessException e) {
268 assert(false);
269 } catch (InvocationTargetException e) {
270 assert(false);
271 }
272 }
273 cacheKey.keyChar = keyChar;
274 cacheKey.keyCode = keyCode;
275 cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
276 cacheKey.onKeyRelease = onKeyRelease;
277
278 AWTKeyStroke stroke = (AWTKeyStroke)cache.get(cacheKey);
279 if (stroke == null) {
280 stroke = cacheKey;
281 cache.put(stroke, stroke);
282 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
496 * @return an <code>AWTKeyStroke</code> object for that String
497 * @throws IllegalArgumentException if <code>s</code> is <code>null</code>,
498 * or is formatted incorrectly
499 */
500 public static AWTKeyStroke getAWTKeyStroke(String s) {
501 if (s == null) {
502 throw new IllegalArgumentException("String cannot be null");
503 }
504
505 final String errmsg = "String formatted incorrectly";
506
507 StringTokenizer st = new StringTokenizer(s, " ");
508
509 int mask = 0;
510 boolean released = false;
511 boolean typed = false;
512 boolean pressed = false;
513
514 synchronized (AWTKeyStroke.class) {
515 if (modifierKeywords == null) {
516 Map uninitializedMap = new HashMap(8, 1.0f);
517 uninitializedMap.put("shift",
518 Integer.valueOf(InputEvent.SHIFT_DOWN_MASK
519 |InputEvent.SHIFT_MASK));
520 uninitializedMap.put("control",
521 Integer.valueOf(InputEvent.CTRL_DOWN_MASK
522 |InputEvent.CTRL_MASK));
523 uninitializedMap.put("ctrl",
524 Integer.valueOf(InputEvent.CTRL_DOWN_MASK
525 |InputEvent.CTRL_MASK));
526 uninitializedMap.put("meta",
527 Integer.valueOf(InputEvent.META_DOWN_MASK
528 |InputEvent.META_MASK));
529 uninitializedMap.put("alt",
530 Integer.valueOf(InputEvent.ALT_DOWN_MASK
531 |InputEvent.ALT_MASK));
532 uninitializedMap.put("altGraph",
533 Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK
534 |InputEvent.ALT_GRAPH_MASK));
535 uninitializedMap.put("button1",
536 Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK));
844 }
845 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
846 modifiers |= InputEvent.ALT_MASK;
847 }
848 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
849 modifiers |= InputEvent.ALT_GRAPH_MASK;
850 }
851 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
852 modifiers |= InputEvent.CTRL_MASK;
853 }
854 if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
855 modifiers |= InputEvent.META_MASK;
856 }
857
858 return modifiers;
859 }
860
861 }
862
863 class VKCollection {
864 Map code2name;
865 Map name2code;
866
867 public VKCollection() {
868 code2name = new HashMap();
869 name2code = new HashMap();
870 }
871
872 public synchronized void put(String name, Integer code) {
873 assert((name != null) && (code != null));
874 assert(findName(code) == null);
875 assert(findCode(name) == null);
876 code2name.put(code, name);
877 name2code.put(name, code);
878 }
879
880 public synchronized Integer findCode(String name) {
881 assert(name != null);
882 return (Integer)name2code.get(name);
883 }
884
885 public synchronized String findName(Integer code) {
886 assert(code != null);
887 return (String)code2name.get(code);
888 }
889 }
|
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 *
61 * @see #getAWTKeyStroke
62 *
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 AWTKeyStro
87 */
88 private static Class<AWTKeyStroke> getAWTKeyStrokeClass() {
89 Class<AWTKeyStroke> clazz = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
90 if (clazz == null) {
91 clazz = AWTKeyStroke.class;
92 AppContext.getAppContext().put(AWTKeyStroke.class, AWTKeyStroke.class);
93 }
94 return clazz;
95 }
96
97 private char keyChar = KeyEvent.CHAR_UNDEFINED;
98 private int keyCode = KeyEvent.VK_UNDEFINED;
99 private int modifiers;
100 private boolean onKeyRelease;
101
102 static {
103 /* ensure that the necessary native libraries are loaded */
104 Toolkit.loadLibraries();
105 }
106
107 /**
108 * Constructs an <code>AWTKeyStroke</code> with default values.
109 * The default values used are:
165 * method, the factory methods will return instances of the specified
166 * Class. The specified Class must be either <code>AWTKeyStroke</code>
167 * or derived from <code>AWTKeyStroke</code>, and it must have a
168 * no-arg constructor. The constructor can be of any accessibility,
169 * including <code>private</code>. This operation
170 * flushes the current <code>AWTKeyStroke</code> cache.
171 *
172 * @param subclass the new Class of which the factory methods should create
173 * instances
174 * @throws IllegalArgumentException if subclass is <code>null</code>,
175 * or if subclass does not have a no-arg constructor
176 * @throws ClassCastException if subclass is not
177 * <code>AWTKeyStroke</code>, or a class derived from
178 * <code>AWTKeyStroke</code>
179 */
180 protected static void registerSubclass(Class<?> subclass) {
181 if (subclass == null) {
182 throw new IllegalArgumentException("subclass cannot be null");
183 }
184 synchronized (AWTKeyStroke.class) {
185 Class<AWTKeyStroke> keyStrokeClass = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);
186 if (keyStrokeClass != null && keyStrokeClass.equals(subclass)){
187 // Already registered
188 return;
189 }
190 }
191 if (!AWTKeyStroke.class.isAssignableFrom(subclass)) {
192 throw new ClassCastException("subclass is not derived from AWTKeyStroke");
193 }
194
195 Constructor ctor = getCtor(subclass);
196
197 String couldNotInstantiate = "subclass could not be instantiated";
198
199 if (ctor == null) {
200 throw new IllegalArgumentException(couldNotInstantiate);
201 }
202 try {
203 AWTKeyStroke stroke = (AWTKeyStroke)ctor.newInstance((Object[]) null);
204 if (stroke == null) {
205 throw new IllegalArgumentException(couldNotInstantiate);
212 throw new IllegalArgumentException(couldNotInstantiate);
213 } catch (IllegalAccessException e) {
214 throw new IllegalArgumentException(couldNotInstantiate);
215 } catch (InvocationTargetException e) {
216 throw new IllegalArgumentException(couldNotInstantiate);
217 }
218
219 synchronized (AWTKeyStroke.class) {
220 AppContext.getAppContext().put(AWTKeyStroke.class, subclass);
221 AppContext.getAppContext().remove(APP_CONTEXT_CACHE_KEY);
222 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
223 }
224 }
225
226 /* returns noarg Constructor for class with accessible flag. No security
227 threat as accessible flag is set only for this Constructor object,
228 not for Class constructor.
229 */
230 private static Constructor getCtor(final Class clazz)
231 {
232 Constructor ctor = AccessController.doPrivileged(new PrivilegedAction<Constructor>() {
233 public Constructor run() {
234 try {
235 Constructor ctor = clazz.getDeclaredConstructor((Class[]) null);
236 if (ctor != null) {
237 ctor.setAccessible(true);
238 }
239 return ctor;
240 } catch (SecurityException e) {
241 } catch (NoSuchMethodException e) {
242 }
243 return null;
244 }
245 });
246 return (Constructor)ctor;
247 }
248
249 private static synchronized AWTKeyStroke getCachedStroke
250 (char keyChar, int keyCode, int modifiers, boolean onKeyRelease)
251 {
252 Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);
253 AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);
254
255 if (cache == null) {
256 cache = new HashMap<>();
257 AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);
258 }
259
260 if (cacheKey == null) {
261 try {
262 Class<AWTKeyStroke> clazz = getAWTKeyStrokeClass();
263 cacheKey = (AWTKeyStroke)getCtor(clazz).newInstance((Object[]) null);
264 AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);
265 } catch (InstantiationException e) {
266 assert(false);
267 } catch (IllegalAccessException e) {
268 assert(false);
269 } catch (InvocationTargetException e) {
270 assert(false);
271 }
272 }
273 cacheKey.keyChar = keyChar;
274 cacheKey.keyCode = keyCode;
275 cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));
276 cacheKey.onKeyRelease = onKeyRelease;
277
278 AWTKeyStroke stroke = (AWTKeyStroke)cache.get(cacheKey);
279 if (stroke == null) {
280 stroke = cacheKey;
281 cache.put(stroke, stroke);
282 AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);
496 * @return an <code>AWTKeyStroke</code> object for that String
497 * @throws IllegalArgumentException if <code>s</code> is <code>null</code>,
498 * or is formatted incorrectly
499 */
500 public static AWTKeyStroke getAWTKeyStroke(String s) {
501 if (s == null) {
502 throw new IllegalArgumentException("String cannot be null");
503 }
504
505 final String errmsg = "String formatted incorrectly";
506
507 StringTokenizer st = new StringTokenizer(s, " ");
508
509 int mask = 0;
510 boolean released = false;
511 boolean typed = false;
512 boolean pressed = false;
513
514 synchronized (AWTKeyStroke.class) {
515 if (modifierKeywords == null) {
516 Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f);
517 uninitializedMap.put("shift",
518 Integer.valueOf(InputEvent.SHIFT_DOWN_MASK
519 |InputEvent.SHIFT_MASK));
520 uninitializedMap.put("control",
521 Integer.valueOf(InputEvent.CTRL_DOWN_MASK
522 |InputEvent.CTRL_MASK));
523 uninitializedMap.put("ctrl",
524 Integer.valueOf(InputEvent.CTRL_DOWN_MASK
525 |InputEvent.CTRL_MASK));
526 uninitializedMap.put("meta",
527 Integer.valueOf(InputEvent.META_DOWN_MASK
528 |InputEvent.META_MASK));
529 uninitializedMap.put("alt",
530 Integer.valueOf(InputEvent.ALT_DOWN_MASK
531 |InputEvent.ALT_MASK));
532 uninitializedMap.put("altGraph",
533 Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK
534 |InputEvent.ALT_GRAPH_MASK));
535 uninitializedMap.put("button1",
536 Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK));
844 }
845 if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {
846 modifiers |= InputEvent.ALT_MASK;
847 }
848 if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
849 modifiers |= InputEvent.ALT_GRAPH_MASK;
850 }
851 if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {
852 modifiers |= InputEvent.CTRL_MASK;
853 }
854 if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {
855 modifiers |= InputEvent.META_MASK;
856 }
857
858 return modifiers;
859 }
860
861 }
862
863 class VKCollection {
864 Map<Integer, String> code2name;
865 Map<String, Integer> name2code;
866
867 public VKCollection() {
868 code2name = new HashMap<>();
869 name2code = new HashMap<>();
870 }
871
872 public synchronized void put(String name, Integer code) {
873 assert((name != null) && (code != null));
874 assert(findName(code) == null);
875 assert(findCode(name) == null);
876 code2name.put(code, name);
877 name2code.put(name, code);
878 }
879
880 public synchronized Integer findCode(String name) {
881 assert(name != null);
882 return (Integer)name2code.get(name);
883 }
884
885 public synchronized String findName(Integer code) {
886 assert(code != null);
887 return (String)code2name.get(code);
888 }
889 }
|