    rev 4839 : RT-2529: Locale specific runtime errors (Turkish)

       1 /*
       2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
       3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       4  *
       5  * This code is free software; you can redistribute it and/or modify it
       6  * under the terms of the GNU General Public License version 2 only, as
       7  * published by the Free Software Foundation.  Oracle designates this
       8  * particular file as subject to the "Classpath" exception as provided
       9  * by Oracle in the LICENSE file that accompanied this code.
      10  *
      11  * This code is distributed in the hope that it will be useful, but WITHOUT
      12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  * version 2 for more details (a copy is included in the LICENSE file that
      15  * accompanied this code).
      16  *
      17  * You should have received a copy of the GNU General Public License version
      18  * 2 along with this work; if not, write to the Free Software Foundation,
      19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      20  *
      21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      22  * or visit www.oracle.com if you need additional information or have any
      23  * questions.
      24  */
      25
      26 package javafx.scene.input;
      27
      28 import com.sun.javafx.tk.Toolkit;
      29 import java.util.ArrayList;
      30 import java.util.List;
      31 import java.util.Locale;
      32
      33 // PENDING_DOC_REVIEW
      34 /**
      35  * Represents a combination of keys which are used in keyboard shortcuts.
      36  * A key combination consists of a main key and a set of modifier keys. The main
      37  * key can be specified by its key code - {@code KeyCodeCombination} or key
      38  * character - {@code KeyCharacterCombination}. A modifier key is {@code shift},
      39  * {@code control}, {@code alt}, {@code meta} or {@code shortcut} and can be
      40  * defined as {@code DOWN}, {@code UP} or {@code ANY}.
      41  * <p>
      42  * The {@code shortcut} modifier is used to represent the modifier key which is
      43  * used commonly in keyboard shortcuts on the host platform. This is for
      44  * example {@code control} on Windows and {@code meta} (command key) on Mac.
      45  * By using {@code shortcut} key modifier developers can create platform
      46  * independent shortcuts. So the "Shortcut+C" key combination is handled
      47  * internally as "Ctrl+C" on Windows and "Meta+C" on Mac.
      48  * @since JavaFX 2.0
      49  */
      50 public abstract class KeyCombination {
      51
      52     /** Modifier which specifies that the {@code shift} key must be down. */
      53     public static final Modifier SHIFT_DOWN =
      54             new Modifier(KeyCode.SHIFT, ModifierValue.DOWN);
      55     /**
      56      * Modifier which specifies that the {@code shift} key can be either up or
      57      * down.
      58      */
      59     public static final Modifier SHIFT_ANY =
      60             new Modifier(KeyCode.SHIFT, ModifierValue.ANY);
      61     /** Modifier which specifies that the {@code control} key must be down. */
      62     public static final Modifier CONTROL_DOWN =
      63             new Modifier(KeyCode.CONTROL, ModifierValue.DOWN);
      64     /**
      65      * Modifier which specifies that the {@code control} key can be either up or
      66      * down.
      67      */
      68     public static final Modifier CONTROL_ANY =
      69             new Modifier(KeyCode.CONTROL, ModifierValue.ANY);
      70     /** Modifier which specifies that the {@code alt} key must be down. */
      71     public static final Modifier ALT_DOWN =
      72             new Modifier(KeyCode.ALT, ModifierValue.DOWN);
      73     /**
      74      * Modifier which specifies that the {@code alt} key can be either up or
      75      * down.
      76      */
      77     public static final Modifier ALT_ANY =
      78             new Modifier(KeyCode.ALT, ModifierValue.ANY);
      79     /** Modifier which specifies that the {@code meta} key must be down. */
      80     public static final Modifier META_DOWN =
      81             new Modifier(KeyCode.META, ModifierValue.DOWN);
      82     /**
      83      * Modifier which specifies that the {@code meta} key can be either up or
      84      * down.
      85      */
      86     public static final Modifier META_ANY =
      87             new Modifier(KeyCode.META, ModifierValue.ANY);
      88     /** Modifier which specifies that the {@code shortcut} key must be down. */
      89     public static final Modifier SHORTCUT_DOWN =
      90             new Modifier(KeyCode.SHORTCUT, ModifierValue.DOWN);
      91     /**
      92      * Modifier which specifies that the {@code shortcut} key can be either up
      93      * or down.
      94      */
      95     public static final Modifier SHORTCUT_ANY =
      96             new Modifier(KeyCode.SHORTCUT, ModifierValue.ANY);
      97
      98     private static final Modifier[] POSSIBLE_MODIFIERS = {
      99         SHIFT_DOWN, SHIFT_ANY,
     100         CONTROL_DOWN, CONTROL_ANY,
     101         ALT_DOWN, ALT_ANY,
     102         META_DOWN, META_ANY,
     103         SHORTCUT_DOWN, SHORTCUT_ANY
     104     };
     105
     106     /**
     107      * A KeyCombination that will match with no events.
     108      */
     109     public static final KeyCombination NO_MATCH = new KeyCombination() {
     110         @Override
     111         public boolean match(KeyEvent e) {
     112             return false;
     113         }
     114     };
     115
     116     /** The state of the {@code shift} key in this key combination. */
     117     private final ModifierValue shift;
     118
     119     /**
     120      * The state of the {@code shift} key in this key combination.
     121      * @return The state of the {@code shift} key in this key combination
     122      */
     123     public final ModifierValue getShift() {
     124         return shift;
     125     }
     126     /** The state of the {@code control} key in this key combination. */
     127     private final ModifierValue control;
     128
     129     /**
     130      * The state of the {@code control} key in this key combination.
     131      * @return The state of the {@code control} key in this key combination
     132      */
     133     public final ModifierValue getControl() {
     134         return control;
     135     }
     136     /** The state of the {@code alt} key in this key combination. */
     137     private final ModifierValue alt;
     138
     139     /**
     140      * The state of the {@code alt} key in this key combination.
     141      * @return The state of the {@code alt} key in this key combination.
     142      */
     143     public final ModifierValue getAlt() {
     144         return alt;
     145     }
     146     /** The state of the {@code meta} key in this key combination. */
     147     private final ModifierValue meta;
     148
     149     /**
     150      * The state of the {@code meta} key in this key combination.
     151      * @return The state of the {@code meta} key in this key combination
     152      */
     153     public final ModifierValue getMeta() {
     154         return meta;
     155     }
     156
     157     /** The state of the {@code shortcut} key in this key combination. */
     158     private final ModifierValue shortcut;
     159
     160     /**
     161      * The state of the {@code shortcut} key in this key combination.
     162      * @return The state of the {@code shortcut} key in this key combination
     163      */
     164     public final ModifierValue getShortcut() {
     165         return shortcut;
     166     }
     167
     168     /**
     169      * Constructs a {@code KeyCombination} with an explicit specification
     170      * of all modifier keys. Each modifier key can be set to {@code DOWN},
     171      * {@code UP} or {@code ANY}.
     172      *
     173      * @param shift the value of the {@code shift} modifier key
     174      * @param control the value of the {@code control} modifier key
     175      * @param alt the value of the {@code alt} modifier key
     176      * @param meta the value of the {@code meta} modifier key
     177      * @param shortcut the value of the {@code shortcut} modifier key
     178      */
     179     protected KeyCombination(final ModifierValue shift,
     180                              final ModifierValue control,
     181                              final ModifierValue alt,
     182                              final ModifierValue meta,
     183                              final ModifierValue shortcut) {
     184         if ((shift == null)
     185                 || (control == null)
     186                 || (alt == null)
     187                 || (meta == null)
     188                 || (shortcut == null)) {
     189             throw new NullPointerException("Modifier value must not be null!");
     190         }
     191
     192         this.shift = shift;
     193         this.control = control;
     194         this.alt = alt;
     195         this.meta = meta;
     196         this.shortcut = shortcut;
     197     }
     198
     199     /**
     200      * Constructs a {@code KeyCombination} with the specified list of modifiers.
     201      * All modifier keys which are not explicitly listed are set to the
     202      * default {@code UP} value.
     203      * <p>
     204      * All possible modifiers which change the default modifier value are
     205      * defined as constants in the {@code KeyCombination} class.
     206      *
     207      * @param modifiers the list of modifier keys and their corresponding values
     208      */
     209     protected KeyCombination(final Modifier... modifiers) {
     210         this(getModifierValue(modifiers, KeyCode.SHIFT),
     211              getModifierValue(modifiers, KeyCode.CONTROL),
     212              getModifierValue(modifiers, KeyCode.ALT),
     213              getModifierValue(modifiers, KeyCode.META),
     214              getModifierValue(modifiers, KeyCode.SHORTCUT));
     215     }
     216
     217     /**
     218      * Tests whether this key combination matches the combination in the given
     219      * {@code KeyEvent}.
     220      * <p>
     221      * The implementation of this method in the {@code KeyCombination} class
     222      * does only a partial test with the modifier keys. This method is
     223      * overridden in subclasses to include the main key in the test.
     224      *
     225      * @param event the key event
     226      * @return {@code true} if the key combinations match, {@code false}
     227      *      otherwise
     228      */
     229     public boolean match(final KeyEvent event) {
     230         final KeyCode shortcutKey =
     231                 Toolkit.getToolkit().getPlatformShortcutKey();
     232         return test(KeyCode.SHIFT, shift, shortcutKey, shortcut,
     233                     event.isShiftDown())
     234                 && test(KeyCode.CONTROL, control, shortcutKey, shortcut,
     235                         event.isControlDown())
     236                 && test(KeyCode.ALT, alt, shortcutKey, shortcut,
     237                         event.isAltDown())
     238                 && test(KeyCode.META, meta, shortcutKey, shortcut,
     239                         event.isMetaDown());
     240     }
     241
     242     /**
     243      * Returns a string representation of this {@code KeyCombination}.
     244      * <p>
     245      * The string representation consists of sections separated by plus
     246      * characters. Each section specifies either a modifier key or the main key.
     247      * <p>
     248      * A modifier key section contains the {@code KeyCode} name of a modifier
     249      * key. It can be prefixed with the {@code Ignored} keyword. A non-prefixed
     250      * modifier key implies its {@code DOWN} value while the prefixed version
     251      * implies the {@code ANY} (ignored) value. If some modifier key is not
     252      * specified in the string at all, it means it has the default {@code UP}
     253      * value.
     254      * <p>
     255      * The format of the main key section of the key combination string depends
     256      * on the {@code KeyCombination} subclass. It is either the key code name
     257      * for {@code KeyCodeCombination} or the single quoted key character for
     258      * {@code KeyCharacterCombination}.
     259      * <p>
     260      * Examples of {@code KeyCombination} string representations:
     261
     262 <PRE>
     263 "Ctrl+Alt+Q"
     264 "Ignore Shift+Ctrl+A"
     265 "Alt+'w'"
     266 </PRE>
     267
     268      * @return the string representation of this {@code KeyCombination}
     269      */
     270     public String getName() {
     271         StringBuilder sb = new StringBuilder();
     272         addModifiersIntoString(sb);
     273
     274         return sb.toString();
     275     }
     276
     277     /**
     278      * Tests whether this {@code KeyCombination} equals to the specified object.
     279      *
     280      * @param obj the object to compare to
     281      * @return {@code true} if the objects are equal, {@code false} otherwise
     282      */
     283     @Override
     284     public boolean equals(final Object obj) {
     285         if (!(obj instanceof KeyCombination)) {
     286             return false;
     287         }
     288
     289         final KeyCombination other = (KeyCombination) obj;
     290         return (shift == other.shift)
     291                 && (control == other.control)
     292                 && (alt == other.alt)
     293                 && (meta == other.meta)
     294                 && (shortcut == other.shortcut);
     295     }
     296
     297     /**
     298      * Returns a hash code value for this {@code KeyCombination}.
     299      *
     300      * @return the hash code value
     301      */
     302     @Override
     303     public int hashCode() {
     304         int hash = 7;
     305
     306         hash = 23 * hash + shift.hashCode();
     307         hash = 23 * hash + control.hashCode();
     308         hash = 23 * hash + alt.hashCode();
     309         hash = 23 * hash + meta.hashCode();
     310         hash = 23 * hash + shortcut.hashCode();
     311
     312         return hash;
     313     }
     314
     315     /**
     316      * Returns a string representation of this object. Implementation returns
     317      * the result of the {@code getName()} call.
     318      *
     319      * @return the string representation of this {@code KeyCombination}
     320      */
     321     @Override
     322     public String toString() {
     323         return getName();
     324     }
     325
     326     /**
     327      * Constructs a new {@code KeyCombination} from the specified string. The
     328      * string should be in the same format as produced by the {@code getName}
     329      * method.
     330      * <p>
     331      * If the main key section string is quoted in single quotes the method
     332      * creates a new {@code KeyCharacterCombination} for the unquoted substring.
     333      * Otherwise it finds the key code which name corresponds to the main key
     334      * section string and creates a {@code KeyCodeCombination} for it. If this
     335      * can't be done, it falls back to the {@code KeyCharacterCombination}.
     336      *
     337      * @param value the string which represents the requested key combination
     338      * @return the constructed {@code KeyCombination}
     339      * @since JavaFX 2.1
     340      */
     341     public static KeyCombination valueOf(String value) {
     342         final List<Modifier> modifiers = new ArrayList<Modifier>(4);
     343
     344         final String[] tokens = splitName(value);
     345
     346         KeyCode keyCode = null;
     347         String keyCharacter = null;
     348         for (String token : tokens) {
     349
     350             if ((token.length() > 2)
     351                     && (token.charAt(0) == '\'')
     352                     && (token.charAt(token.length() - 1) == '\'')) {
     353                 if ((keyCode != null) || (keyCharacter != null)) {
     354                     throw new IllegalArgumentException(
     355                             "Cannot parse key binding " + value);
     356                 }
     357
     358                 keyCharacter = token.substring(1, token.length() - 1)
     359                         .replace("\\'", "'");
     360                 continue;
     361             }
     362
     363             final String normalizedToken = normalizeToken(token);
     364
     365             final Modifier modifier = getModifier(normalizedToken);
     366             if (modifier != null) {
     367                 modifiers.add(modifier);
     368                 continue;
     369             }
     370
     371             if ((keyCode != null) || (keyCharacter != null)) {
     372                 throw new IllegalArgumentException(
     373                         "Cannot parse key binding " + value);
     374             }
     375
     376             keyCode = KeyCode.getKeyCode(normalizedToken);
     377             if (keyCode == null) {
     378                 keyCharacter = token;
     379             }
     380         }
     381
     382         if ((keyCode == null) && (keyCharacter == null)) {
     383             throw new IllegalArgumentException(
     384                     "Cannot parse key binding " + value);
     385         }
     386
     387         final Modifier[] modifierArray =
     388                 modifiers.toArray(new Modifier[modifiers.size()]);
     389         return (keyCode != null)
     390                     ? new KeyCodeCombination(keyCode, modifierArray)
     391                     : new KeyCharacterCombination(keyCharacter, modifierArray);
     392     }
     393
     394     /**
     395      * Constructs a new {@code KeyCombination} from the specified string. This
     396      * method simply delegates to {@link #valueOf(String)}.
     397      *
     398      * @param name the string which represents the requested key combination
     399      * @return the constructed {@code KeyCombination}
     400      *
     401      * @see #valueOf(String)
     402      */
     403     public static KeyCombination keyCombination(String name) {
     404         return valueOf(name);
     405     }
     406
     407     /**
     408      * This class represents a pair of modifier key and its value.
     409      * @since JavaFX 2.0
     410      */
     411     public static final class Modifier {
     412         private final KeyCode key;
     413         private final ModifierValue value;
     414
     415         private Modifier(final KeyCode key,
     416                          final ModifierValue value) {
     417             this.key = key;
     418             this.value = value;
     419         }
     420
     421         /**
     422          * Gets the modifier key of this {@code Modifier}.
     423          *
     424          * @return the modifier key
     425          */
     426         public KeyCode getKey() {
     427             return key;
     428         }
     429
     430         /**
     431          * Gets the modifier value of this {@code Modifier}.
     432          *
     433          * @return the modifier value
     434          */
     435         public ModifierValue getValue() {
     436             return value;
     437         }
     438
     439         /**
     440          * Returns a string representation of the modifier.
     441          * @return a string representation of the modifier
     442          */
     443         @Override
     444         public String toString() {
     445             return ((value == ModifierValue.ANY) ? "Ignore " : "")
     446                        + key.getName();
     447
     448         }
     449     }
     450
     451     /**
     452      * {@code ModifierValue} specifies state of modifier keys.
     453      * @since JavaFX 2.0
     454      */
     455     public static enum ModifierValue {
     456         /** Constant which indicates that the modifier key must be down. */
     457         DOWN,
     458         /** Constant which indicates that the modifier key must be up. */
     459         UP,
     460         /**
     461          * Constant which indicates that the modifier key can be either up or
     462          * down.
     463          */
     464         ANY
     465     }
     466
     467     private void addModifiersIntoString(final StringBuilder sb) {
     468         addModifierIntoString(sb, KeyCode.SHIFT, shift);
     469         addModifierIntoString(sb, KeyCode.CONTROL, control);
     470         addModifierIntoString(sb, KeyCode.ALT, alt);
     471         addModifierIntoString(sb, KeyCode.META, meta);
     472         addModifierIntoString(sb, KeyCode.SHORTCUT, shortcut);
     473     }
     474
     475     private static void addModifierIntoString(
     476             final StringBuilder sb,
     477             final KeyCode modifierKey,
     478             final ModifierValue modifierValue) {
     479
     480         if (modifierValue == ModifierValue.UP) {
     481             return;
     482         }
     483
     484         if (sb.length() > 0) {
     485             sb.append("+");
     486         }
     487
     488         if (modifierValue == ModifierValue.ANY) {
     489             sb.append("Ignore ");
     490         }
     491
     492         sb.append(modifierKey.getName());
     493     }
     494
     495     private static boolean test(final KeyCode testedModifierKey,
     496                                 final ModifierValue testedModifierValue,
     497                                 final KeyCode shortcutModifierKey,
     498                                 final ModifierValue shortcutModifierValue,
     499                                 final boolean isKeyDown) {
     500         final ModifierValue finalModifierValue =
     501                 (testedModifierKey == shortcutModifierKey)
     502                         ? resolveModifierValue(testedModifierValue,
     503                                                shortcutModifierValue)
     504                         : testedModifierValue;
     505
     506         return test(finalModifierValue, isKeyDown);
     507
     508     }
     509
     510     private static boolean test(final ModifierValue modifierValue,
     511                                 final boolean isDown) {
     512         switch (modifierValue) {
     513             case DOWN:
     514                 return isDown;
     515
     516             case UP:
     517                 return !isDown;
     518
     519             case ANY:
     520             default:
     521                 return true;
     522         }
     523     }
     524
     525     private static ModifierValue resolveModifierValue(
     526             final ModifierValue firstValue,
     527             final ModifierValue secondValue) {
     528         if ((firstValue == ModifierValue.DOWN)
     529                 || (secondValue == ModifierValue.DOWN)) {
     530             return ModifierValue.DOWN;
     531         }
     532
     533         if ((firstValue == ModifierValue.ANY)
     534                 || (secondValue == ModifierValue.ANY)) {
     535             return ModifierValue.ANY;
     536         }
     537
     538         return ModifierValue.UP;
     539     }
     540
     541     static Modifier getModifier(final String name) {
     542         for (final Modifier modifier: POSSIBLE_MODIFIERS) {
     543             if (modifier.toString().equals(name)) {
     544                 return modifier;
     545             }
     546         }
     547
     548         return null;
     549     }
     550
     551     private static ModifierValue getModifierValue(
     552             final Modifier[] modifiers,
     553             final KeyCode modifierKey) {
     554         ModifierValue modifierValue = ModifierValue.UP;
     555         for (final Modifier modifier: modifiers) {
     556             if (modifier == null) {
     557                 throw new NullPointerException("Modifier must not be null!");
     558             }
     559
     560             if (modifier.getKey() == modifierKey) {
     561                 if (modifierValue != ModifierValue.UP) {
     562                     throw new IllegalArgumentException(
     563                             (modifier.getValue() != modifierValue)
     564                                     ? "Conflicting modifiers specified!"
     565                                     : "Duplicate modifiers specified!");
     566                 }
     567
     568                 modifierValue = modifier.getValue();
     569             }
     570         }
     571
     572         return modifierValue;
     573     }
     574
     575     private static String normalizeToken(final String token) {
     576         final String[] words = token.split("\\s+");
     577         final StringBuilder sb = new StringBuilder();
     578
     579         for (final String word: words) {
     580             if (sb.length() > 0) {
     581                 sb.append(' ');
     582             }
     583
     584             sb.append(word.substring(0, 1).toUpperCase(Locale.ROOT));
     585             sb.append(word.substring(1).toLowerCase(Locale.ROOT));
     586         }
     587
     588         return sb.toString();
     589     }
     590
     591     private static String[] splitName(String name) {
     592         List<String> tokens = new ArrayList<String>();
     593         char[] chars = name.trim().toCharArray();
     594
     595         final int STATE_BASIC = 0;      // general text
     596         final int STATE_WHITESPACE = 1; // spaces found
     597         final int STATE_SEPARATOR = 2;  // plus found
     598         final int STATE_QUOTED = 3;     // quoted text
     599
     600         int state = STATE_BASIC;
     601         int tokenStart = 0;
     602         int tokenEnd = -1;
     603
     604         for (int i = 0; i < chars.length; i++) {
     605             char c = chars[i];
     606             switch(state) {
     607                 case STATE_BASIC:
     608                     switch(c) {
     609                         case ' ':
     610                         case '\t':
     611                         case '\n':
     612                         case '\f':
     613                         case '\r':
     614                         case '\u000B':
     615                             tokenEnd = i;
     616                             state = STATE_WHITESPACE;
     617                             break;
     618                         case '+':
     619                             tokenEnd = i;
     620                             state = STATE_SEPARATOR;
     621                             break;
     622                         case '\'':
     623                             if (i == 0 || chars[i - 1] != '\\') {
     624                                 state = STATE_QUOTED;
     625                             }
     626                             break;
     627                         default:
     628                             break;
     629                     }
     630                     break;
     631                 case STATE_WHITESPACE:
     632                     switch(c) {
     633                         case ' ':
     634                         case '\t':
     635                         case '\n':
     636                         case '\f':
     637                         case '\r':
     638                         case '\u000B':
     639                             break;
     640                         case '+':
     641                             state = STATE_SEPARATOR;
     642                             break;
     643                         case '\'':
     644                             state = STATE_QUOTED;
     645                             tokenEnd = -1;
     646                             break;
     647                         default:
     648                             state = STATE_BASIC;
     649                             tokenEnd = -1;
     650                             break;
     651                     }
     652                     break;
     653                 case STATE_SEPARATOR:
     654                     switch(c) {
     655                         case ' ':
     656                         case '\t':
     657                         case '\n':
     658                         case '\f':
     659                         case '\r':
     660                         case '\u000B':
     661                             break;
     662                         case '+':
     663                             throw new IllegalArgumentException(
     664                                     "Cannot parse key binding " + name);
     665                         default:
     666                             if (tokenEnd <= tokenStart) {
     667                                 throw new IllegalArgumentException(
     668                                         "Cannot parse key binding " + name);
     669                             }
     670                             tokens.add(new String(chars,
     671                                     tokenStart, tokenEnd - tokenStart));
     672                             tokenStart = i;
     673                             tokenEnd = -1;
     674                             state = (c == '\'' ? STATE_QUOTED : STATE_BASIC);
     675                             break;
     676                     }
     677                     break;
     678                 case STATE_QUOTED:
     679                     if (c == '\'' && chars[i - 1] != '\\') {
     680                         state = STATE_BASIC;
     681                     }
     682                     break;
     683             }
     684         }
     685
     686         switch(state) {
     687             case STATE_BASIC:
     688             case STATE_WHITESPACE:
     689                 tokens.add(new String(chars,
     690                         tokenStart, chars.length - tokenStart));
     691                 break;
     692             case STATE_SEPARATOR:
     693             case STATE_QUOTED:
     694                 throw new IllegalArgumentException(
     695                         "Cannot parse key binding " + name);
     696         }
     697
     698         return tokens.toArray(new String[tokens.size()]);
     699     }
     700 }
    --- EOF ---