//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed 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.

package de.ugoe.cs.autoquest.keyboardmaps;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;

import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * Helper class that maps keyboard input based on a key map.
 * </p>
 * 
 * @version 1.0
 * @author Patrick Harms
 */
public class KeyboardMap {

    /**
     * <p>
     * Locale used for the mapping.
     * </p>
     */
    private Locale locale;

    /**
     * <p>
     * Name of the mappings-file.
     * </p>
     */
    private String fileName;

    /**
     * <p>
     * Map that defines which key strokes define a virtual key.
     * </p>
     */
    private Map<VirtualKey, List<KeyStroke>> keyStrokes =
        new HashMap<VirtualKey, List<KeyStroke>>();

    /**
     * <p>
     * Creates a new KeyboardMap.
     * </p>
     * 
     * @param locale
     *            Locale that is used for the keyboard map
     */
    KeyboardMap(Locale locale) {
        this.locale = locale;

        if ((this.locale == Locale.ENGLISH) || (this.locale == Locale.US) ||
            (this.locale == Locale.CANADA))
        {
            fileName = "en-us";
        }
        else if (locale == Locale.UK) {
            fileName = "en-gb";
        }
        else {
            fileName = locale.getLanguage();
        }
    }

    /**
     * <p>
     * Returns a character that is generated, given a {@link VirtualKey} is pressed while the
     * respective modifiers are active.
     * </p>
     * 
     * @param key
     *            key that is pressed
     * @param numlock
     *            true, if numlock is pressed; false otherwise
     * @param shift
     *            true, if shift is pressed; false otherwise
     * @param altgr
     *            true, if altgr is pressed; false otherwise
     * @param inhibit
     *            true, if inhibit is pressed; false otherwise
     * @return generated character
     */
    public char getCharacterFor(VirtualKey key,
                                boolean numlock,
                                boolean shift,
                                boolean altgr,
                                boolean inhibit)
    {
        List<KeyStroke> candidates = keyStrokes.get(key);

        if (candidates == null) {
            return Character.UNASSIGNED;
        }

        // try to find the key stroke
        for (KeyStroke keyStroke : candidates) {
            if ((numlock == keyStroke.getNumlock()) && (!keyStroke.getLocalstate()) &&
                (shift == keyStroke.getShift()) && (altgr == keyStroke.getAltgr()) &&
                (inhibit == keyStroke.getInhibit()))
            {
                return keyStroke.getCharacter();
            }
        }

        // try to find the key stroke with a local state ignoring the other keys
        for (KeyStroke keyStroke : candidates) {
            if ((numlock == keyStroke.getNumlock()) && (keyStroke.getLocalstate()) &&
                (inhibit == keyStroke.getInhibit()))
            {
                return keyStroke.getCharacter();
            }
        }

        return Character.UNASSIGNED;
    }

    /**
     * 
     * <p>
     * Initializes the keyboard map.
     * </p>
     * 
     * @throws IllegalArgumentException thrown if there is a problem loading the keyboard map
     */
    void init() throws IllegalArgumentException {
        Console.traceln(Level.FINE, "initializing keymap for locale " + locale);

        List<String[]> deadKeySequences = new ArrayList<String[]>();
        List<String[]> keyStrokes = new ArrayList<String[]>();
        readStream(getStream(fileName), deadKeySequences, keyStrokes);

        Console.traceln(Level.FINER, "read " + keyStrokes.size() + " key strokes and " +
            deadKeySequences.size() + " dead key sequences");

        VirtualKeySynonyms virtualKeySynonyms = determineVirtualKeySynonyms(keyStrokes);
        processKeyStrokes(keyStrokes, virtualKeySynonyms);
        // TODO still required? processDeadKeySequences(deadKeySequences);
    }

    /**
     * <p>
     * Returns a {@link InputStream} for a given filename.
     * </p>
     * 
     * @param name
     *            the filename
     * @return the {@link InputStream}
     */
    private InputStream getStream(String name) {
        Console.traceln(Level.FINER, "reading keymap for locale " + locale +
            " from resource keymaps/" + name);

        InputStream stream =
            this.getClass().getClassLoader().getResourceAsStream("keymaps/" + name);

        if (stream == null) {
            throw new IllegalArgumentException("no keyboard map available for locale " + locale);
        }

        return stream;
    }

    /**
     * <p>
     * Reads a keyboard map from a input stream.
     * </p>
     * 
     * @param stream
     *            input stream with the keyboard map
     * @param deadKeySequences
     *            the dead key sequences
     * @param keyStrokes
     *            the keystrokes
     * @throws IllegalArgumentException
     *             thrown if there is an error reading the keyboard map
     */
    private void readStream(InputStream stream,
                            List<String[]> deadKeySequences,
                            List<String[]> keyStrokes) throws IllegalArgumentException
    {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        }
        catch (UnsupportedEncodingException e) {
            Console.traceln(Level.WARNING, "no keyboard map available for locale " + locale);
            throw new IllegalArgumentException("provided stream can not be read due to invalid encoding",
                                               e);
        }

