Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/awt/AWTKeyStroke.java
38829 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package java.awt;2526import java.awt.event.KeyEvent;27import sun.awt.AppContext;28import java.awt.event.InputEvent;29import java.util.Collections;30import java.util.HashMap;31import java.util.Map;32import java.util.StringTokenizer;33import java.io.Serializable;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.lang.reflect.Constructor;37import java.lang.reflect.InvocationTargetException;38import java.lang.reflect.Modifier;39import java.lang.reflect.Field;4041/**42* An <code>AWTKeyStroke</code> represents a key action on the43* keyboard, or equivalent input device. <code>AWTKeyStroke</code>s44* can correspond to only a press or release of a45* particular key, just as <code>KEY_PRESSED</code> and46* <code>KEY_RELEASED</code> <code>KeyEvent</code>s do;47* alternately, they can correspond to typing a specific Java character, just48* as <code>KEY_TYPED</code> <code>KeyEvent</code>s do.49* In all cases, <code>AWTKeyStroke</code>s can specify modifiers50* (alt, shift, control, meta, altGraph, or a combination thereof) which must be present51* during the action for an exact match.52* <p>53* <code>AWTKeyStrokes</code> are immutable, and are intended54* to be unique. Client code should never create an55* <code>AWTKeyStroke</code> on its own, but should instead use56* a variant of <code>getAWTKeyStroke</code>. Client use of these factory57* methods allows the <code>AWTKeyStroke</code> implementation58* to cache and share instances efficiently.59*60* @see #getAWTKeyStroke61*62* @author Arnaud Weber63* @author David Mendenhall64* @since 1.465*/66public class AWTKeyStroke implements Serializable {67static final long serialVersionUID = -6430539691155161871L;6869private static Map<String, Integer> modifierKeywords;70/**71* Associates VK_XXX (as a String) with code (as Integer). This is72* done to avoid the overhead of the reflective call to find the73* constant.74*/75private static VKCollection vks;7677//A key for the collection of AWTKeyStrokes within AppContext.78private static Object APP_CONTEXT_CACHE_KEY = new Object();79//A key withing the cache80private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke();8182/*83* Reads keystroke class from AppContext and if null, puts there the84* AWTKeyStroke class.85* Must be called under locked AWTKeyStro86*/87private static Class<AWTKeyStroke> getAWTKeyStrokeClass() {88Class<AWTKeyStroke> clazz = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);89if (clazz == null) {90clazz = AWTKeyStroke.class;91AppContext.getAppContext().put(AWTKeyStroke.class, AWTKeyStroke.class);92}93return clazz;94}9596private char keyChar = KeyEvent.CHAR_UNDEFINED;97private int keyCode = KeyEvent.VK_UNDEFINED;98private int modifiers;99private boolean onKeyRelease;100101static {102/* ensure that the necessary native libraries are loaded */103Toolkit.loadLibraries();104}105106/**107* Constructs an <code>AWTKeyStroke</code> with default values.108* The default values used are:109* <table border="1" summary="AWTKeyStroke default values">110* <tr><th>Property</th><th>Default Value</th></tr>111* <tr>112* <td>Key Char</td>113* <td><code>KeyEvent.CHAR_UNDEFINED</code></td>114* </tr>115* <tr>116* <td>Key Code</td>117* <td><code>KeyEvent.VK_UNDEFINED</code></td>118* </tr>119* <tr>120* <td>Modifiers</td>121* <td>none</td>122* </tr>123* <tr>124* <td>On key release?</td>125* <td><code>false</code></td>126* </tr>127* </table>128*129* <code>AWTKeyStroke</code>s should not be constructed130* by client code. Use a variant of <code>getAWTKeyStroke</code>131* instead.132*133* @see #getAWTKeyStroke134*/135protected AWTKeyStroke() {136}137138/**139* Constructs an <code>AWTKeyStroke</code> with the specified140* values. <code>AWTKeyStroke</code>s should not be constructed141* by client code. Use a variant of <code>getAWTKeyStroke</code>142* instead.143*144* @param keyChar the character value for a keyboard key145* @param keyCode the key code for this <code>AWTKeyStroke</code>146* @param modifiers a bitwise-ored combination of any modifiers147* @param onKeyRelease <code>true</code> if this148* <code>AWTKeyStroke</code> corresponds149* to a key release; <code>false</code> otherwise150* @see #getAWTKeyStroke151*/152protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,153boolean onKeyRelease) {154this.keyChar = keyChar;155this.keyCode = keyCode;156this.modifiers = modifiers;157this.onKeyRelease = onKeyRelease;158}159160/**161* Registers a new class which the factory methods in162* <code>AWTKeyStroke</code> will use when generating new163* instances of <code>AWTKeyStroke</code>s. After invoking this164* method, the factory methods will return instances of the specified165* Class. The specified Class must be either <code>AWTKeyStroke</code>166* or derived from <code>AWTKeyStroke</code>, and it must have a167* no-arg constructor. The constructor can be of any accessibility,168* including <code>private</code>. This operation169* flushes the current <code>AWTKeyStroke</code> cache.170*171* @param subclass the new Class of which the factory methods should create172* instances173* @throws IllegalArgumentException if subclass is <code>null</code>,174* or if subclass does not have a no-arg constructor175* @throws ClassCastException if subclass is not176* <code>AWTKeyStroke</code>, or a class derived from177* <code>AWTKeyStroke</code>178*/179protected static void registerSubclass(Class<?> subclass) {180if (subclass == null) {181throw new IllegalArgumentException("subclass cannot be null");182}183synchronized (AWTKeyStroke.class) {184Class<AWTKeyStroke> keyStrokeClass = (Class)AppContext.getAppContext().get(AWTKeyStroke.class);185if (keyStrokeClass != null && keyStrokeClass.equals(subclass)){186// Already registered187return;188}189}190if (!AWTKeyStroke.class.isAssignableFrom(subclass)) {191throw new ClassCastException("subclass is not derived from AWTKeyStroke");192}193194Constructor ctor = getCtor(subclass);195196String couldNotInstantiate = "subclass could not be instantiated";197198if (ctor == null) {199throw new IllegalArgumentException(couldNotInstantiate);200}201try {202AWTKeyStroke stroke = (AWTKeyStroke)ctor.newInstance((Object[]) null);203if (stroke == null) {204throw new IllegalArgumentException(couldNotInstantiate);205}206} catch (NoSuchMethodError e) {207throw new IllegalArgumentException(couldNotInstantiate);208} catch (ExceptionInInitializerError e) {209throw new IllegalArgumentException(couldNotInstantiate);210} catch (InstantiationException e) {211throw new IllegalArgumentException(couldNotInstantiate);212} catch (IllegalAccessException e) {213throw new IllegalArgumentException(couldNotInstantiate);214} catch (InvocationTargetException e) {215throw new IllegalArgumentException(couldNotInstantiate);216}217218synchronized (AWTKeyStroke.class) {219AppContext.getAppContext().put(AWTKeyStroke.class, subclass);220AppContext.getAppContext().remove(APP_CONTEXT_CACHE_KEY);221AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);222}223}224225/* returns noarg Constructor for class with accessible flag. No security226threat as accessible flag is set only for this Constructor object,227not for Class constructor.228*/229private static Constructor getCtor(final Class clazz)230{231Constructor ctor = AccessController.doPrivileged(new PrivilegedAction<Constructor>() {232public Constructor run() {233try {234Constructor ctor = clazz.getDeclaredConstructor((Class[]) null);235if (ctor != null) {236ctor.setAccessible(true);237}238return ctor;239} catch (SecurityException e) {240} catch (NoSuchMethodException e) {241}242return null;243}244});245return (Constructor)ctor;246}247248private static synchronized AWTKeyStroke getCachedStroke249(char keyChar, int keyCode, int modifiers, boolean onKeyRelease)250{251Map<AWTKeyStroke, AWTKeyStroke> cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY);252AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY);253254if (cache == null) {255cache = new HashMap<>();256AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache);257}258259if (cacheKey == null) {260try {261Class<AWTKeyStroke> clazz = getAWTKeyStrokeClass();262cacheKey = (AWTKeyStroke)getCtor(clazz).newInstance((Object[]) null);263AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey);264} catch (InstantiationException e) {265assert(false);266} catch (IllegalAccessException e) {267assert(false);268} catch (InvocationTargetException e) {269assert(false);270}271}272cacheKey.keyChar = keyChar;273cacheKey.keyCode = keyCode;274cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers));275cacheKey.onKeyRelease = onKeyRelease;276277AWTKeyStroke stroke = (AWTKeyStroke)cache.get(cacheKey);278if (stroke == null) {279stroke = cacheKey;280cache.put(stroke, stroke);281AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY);282}283return stroke;284}285286/**287* Returns a shared instance of an <code>AWTKeyStroke</code>288* that represents a <code>KEY_TYPED</code> event for the289* specified character.290*291* @param keyChar the character value for a keyboard key292* @return an <code>AWTKeyStroke</code> object for that key293*/294public static AWTKeyStroke getAWTKeyStroke(char keyChar) {295return getCachedStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);296}297298/**299* Returns a shared instance of an {@code AWTKeyStroke}300* that represents a {@code KEY_TYPED} event for the301* specified Character object and a set of modifiers. Note302* that the first parameter is of type Character rather than303* char. This is to avoid inadvertent clashes with304* calls to <code>getAWTKeyStroke(int keyCode, int modifiers)</code>.305*306* The modifiers consist of any combination of following:<ul>307* <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK308* <li>java.awt.event.InputEvent.CTRL_DOWN_MASK309* <li>java.awt.event.InputEvent.META_DOWN_MASK310* <li>java.awt.event.InputEvent.ALT_DOWN_MASK311* <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK312* </ul>313* The old modifiers listed below also can be used, but they are314* mapped to _DOWN_ modifiers. <ul>315* <li>java.awt.event.InputEvent.SHIFT_MASK316* <li>java.awt.event.InputEvent.CTRL_MASK317* <li>java.awt.event.InputEvent.META_MASK318* <li>java.awt.event.InputEvent.ALT_MASK319* <li>java.awt.event.InputEvent.ALT_GRAPH_MASK320* </ul>321* also can be used, but they are mapped to _DOWN_ modifiers.322*323* Since these numbers are all different powers of two, any combination of324* them is an integer in which each bit represents a different modifier325* key. Use 0 to specify no modifiers.326*327* @param keyChar the Character object for a keyboard character328* @param modifiers a bitwise-ored combination of any modifiers329* @return an <code>AWTKeyStroke</code> object for that key330* @throws IllegalArgumentException if <code>keyChar</code> is331* <code>null</code>332*333* @see java.awt.event.InputEvent334*/335public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers)336{337if (keyChar == null) {338throw new IllegalArgumentException("keyChar cannot be null");339}340return getCachedStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,341modifiers, false);342}343344/**345* Returns a shared instance of an <code>AWTKeyStroke</code>,346* given a numeric key code and a set of modifiers, specifying347* whether the key is activated when it is pressed or released.348* <p>349* The "virtual key" constants defined in350* <code>java.awt.event.KeyEvent</code> can be351* used to specify the key code. For example:<ul>352* <li><code>java.awt.event.KeyEvent.VK_ENTER</code>353* <li><code>java.awt.event.KeyEvent.VK_TAB</code>354* <li><code>java.awt.event.KeyEvent.VK_SPACE</code>355* </ul>356* Alternatively, the key code may be obtained by calling357* <code>java.awt.event.KeyEvent.getExtendedKeyCodeForChar</code>.358*359* The modifiers consist of any combination of:<ul>360* <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK361* <li>java.awt.event.InputEvent.CTRL_DOWN_MASK362* <li>java.awt.event.InputEvent.META_DOWN_MASK363* <li>java.awt.event.InputEvent.ALT_DOWN_MASK364* <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK365* </ul>366* The old modifiers <ul>367* <li>java.awt.event.InputEvent.SHIFT_MASK368* <li>java.awt.event.InputEvent.CTRL_MASK369* <li>java.awt.event.InputEvent.META_MASK370* <li>java.awt.event.InputEvent.ALT_MASK371* <li>java.awt.event.InputEvent.ALT_GRAPH_MASK372* </ul>373* also can be used, but they are mapped to _DOWN_ modifiers.374*375* Since these numbers are all different powers of two, any combination of376* them is an integer in which each bit represents a different modifier377* key. Use 0 to specify no modifiers.378*379* @param keyCode an int specifying the numeric code for a keyboard key380* @param modifiers a bitwise-ored combination of any modifiers381* @param onKeyRelease <code>true</code> if the <code>AWTKeyStroke</code>382* should represent a key release; <code>false</code> otherwise383* @return an AWTKeyStroke object for that key384*385* @see java.awt.event.KeyEvent386* @see java.awt.event.InputEvent387*/388public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,389boolean onKeyRelease) {390return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,391onKeyRelease);392}393394/**395* Returns a shared instance of an <code>AWTKeyStroke</code>,396* given a numeric key code and a set of modifiers. The returned397* <code>AWTKeyStroke</code> will correspond to a key press.398* <p>399* The "virtual key" constants defined in400* <code>java.awt.event.KeyEvent</code> can be401* used to specify the key code. For example:<ul>402* <li><code>java.awt.event.KeyEvent.VK_ENTER</code>403* <li><code>java.awt.event.KeyEvent.VK_TAB</code>404* <li><code>java.awt.event.KeyEvent.VK_SPACE</code>405* </ul>406* The modifiers consist of any combination of:<ul>407* <li>java.awt.event.InputEvent.SHIFT_DOWN_MASK408* <li>java.awt.event.InputEvent.CTRL_DOWN_MASK409* <li>java.awt.event.InputEvent.META_DOWN_MASK410* <li>java.awt.event.InputEvent.ALT_DOWN_MASK411* <li>java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK412* </ul>413* The old modifiers <ul>414* <li>java.awt.event.InputEvent.SHIFT_MASK415* <li>java.awt.event.InputEvent.CTRL_MASK416* <li>java.awt.event.InputEvent.META_MASK417* <li>java.awt.event.InputEvent.ALT_MASK418* <li>java.awt.event.InputEvent.ALT_GRAPH_MASK419* </ul>420* also can be used, but they are mapped to _DOWN_ modifiers.421*422* Since these numbers are all different powers of two, any combination of423* them is an integer in which each bit represents a different modifier424* key. Use 0 to specify no modifiers.425*426* @param keyCode an int specifying the numeric code for a keyboard key427* @param modifiers a bitwise-ored combination of any modifiers428* @return an <code>AWTKeyStroke</code> object for that key429*430* @see java.awt.event.KeyEvent431* @see java.awt.event.InputEvent432*/433public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) {434return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,435false);436}437438/**439* Returns an <code>AWTKeyStroke</code> which represents the440* stroke which generated a given <code>KeyEvent</code>.441* <p>442* This method obtains the keyChar from a <code>KeyTyped</code>443* event, and the keyCode from a <code>KeyPressed</code> or444* <code>KeyReleased</code> event. The <code>KeyEvent</code> modifiers are445* obtained for all three types of <code>KeyEvent</code>.446*447* @param anEvent the <code>KeyEvent</code> from which to448* obtain the <code>AWTKeyStroke</code>449* @throws NullPointerException if <code>anEvent</code> is null450* @return the <code>AWTKeyStroke</code> that precipitated the event451*/452public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) {453int id = anEvent.getID();454switch(id) {455case KeyEvent.KEY_PRESSED:456case KeyEvent.KEY_RELEASED:457return getCachedStroke(KeyEvent.CHAR_UNDEFINED,458anEvent.getKeyCode(),459anEvent.getModifiers(),460(id == KeyEvent.KEY_RELEASED));461case KeyEvent.KEY_TYPED:462return getCachedStroke(anEvent.getKeyChar(),463KeyEvent.VK_UNDEFINED,464anEvent.getModifiers(),465false);466default:467// Invalid ID for this KeyEvent468return null;469}470}471472/**473* Parses a string and returns an <code>AWTKeyStroke</code>.474* The string must have the following syntax:475* <pre>476* <modifiers>* (<typedID> | <pressedReleasedID>)477*478* modifiers := shift | control | ctrl | meta | alt | altGraph479* typedID := typed <typedKey>480* typedKey := string of length 1 giving Unicode character.481* pressedReleasedID := (pressed | released) key482* key := KeyEvent key code name, i.e. the name following "VK_".483* </pre>484* If typed, pressed or released is not specified, pressed is assumed. Here485* are some examples:486* <pre>487* "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);488* "control DELETE" => getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);489* "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);490* "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);491* "typed a" => getAWTKeyStroke('a');492* </pre>493*494* @param s a String formatted as described above495* @return an <code>AWTKeyStroke</code> object for that String496* @throws IllegalArgumentException if <code>s</code> is <code>null</code>,497* or is formatted incorrectly498*/499public static AWTKeyStroke getAWTKeyStroke(String s) {500if (s == null) {501throw new IllegalArgumentException("String cannot be null");502}503504final String errmsg = "String formatted incorrectly";505506StringTokenizer st = new StringTokenizer(s, " ");507508int mask = 0;509boolean released = false;510boolean typed = false;511boolean pressed = false;512513synchronized (AWTKeyStroke.class) {514if (modifierKeywords == null) {515Map<String, Integer> uninitializedMap = new HashMap<>(8, 1.0f);516uninitializedMap.put("shift",517Integer.valueOf(InputEvent.SHIFT_DOWN_MASK518|InputEvent.SHIFT_MASK));519uninitializedMap.put("control",520Integer.valueOf(InputEvent.CTRL_DOWN_MASK521|InputEvent.CTRL_MASK));522uninitializedMap.put("ctrl",523Integer.valueOf(InputEvent.CTRL_DOWN_MASK524|InputEvent.CTRL_MASK));525uninitializedMap.put("meta",526Integer.valueOf(InputEvent.META_DOWN_MASK527|InputEvent.META_MASK));528uninitializedMap.put("alt",529Integer.valueOf(InputEvent.ALT_DOWN_MASK530|InputEvent.ALT_MASK));531uninitializedMap.put("altGraph",532Integer.valueOf(InputEvent.ALT_GRAPH_DOWN_MASK533|InputEvent.ALT_GRAPH_MASK));534uninitializedMap.put("button1",535Integer.valueOf(InputEvent.BUTTON1_DOWN_MASK));536uninitializedMap.put("button2",537Integer.valueOf(InputEvent.BUTTON2_DOWN_MASK));538uninitializedMap.put("button3",539Integer.valueOf(InputEvent.BUTTON3_DOWN_MASK));540modifierKeywords =541Collections.synchronizedMap(uninitializedMap);542}543}544545int count = st.countTokens();546547for (int i = 1; i <= count; i++) {548String token = st.nextToken();549550if (typed) {551if (token.length() != 1 || i != count) {552throw new IllegalArgumentException(errmsg);553}554return getCachedStroke(token.charAt(0), KeyEvent.VK_UNDEFINED,555mask, false);556}557558if (pressed || released || i == count) {559if (i != count) {560throw new IllegalArgumentException(errmsg);561}562563String keyCodeName = "VK_" + token;564int keyCode = getVKValue(keyCodeName);565566return getCachedStroke(KeyEvent.CHAR_UNDEFINED, keyCode,567mask, released);568}569570if (token.equals("released")) {571released = true;572continue;573}574if (token.equals("pressed")) {575pressed = true;576continue;577}578if (token.equals("typed")) {579typed = true;580continue;581}582583Integer tokenMask = (Integer)modifierKeywords.get(token);584if (tokenMask != null) {585mask |= tokenMask.intValue();586} else {587throw new IllegalArgumentException(errmsg);588}589}590591throw new IllegalArgumentException(errmsg);592}593594private static VKCollection getVKCollection() {595if (vks == null) {596vks = new VKCollection();597}598return vks;599}600/**601* Returns the integer constant for the KeyEvent.VK field named602* <code>key</code>. This will throw an603* <code>IllegalArgumentException</code> if <code>key</code> is604* not a valid constant.605*/606private static int getVKValue(String key) {607VKCollection vkCollect = getVKCollection();608609Integer value = vkCollect.findCode(key);610611if (value == null) {612int keyCode = 0;613final String errmsg = "String formatted incorrectly";614615try {616keyCode = KeyEvent.class.getField(key).getInt(KeyEvent.class);617} catch (NoSuchFieldException nsfe) {618throw new IllegalArgumentException(errmsg);619} catch (IllegalAccessException iae) {620throw new IllegalArgumentException(errmsg);621}622value = Integer.valueOf(keyCode);623vkCollect.put(key, value);624}625return value.intValue();626}627628/**629* Returns the character for this <code>AWTKeyStroke</code>.630*631* @return a char value632* @see #getAWTKeyStroke(char)633* @see KeyEvent#getKeyChar634*/635public final char getKeyChar() {636return keyChar;637}638639/**640* Returns the numeric key code for this <code>AWTKeyStroke</code>.641*642* @return an int containing the key code value643* @see #getAWTKeyStroke(int,int)644* @see KeyEvent#getKeyCode645*/646public final int getKeyCode() {647return keyCode;648}649650/**651* Returns the modifier keys for this <code>AWTKeyStroke</code>.652*653* @return an int containing the modifiers654* @see #getAWTKeyStroke(int,int)655*/656public final int getModifiers() {657return modifiers;658}659660/**661* Returns whether this <code>AWTKeyStroke</code> represents a key release.662*663* @return <code>true</code> if this <code>AWTKeyStroke</code>664* represents a key release; <code>false</code> otherwise665* @see #getAWTKeyStroke(int,int,boolean)666*/667public final boolean isOnKeyRelease() {668return onKeyRelease;669}670671/**672* Returns the type of <code>KeyEvent</code> which corresponds to673* this <code>AWTKeyStroke</code>.674*675* @return <code>KeyEvent.KEY_PRESSED</code>,676* <code>KeyEvent.KEY_TYPED</code>,677* or <code>KeyEvent.KEY_RELEASED</code>678* @see java.awt.event.KeyEvent679*/680public final int getKeyEventType() {681if (keyCode == KeyEvent.VK_UNDEFINED) {682return KeyEvent.KEY_TYPED;683} else {684return (onKeyRelease)685? KeyEvent.KEY_RELEASED686: KeyEvent.KEY_PRESSED;687}688}689690/**691* Returns a numeric value for this object that is likely to be unique,692* making it a good choice as the index value in a hash table.693*694* @return an int that represents this object695*/696public int hashCode() {697return (((int)keyChar) + 1) * (2 * (keyCode + 1)) * (modifiers + 1) +698(onKeyRelease ? 1 : 2);699}700701/**702* Returns true if this object is identical to the specified object.703*704* @param anObject the Object to compare this object to705* @return true if the objects are identical706*/707public final boolean equals(Object anObject) {708if (anObject instanceof AWTKeyStroke) {709AWTKeyStroke ks = (AWTKeyStroke)anObject;710return (ks.keyChar == keyChar && ks.keyCode == keyCode &&711ks.onKeyRelease == onKeyRelease &&712ks.modifiers == modifiers);713}714return false;715}716717/**718* Returns a string that displays and identifies this object's properties.719* The <code>String</code> returned by this method can be passed720* as a parameter to <code>getAWTKeyStroke(String)</code> to produce721* a key stroke equal to this key stroke.722*723* @return a String representation of this object724* @see #getAWTKeyStroke(String)725*/726public String toString() {727if (keyCode == KeyEvent.VK_UNDEFINED) {728return getModifiersText(modifiers) + "typed " + keyChar;729} else {730return getModifiersText(modifiers) +731(onKeyRelease ? "released" : "pressed") + " " +732getVKText(keyCode);733}734}735736static String getModifiersText(int modifiers) {737StringBuilder buf = new StringBuilder();738739if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 ) {740buf.append("shift ");741}742if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0 ) {743buf.append("ctrl ");744}745if ((modifiers & InputEvent.META_DOWN_MASK) != 0 ) {746buf.append("meta ");747}748if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0 ) {749buf.append("alt ");750}751if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0 ) {752buf.append("altGraph ");753}754if ((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0 ) {755buf.append("button1 ");756}757if ((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0 ) {758buf.append("button2 ");759}760if ((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {761buf.append("button3 ");762}763764return buf.toString();765}766767static String getVKText(int keyCode) {768VKCollection vkCollect = getVKCollection();769Integer key = Integer.valueOf(keyCode);770String name = vkCollect.findName(key);771if (name != null) {772return name.substring(3);773}774int expected_modifiers =775(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);776777Field[] fields = KeyEvent.class.getDeclaredFields();778for (int i = 0; i < fields.length; i++) {779try {780if (fields[i].getModifiers() == expected_modifiers781&& fields[i].getType() == Integer.TYPE782&& fields[i].getName().startsWith("VK_")783&& fields[i].getInt(KeyEvent.class) == keyCode)784{785name = fields[i].getName();786vkCollect.put(name, key);787return name.substring(3);788}789} catch (IllegalAccessException e) {790assert(false);791}792}793return "UNKNOWN";794}795796/**797* Returns a cached instance of <code>AWTKeyStroke</code> (or a subclass of798* <code>AWTKeyStroke</code>) which is equal to this instance.799*800* @return a cached instance which is equal to this instance801*/802protected Object readResolve() throws java.io.ObjectStreamException {803synchronized (AWTKeyStroke.class) {804if (getClass().equals(getAWTKeyStrokeClass())) {805return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease);806}807}808return this;809}810811private static int mapOldModifiers(int modifiers) {812if ((modifiers & InputEvent.SHIFT_MASK) != 0) {813modifiers |= InputEvent.SHIFT_DOWN_MASK;814}815if ((modifiers & InputEvent.ALT_MASK) != 0) {816modifiers |= InputEvent.ALT_DOWN_MASK;817}818if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {819modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;820}821if ((modifiers & InputEvent.CTRL_MASK) != 0) {822modifiers |= InputEvent.CTRL_DOWN_MASK;823}824if ((modifiers & InputEvent.META_MASK) != 0) {825modifiers |= InputEvent.META_DOWN_MASK;826}827828modifiers &= InputEvent.SHIFT_DOWN_MASK829| InputEvent.ALT_DOWN_MASK830| InputEvent.ALT_GRAPH_DOWN_MASK831| InputEvent.CTRL_DOWN_MASK832| InputEvent.META_DOWN_MASK833| InputEvent.BUTTON1_DOWN_MASK834| InputEvent.BUTTON2_DOWN_MASK835| InputEvent.BUTTON3_DOWN_MASK;836837return modifiers;838}839840private static int mapNewModifiers(int modifiers) {841if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) {842modifiers |= InputEvent.SHIFT_MASK;843}844if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) {845modifiers |= InputEvent.ALT_MASK;846}847if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {848modifiers |= InputEvent.ALT_GRAPH_MASK;849}850if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) {851modifiers |= InputEvent.CTRL_MASK;852}853if ((modifiers & InputEvent.META_DOWN_MASK) != 0) {854modifiers |= InputEvent.META_MASK;855}856857return modifiers;858}859860}861862class VKCollection {863Map<Integer, String> code2name;864Map<String, Integer> name2code;865866public VKCollection() {867code2name = new HashMap<>();868name2code = new HashMap<>();869}870871public synchronized void put(String name, Integer code) {872assert((name != null) && (code != null));873assert(findName(code) == null);874assert(findCode(name) == null);875code2name.put(code, name);876name2code.put(name, code);877}878879public synchronized Integer findCode(String name) {880assert(name != null);881return (Integer)name2code.get(name);882}883884public synchronized String findName(Integer code) {885assert(code != null);886return (String)code2name.get(code);887}888}889890891