/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Dmitry A. Durnev
*/
package java.awt;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
public class AWTKeyStroke implements Serializable {
private static final long serialVersionUID = -6430539691155161871L;
private static final Map cache = new HashMap(); //Map<AWTKeyStroke, ? extends AWTKeyStroke>
private static final Map keyEventTypesMap = new HashMap(); //Map<int, String>
private static Constructor subConstructor;
static {
keyEventTypesMap.put(new Integer(KeyEvent.KEY_PRESSED), "pressed"); //$NON-NLS-1$
keyEventTypesMap.put(new Integer(KeyEvent.KEY_RELEASED), "released"); //$NON-NLS-1$
keyEventTypesMap.put(new Integer(KeyEvent.KEY_TYPED), "typed"); //$NON-NLS-1$
}
private char keyChar;
private int keyCode;
private int modifiers;
private boolean onKeyRelease;
protected AWTKeyStroke(char keyChar, int keyCode, int modifiers,
boolean onKeyRelease)
{
setAWTKeyStroke(keyChar, keyCode, modifiers, onKeyRelease);
}
private void setAWTKeyStroke( char keyChar, int keyCode, int modifiers,
boolean onKeyRelease)
{
this.keyChar = keyChar;
this.keyCode = keyCode;
this.modifiers = modifiers;
this.onKeyRelease = onKeyRelease;
}
protected AWTKeyStroke() {
this(KeyEvent.CHAR_UNDEFINED, KeyEvent.VK_UNDEFINED, 0, false);
}
public int hashCode() {
return modifiers + ( keyCode != KeyEvent.VK_UNDEFINED ?
keyCode : keyChar) + (onKeyRelease ? -1 : 0);
}
public final int getModifiers() {
return modifiers;
}
public final boolean equals(Object anObject) {
if (anObject instanceof AWTKeyStroke) {
AWTKeyStroke key = (AWTKeyStroke)anObject;
return ((key.keyCode == keyCode) && (key.keyChar == keyChar) &&
(key.modifiers == modifiers) &&
(key.onKeyRelease == onKeyRelease));
}
return false;
}
public String toString() {
int type = getKeyEventType();
return InputEvent.getModifiersExText(getModifiers()) + " " + //$NON-NLS-1$
keyEventTypesMap.get(new Integer(type)) + " " + //$NON-NLS-1$
(type == KeyEvent.KEY_TYPED ? new String(new char[] {keyChar}) :
KeyEvent.getKeyText(keyCode));
}
public final int getKeyCode() {
return keyCode;
}
public final char getKeyChar() {
return keyChar;
}
private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode,
int modifiers,
boolean onKeyRelease) {
AWTKeyStroke key = newInstance(keyChar, keyCode, modifiers, onKeyRelease);
AWTKeyStroke value = (AWTKeyStroke) cache.get(key);
if (value == null) {
value = key;
cache.put(key, value);
}
return value;
}
private static AWTKeyStroke newInstance(char keyChar, int keyCode,
int modifiers,
boolean onKeyRelease) {
AWTKeyStroke key;
if (subConstructor == null) {
key = new AWTKeyStroke();
} else {
try {
key = (AWTKeyStroke) subConstructor.newInstance(new Object[0]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
int allModifiers = getAllModifiers(modifiers);
key.setAWTKeyStroke(keyChar, keyCode, allModifiers, onKeyRelease);
return key;
}
private static int addMask(int mod, int mask) {
return ((mod & mask) != 0) ? (mod | mask) : mod;
}
/**
* return all (old & new) modifiers corresponding to
* @param mod old or new modifiers
* @return old and new modifiers together
*/
static int getAllModifiers(int mod) {
int allMod = mod;
int shift = (InputEvent.SHIFT_MASK | InputEvent.SHIFT_DOWN_MASK);
int ctrl = (InputEvent.CTRL_MASK | InputEvent.CTRL_DOWN_MASK);
int meta = (InputEvent.META_MASK | InputEvent.META_DOWN_MASK);
int alt = (InputEvent.ALT_MASK | InputEvent.ALT_DOWN_MASK);
int altGr = (InputEvent.ALT_GRAPH_MASK | InputEvent.ALT_GRAPH_DOWN_MASK);
// button modifiers are not converted between old & new
allMod = addMask(allMod, shift);
allMod = addMask(allMod, ctrl);
allMod = addMask(allMod, meta);
allMod = addMask(allMod, alt);
allMod = addMask(allMod, altGr);
return allMod;
}
public static AWTKeyStroke getAWTKeyStroke(String s) {
if (s == null) {
throw new IllegalArgumentException("null argument");
}
StringTokenizer tokenizer = new StringTokenizer(s);
Boolean release = null;
int modifiers = 0;
int keyCode = KeyEvent.VK_UNDEFINED;
char keyChar = KeyEvent.CHAR_UNDEFINED;
boolean typed = false;
long modifier = 0;
String token = null;
do {
token = getNextToken(tokenizer);
modifier = parseModifier(token);
modifiers |= modifier;
} while (modifier > 0);
typed = parseTypedID(token);
if (typed) {
token = getNextToken(tokenizer);
keyChar = parseTypedKey(token);
}
if (keyChar == KeyEvent.CHAR_UNDEFINED) {
release = parsePressedReleasedID(token);
if (release != null) {
token = getNextToken(tokenizer);
}
keyCode = parseKey(token);
}
if (tokenizer.hasMoreTokens()) {
// awt.66=Invalid format
throw new IllegalArgumentException("invalid format");
}
return getAWTKeyStroke(keyChar, keyCode, modifiers,
release == Boolean.TRUE);
}
private static String getNextToken(StringTokenizer tokenizer) {
try {
return tokenizer.nextToken();
} catch (NoSuchElementException exception) {
throw new IllegalArgumentException("invalid format");
}
}
static int getKeyCode(String s) {
try {
Field vk = KeyEvent.class.getField("VK_" + s); //$NON-NLS-1$
return vk.getInt(null);
} catch (Exception e) {
if (s.length() != 1) {
throw new IllegalArgumentException("invalid format");
}
return KeyEvent.VK_UNDEFINED;
}
}
public static AWTKeyStroke getAWTKeyStroke(char keyChar) {
return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false);
}
public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers,
boolean onKeyRelease) {
return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, modifiers,
onKeyRelease);
}
public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) {
if (keyChar == null) {
throw new IllegalArgumentException("keyChar parameter is null");
}
return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED,
modifiers, false);
}
public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) {
return getAWTKeyStroke(keyCode, modifiers, false);
}
public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent anEvent) {
int id = anEvent.getID();
char undef = KeyEvent.CHAR_UNDEFINED;
char keyChar = (id == KeyEvent.KEY_TYPED ? anEvent.getKeyChar() :
undef);
int keyCode = (keyChar == undef ? anEvent.getKeyCode() :
KeyEvent.VK_UNDEFINED);
return getAWTKeyStroke(keyChar, keyCode, anEvent.getModifiersEx(),
id == KeyEvent.KEY_RELEASED);
}
public final int getKeyEventType() {
if (keyCode == KeyEvent.VK_UNDEFINED) {
return KeyEvent.KEY_TYPED;
}
return (onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED);
}
public final boolean isOnKeyRelease() {
return onKeyRelease;
}
protected Object readResolve() throws ObjectStreamException {
return getAWTKeyStroke(this.keyChar, this.keyCode,
this.modifiers, this.onKeyRelease);
}
protected static void registerSubclass(Class subclass) {
if (subclass == null) {
throw new IllegalArgumentException("subclass parameter is null");
}
if (! AWTKeyStroke.class.isAssignableFrom(subclass)) {
throw new ClassCastException("subclass is not derived from AWTKeyStroke");
}
try {
subConstructor = subclass.getDeclaredConstructor(new Class[0]);
subConstructor.setAccessible(true);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
// awt.68=subclass could not be instantiated
throw new IllegalArgumentException("subclass could not be instantiated");
}
cache.clear(); //flush the cache
}
private static long parseModifier(String strMod) {
long modifiers = 0l;
if (strMod.equals("shift")) { //$NON-NLS-1$
modifiers |= InputEvent.SHIFT_DOWN_MASK;
} else if (strMod.equals("control") || strMod.equals("ctrl")) { //$NON-NLS-1$ //$NON-NLS-2$
modifiers |= InputEvent.CTRL_DOWN_MASK;
} else if (strMod.equals("meta")) { //$NON-NLS-1$
modifiers |= InputEvent.META_DOWN_MASK;
} else if (strMod.equals("alt")) { //$NON-NLS-1$
modifiers |= InputEvent.ALT_DOWN_MASK;
} else if (strMod.equals("altGraph")) { //$NON-NLS-1$
modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
} else if (strMod.equals("button1")) { //$NON-NLS-1$
modifiers |= InputEvent.BUTTON1_DOWN_MASK;
} else if (strMod.equals("button2")) { //$NON-NLS-1$
modifiers |= InputEvent.BUTTON2_DOWN_MASK;
} else if (strMod.equals("button3")) { //$NON-NLS-1$
modifiers |= InputEvent.BUTTON3_DOWN_MASK;
}
return modifiers;
}
private static boolean parseTypedID(String strTyped) {
if (strTyped.equals("typed")) { //$NON-NLS-1$
return true;
}
return false;
}
private static char parseTypedKey(String strChar) {
char keyChar = KeyEvent.CHAR_UNDEFINED;
if (strChar.length() != 1) {
throw new IllegalArgumentException("invalid format");
}
keyChar = strChar.charAt(0);
return keyChar;
}
private static Boolean parsePressedReleasedID(String str) {
if (str.equals("pressed")) { //$NON-NLS-1$
return Boolean.FALSE;
} else if (str.equals("released")) { //$NON-NLS-1$
return Boolean.TRUE;
}
return null;
}
private static int parseKey(String strCode) {
int keyCode = KeyEvent.VK_UNDEFINED;
keyCode = getKeyCode(strCode);
if (keyCode == KeyEvent.VK_UNDEFINED) {
throw new IllegalArgumentException("invalid format");
}
return keyCode;
}
}