        try {
            String line;
            while ((line = in.readLine()) != null) {
                if (!"".equals(line)) {
                    processLine(line, deadKeySequences, keyStrokes);
                }
            }
        }
        catch (IOException e) {
            Console.traceln(Level.WARNING, "no keyboard map available for locale " + locale);
            throw new IllegalArgumentException("no keyboard map available for locale " + locale, e);
        }
        finally {
            try {
                in.close();
            }
            catch (IOException e) {
                Console.traceln(Level.WARNING,
                                "could not close input stream for reading keyboard map");
            }
        }
    }

    /**
     * <p>
     * Processes a line of a keyboard map file.
     * </p>
     * 
     * @param line
     *            the line in the keyboard map file
     * @param deadKeySequences
     *            the dead key sequences
     * @param keyStrokes
     *            the keystrokes
     */
    private void processLine(String line, List<String[]> deadKeySequences, List<String[]> keyStrokes)
    {
        String[] values = line.split(" ");

        if (values.length <= 0) {
            return;
        }

        // ignore comments
        if (values[0].startsWith("#")) {
            return;
        }

        if ("map".equals(values[0])) {
            // this is the map id. Ignore it.
        }
        else if ("include".equals(values[0])) {
            // process all includes
            for (int i = 1; i < values.length; i++) {
                if (!values[i].startsWith("#")) {
                    readStream(getStream(values[i]), deadKeySequences, keyStrokes);
                }
                else {
                    break;
                }
            }
        }
        else if ("sequence".equals(values[0])) {
            deadKeySequences.add(values);
        }
        else {
            boolean alreadyAdded = false;

            // check, if there is a replacement
            for (int i = 0; i < keyStrokes.size(); i++) {
                if (keyStrokes.get(i)[0].equals(values[0])) {
                    Console.traceln(Level.FINEST, "replacing key stroke " + values[0] +
                                    " with former keyid " + keyStrokes.get(i)[1] +
                                    " with new key id " + values[1]);
                    keyStrokes.set(i, values);
                    alreadyAdded = true;
                    break;
                }
            }

            if (!alreadyAdded) {
                keyStrokes.add(values);
            }
        }
    }

    /**
     * <p>
     * Determines synonyms for virtual keys for the keyboard map.
     * </p>
     * 
     * @param keyStrokes
     *            the keystrokes
     * @return the synonyms
     */
    private VirtualKeySynonyms determineVirtualKeySynonyms(List<String[]> keyStrokes) {
        Console.traceln(Level.FINER, "determining virtual keys and synonyms for the keymap");

        VirtualKeySynonyms virtualKeySynonyms = new VirtualKeySynonyms();

        // for debugging purposes, determine which key strokes are not matched to virtual keys.
        List<String[]> unmatchedKeyStrokes = new ArrayList<String[]>();

        for (String[] keyStroke : keyStrokes) {
            String keyStrokeName = keyStroke[0];
            int keyId = getKeyId(keyStroke[1]);
            // System.err.println(keyStrokeName + "  0x" + Integer.toHexString(keyId));

            VirtualKey virtualKey = determineVirtualKey(keyStrokeName);

            if (virtualKey != null) {
                virtualKeySynonyms.add(keyId, virtualKey);
            }
            else {
                unmatchedKeyStrokes.add(keyStroke);
            }
        }

        for (String[] unmatchedKeyStroke : unmatchedKeyStrokes) {
            if (!virtualKeySynonyms.containsKey(getKeyId(unmatchedKeyStroke[1]))) {
                Console.traceln(Level.FINEST, "no virtual key mapped to key stroke " +
                                unmatchedKeyStroke[0] + "(" + unmatchedKeyStroke[1] +
                                ") of keyboard map for locale " + locale);
            }
        }

        return virtualKeySynonyms;
    }

    /**
     * <p>
     * Converts a hexadecimal Id that is contained in a string into the integer Id of the key.
     * </p>
     * 
     * @param keyIdString
     *            the Id string
     * @return the Id as integer
     */
    private int getKeyId(String keyIdString) {
        if (keyIdString.startsWith("0x")) {
            keyIdString = keyIdString.substring(2);
        }

        return Integer.parseInt(keyIdString, 16);
    }

    /**
     * <p>
     * Processes a list of key strokes.
     * </p>
     * 
     * @param keyStrokes
     *            the key strokes
     * @param virtualKeySynonyms
     *            synonyms of the involved virtual keys
     */
    private void processKeyStrokes(List<String[]> keyStrokes, VirtualKeySynonyms virtualKeySynonyms)
    {
        for (String[] keyStroke : keyStrokes) {
            handleKeyStroke(keyStroke, virtualKeySynonyms);
        }

        addKeyStrokesIndependentOfNumLock();
    }

    /**
     * <p>
     * Handles a key stroke.
     * </p>
     * 
     * @param values
     *            contains the name, string Id, and modifiers of the key stroke
     * @param virtualKeySynonyms
     *            synonyms of the involved virtual keys
     */
    private void handleKeyStroke(String[] values, VirtualKeySynonyms virtualKeySynonyms) {
        String keyStrokeName = values[0];
        String keyIdString = values[1];
        if (keyIdString.startsWith("0x")) {
            keyIdString = keyIdString.substring(2);
        }

        int keyId = Integer.parseInt(keyIdString, 16);

        // parse the conditions
        boolean numlock = false;
        boolean localstate = false;
        boolean shift = false;
        boolean altgr = false;
        boolean addupper = false;
        boolean inhibit = false;

        for (int i = 2; i < values.length; i++) {
            if (!values[i].startsWith("#")) {
                if ("numlock".equals(values[i])) {
                    numlock = true;
                }
                else if ("localstate".equals(values[i])) {
                    localstate = true;
                }
                else if ("shift".equals(values[i])) {
                    shift = true;
                }
                else if ("altgr".equals(values[i])) {
                    altgr = true;
                }
                else if ("addupper".equals(values[i])) {
                    addupper = true;
                }
                else if ("inhibit".equals(values[i])) {
                    inhibit = true;
                }
                else {
                    Console.traceln(Level.SEVERE, "unknown condition " + values[i] +
                                    " specified for key stroke " + keyStrokeName +
                                    " through keyboard map for locale " + locale);
                    throw new IllegalArgumentException
                        ("no keyboard map available for locale " + locale);
                }
            }
            else {
                break;
            }
        }

        addAllRepresentedKeyStrokes(keyStrokeName, keyId, numlock, localstate, shift, altgr,
                                    addupper, inhibit, virtualKeySynonyms);
    }

    /**
     * <p>
     * Adds a key stroke and all synonyms to the keyboard map.
     * </p>
     * 
     * @param keyStrokeName
     *            name of the key stroke
     * @param keyId
     *            id of the key stroke
     * @param numlock
     *            true, if numlock is pressed; false otherwise
     * @param localstate
     *            true, if localstate is pressed; false otherwise
     * @param shift
     *            true, if shift is pressed; false otherwise
     * @param altgr
     *            true, if altgr is pressed; false otherwise
     * @param addupper
     *            true, if addupper is pressed; false otherwise
     * @param inhibit
     *            true, if inhibit is pressed; false otherwise
     * @param virtualKeySynonyms
     *            synonyms of the involved virtual keys
     */
    private void addAllRepresentedKeyStrokes(String keyStrokeName,
                                             int keyId,
                                             boolean numlock,
                                             boolean localstate,
                                             boolean shift,
                                             boolean altgr,
                                             boolean addupper,
                                             boolean inhibit,
                                             VirtualKeySynonyms virtualKeySynonyms)
    {
        VirtualKey[] virtualKeys = virtualKeySynonyms.getVirtualKeySynonyms(keyId);

        if (virtualKeys == null) {
            Console.traceln(Level.SEVERE, "no virtual key mapped to key stroke " + keyStrokeName +
                " of keyboard map for locale " + locale);
            return;
        }

        for (VirtualKey virtualKey : virtualKeys) {
            if (addupper) {
                char c = determineCharacter(keyStrokeName, true);
                addKeyStroke(keyStrokeName, virtualKey, numlock, localstate, true, altgr, inhibit,
                             c);

                c = determineCharacter(keyStrokeName, false);
                addKeyStroke(keyStrokeName, virtualKey, numlock, localstate, false, altgr, inhibit,
                             c);
            }
            else {
                char c = determineCharacter(keyStrokeName, false);
                addKeyStroke(keyStrokeName, virtualKey, numlock, localstate, shift, altgr, inhibit,
                             c);
            }
        }
    }

    /**
     * <p>
     * Adds a key stroke and to the keyboard map.
     * </p>
     * 
     * @param keyStrokeName
     *            name of the key stroke
     * @param virtualKey
     *            the associated virtual key
     * @param numlock
     *            true, if numlock is pressed; false otherwise
     * @param localstate
     *            true, if localstate is pressed; false otherwise
     * @param shift
     *            true, if shift is pressed; false otherwise
     * @param altgr
     *            true, if altgr is pressed; false otherwise
     * @param addupper
     *            true, if addupper is pressed; false otherwise
     * @param inhibit
     *            true, if inhibit is pressed; false otherwise
     * @param character
     *            the resulting character
     */
    private void addKeyStroke(String keyStrokeName,
                              VirtualKey virtualKey,
                              boolean numlock,
                              boolean localstate,
                              boolean shift,
                              boolean altgr,
                              boolean inhibit,
                              char character)
    {
        KeyStroke keyStroke =
            new KeyStroke(keyStrokeName, virtualKey, numlock, localstate, shift, altgr, inhibit,
                          character);

        List<KeyStroke> keyStrokeList = keyStrokes.get(keyStroke.getVirtualKey());

        if (keyStrokeList == null) {
            keyStrokeList = new ArrayList<KeyStroke>();
            keyStrokes.put(keyStroke.getVirtualKey(), keyStrokeList);
        }

        keyStrokeList.add(keyStroke);
    }

    /**
     * <p>
     * Adds key strokes independent of numlock.
     * </p>
     */
    private void addKeyStrokesIndependentOfNumLock() {
        for (Map.Entry<VirtualKey, List<KeyStroke>> entry : keyStrokes.entrySet()) {
            List<KeyStroke> keyStrokesToAdd = new ArrayList<KeyStroke>();
            for (KeyStroke keyStroke : entry.getValue()) {
                if (!keyStroke.getNumlock()) {
                    boolean foundPositiveNumlockVariant = false;
                    for (KeyStroke candidate : entry.getValue()) {
                        if ((candidate.getShift() == keyStroke.getShift()) &&
                            (candidate.getAltgr() == keyStroke.getAltgr()) &&
                            (candidate.getLocalstate() == keyStroke.getLocalstate()) &&
                            (candidate.getInhibit() == keyStroke.getInhibit()) &&
                            (candidate.getNumlock()))
                        {
                            foundPositiveNumlockVariant = true;
                            break;
                        }
                    }

                    if (!foundPositiveNumlockVariant) {
                        keyStrokesToAdd.add(keyStroke);
                    }
                }
            }

            for (KeyStroke keyStroke : keyStrokesToAdd) {
                addKeyStroke(keyStroke.getKeyStrokeName(), keyStroke.getVirtualKey(), true,
                             keyStroke.getLocalstate(), keyStroke.getShift(), keyStroke.getAltgr(),
                             keyStroke.getInhibit(), keyStroke.getCharacter());
            }
        }
    }

    /**
     * <p>
     * Determines a {@link VirtualKey} depending on a key stroke name
     * </p>
     * 
     * @param keyStrokeName
     *            name of the key stroke
     * @return related virtual key
     */
    private VirtualKey determineVirtualKey(String keyStrokeName) {
        if ("Shift_R".equals(keyStrokeName)) {
            return VirtualKey.SHIFT;
        }
        else if ("Shift_L".equals(keyStrokeName)) {
            return VirtualKey.SHIFT;
        }
        else if ("Alt_R".equals(keyStrokeName)) {
            return VirtualKey.ALT_GRAPH;
        }
        else if ("Mode_switch".equals(keyStrokeName)) {
            return VirtualKey.MODECHANGE;
        }
        else if ("ISO_Level3_Shift".equals(keyStrokeName)) {
            return VirtualKey.SHIFT;
        }
        else if ("Alt_L".equals(keyStrokeName)) {
            return VirtualKey.ALT;
        }
        else if ("Control_R".equals(keyStrokeName)) {
            return VirtualKey.CONTROL;
        }
        else if ("Control_L".equals(keyStrokeName)) {
            return VirtualKey.CONTROL;
        }
        else if ("Menu".equals(keyStrokeName)) {
            return VirtualKey.WINDOWS;
        }
        else if ("1".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_1;
        }
        else if ("2".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_2;
        }
        else if ("3".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_3;
        }
        else if ("4".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_4;
        }
        else if ("5".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_5;
        }
        else if ("6".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_6;
        }
        else if ("7".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_7;
        }
        else if ("8".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_8;
        }
        else if ("9".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_9;
        }
        else if ("0".equals(keyStrokeName)) {
            return VirtualKey.DIGIT_0;
        }
        else if ("BackSpace".equals(keyStrokeName)) {
            return VirtualKey.BACK_SPACE;
        }
        else if ("Tab".equals(keyStrokeName)) {
            return VirtualKey.TAB;
        }
        else if ("q".equals(keyStrokeName)) {
            return VirtualKey.LETTER_Q;
        }
        else if ("w".equals(keyStrokeName)) {
            return VirtualKey.LETTER_W;
        }
        else if ("e".equals(keyStrokeName)) {
            return VirtualKey.LETTER_E;
        }
        else if ("r".equals(keyStrokeName)) {
            return VirtualKey.LETTER_R;
        }
        else if ("t".equals(keyStrokeName)) {
            return VirtualKey.LETTER_T;
        }
        else if ("y".equals(keyStrokeName)) {
            return VirtualKey.LETTER_Y;
        }
        else if ("u".equals(keyStrokeName)) {
            return VirtualKey.LETTER_U;
        }
        else if ("i".equals(keyStrokeName)) {
            return VirtualKey.LETTER_I;
        }
        else if ("o".equals(keyStrokeName)) {
            return VirtualKey.LETTER_O;
        }
        else if ("p".equals(keyStrokeName)) {
            return VirtualKey.LETTER_P;
        }
        else if ("a".equals(keyStrokeName)) {
            return VirtualKey.LETTER_A;
        }
        else if ("s".equals(keyStrokeName)) {
            return VirtualKey.LETTER_S;
        }
        else if ("d".equals(keyStrokeName)) {
            return VirtualKey.LETTER_D;
        }
        else if ("f".equals(keyStrokeName)) {
            return VirtualKey.LETTER_F;
        }
        else if ("g".equals(keyStrokeName)) {
            return VirtualKey.LETTER_G;
        }
        else if ("h".equals(keyStrokeName)) {
            return VirtualKey.LETTER_H;
        }
        else if ("j".equals(keyStrokeName)) {
            return VirtualKey.LETTER_J;
        }
        else if ("k".equals(keyStrokeName)) {
            return VirtualKey.LETTER_K;
        }
        else if ("l".equals(keyStrokeName)) {
            return VirtualKey.LETTER_L;
        }
        else if ("Return".equals(keyStrokeName)) {
            return VirtualKey.ENTER;
        }
        else if ("z".equals(keyStrokeName)) {
            return VirtualKey.LETTER_Z;
        }
        else if ("x".equals(keyStrokeName)) {
            return VirtualKey.LETTER_X;
        }
        else if ("c".equals(keyStrokeName)) {
            return VirtualKey.LETTER_C;
        }
        else if ("v".equals(keyStrokeName)) {
            return VirtualKey.LETTER_V;
        }
        else if ("b".equals(keyStrokeName)) {
            return VirtualKey.LETTER_B;
        }
        else if ("n".equals(keyStrokeName)) {
            return VirtualKey.LETTER_N;
        }
        else if ("m".equals(keyStrokeName)) {
            return VirtualKey.LETTER_M;
        }
        else if ("space".equals(keyStrokeName)) {
            return VirtualKey.SPACE;
        }
        else if ("less".equals(keyStrokeName)) {
            return VirtualKey.LESS;
        }
        else if ("greater".equals(keyStrokeName)) {
            return VirtualKey.GREATER;
        }
        else if ("Escape".equals(keyStrokeName)) {
            return VirtualKey.ESCAPE;
        }
        else if ("F1".equals(keyStrokeName)) {
            return VirtualKey.F1;
        }
        else if ("F2".equals(keyStrokeName)) {
            return VirtualKey.F2;
        }
        else if ("F3".equals(keyStrokeName)) {
            return VirtualKey.F3;
        }
        else if ("F4".equals(keyStrokeName)) {
            return VirtualKey.F4;
        }
        else if ("F5".equals(keyStrokeName)) {
            return VirtualKey.F5;
        }
        else if ("F6".equals(keyStrokeName)) {
            return VirtualKey.F6;
        }
        else if ("F7".equals(keyStrokeName)) {
            return VirtualKey.F7;
        }
        else if ("F8".equals(keyStrokeName)) {
            return VirtualKey.F8;
        }
        else if ("F9".equals(keyStrokeName)) {
            return VirtualKey.F9;
        }
        else if ("F10".equals(keyStrokeName)) {
            return VirtualKey.F10;
        }
        else if ("F11".equals(keyStrokeName)) {
            return VirtualKey.F11;
        }
        else if ("F12".equals(keyStrokeName)) {
            return VirtualKey.F12;
        }
        else if ("F13".equals(keyStrokeName)) {
            return VirtualKey.F13;
        }
        else if ("F14".equals(keyStrokeName)) {
            return VirtualKey.F14;
        }
        else if ("F15".equals(keyStrokeName)) {
            return VirtualKey.F15;
        }
        else if ("F16".equals(keyStrokeName)) {
            return VirtualKey.F16;
        }
        else if ("F17".equals(keyStrokeName)) {
            return VirtualKey.F17;
        }
        else if ("F18".equals(keyStrokeName)) {
            return VirtualKey.F18;
        }
        else if ("F19".equals(keyStrokeName)) {
            return VirtualKey.F19;
        }
        else if ("F20".equals(keyStrokeName)) {
            return VirtualKey.F20;
        }
        else if ("F21".equals(keyStrokeName)) {
            return VirtualKey.F21;
        }
        else if ("F22".equals(keyStrokeName)) {
            return VirtualKey.F22;
        }
        else if ("F23".equals(keyStrokeName)) {
            return VirtualKey.F23;
        }
        else if ("F24".equals(keyStrokeName)) {
            return VirtualKey.F24;
        }
        else if ("Print".equals(keyStrokeName)) {
            return VirtualKey.PRINTSCREEN;
        }
        else if ("Scroll_Lock".equals(keyStrokeName)) {
            return VirtualKey.SCROLL_LOCK;
        }
        else if ("Insert".equals(keyStrokeName)) {
            return VirtualKey.INSERT;
        }
        else if ("Delete".equals(keyStrokeName)) {
            return VirtualKey.DELETE;
        }
        else if ("Home".equals(keyStrokeName)) {
            return VirtualKey.HOME;
        }
        else if ("End".equals(keyStrokeName)) {
            return VirtualKey.END;
        }
        else if ("Page_Up".equals(keyStrokeName)) {
            return VirtualKey.PAGE_UP;
        }
        else if ("Page_Down".equals(keyStrokeName)) {
            return VirtualKey.PAGE_DOWN;
        }
        else if ("Left".equals(keyStrokeName)) {
            return VirtualKey.LEFT;
        }
        else if ("Up".equals(keyStrokeName)) {
            return VirtualKey.UP;
        }
        else if ("Down".equals(keyStrokeName)) {
            return VirtualKey.DOWN;
        }
        else if ("Right".equals(keyStrokeName)) {
            return VirtualKey.RIGHT;
        }
        else if ("Num_Lock".equals(keyStrokeName)) {
            return VirtualKey.NUM_LOCK;
        }
        else if ("KP_Divide".equals(keyStrokeName)) {
            return VirtualKey.SLASH;
        }
        else if ("KP_Multiply".equals(keyStrokeName)) {
            return VirtualKey.ASTERISK;
        }
        else if ("KP_Subtract".equals(keyStrokeName)) {
            return VirtualKey.MINUS;
        }
        else if ("KP_Add".equals(keyStrokeName)) {
            return VirtualKey.PLUS;
        }
        else if ("KP_Enter".equals(keyStrokeName)) {
            return VirtualKey.ENTER;
        }
        else if ("KP_Decimal".equals(keyStrokeName)) {
            return VirtualKey.DECIMAL;
        }
        else if ("KP_Separator".equals(keyStrokeName)) {
            return VirtualKey.SEPARATOR;
        }
        else if ("KP_Delete".equals(keyStrokeName)) {
            return VirtualKey.DELETE;
        }
        else if ("KP_0".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_0;
        }
        else if ("KP_Insert".equals(keyStrokeName)) {
            return VirtualKey.INSERT;
        }
        else if ("KP_1".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_1;
        }
        else if ("KP_End".equals(keyStrokeName)) {
            return VirtualKey.END;
        }
        else if ("KP_2".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_2;
        }
        else if ("KP_Down".equals(keyStrokeName)) {
            return VirtualKey.KP_DOWN;
        }
        else if ("KP_3".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_3;
        }
        else if ("KP_Next".equals(keyStrokeName)) {
            return VirtualKey.PAGE_DOWN;
        }
        else if ("KP_4".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_4;
        }
        else if ("KP_Left".equals(keyStrokeName)) {
            return VirtualKey.KP_LEFT;
        }
        else if ("KP_5".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_5;
        }
        else if ("KP_Begin".equals(keyStrokeName)) {
            return VirtualKey.BEGIN;
        }
        else if ("KP_6".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_6;
        }
        else if ("KP_Right".equals(keyStrokeName)) {
            return VirtualKey.KP_RIGHT;
        }
        else if ("KP_7".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_7;
        }
        else if ("KP_Home".equals(keyStrokeName)) {
            return VirtualKey.HOME;
        }
        else if ("KP_8".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_8;
        }
        else if ("KP_Up".equals(keyStrokeName)) {
            return VirtualKey.KP_UP;
        }
        else if ("KP_9".equals(keyStrokeName)) {
            return VirtualKey.NUMPAD_9;
        }
        else if ("KP_Prior".equals(keyStrokeName)) {
            return VirtualKey.PAGE_UP;
        }
        else if ("Caps_Lock".equals(keyStrokeName)) {
            return VirtualKey.CAPS_LOCK;
        }
        else if ("exclam".equals(keyStrokeName)) {
            return VirtualKey.EXCLAMATION_MARK;
        }
        else if ("exclamdown".equals(keyStrokeName)) {
            return VirtualKey.INVERTED_EXCLAMATION_MARK;
        }
        else if ("quotedbl".equals(keyStrokeName)) {
            return VirtualKey.QUOTEDBL;
        }
        else if ("slash".equals(keyStrokeName)) {
            return VirtualKey.SLASH;
        }
        else if ("backslash".equals(keyStrokeName)) {
            return VirtualKey.BACK_SLASH;
        }
        else if ("dead_acute".equals(keyStrokeName)) {
            return VirtualKey.DEAD_ACUTE;
        }
        else if ("dead_diaresis".equals(keyStrokeName)) {
            return VirtualKey.DEAD_DIAERESIS;
        }
        else if ("dead_abovering".equals(keyStrokeName)) {
            return VirtualKey.DEAD_ABOVERING;
        }
        else if ("plus".equals(keyStrokeName)) {
            return VirtualKey.PLUS;
        }
        else if ("asterisk".equals(keyStrokeName)) {
            return VirtualKey.ASTERISK;
        }
        else if ("dead_tilde".equals(keyStrokeName)) {
            return VirtualKey.DEAD_TILDE;
        }
        else if ("dead_doubleacute".equals(keyStrokeName)) {
            return VirtualKey.DEAD_DOUBLEACUTE;
        }
        else if ("dead_caron".equals(keyStrokeName)) {
            return VirtualKey.DEAD_CARON;
        }
        else if ("dead_circumflex".equals(keyStrokeName)) {
            return VirtualKey.DEAD_CIRCUMFLEX;
        }
        else if ("comma".equals(keyStrokeName)) {
            return VirtualKey.COMMA;
        }
        else if ("semicolon".equals(keyStrokeName)) {
            return VirtualKey.SEMICOLON;
        }
        else if ("multiply".equals(keyStrokeName)) {
            return VirtualKey.MULTIPLY;
        }
        else if ("period".equals(keyStrokeName)) {
            return VirtualKey.PERIOD;
        }
        else if ("colon".equals(keyStrokeName)) {
            return VirtualKey.COLON;
        }
        else if ("dead_breve".equals(keyStrokeName)) {
            return VirtualKey.DEAD_BREVE;
        }
        else if ("division".equals(keyStrokeName)) {
            return VirtualKey.DIVIDE;
        }
        else if ("minus".equals(keyStrokeName)) {
            return VirtualKey.MINUS;
        }
        else if ("underscore".equals(keyStrokeName)) {
            return VirtualKey.UNDERSCORE;
        }
        else if ("dead_abovedot".equals(keyStrokeName)) {
            return VirtualKey.DEAD_ABOVEDOT;
        }
        else if ("bracketleft".equals(keyStrokeName)) {
            return VirtualKey.OPEN_BRACKET;
        }
        else if ("bracketright".equals(keyStrokeName)) {
            return VirtualKey.CLOSE_BRACKET;
        }
        else if ("grave".equals(keyStrokeName)) {
            return VirtualKey.DEAD_GRAVE;
        }
        else if ("equal".equals(keyStrokeName)) {
            return VirtualKey.EQUALS;
        }
        else if ("dead_macron".equals(keyStrokeName)) {
            return VirtualKey.DEAD_MACRON;
        }
        else if ("dead_ogonek".equals(keyStrokeName)) {
            return VirtualKey.DEAD_OGONEK;
        }
        else if ("dead_cedilla".equals(keyStrokeName)) {
            return VirtualKey.DEAD_CEDILLA;
        }
        else if ("ampersand".equals(keyStrokeName)) {
            return VirtualKey.AMPERSAND;
        }
        else if ("parenleft".equals(keyStrokeName)) {
            return VirtualKey.LEFT_PARENTHESIS;
        }
        else if ("parenright".equals(keyStrokeName)) {
            return VirtualKey.RIGHT_PARENTHESIS;
        }
        else if ("braceleft".equals(keyStrokeName)) {
            return VirtualKey.BRACELEFT;
        }
        else if ("braceright".equals(keyStrokeName)) {
            return VirtualKey.BRACERIGHT;
        }
        else if ("at".equals(keyStrokeName)) {
            return VirtualKey.AT;
        }
        else if ("dollar".equals(keyStrokeName)) {
            return VirtualKey.DOLLAR;
        }
        else if ("EuroSign".equals(keyStrokeName)) {
            return VirtualKey.EURO_SIGN;
        }
        else if ("Begin".equals(keyStrokeName)) {
            return VirtualKey.BEGIN;
        }
        else if ("numbersign".equals(keyStrokeName)) {
            return VirtualKey.NUMBER_SIGN;
        }
        else if ("asciicircum".equals(keyStrokeName)) {
            return VirtualKey.CIRCUMFLEX;
        }
        else if ("Kanji".equals(keyStrokeName)) {
            return VirtualKey.KANJI;
        }
        else if ("Katakana".equals(keyStrokeName)) {
            return VirtualKey.KATAKANA;
        }
        else if ("Hiragana_Katakana".equals(keyStrokeName)) {
            return VirtualKey.HIRAGANA;
        }
        else if ("Muhenkan".equals(keyStrokeName)) {
            // I found this in the KeyEvent description
            return VirtualKey.NONCONVERT;
        }
        else if ("kan".equals(keyStrokeName)) {
            // I found this in the KeyEvent description
            return VirtualKey.NONCONVERT;
        }
        else if ("Henkan_Mode".equals(keyStrokeName)) {
            // I found this in the key event description
            return VirtualKey.CONVERT;
        }
        else if ("voicedsound".equals(keyStrokeName)) {
            return VirtualKey.DEAD_VOICED_SOUND;
        }
        else if ("semivoicedsound".equals(keyStrokeName)) {
            return VirtualKey.DEAD_SEMIVOICED_SOUND;
        }
        else if ("Menu".equals(keyStrokeName)) {
            return VirtualKey.CONTEXT_MENU;
        }
        else {
            Console.traceln(Level.FINEST, "unknown virtual key for key stroke " + keyStrokeName +
                " specified through " + "keyboard map for locale " + locale);

            return null;
        }

        // for the following virtual keys no key stroke names are provided in the key maps
        /*
         * CANCEL(KeyEvent.VK_CANCEL), CLEAR(KeyEvent.VK_CLEAR), PAUSE(KeyEvent.VK_PAUSE),
         * HELP(KeyEvent.VK_HELP), META(KeyEvent.VK_META),
         * 
         * BACK_QUOTE(KeyEvent.VK_BACK_QUOTE), QUOTE(KeyEvent.VK_QUOTE),
         * 
         * DEAD_IOTA(KeyEvent.VK_DEAD_IOTA),
         * 
         * FINAL(KeyEvent.VK_FINAL), CONVERT(KeyEvent.VK_CONVERT),
         * NONCONVERT(KeyEvent.VK_NONCONVERT), ACCEPT(KeyEvent.VK_ACCEPT), KANA(KeyEvent.VK_KANA),
         * ALPHANUMERIC(KeyEvent.VK_ALPHANUMERIC), FULL_WIDTH(KeyEvent.VK_FULL_WIDTH),
         * HALF_WIDTH(KeyEvent.VK_HALF_WIDTH), ROMAN_CHARACTERS(KeyEvent.VK_ROMAN_CHARACTERS),
         * ALL_CANDIDATES(KeyEvent.VK_ALL_CANDIDATES),
         * PREVIOUS_CANDIDATE(KeyEvent.VK_PREVIOUS_CANDIDATE), CODE_INPUT(KeyEvent.VK_CODE_INPUT),
         * JAPANESE_KATAKANA(KeyEvent.VK_JAPANESE_KATAKANA),
         * JAPANESE_HIRAGANA(KeyEvent.VK_JAPANESE_HIRAGANA),
         * JAPANESE_ROMAN(KeyEvent.VK_JAPANESE_ROMAN), KANA_LOCK(KeyEvent.VK_KANA_LOCK),
         * INPUT_METHOD_ON_OFF(KeyEvent.VK_INPUT_METHOD_ON_OFF),
         * 
         * CUT(KeyEvent.VK_CUT), COPY(KeyEvent.VK_COPY), PASTE(KeyEvent.VK_PASTE),
         * UNDO(KeyEvent.VK_UNDO), AGAIN(KeyEvent.VK_AGAIN), FIND(KeyEvent.VK_FIND),
         * PROPS(KeyEvent.VK_PROPS), STOP(KeyEvent.VK_STOP), COMPOSE(KeyEvent.VK_COMPOSE),
         */
    }

    /**
     * <p>
     * Determines the character that is generated by a key stroke.
     * </p>
     *
     * @param keyStrokeName name of the key stroke
     * @param getUpper defines whether the upper case version of the key stroke is returned or not
     * @return the character
     */
    private char determineCharacter(String keyStrokeName, boolean getUpper) {
        if ("Shift_R".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Shift_L".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Alt_R".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Mode_switch".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("ISO_Level3_Shift".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Alt_L".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Control_R".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Control_L".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Menu".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("1".equals(keyStrokeName)) {
            return '1';
        }
        else if ("2".equals(keyStrokeName)) {
            return '2';
        }
        else if ("3".equals(keyStrokeName)) {
            return '3';
        }
        else if ("4".equals(keyStrokeName)) {
            return '4';
        }
        else if ("5".equals(keyStrokeName)) {
            return '5';
        }
        else if ("6".equals(keyStrokeName)) {
            return '6';
        }
        else if ("7".equals(keyStrokeName)) {
            return '7';
        }
        else if ("8".equals(keyStrokeName)) {
            return '8';
        }
        else if ("9".equals(keyStrokeName)) {
            return '9';
        }
        else if ("0".equals(keyStrokeName)) {
            return '0';
        }
        else if ("BackSpace".equals(keyStrokeName)) {
            return '\b';
        }
        else if ("Tab".equals(keyStrokeName)) {
            return '\t';
        }
        else if ("ISO_Left_Tab".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("q".equals(keyStrokeName)) {
            return getUpper ? 'Q' : 'q';
        }
        else if ("w".equals(keyStrokeName)) {
            return getUpper ? 'W' : 'w';
        }
        else if ("e".equals(keyStrokeName)) {
            return getUpper ? 'E' : 'e';
        }
        else if ("r".equals(keyStrokeName)) {
            return getUpper ? 'R' : 'r';
        }
        else if ("t".equals(keyStrokeName)) {
            return getUpper ? 'T' : 't';
        }
        else if ("y".equals(keyStrokeName)) {
            return getUpper ? 'Y' : 'y';
        }
        else if ("u".equals(keyStrokeName)) {
            return getUpper ? 'U' : 'u';
        }
        else if ("i".equals(keyStrokeName)) {
            return getUpper ? 'I' : 'i';
        }
        else if ("o".equals(keyStrokeName)) {
            return getUpper ? 'O' : 'o';
        }
        else if ("p".equals(keyStrokeName)) {
            return getUpper ? 'P' : 'p';
        }
        else if ("a".equals(keyStrokeName)) {
            return getUpper ? 'A' : 'a';
        }
        else if ("s".equals(keyStrokeName)) {
            return getUpper ? 'S' : 's';
        }
        else if ("d".equals(keyStrokeName)) {
            return getUpper ? 'D' : 'd';
        }
        else if ("f".equals(keyStrokeName)) {
            return getUpper ? 'F' : 'f';
        }
        else if ("g".equals(keyStrokeName)) {
            return getUpper ? 'G' : 'g';
        }
        else if ("h".equals(keyStrokeName)) {
            return getUpper ? 'H' : 'h';
        }
        else if ("j".equals(keyStrokeName)) {
            return getUpper ? 'J' : 'j';
        }
        else if ("k".equals(keyStrokeName)) {
            return getUpper ? 'K' : 'k';
        }
        else if ("l".equals(keyStrokeName)) {
            return getUpper ? 'L' : 'l';
        }
        else if ("Return".equals(keyStrokeName)) {
            return '\n';
        }
        else if ("z".equals(keyStrokeName)) {
            return getUpper ? 'Z' : 'z';
        }
        else if ("x".equals(keyStrokeName)) {
            return getUpper ? 'X' : 'x';
        }
        else if ("c".equals(keyStrokeName)) {
            return getUpper ? 'C' : 'c';
        }
        else if ("v".equals(keyStrokeName)) {
            return getUpper ? 'V' : 'v';
        }
        else if ("b".equals(keyStrokeName)) {
            return getUpper ? 'B' : 'b';
        }
        else if ("n".equals(keyStrokeName)) {
            return getUpper ? 'N' : 'n';
        }
        else if ("m".equals(keyStrokeName)) {
            return getUpper ? 'M' : 'm';
        }
        else if ("space".equals(keyStrokeName)) {
            return ' ';
        }
        else if ("less".equals(keyStrokeName)) {
            return '<';
        }
        else if ("greater".equals(keyStrokeName)) {
            return '>';
        }
        else if ("bar".equals(keyStrokeName)) {
            return '|';
        }
        else if ("brokenbar".equals(keyStrokeName)) {
            return '¦';
        }
        else if ("Escape".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F1".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F2".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F3".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F4".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F5".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F6".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F7".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F8".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F9".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F10".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F11".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("SunF36".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F12".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("SunF37".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Print".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Sys_Req".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Execute".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F22".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Scroll_Lock".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F23".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Insert".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Delete".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Home".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("End".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Page_Up".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Page_Down".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Left".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Up".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Down".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Right".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Num_Lock".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_Divide".equals(keyStrokeName)) {
            return '/';
        }
        else if ("KP_Multiply".equals(keyStrokeName)) {
            return '*';
        }
        else if ("KP_Subtract".equals(keyStrokeName)) {
            return '-';
        }
        else if ("KP_Add".equals(keyStrokeName)) {
            return '+';
        }
        else if ("KP_Enter".equals(keyStrokeName)) {
            return '\n';
        }
        else if ("KP_Decimal".equals(keyStrokeName)) {
            return ',';
        }
        else if ("KP_Separator".equals(keyStrokeName)) {
            return ',';
        }
        else if ("KP_Delete".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_0".equals(keyStrokeName)) {
            return '0';
        }
        else if ("KP_Insert".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_1".equals(keyStrokeName)) {
            return '1';
        }
        else if ("KP_End".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_2".equals(keyStrokeName)) {
            return '2';
        }
        else if ("KP_Down".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_3".equals(keyStrokeName)) {
            return '3';
        }
        else if ("KP_Next".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_4".equals(keyStrokeName)) {
            return '4';
        }
        else if ("KP_Left".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_5".equals(keyStrokeName)) {
            return '5';
        }
        else if ("KP_Begin".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_6".equals(keyStrokeName)) {
            return '6';
        }
        else if ("KP_Right".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_7".equals(keyStrokeName)) {
            return '7';
        }
        else if ("KP_Home".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_8".equals(keyStrokeName)) {
            return '8';
        }
        else if ("KP_Up".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("KP_9".equals(keyStrokeName)) {
            return '9';
        }
        else if ("KP_Prior".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Caps_Lock".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Multi_key".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("exclam".equals(keyStrokeName)) {
            return '!';
        }
        else if ("onesuperior".equals(keyStrokeName)) {
            return '¹';
        }
        else if ("exclamdown".equals(keyStrokeName)) {
            return '¡';
        }
        else if ("quotedbl".equals(keyStrokeName)) {
            return '"';
        }
        else if ("twosuperior".equals(keyStrokeName)) {
            return '²';
        }
        else if ("oneeighth".equals(keyStrokeName)) {
            return '⅛';
        }
        else if ("section".equals(keyStrokeName)) {
            return '§';
        }
        else if ("threesuperior".equals(keyStrokeName)) {
            return '³';
        }
        else if ("sterling".equals(keyStrokeName)) {
            return '£';
        }
        else if ("dollar".equals(keyStrokeName)) {
            return '$';
        }
        else if ("onequarter".equals(keyStrokeName)) {
            return '¼';
        }
        else if ("currency".equals(keyStrokeName)) {
            return '¤';
        }
        else if ("percent".equals(keyStrokeName)) {
            return '%';
        }
        else if ("onehalf".equals(keyStrokeName)) {
            return '½';
        }
        else if ("threeeighths".equals(keyStrokeName)) {
            return '⅜';
        }
        else if ("ampersand".equals(keyStrokeName)) {
            return '&';
        }
        else if ("threequarters".equals(keyStrokeName)) {
            return '¾';
        }
        else if ("fiveeighths".equals(keyStrokeName)) {
            return '⅝';
        }
        else if ("slash".equals(keyStrokeName)) {
            return '/';
        }
        else if ("braceleft".equals(keyStrokeName)) {
            return '{';
        }
        else if ("seveneighths".equals(keyStrokeName)) {
            return '⅞';
        }
        else if ("parenleft".equals(keyStrokeName)) {
            return '(';
        }
        else if ("bracketleft".equals(keyStrokeName)) {
            return '[';
        }
        else if ("trademark".equals(keyStrokeName)) {
            return '™';
        }
        else if ("parenright".equals(keyStrokeName)) {
            return ')';
        }
        else if ("bracketright".equals(keyStrokeName)) {
            return ']';
        }
        else if ("plusminus".equals(keyStrokeName)) {
            return '±';
        }
        else if ("equal".equals(keyStrokeName)) {
            return '=';
        }
        else if ("braceright".equals(keyStrokeName)) {
            return '}';
        }
        else if ("ssharp".equals(keyStrokeName)) {
            return 'ß';
        }
        else if ("question".equals(keyStrokeName)) {
            return '?';
        }
        else if ("backslash".equals(keyStrokeName)) {
            return '\\';
        }
        else if ("questiondown".equals(keyStrokeName)) {
            return '¿';
        }
        else if ("acute".equals(keyStrokeName)) {
            return '´';
        }
        else if ("dead_acute".equals(keyStrokeName)) {
            return 0x0301;
        }
        else if ("grave".equals(keyStrokeName)) {
            return '`';
        }
        else if ("dead_grave".equals(keyStrokeName)) {
            return 0x0300;
        }
        else if ("dead_cedilla".equals(keyStrokeName)) {
            return 0x0327;
        }
        else if ("dead_ogonek".equals(keyStrokeName)) {
            return 0x0328;
        }
        else if ("at".equals(keyStrokeName)) {
            return '@';
        }
        else if ("Greek_OMEGA".equals(keyStrokeName)) {
            return 'Ω';
        }
        else if ("EuroSign".equals(keyStrokeName)) {
            return '€';
        }
        else if ("paragraph".equals(keyStrokeName)) {
            return 0x2029;
        }
        else if ("registered".equals(keyStrokeName)) {
            return '®';
        }
        else if ("tslash".equals(keyStrokeName)) {
            return 'ŧ';
        }
        else if ("Tslash".equals(keyStrokeName)) {
            return 'Ŧ';
        }
        else if ("z".equals(keyStrokeName)) {
            return getUpper ? 'Z' : 'z';
        }
        else if ("leftarrow".equals(keyStrokeName)) {
            return '←';
        }
        else if ("yen".equals(keyStrokeName)) {
            return '¥';
        }
        else if ("downarrow".equals(keyStrokeName)) {
            return '↓';
        }
        else if ("uparrow".equals(keyStrokeName)) {
            return '↑';
        }
        else if ("rightarrow".equals(keyStrokeName)) {
            return '→';
        }
        else if ("idotless".equals(keyStrokeName)) {
            return 'ı';
        }
        else if ("oslash".equals(keyStrokeName)) {
            return 'ø';
        }
        else if ("Ooblique".equals(keyStrokeName)) {
            return 'Ø';
        }
        else if ("thorn".equals(keyStrokeName)) {
            return 'þ';
        }
        else if ("THORN".equals(keyStrokeName)) {
            return 'Þ';
        }
        else if ("udiaeresis".equals(keyStrokeName)) {
            return getUpper ? 'Ü' : 'ü';
        }
        else if ("Udiaeresis".equals(keyStrokeName)) {
            return getUpper ? 'Ü' : 'ü';
        }
        else if ("dead_diaeresis".equals(keyStrokeName)) {
            return 0x0308;
        }
        else if ("dead_abovering".equals(keyStrokeName)) {
            return 0x030A;
        }
        else if ("plus".equals(keyStrokeName)) {
            return '+';
        }
        else if ("asterisk".equals(keyStrokeName)) {
            return '*';
        }
        else if ("asciitilde".equals(keyStrokeName)) {
            return '~';
        }
        else if ("dead_tilde".equals(keyStrokeName)) {
            return 0x0303;
        }
        else if ("dead_macron".equals(keyStrokeName)) {
            return 0x0304;
        }
        else if ("ae".equals(keyStrokeName)) {
            return 'æ';
        }
        else if ("AE".equals(keyStrokeName)) {
            return 'Æ';
        }
        else if ("eth".equals(keyStrokeName)) {
            return 'ð';
        }
        else if ("ETH".equals(keyStrokeName)) {
            return 'Ð';
        }
        else if ("dstroke".equals(keyStrokeName)) {
            return getUpper ? 'Đ' : 'đ';
        }
        else if ("ordfeminine".equals(keyStrokeName)) {
            return 'ª';
        }
        else if ("eng".equals(keyStrokeName)) {
            return 'ŋ';
        }
        else if ("ENG".equals(keyStrokeName)) {
            return 'Ŋ';
        }
        else if ("hstroke".equals(keyStrokeName)) {
            return 'ħ';
        }
        else if ("Hstroke".equals(keyStrokeName)) {
            return 'Ħ';
        }
        else if ("kra".equals(keyStrokeName)) {
            return 'ĸ';
        }
        else if ("odiaeresis".equals(keyStrokeName)) {
            return 'ö';
        }
        else if ("Odiaeresis".equals(keyStrokeName)) {
            return 'Ö';
        }
        else if ("dead_doubleacute".equals(keyStrokeName)) {
            return 0x030B;
        }
        else if ("adiaeresis".equals(keyStrokeName)) {
            return 'ä';
        }
        else if ("Adiaeresis".equals(keyStrokeName)) {
            return 'Ä';
        }
        else if ("dead_caron".equals(keyStrokeName)) {
            return 0x030C;
        }
        else if ("asciicircum".equals(keyStrokeName)) {
            return '^';
        }
        else if ("dead_circumflex".equals(keyStrokeName)) {
            return 0x0302;
        }
        else if ("degree".equals(keyStrokeName)) {
            return '°';
        }
        else if ("notsign".equals(keyStrokeName)) {
            return '¬';
        }
        else if ("numbersign".equals(keyStrokeName)) {
            return '#';
        }
        else if ("apostrophe".equals(keyStrokeName)) {
            return '\'';
        }
        else if ("dead_breve".equals(keyStrokeName)) {
            return 0x0306;
        }
        else if ("y".equals(keyStrokeName)) {
            return getUpper ? 'Y' : 'y';
        }
        else if ("guillemotleft".equals(keyStrokeName)) {
            return '»';
        }
        else if ("guillemotright".equals(keyStrokeName)) {
            return '«';
        }
        else if ("cent".equals(keyStrokeName)) {
            return '¢';
        }
        else if ("copyright".equals(keyStrokeName)) {
            return '©';
        }
        else if ("leftdoublequotemark".equals(keyStrokeName)) {
            return '„';
        }
        else if ("rightdoublequotemark".equals(keyStrokeName)) {
            return '“';
        }
        else if ("mu".equals(keyStrokeName)) {
            return 'µ';
        }
        else if ("masculine".equals(keyStrokeName)) {
            return 'º';
        }
        else if ("comma".equals(keyStrokeName)) {
            return ',';
        }
        else if ("semicolon".equals(keyStrokeName)) {
            return ';';
        }
        else if ("horizconnector".equals(keyStrokeName)) {
            return '·';
        }
        else if ("multiply".equals(keyStrokeName)) {
            return '×';
        }
        else if ("period".equals(keyStrokeName)) {
            return '.';
        }
        else if ("colon".equals(keyStrokeName)) {
            return ':';
        }
        else if ("periodcentered".equals(keyStrokeName)) {
            return '…';
        }
        else if ("division".equals(keyStrokeName)) {
            return '÷';
        }
        else if ("minus".equals(keyStrokeName)) {
            return '-';
        }
        else if ("underscore".equals(keyStrokeName)) {
            return '_';
        }
        else if ("dead_belowdot".equals(keyStrokeName)) {
            return 0x0323;
        }
        else if ("dead_abovedot".equals(keyStrokeName)) {
            return 0x0307;
        }
        else if ("eacute".equals(keyStrokeName)) {
            return getUpper ? 'É' : 'é';
        }
        else if ("Eacute".equals(keyStrokeName)) {
            return getUpper ? 'É' : 'é';
        }
        else if ("egrave".equals(keyStrokeName)) {
            return getUpper ? 'È' : 'è';
        }
        else if ("Egrave".equals(keyStrokeName)) {
            return getUpper ? 'È' : 'è';
        }
        else if ("ccedilla".equals(keyStrokeName)) {
            return getUpper ? 'Ç' : 'ç';
        }
        else if ("Ccedilla".equals(keyStrokeName)) {
            return getUpper ? 'Ç' : 'ç';
        }
        else if ("agrave".equals(keyStrokeName)) {
            return getUpper ? 'À' : 'à';
        }
        else if ("Agrave".equals(keyStrokeName)) {
            return getUpper ? 'À' : 'à';
        }
        else if ("lstroke".equals(keyStrokeName)) {
            return getUpper ? 'Ł' : 'ł';
        }
        else if ("Lstroke".equals(keyStrokeName)) {
            return getUpper ? 'Ł' : 'ł';
        }
        else if ("ugrave".equals(keyStrokeName)) {
            return getUpper ? 'Ù' : 'ù';
        }
        else if ("Ugrave".equals(keyStrokeName)) {
            return getUpper ? 'Ù' : 'ù';
        }
        else if ("igrave".equals(keyStrokeName)) {
            return getUpper ? 'Ì' : 'ì';
        }
        else if ("Igrave".equals(keyStrokeName)) {
            return getUpper ? 'Ì' : 'ì';
        }
        else if ("ograve".equals(keyStrokeName)) {
            return getUpper ? 'Ò' : 'ò';
        }
        else if ("ograve".equals(keyStrokeName)) {
            return getUpper ? 'Ò' : 'ò';
        }
        else if ("keyboard_type".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("keyboard_subtype".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("keyboard_functionkeys".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("kana_NU".equals(keyStrokeName)) {
            return 'ヌ';
        }
        else if ("kana_FU".equals(keyStrokeName)) {
            return 'フ';
        }
        else if ("kana_A".equals(keyStrokeName)) {
            return 'ア';
        }
        else if ("kana_a".equals(keyStrokeName)) {
            return 'ァ';
        }
        else if ("kana_U".equals(keyStrokeName)) {
            return 'ウ';
        }
        else if ("kana_u".equals(keyStrokeName)) {
            return 'ゥ';
        }
        else if ("kana_E".equals(keyStrokeName)) {
            return 'エ';
        }
        else if ("kana_e".equals(keyStrokeName)) {
            return 'ェ';
        }
        else if ("kana_O".equals(keyStrokeName)) {
            return 'オ';
        }
        else if ("kana_o".equals(keyStrokeName)) {
            return 'ォ';
        }
        else if ("kana_YA".equals(keyStrokeName)) {
            return 'ヤ';
        }
        else if ("kana_ya".equals(keyStrokeName)) {
            return 'ャ';
        }
        else if ("kana_YU".equals(keyStrokeName)) {
            return 'ユ';
        }
        else if ("kana_yu".equals(keyStrokeName)) {
            return 'ュ';
        }
        else if ("kana_YO".equals(keyStrokeName)) {
            return 'ヨ';
        }
        else if ("kana_yo".equals(keyStrokeName)) {
            return 'ョ';
        }
        else if ("kana_WA".equals(keyStrokeName)) {
            return 'ワ';
        }
        else if ("kana_WO".equals(keyStrokeName)) {
            return 'ヲ';
        }
        else if ("kana_HO".equals(keyStrokeName)) {
            return 'ホ';
        }
        else if ("kana_HE".equals(keyStrokeName)) {
            return 'ヘ';
        }
        else if ("kana_TA".equals(keyStrokeName)) {
            return 'タ';
        }
        else if ("kana_TE".equals(keyStrokeName)) {
            return 'テ';
        }
        else if ("kana_I".equals(keyStrokeName)) {
            return 'イ';
        }
        else if ("kana_i".equals(keyStrokeName)) {
            return 'ィ';
        }
        else if ("kana_SU".equals(keyStrokeName)) {
            return 'ス';
        }
        else if ("kana_KA".equals(keyStrokeName)) {
            return 'カ';
        }
        else if ("kana_N".equals(keyStrokeName)) {
            return 'ン';
        }
        else if ("kana_NA".equals(keyStrokeName)) {
            return 'ナ';
        }
        else if ("kana_NI".equals(keyStrokeName)) {
            return 'ニ';
        }
        else if ("kana_RA".equals(keyStrokeName)) {
            return 'ラ';
        }
        else if ("kana_SE".equals(keyStrokeName)) {
            return 'セ';
        }
        else if ("voicedsound".equals(keyStrokeName)) {
            return 0x3099;
        }
        else if ("semivoicedsound".equals(keyStrokeName)) {
            return 0x309A;
        }
        else if ("kana_openingbracket".equals(keyStrokeName)) {
            return 0x04A2;
        }
        else if ("kana_closingbracket".equals(keyStrokeName)) {
            return 0x04A3;
        }
        else if ("kana_CHI".equals(keyStrokeName)) {
            return 'チ';
        }
        else if ("kana_TO".equals(keyStrokeName)) {
            return 'ト';
        }
        else if ("kana_SHI".equals(keyStrokeName)) {
            return 'シ';
        }
        else if ("kana_HA".equals(keyStrokeName)) {
            return 'ハ';
        }
        else if ("kana_KI".equals(keyStrokeName)) {
            return 'キ';
        }
        else if ("kana_KU".equals(keyStrokeName)) {
            return 'ク';
        }
        else if ("kana_MA".equals(keyStrokeName)) {
            return 'マ';
        }
        else if ("kana_NO".equals(keyStrokeName)) {
            return 'ノ';
        }
        else if ("kana_RI".equals(keyStrokeName)) {
            return 'リ';
        }
        else if ("kana_RE".equals(keyStrokeName)) {
            return 'レ';
        }
        else if ("kana_KE".equals(keyStrokeName)) {
            return 'ケ';
        }
        else if ("Zenkaku_Hankaku".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Kanji".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("kana_MU".equals(keyStrokeName)) {
            return 'ム';
        }
        else if ("kana_TSU".equals(keyStrokeName)) {
            return 'ツ';
        }
        else if ("kana_tsu".equals(keyStrokeName)) {
            return 'ッ';
        }
        else if ("kana_SA".equals(keyStrokeName)) {
            return 'サ';
        }
        else if ("kana_SO".equals(keyStrokeName)) {
            return 'ソ';
        }
        else if ("kana_HI".equals(keyStrokeName)) {
            return 'ヒ';
        }
        else if ("kana_KO".equals(keyStrokeName)) {
            return 'コ';
        }
        else if ("kana_MI".equals(keyStrokeName)) {
            return 'ミ';
        }
        else if ("kana_MO".equals(keyStrokeName)) {
            return 'モ';
        }
        else if ("kana_NE".equals(keyStrokeName)) {
            return 'ネ';
        }
        else if ("kana_comma".equals(keyStrokeName)) {
            return '､';
        }
        else if ("kana_RU".equals(keyStrokeName)) {
            return 'ル';
        }
        else if ("kana_fullstop".equals(keyStrokeName)) {
            return '｡';
        }
        else if ("kana_ME".equals(keyStrokeName)) {
            return 'メ';
        }
        else if ("kana_conjunctive".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Henkan_Mode".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Hiragana_Katakana".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Katakana".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Romaji".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Muhenkan".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Eisu_toggle".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Eisu_toggle".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("F13".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Hangul".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else if ("Hangul_Hanja".equals(keyStrokeName)) {
            return Character.UNASSIGNED;
        }
        else {
            Console.traceln(Level.SEVERE, "unknown key stroke name " + keyStrokeName +
                            " specified through keyboard map " + "for locale " + locale);

            // if (shift)
            // {
            // System.err.println("    else if (\"" + keyStrokeName + "\".equals(keyStrokeName))");
            // System.err.println("    {");
            // System.err.println("      return shift ? '" +
            // Character.toUpperCase(keyStrokeName.charAt(0)) +
            // "' : '" + Character.toLowerCase(keyStrokeName.charAt(0)) + "';");
            // System.err.println("    }");
            // }
            // else
            // {
            // System.err.println("    else if (\"" + keyStrokeName + "\".equals(keyStrokeName))");
            // System.err.println("    {");
            // System.err.println("      return '" + keyStrokeName + "';");
            // System.err.println("    }");
            // }
            //
            // return 0x0;
            throw new IllegalArgumentException("no keyboard map available for locale " + locale);
        }
    }
}
