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
|