From 2f16fd40faab7287dfcae4899050b9df360d0c29 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 20 Apr 2012 04:35:04 +0900 Subject: [PATCH] Make KeySpecParser case insensitive Change-Id: I76c3e9179dd8777d3cf3138ad76513d83672debf --- java/res/xml-sw600dp/key_styles_common.xml | 4 - java/res/xml-sw768dp/key_styles_common.xml | 7 +- java/res/xml/key_styles_common.xml | 10 +- java/res/xml/key_styles_enter.xml | 2 +- java/res/xml/rowkeys_georgian1.xml | 20 - java/res/xml/rowkeys_georgian2.xml | 54 +-- java/res/xml/rowkeys_georgian3.xml | 42 +- .../keyboard/internal/KeySpecParser.java | 33 +- .../keyboard/internal/KeyboardCodesSet.java | 5 +- .../keyboard/internal/KeyboardIconsSet.java | 11 +- .../keyboard/internal/KeyboardLabelsSet.java | 22 +- tests/res/values/donottranslate.xml | 6 + .../internal/KeySpecParserCsvTests.java | 110 ++++- .../keyboard/internal/KeySpecParserTests.java | 384 ++++++++++++------ .../keyboard/internal/KeyboardLabelsSet.tmpl | 22 +- 15 files changed, 468 insertions(+), 264 deletions(-) diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index e0676e890..0d54dfedb 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -98,7 +98,6 @@ latin:code="!code/key_shortcut" latin:keyIcon="iconShortcutKey" latin:keyIconDisabled="iconDisabledShortcutKey" - latin:keyLabelFlags="preserveCase" latin:keyActionFlags="noKeyPreview|altCodeWhileTyping" latin:backgroundType="functional" /> @@ -122,7 +122,6 @@ latin:code="!code/key_shortcut" latin:keyIcon="iconShortcutKey" latin:keyIconDisabled="iconDisabledShortcutKey" - latin:keyLabelFlags="preserveCase" latin:keyActionFlags="noKeyPreview|altCodeWhileTyping" latin:altCode="!code/key_space" latin:parentStyle="f1MoreKeysStyle" /> @@ -159,7 +158,7 @@ latin:code="!code/key_switch_alpha_symbol" latin:keyIcon="iconShortcutForLabel" latin:keyLabel="!label/label_to_symbol_with_microphone_key" - latin:keyLabelFlags="withIconRight|preserveCase" + latin:keyLabelFlags="withIconRight" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> @@ -168,7 +167,6 @@ latin:styleName="toSymbolKeyStyle" latin:code="!code/key_switch_alpha_symbol" latin:keyLabel="!label/label_to_symbol_key" - latin:keyLabelFlags="preserveCase" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> @@ -184,21 +182,19 @@ latin:styleName="toMoreSymbolKeyStyle" latin:code="!code/key_shift" latin:keyLabel="!label/label_to_more_symbol_key" - latin:keyLabelFlags="preserveCase" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml index f4c7ad46b..506ed47d6 100644 --- a/java/res/xml/key_styles_enter.xml +++ b/java/res/xml/key_styles_enter.xml @@ -94,7 +94,7 @@ latin:styleName="defaultEnterKeyStyle" latin:code="!code/key_enter" latin:keyIcon="iconReturnKey" - latin:keyLabelFlags="autoXScale|preserveCase|followKeyLabelRatio" + latin:keyLabelFlags="autoXScale|followKeyLabelRatio" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" latin:parentStyle="navigateMoreKeysStyle" /> diff --git a/java/res/xml/rowkeys_georgian1.xml b/java/res/xml/rowkeys_georgian1.xml index fcdf3fa58..d31a4c79c 100644 --- a/java/res/xml/rowkeys_georgian1.xml +++ b/java/res/xml/rowkeys_georgian1.xml @@ -27,55 +27,45 @@ > @@ -83,13 +73,11 @@ diff --git a/java/res/xml/rowkeys_georgian2.xml b/java/res/xml/rowkeys_georgian2.xml index 5673b9002..cdccda31f 100644 --- a/java/res/xml/rowkeys_georgian2.xml +++ b/java/res/xml/rowkeys_georgian2.xml @@ -26,82 +26,64 @@ latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" > + latin:keyLabel="A" /> + latin:keyLabel="შ" /> + latin:keyLabel="D" /> + latin:keyLabel="F" /> + latin:keyLabel="G" /> + latin:keyLabel="H" /> + latin:keyLabel="ჟ" /> + latin:keyLabel="K" /> + latin:keyLabel="L" /> + latin:moreKeys="ჺ" /> + latin:keyLabel="ს" /> + latin:keyLabel="დ" /> + latin:moreKeys="ჶ" /> + latin:moreKeys="ჹ" /> + latin:moreKeys="ჵ" /> + latin:moreKeys="ჷ" /> + latin:keyLabel="კ" /> + latin:keyLabel="ლ" /> diff --git a/java/res/xml/rowkeys_georgian3.xml b/java/res/xml/rowkeys_georgian3.xml index 1c0f45bef..a3714586f 100644 --- a/java/res/xml/rowkeys_georgian3.xml +++ b/java/res/xml/rowkeys_georgian3.xml @@ -27,63 +27,49 @@ > + latin:keyLabel="ძ" /> + latin:keyLabel="X" /> + latin:keyLabel="ჩ" /> + latin:keyLabel="V" /> + latin:keyLabel="B" /> + latin:keyLabel="N" /> + latin:keyLabel="M" /> + latin:keyLabel="ზ" /> + latin:moreKeys="ჴ" /> + latin:keyLabel="ც" /> + latin:moreKeys="ჳ" /> + latin:keyLabel="ბ" /> + latin:moreKeys="ჼ" /> + latin:keyLabel="მ" /> diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 8261400b2..e24cfa0e7 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -76,7 +76,7 @@ public class KeySpecParser { } private static boolean hasIcon(String moreKeySpec) { - if (moreKeySpec.startsWith(PREFIX_ICON)) { + if (moreKeySpec.regionMatches(true, 0, PREFIX_ICON, 0, PREFIX_ICON.length())) { final int end = indexOfLabelEnd(moreKeySpec, 0); if (end > 0) { return true; @@ -88,8 +88,8 @@ public class KeySpecParser { private static boolean hasCode(String moreKeySpec) { final int end = indexOfLabelEnd(moreKeySpec, 0); - if (end > 0 && end + 1 < moreKeySpec.length() - && moreKeySpec.substring(end + 1).startsWith(PREFIX_CODE)) { + if (end > 0 && end + 1 < moreKeySpec.length() && moreKeySpec.regionMatches( + true, end + 1, PREFIX_CODE, 0, PREFIX_CODE.length())) { return true; } return false; @@ -210,9 +210,9 @@ public class KeySpecParser { public static int parseCode(String text, KeyboardCodesSet codesSet, int defCode) { if (text == null) return defCode; - if (text.startsWith(PREFIX_CODE)) { + if (text.regionMatches(true, 0, PREFIX_CODE, 0, PREFIX_CODE.length())) { return codesSet.getCode(text.substring(PREFIX_CODE.length())); - } else if (text.startsWith(PREFIX_HEX)) { + } else if (text.regionMatches(true, 0, PREFIX_HEX, 0, PREFIX_HEX.length())) { return Integer.parseInt(text.substring(PREFIX_HEX.length()), 16); } else { return Integer.parseInt(text); @@ -350,20 +350,22 @@ public class KeySpecParser { throw new RuntimeException("too many @string/resource indirection: " + text); } + final int prefixLen = PREFIX_LABEL.length(); final int size = text.length(); - if (size < PREFIX_LABEL.length()) { + if (size < prefixLen) { return text; } sb = null; for (int pos = 0; pos < size; pos++) { final char c = text.charAt(pos); - if (text.startsWith(PREFIX_LABEL, pos) && labelsSet != null) { + if (text.regionMatches(true, pos, PREFIX_LABEL, 0, prefixLen) + && labelsSet != null) { if (sb == null) { sb = new StringBuilder(text.substring(0, pos)); } - final int end = searchLabelNameEnd(text, pos + PREFIX_LABEL.length()); - final String name = text.substring(pos + PREFIX_LABEL.length(), end); + final int end = searchLabelNameEnd(text, pos + prefixLen); + final String name = text.substring(pos + prefixLen, end); sb.append(labelsSet.getLabel(name)); pos = end - 1; } else if (c == ESCAPE_CHAR) { @@ -389,8 +391,9 @@ public class KeySpecParser { final int size = text.length(); for (int pos = start; pos < size; pos++) { final char c = text.charAt(pos); - // String resource name should be consisted of [a-z_0-9]. - if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { + // Label name should be consisted of [a-zA-Z_0-9]. + if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9') + || (c >= 'A' && c <= 'Z')) { continue; } return pos; @@ -442,17 +445,19 @@ public class KeySpecParser { if (moreKeys == null) { return defaultValue; } + final int keyLen = key.length(); boolean foundValue = false; int value = defaultValue; for (int i = 0; i < moreKeys.length; i++) { final String moreKeySpec = moreKeys[i]; - if (moreKeySpec == null || !moreKeySpec.startsWith(key)) { + if (moreKeySpec == null || !moreKeySpec.regionMatches(true, 0, key, 0, keyLen)) { continue; } moreKeys[i] = null; try { if (!foundValue) { - value = Integer.parseInt(moreKeySpec.substring(key.length())); + value = Integer.parseInt(moreKeySpec.substring(keyLen)); + foundValue = true; } } catch (NumberFormatException e) { throw new RuntimeException( @@ -469,7 +474,7 @@ public class KeySpecParser { boolean value = false; for (int i = 0; i < moreKeys.length; i++) { final String moreKeySpec = moreKeys[i]; - if (moreKeySpec == null || !moreKeySpec.equals(key)) { + if (moreKeySpec == null || !moreKeySpec.equalsIgnoreCase(key)) { continue; } moreKeys[i] = null; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index 736a96c56..c10a394c1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -33,7 +33,10 @@ public class KeyboardCodesSet { } public int getCode(final String name) { - final Integer id = sNameToIdMap.get(name); + Integer id = sNameToIdMap.get(name); + if (id == null) { + id = sNameToIdMap.get(name.toLowerCase()); + } if (id == null) throw new RuntimeException("Unknown key code: " + name); return mCodes[id]; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index ded89b1b8..07636249f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -36,7 +36,9 @@ public class KeyboardIconsSet { private static final HashMap ATTR_ID_TO_ICON_ID = new HashMap(); - private static final HashMap NAME_TO_ICON_ID = new HashMap(); + // Lower case icon name to icon id map. + private static final HashMap sLowerCaseNameToIdsMap = + new HashMap(); private static final String[] ICON_NAMES = new String[NUM_ICONS + 1]; private static final int ATTR_UNDEFINED = 0; @@ -66,7 +68,7 @@ public class KeyboardIconsSet { if (attrId != ATTR_UNDEFINED) { ATTR_ID_TO_ICON_ID.put(attrId, iconId); } - NAME_TO_ICON_ID.put(name, iconId); + sLowerCaseNameToIdsMap.put(name.toLowerCase(), iconId); ICON_NAMES[iconId] = name; } @@ -94,7 +96,10 @@ public class KeyboardIconsSet { } public static int getIconId(final String name) { - final Integer iconId = NAME_TO_ICON_ID.get(name); + Integer iconId = sLowerCaseNameToIdsMap.get(name); + if (iconId == null) { + iconId = sLowerCaseNameToIdsMap.get(name.toLowerCase()); + } if (iconId != null) { return iconId; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java index 0518b07ff..bd95848a9 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java @@ -31,7 +31,8 @@ public final class KeyboardLabelsSet { // Language to labels map. private static final HashMap sLocaleToLabelsMap = new HashMap(); - private static final HashMap sNameToIdMap = new HashMap(); + private static final HashMap sLowerCaseNameToIdsMap = + new HashMap(); private String[] mLabels; // Resource name to label map. @@ -60,12 +61,21 @@ public final class KeyboardLabelsSet { } public String getLabel(final String name) { - if (mResourceNameToLabelsMap.containsKey(name)) { - return mResourceNameToLabelsMap.get(name); + String lowerCaseName = null; + String label = mResourceNameToLabelsMap.get(name); + if (label == null) { + lowerCaseName = name.toLowerCase(); + label = mResourceNameToLabelsMap.get(lowerCaseName); + } + if (label != null) { + return label; + } + Integer id = sLowerCaseNameToIdsMap.get(name); + if (id == null) { + id = sLowerCaseNameToIdsMap.get(lowerCaseName); // lowerCaseName != null } - final Integer id = sNameToIdMap.get(name); if (id == null) throw new RuntimeException("Unknown label: " + name); - final String label = (id < mLabels.length) ? mLabels[id] : null; + label = (id < mLabels.length) ? mLabels[id] : null; return (label == null) ? LANGUAGE_DEFAULT[id] : label; } @@ -2492,7 +2502,7 @@ public final class KeyboardLabelsSet { static { int id = 0; for (final String name : NAMES) { - sNameToIdMap.put(name, id++); + sLowerCaseNameToIdsMap.put(name, id++); } for (int i = 0; i < LANGUAGES_AND_LABELS.length; i += 2) { diff --git a/tests/res/values/donottranslate.xml b/tests/res/values/donottranslate.xml index 42181ed92..875a55804 100644 --- a/tests/res/values/donottranslate.xml +++ b/tests/res/values/donottranslate.xml @@ -52,5 +52,11 @@ " ab\\\\ , d\\\\\\, , g\\,i " !label/multiple_chars x,!label/multiple_chars,y + !label/indirect_string infinite,!label/infinite_indirection,loop + !LABEL/MULTIPLE_CHARS + x,!LABEL/MULTIPLE_CHARS,y + !LABEL/UPPER_INDIRECT_STRING + infinite,!LABEL/INFINITE_INDIRECTION,loop + !fixedColumnOrder!2,!label/action_previous_as_more_key,!label/action_next_as_more_key diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java index d780352b7..093f75247 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java @@ -17,7 +17,6 @@ package com.android.inputmethod.keyboard.internal; import android.test.AndroidTestCase; -import android.text.TextUtils; import java.lang.reflect.Field; import java.util.ArrayList; @@ -32,6 +31,7 @@ public class KeySpecParserCsvTests extends AndroidTestCase { super.setUp(); mLabelsSet.setLanguage(Locale.ENGLISH.getLanguage()); + mLabelsSet.loadStringResources(getContext()); final String[] testResourceNames = getAllResourceIdNames( com.android.inputmethod.latin.tests.R.string.class); mLabelsSet.loadStringResourcesInternal(getTestContext(), @@ -49,20 +49,28 @@ public class KeySpecParserCsvTests extends AndroidTestCase { return names.toArray(new String[names.size()]); } - private void assertTextArray(String message, String value, String ... expected) { - final String actual[] = KeySpecParser.parseCsvString(value, mLabelsSet); - if (expected.length == 0) { - assertNull(message + ": expected=null actual=" + Arrays.toString(actual), - actual); + private static void assertArrayEquals(String message, Object[] expected, Object[] actual) { + if (expected == actual) { return; } - assertEquals(message + ": expected=" + Arrays.toString(expected) - + " actual=" + Arrays.toString(actual) - + ": result length", expected.length, actual.length); - for (int i = 0; i < actual.length; i++) { - final boolean equals = TextUtils.equals(expected[i], actual[i]); - assertTrue(format(message + ": result at " + i + ":", expected[i], actual[i]), equals); + if (expected == null || actual == null) { + assertEquals(message, Arrays.toString(expected), Arrays.toString(actual)); + return; } + if (expected.length != actual.length) { + assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual)); + return; + } + for (int i = 0; i < expected.length; i++) { + assertEquals(message + " [" + i + "]", + Arrays.toString(expected), Arrays.toString(actual)); + } + } + + private void assertTextArray(String message, String value, String ... expectedArray) { + final String[] actual = KeySpecParser.parseCsvString(value, mLabelsSet); + final String[] expected = (expectedArray.length == 0) ? null : expectedArray; + assertArrayEquals(message, expected, actual); } private void assertError(String message, String value, String ... expected) { @@ -116,6 +124,7 @@ public class KeySpecParserCsvTests extends AndroidTestCase { assertTextArray("Incomplete resource reference 1", "label", "label"); assertTextArray("Incomplete resource reference 2", "!label", "!label"); + assertTextArray("Incomplete RESOURCE REFERENCE 2", "!LABEL", "!LABEL"); assertTextArray("Incomplete resource reference 3", "label/", "label/"); assertTextArray("Incomplete resource reference 4", "!" + SURROGATE2, "!" + SURROGATE2); } @@ -150,7 +159,9 @@ public class KeySpecParserCsvTests extends AndroidTestCase { assertTextArray("Escaped !label", "\\!label", "\\!label"); assertTextArray("Escaped !label/", "\\!label/", "\\!label/"); - assertTextArray("Escaped !label/", "\\!label/empty_string", "\\!label/empty_string"); + assertTextArray("Escaped !LABEL/", "\\!LABEL/", "\\!LABEL/"); + assertTextArray("Escaped !label/name", "\\!label/empty_string", "\\!label/empty_string"); + assertTextArray("Escaped !LABEL/NAME", "\\!LABEL/EMPTY_STRING", "\\!LABEL/EMPTY_STRING"); } public void testParseCsvTextMulti() { @@ -183,6 +194,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase { assertTextArray("Multiple escaped !label", "\\!,\\!label/empty_string", "\\!", "\\!label/empty_string"); + assertTextArray("Multiple escaped !LABEL", "\\!,\\!LABEL/EMPTY_STRING", + "\\!", "\\!LABEL/EMPTY_STRING"); } public void testParseCsvResourceError() { @@ -193,11 +206,15 @@ public class KeySpecParserCsvTests extends AndroidTestCase { public void testParseCsvResourceZero() { assertTextArray("Empty string", "!label/empty_string"); + assertTextArray("EMPTY STRING", + "!LABEL/EMPTY_STRING"); } public void testParseCsvResourceSingle() { assertTextArray("Single char", "!label/single_char", "a"); + assertTextArray("SINGLE CHAR", + "!LABEL/SINGLE_CHAR", "a"); assertTextArray("Space", "!label/space", " "); assertTextArray("Single label", @@ -215,6 +232,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase { assertTextArray("Escape and single char", "\\\\!label/single_char", "\\\\a"); + assertTextArray("Escape and SINGLE CHAR", + "\\\\!LABEL/SINGLE_CHAR", "\\\\a"); } public void testParseCsvResourceSingleEscaped() { @@ -247,6 +266,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase { public void testParseCsvResourceMulti() { assertTextArray("Multiple chars", "!label/multiple_chars", "a", "b", "c"); + assertTextArray("MULTIPLE CHARS", + "!LABEL/MULTIPLE_CHARS", "a", "b", "c"); assertTextArray("Multiple chars surrounded by spaces", "!label/multiple_chars_surrounded_by_spaces", " a ", " b ", " c "); @@ -280,6 +301,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase { public void testParseMultipleResources() { assertTextArray("Literals and resources", "1,!label/multiple_chars,z", "1", "a", "b", "c", "z"); + assertTextArray("Literals and RESOURCES", + "1,!LABEL/MULTIPLE_CHARS,z", "1", "a", "b", "c", "z"); assertTextArray("Literals and resources and escape at end", "\\1,!label/multiple_chars,z\\", "\\1", "a", "b", "c", "z\\"); assertTextArray("Multiple single resource chars and labels", @@ -288,6 +311,9 @@ public class KeySpecParserCsvTests extends AndroidTestCase { assertTextArray("Multiple single resource chars and labels 2", "!label/single_char,!label/single_label,!label/escaped_comma_escape", "a", "abc", "a\\,\\"); + assertTextArray("Multiple single RESOURCE chars and LABELS 2", + "!LABEL/SINGLE_CHAR,!LABEL/SINGLE_LABEL,!LABEL/ESCAPED_COMMA_ESCAPE", + "a", "abc", "a\\,\\"); assertTextArray("Multiple multiple resource chars and labels", "!label/multiple_chars,!label/multiple_labels,!label/multiple_chars_with_comma", "a", "b", "c", "abc", "def", "ghi", "a", "\\,", "c"); @@ -304,10 +330,68 @@ public class KeySpecParserCsvTests extends AndroidTestCase { "!label/indirect_string", "a", "b", "c"); assertTextArray("Indirect with literal", "1,!label/indirect_string_with_literal,2", "1", "x", "a", "b", "c", "y", "2"); + assertTextArray("Indirect2", + "!label/indirect2_string", "a", "b", "c"); + + assertTextArray("INDIRECT", + "!LABEL/INDIRECT_STRING", "a", "b", "c"); + assertTextArray("INDIRECT with literal", + "1,!LABEL/INDIRECT_STRING_WITH_LITERAL,2", "1", "x", "a", "b", "c", "y", "2"); + assertTextArray("INDIRECT2", + "!LABEL/INDIRECT2_STRING", "a", "b", "c"); + + assertTextArray("Upper indirect", + "!label/upper_indirect_string", "a", "b", "c"); + assertTextArray("Upper indirect with literal", + "1,!label/upper_indirect_string_with_literal,2", "1", "x", "a", "b", "c", "y", "2"); + assertTextArray("Upper indirect2", + "!label/upper_indirect2_string", "a", "b", "c"); + + assertTextArray("UPPER INDIRECT", + "!LABEL/upper_INDIRECT_STRING", "a", "b", "c"); + assertTextArray("Upper INDIRECT with literal", + "1,!LABEL/upper_INDIRECT_STRING_WITH_LITERAL,2", "1", "x", "a", "b", "c", "y", "2"); + assertTextArray("Upper INDIRECT2", + "!LABEL/upper_INDIRECT2_STRING", "a", "b", "c"); } public void testParseInfiniteIndirectReference() { assertError("Infinite indirection", "1,!label/infinite_indirection,2", "1", "infinite", "", "loop", "2"); + assertError("INFINITE INDIRECTION", + "1,!LABEL/INFINITE_INDIRECTION,2", "1", "infinite", "", "loop", "2"); + + assertError("Upper infinite indirection", + "1,!label/upper_infinite_indirection,2", + "1", "infinite", "", "loop", "2"); + assertError("Upper INFINITE INDIRECTION", + "1,!LABEL/UPPER_INFINITE_INDIRECTION,2", + "1", "infinite", "", "loop", "2"); + } + + public void testLabelReferece() { + assertTextArray("Label time am", "!label/label_time_am", "AM"); + assertTextArray("LABEL TIME AM", "!LABEL/LABEL_TIME_AM", "AM"); + + assertTextArray("More keys for am pm", "!label/more_keys_for_am_pm", + "!fixedColumnOrder!2", "!hasLabels!", "AM", "PM"); + assertTextArray("MORE KEYS FOR AM OM", "!LABEL/MORE_KEYS_FOR_AM_PM", + "!fixedColumnOrder!2", "!hasLabels!", "AM", "PM"); + + assertTextArray("Settings as more key", "!label/settings_as_more_key", + "!icon/settingsKey|!code/key_settings"); + assertTextArray("SETTINGS AS MORE KEY", "!LABEL/SETTINGS_AS_MORE_KEY", + "!icon/settingsKey|!code/key_settings"); + + assertTextArray("Indirect naviagte actions as more key", + "!label/indirect_navigate_actions_as_more_key", + "!fixedColumnOrder!2", + "!hasLabels!", "Prev|!code/key_action_previous", + "!hasLabels!", "Next|!code/key_action_next"); + assertTextArray("INDIRECT NAVIGATE ACTIONS AS MORE KEY", + "!LABEL/INDIRECT_NAVIGATE_ACTIONS_AS_MORE_KEY", + "!fixedColumnOrder!2", + "!hasLabels!", "Prev|!code/key_action_previous", + "!hasLabels!", "Next|!code/key_action_next"); } } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java index f6a0c5604..79d8d849e 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java @@ -16,9 +16,12 @@ package com.android.inputmethod.keyboard.internal; +import static com.android.inputmethod.keyboard.Keyboard.CODE_OUTPUT_TEXT; +import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED; +import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; + import android.test.AndroidTestCase; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec; import java.util.Arrays; @@ -26,32 +29,40 @@ import java.util.Locale; public class KeySpecParserTests extends AndroidTestCase { private final KeyboardCodesSet mCodesSet = new KeyboardCodesSet(); - - private static final int ICON_UNDEFINED = KeyboardIconsSet.ICON_UNDEFINED; + private final KeyboardLabelsSet mLabelsSet = new KeyboardLabelsSet(); private static final String CODE_SETTINGS_NAME = "key_settings"; private static final String ICON_SETTINGS_NAME = "settingsKey"; private static final String CODE_SETTINGS = "!code/" + CODE_SETTINGS_NAME; private static final String ICON_SETTINGS = "!icon/" + ICON_SETTINGS_NAME; + private static final String CODE_SETTINGS_UPPERCASE = CODE_SETTINGS.toUpperCase(); + private static final String ICON_SETTINGS_UPPERCASE = ICON_SETTINGS.toUpperCase(); private static final String CODE_NON_EXISTING = "!code/non_existing"; private static final String ICON_NON_EXISTING = "!icon/non_existing"; private int mCodeSettings; + private int mCodeActionNext; private int mSettingsIconId; @Override protected void setUp() throws Exception { super.setUp(); - mCodesSet.setLanguage(Locale.ENGLISH.getLanguage()); + final String language = Locale.ENGLISH.getLanguage(); + mCodesSet.setLanguage(language); + mLabelsSet.setLanguage(language); + mLabelsSet.loadStringResources(getContext()); + mCodeSettings = mCodesSet.getCode(CODE_SETTINGS_NAME); + mCodeActionNext = mCodesSet.getCode("key_action_next"); mSettingsIconId = KeyboardIconsSet.getIconId(ICON_SETTINGS_NAME); } private void assertParser(String message, String moreKeySpec, String expectedLabel, String expectedOutputText, int expectedIcon, int expectedCode) { - final MoreKeySpec spec = new MoreKeySpec(moreKeySpec, mCodesSet); + final MoreKeySpec spec = new MoreKeySpec( + KeySpecParser.resolveLabelReference(moreKeySpec, mLabelsSet), mCodesSet); assertEquals(message + ": label:", expectedLabel, spec.mLabel); assertEquals(message + ": ouptputText:", expectedOutputText, spec.mOutputText); assertEquals(message + ": icon:", expectedIcon, spec.mIconId); @@ -106,195 +117,253 @@ public class KeySpecParserTests extends AndroidTestCase { assertParser("Single surrogate pair outputText", "G Clef|" + PAIR1, "G Clef", null, ICON_UNDEFINED, CODE1); assertParser("Single letter with outputText", "a|abc", - "a", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with surrogate outputText", "a|" + SURROGATE1, - "a", SURROGATE1, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single surrogate with outputText", PAIR3 + "|abc", - PAIR3, "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + PAIR3, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with escaped outputText", "a|a\\|c", - "a", "a|c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with escaped surrogate outputText", "a|" + PAIR1 + "\\|" + PAIR2, - "a", PAIR1 + "|" + PAIR2, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", PAIR1 + "|" + PAIR2, ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with comma outputText", "a|a,b", - "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with escaped comma outputText", "a|a\\,b", - "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with outputText starts with bang", "a|!bc", - "a", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with surrogate outputText starts with bang", "a|!" + SURROGATE2, - "a", "!" + SURROGATE2, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "!" + SURROGATE2, ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with outputText contains bang", "a|a!c", - "a", "a!c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single letter with escaped bang outputText", "a|\\!bc", - "a", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Single escaped escape with single outputText", "\\\\|\\\\", "\\", null, ICON_UNDEFINED, '\\'); assertParser("Single escaped bar with single outputText", "\\||\\|", "|", null, ICON_UNDEFINED, '|'); assertParser("Single letter with code", "a|" + CODE_SETTINGS, "a", null, ICON_UNDEFINED, mCodeSettings); + assertParser("Single letter with CODE", "a|" + CODE_SETTINGS_UPPERCASE, + "a", null, ICON_UNDEFINED, mCodeSettings); } public void testLabel() { assertParser("Simple label", "abc", - "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Simple surrogate label", SURROGATE1, - SURROGATE1, SURROGATE1, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + SURROGATE1, SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped bar", "a\\|c", - "a|c", "a|c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a|c", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Surrogate label with escaped bar", PAIR1 + "\\|" + PAIR2, PAIR1 + "|" + PAIR2, PAIR1 + "|" + PAIR2, - ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped escape", "a\\\\c", - "a\\c", "a\\c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a\\c", "a\\c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with comma", "a,c", - "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped comma", "a\\,c", - "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label starts with bang", "!bc", - "!bc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Surrogate label starts with bang", "!" + SURROGATE1, - "!" + SURROGATE1, "!" + SURROGATE1, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "!" + SURROGATE1, "!" + SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label contains bang", "a!c", - "a!c", "a!c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a!c", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped bang", "\\!bc", - "!bc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped letter", "\\abc", - "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with outputText", "abc|def", - "abc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with comma and outputText", "a,c|def", - "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Escaped comma label with outputText", "a\\,c|def", - "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Escaped label with outputText", "a\\|c|def", - "a|c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a|c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped bar outputText", "abc|d\\|f", - "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Escaped escape label with outputText", "a\\\\|def", - "a\\", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a\\", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label starts with bang and outputText", "!bc|def", - "!bc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label contains bang label and outputText", "a!c|def", - "a!c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a!c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Escaped bang label with outputText", "\\!bc|def", - "!bc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with comma outputText", "abc|a,b", - "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped comma outputText", "abc|a\\,b", - "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with outputText starts with bang", "abc|!bc", - "abc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with outputText contains bang", "abc|a!c", - "abc", "a!c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped bang outputText", "abc|\\!bc", - "abc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with escaped bar outputText", "abc|d\\|f", - "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f", - "a|c", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + "a|c", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParser("Label with code", "abc|" + CODE_SETTINGS, "abc", null, ICON_UNDEFINED, mCodeSettings); + assertParser("Label with CODE", "abc|" + CODE_SETTINGS_UPPERCASE, + "abc", null, ICON_UNDEFINED, mCodeSettings); assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS, "a|c", null, ICON_UNDEFINED, mCodeSettings); + assertParser("Escaped label with CODE", "a\\|c|" + CODE_SETTINGS_UPPERCASE, + "a|c", null, ICON_UNDEFINED, mCodeSettings); } public void testIconAndCode() { assertParser("Icon with outputText", ICON_SETTINGS + "|abc", - null, "abc", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT); + null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT); + assertParser("ICON with outputText", ICON_SETTINGS_UPPERCASE + "|abc", + null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT); assertParser("Icon with outputText starts with bang", ICON_SETTINGS + "|!bc", - null, "!bc", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT); + null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT); + assertParser("ICON with outputText starts with bang", ICON_SETTINGS_UPPERCASE + "|!bc", + null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT); assertParser("Icon with outputText contains bang", ICON_SETTINGS + "|a!c", - null, "a!c", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT); + null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT); + assertParser("ICON with outputText contains bang", ICON_SETTINGS_UPPERCASE + "|a!c", + null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT); assertParser("Icon with escaped bang outputText", ICON_SETTINGS + "|\\!bc", - null, "!bc", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT); + null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT); + assertParser("ICON with escaped bang outputText", ICON_SETTINGS_UPPERCASE + "|\\!bc", + null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT); assertParser("Label starts with bang and code", "!bc|" + CODE_SETTINGS, "!bc", null, ICON_UNDEFINED, mCodeSettings); + assertParser("Label starts with bang and CODE", "!bc|" + CODE_SETTINGS_UPPERCASE, + "!bc", null, ICON_UNDEFINED, mCodeSettings); assertParser("Label contains bang and code", "a!c|" + CODE_SETTINGS, "a!c", null, ICON_UNDEFINED, mCodeSettings); + assertParser("Label contains bang and CODE", "a!c|" + CODE_SETTINGS_UPPERCASE, + "a!c", null, ICON_UNDEFINED, mCodeSettings); assertParser("Escaped bang label with code", "\\!bc|" + CODE_SETTINGS, "!bc", null, ICON_UNDEFINED, mCodeSettings); + assertParser("Escaped bang label with CODE", "\\!bc|" + CODE_SETTINGS_UPPERCASE, + "!bc", null, ICON_UNDEFINED, mCodeSettings); assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS, null, null, mSettingsIconId, mCodeSettings); + assertParser("ICON with CODE", ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE, + null, null, mSettingsIconId, mCodeSettings); + } + + public void testResourceReference() { + assertParser("Settings as more key", "!label/settings_as_more_key", + null, null, mSettingsIconId, mCodeSettings); + assertParser("SETTINGS AS MORE KEY", "!LABEL/SETTINGS_AS_MORE_KEY", + null, null, mSettingsIconId, mCodeSettings); + + assertParser("Action next as more key", "!label/label_next_key|!code/key_action_next", + "Next", null, ICON_UNDEFINED, mCodeActionNext); + assertParser("ACTION NEXT AS MORE KEY", "!LABEL/LABEL_NEXT_KEY|!CODE/KEY_ACTION_NEXT", + "Next", null, ICON_UNDEFINED, mCodeActionNext); + + assertParser("Popular domain", + "!label/keylabel_for_popular_domain|!label/keylabel_for_popular_domain ", + ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT); + assertParser("POPULAR DOMAIN", + "!LABEL/KEYLABEL_FOR_POPULAR_DOMAIN|!LABEL/KEYLABEL_FOR_POPULAR_DOMAIN ", + ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT); } public void testFormatError() { assertParserError("Empty spec", "", null, - null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + null, ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Empty label with outputText", "|a", - null, "a", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Empty label with code", "|" + CODE_SETTINGS, null, null, ICON_UNDEFINED, mCodeSettings); + assertParserError("Empty label with CODE", "|" + CODE_SETTINGS_UPPERCASE, + null, null, ICON_UNDEFINED, mCodeSettings); assertParserError("Empty outputText with label", "a|", - "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Empty outputText with icon", ICON_SETTINGS + "|", - null, null, mSettingsIconId, Keyboard.CODE_UNSPECIFIED); + null, null, mSettingsIconId, CODE_UNSPECIFIED); + assertParserError("Empty outputText with ICON", ICON_SETTINGS_UPPERCASE + "|", + null, null, mSettingsIconId, CODE_UNSPECIFIED); assertParserError("Empty icon and code", "|", - null, null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + null, null, ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Icon without code", ICON_SETTINGS, - null, null, mSettingsIconId, Keyboard.CODE_UNSPECIFIED); + null, null, mSettingsIconId, CODE_UNSPECIFIED); + assertParserError("ICON without code", ICON_SETTINGS_UPPERCASE, + null, null, mSettingsIconId, CODE_UNSPECIFIED); assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc", - null, "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT); + null, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT); assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING, - "abc", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "abc", null, ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Third bar at end", "a|b|", - "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Multiple bar", "a|b|c", - "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED); + "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED); assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c", "a", null, ICON_UNDEFINED, mCodeSettings); + assertParserError("Multiple bar with label and CODE", "a|" + CODE_SETTINGS_UPPERCASE + "|c", + "a", null, ICON_UNDEFINED, mCodeSettings); assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c", - null, null, mSettingsIconId, Keyboard.CODE_UNSPECIFIED); + null, null, mSettingsIconId, CODE_UNSPECIFIED); + assertParserError("Multiple bar with ICON and outputText", ICON_SETTINGS_UPPERCASE + "|b|c", + null, null, mSettingsIconId, CODE_UNSPECIFIED); assertParserError("Multiple bar with icon and code", ICON_SETTINGS + "|" + CODE_SETTINGS + "|c", null, null, mSettingsIconId, mCodeSettings); + assertParserError("Multiple bar with ICON and CODE", + ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE + "|c", + null, null, mSettingsIconId, mCodeSettings); } - private static void assertMoreKeys(String message, String[] moreKeys, - String[] additionalMoreKeys, String[] expected) { - final String[] actual = KeySpecParser.insertAdditionalMoreKeys( - moreKeys, additionalMoreKeys); - if (expected == null && actual == null) { + private static void assertArrayEquals(String message, Object[] expected, Object[] actual) { + if (expected == actual) { return; } if (expected == null || actual == null) { assertEquals(message, Arrays.toString(expected), Arrays.toString(actual)); - } else { - if (expected.length != actual.length) { - assertEquals(message, Arrays.toString(expected), Arrays.toString(actual)); - } - for (int i = 0; i < expected.length; i++) { - if (!actual[i].equals(expected[i])) { - assertEquals(message, Arrays.toString(expected), Arrays.toString(actual)); - } - } + return; + } + if (expected.length != actual.length) { + assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual)); + return; + } + for (int i = 0; i < expected.length; i++) { + assertEquals(message + " [" + i + "]", + Arrays.toString(expected), Arrays.toString(actual)); } } + private static void assertInsertAdditionalMoreKeys(String message, String[] moreKeys, + String[] additionalMoreKeys, String[] expected) { + final String[] actual = + KeySpecParser.insertAdditionalMoreKeys( moreKeys, additionalMoreKeys); + assertArrayEquals(message, expected, actual); + } + public void testEmptyEntry() { - assertMoreKeys("null more keys and null additons", + assertInsertAdditionalMoreKeys("null more keys and null additons", null, null, null); - assertMoreKeys("null more keys and empty additons", + assertInsertAdditionalMoreKeys("null more keys and empty additons", null, new String[0], null); - assertMoreKeys("empty more keys and null additons", + assertInsertAdditionalMoreKeys("empty more keys and null additons", new String[0], null, null); - assertMoreKeys("empty more keys and empty additons", + assertInsertAdditionalMoreKeys("empty more keys and empty additons", new String[0], new String[0], null); - assertMoreKeys("filter out empty more keys", + assertInsertAdditionalMoreKeys("filter out empty more keys", new String[] { null, "a", "", "b", null }, null, new String[] { "a", "b" }); - assertMoreKeys("filter out empty additons", + assertInsertAdditionalMoreKeys("filter out empty additons", new String[] { "a", "%", "b", "%", "c", "%", "d" }, new String[] { null, "A", "", "B", null }, new String[] { "a", "A", "b", "B", "c", "d" }); @@ -302,188 +371,261 @@ public class KeySpecParserTests extends AndroidTestCase { public void testInsertAdditionalMoreKeys() { // Escaped marker. - assertMoreKeys("escaped marker", + assertInsertAdditionalMoreKeys("escaped marker", new String[] { "\\%", "%-)" }, new String[] { "1", "2" }, new String[] { "1", "2", "\\%", "%-)" }); // 0 more key. - assertMoreKeys("null & null", null, null, null); - assertMoreKeys("null & 1 additon", + assertInsertAdditionalMoreKeys("null & null", null, null, null); + assertInsertAdditionalMoreKeys("null & 1 additon", null, new String[] { "1" }, new String[] { "1" }); - assertMoreKeys("null & 2 additons", + assertInsertAdditionalMoreKeys("null & 2 additons", null, new String[] { "1", "2" }, new String[] { "1", "2" }); // 0 additional more key. - assertMoreKeys("1 more key & null", + assertInsertAdditionalMoreKeys("1 more key & null", new String[] { "A" }, null, new String[] { "A" }); - assertMoreKeys("2 more keys & null", + assertInsertAdditionalMoreKeys("2 more keys & null", new String[] { "A", "B" }, null, new String[] { "A", "B" }); // No marker. - assertMoreKeys("1 more key & 1 addtional & no marker", + assertInsertAdditionalMoreKeys("1 more key & 1 addtional & no marker", new String[] { "A" }, new String[] { "1" }, new String[] { "1", "A" }); - assertMoreKeys("1 more key & 2 addtionals & no marker", + assertInsertAdditionalMoreKeys("1 more key & 2 addtionals & no marker", new String[] { "A" }, new String[] { "1", "2" }, new String[] { "1", "2", "A" }); - assertMoreKeys("2 more keys & 1 addtional & no marker", + assertInsertAdditionalMoreKeys("2 more keys & 1 addtional & no marker", new String[] { "A", "B" }, new String[] { "1" }, new String[] { "1", "A", "B" }); - assertMoreKeys("2 more keys & 2 addtionals & no marker", + assertInsertAdditionalMoreKeys("2 more keys & 2 addtionals & no marker", new String[] { "A", "B" }, new String[] { "1", "2" }, new String[] { "1", "2", "A", "B" }); // 1 marker. - assertMoreKeys("1 more key & 1 additon & marker at head", + assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at head", new String[] { "%", "A" }, new String[] { "1" }, new String[] { "1", "A" }); - assertMoreKeys("1 more key & 1 additon & marker at tail", + assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at tail", new String[] { "A", "%" }, new String[] { "1" }, new String[] { "A", "1" }); - assertMoreKeys("2 more keys & 1 additon & marker at middle", + assertInsertAdditionalMoreKeys("2 more keys & 1 additon & marker at middle", new String[] { "A", "%", "B" }, new String[] { "1" }, new String[] { "A", "1", "B" }); // 1 marker & excess additional more keys. - assertMoreKeys("1 more key & 2 additons & marker at head", + assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at head", new String[] { "%", "A", "B" }, new String[] { "1", "2" }, new String[] { "1", "A", "B", "2" }); - assertMoreKeys("1 more key & 2 additons & marker at tail", + assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at tail", new String[] { "A", "B", "%" }, new String[] { "1", "2" }, new String[] { "A", "B", "1", "2" }); - assertMoreKeys("2 more keys & 2 additons & marker at middle", + assertInsertAdditionalMoreKeys("2 more keys & 2 additons & marker at middle", new String[] { "A", "%", "B" }, new String[] { "1", "2" }, new String[] { "A", "1", "B", "2" }); // 2 markers. - assertMoreKeys("0 more key & 2 addtional & 2 markers", + assertInsertAdditionalMoreKeys("0 more key & 2 addtional & 2 markers", new String[] { "%", "%" }, new String[] { "1", "2" }, new String[] { "1", "2" }); - assertMoreKeys("1 more key & 2 addtional & 2 markers at head", + assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at head", new String[] { "%", "%", "A" }, new String[] { "1", "2" }, new String[] { "1", "2", "A" }); - assertMoreKeys("1 more key & 2 addtional & 2 markers at tail", + assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at tail", new String[] { "A", "%", "%" }, new String[] { "1", "2" }, new String[] { "A", "1", "2" }); - assertMoreKeys("2 more keys & 2 addtional & 2 markers at middle", + assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle", new String[] { "A", "%", "%", "B" }, new String[] { "1", "2" }, new String[] { "A", "1", "2", "B" }); - assertMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle", + assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle", new String[] { "%", "A", "%", "B" }, new String[] { "1", "2" }, new String[] { "1", "A", "2", "B" }); - assertMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail", + assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail", new String[] { "%", "A", "B", "%" }, new String[] { "1", "2" }, new String[] { "1", "A", "B", "2" }); - assertMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail", + assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail", new String[] { "A", "%", "B", "%" }, new String[] { "1", "2" }, new String[] { "A", "1", "B", "2" }); // 2 markers & excess additional more keys. - assertMoreKeys("0 more key & 2 additons & 2 markers", + assertInsertAdditionalMoreKeys("0 more key & 2 additons & 2 markers", new String[] { "%", "%" }, new String[] { "1", "2", "3" }, new String[] { "1", "2", "3" }); - assertMoreKeys("1 more key & 2 additons & 2 markers at head", + assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at head", new String[] { "%", "%", "A" }, new String[] { "1", "2", "3" }, new String[] { "1", "2", "A", "3" }); - assertMoreKeys("1 more key & 2 additons & 2 markers at tail", + assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at tail", new String[] { "A", "%", "%" }, new String[] { "1", "2", "3" }, new String[] { "A", "1", "2", "3" }); - assertMoreKeys("2 more keys & 2 additons & 2 markers at middle", + assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle", new String[] { "A", "%", "%", "B" }, new String[] { "1", "2", "3" }, new String[] { "A", "1", "2", "B", "3" }); - assertMoreKeys("2 more keys & 2 additons & 2 markers at head & middle", + assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & middle", new String[] { "%", "A", "%", "B" }, new String[] { "1", "2", "3" }, new String[] { "1", "A", "2", "B", "3" }); - assertMoreKeys("2 more keys & 2 additons & 2 markers at head & tail", + assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & tail", new String[] { "%", "A", "B", "%" }, new String[] { "1", "2", "3" }, new String[] { "1", "A", "B", "2", "3" }); - assertMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail", + assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail", new String[] { "A", "%", "B", "%" }, new String[] { "1", "2", "3" }, new String[] { "A", "1", "B", "2", "3" }); // 0 addtional more key and excess markers. - assertMoreKeys("0 more key & null & excess marker", + assertInsertAdditionalMoreKeys("0 more key & null & excess marker", new String[] { "%" }, null, null); - assertMoreKeys("1 more key & null & excess marker at head", + assertInsertAdditionalMoreKeys("1 more key & null & excess marker at head", new String[] { "%", "A" }, null, new String[] { "A" }); - assertMoreKeys("1 more key & null & excess marker at tail", + assertInsertAdditionalMoreKeys("1 more key & null & excess marker at tail", new String[] { "A", "%" }, null, new String[] { "A" }); - assertMoreKeys("2 more keys & null & excess marker at middle", + assertInsertAdditionalMoreKeys("2 more keys & null & excess marker at middle", new String[] { "A", "%", "B" }, null, new String[] { "A", "B" }); - assertMoreKeys("2 more keys & null & excess markers", + assertInsertAdditionalMoreKeys("2 more keys & null & excess markers", new String[] { "%", "A", "%", "B", "%" }, null, new String[] { "A", "B" }); // Excess markers. - assertMoreKeys("0 more key & 1 additon & excess marker", + assertInsertAdditionalMoreKeys("0 more key & 1 additon & excess marker", new String[] { "%", "%" }, new String[] { "1" }, new String[] { "1" }); - assertMoreKeys("1 more key & 1 additon & excess marker at head", + assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at head", new String[] { "%", "%", "A" }, new String[] { "1" }, new String[] { "1", "A" }); - assertMoreKeys("1 more key & 1 additon & excess marker at tail", + assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at tail", new String[] { "A", "%", "%" }, new String[] { "1" }, new String[] { "A", "1" }); - assertMoreKeys("2 more keys & 1 additon & excess marker at middle", + assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess marker at middle", new String[] { "A", "%", "%", "B" }, new String[] { "1" }, new String[] { "A", "1", "B" }); - assertMoreKeys("2 more keys & 1 additon & excess markers", + assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess markers", new String[] { "%", "A", "%", "B", "%" }, new String[] { "1" }, new String[] { "1", "A", "B" }); - assertMoreKeys("2 more keys & 2 additons & excess markers", + assertInsertAdditionalMoreKeys("2 more keys & 2 additons & excess markers", new String[] { "%", "A", "%", "B", "%" }, new String[] { "1", "2" }, new String[] { "1", "A", "2", "B" }); - assertMoreKeys("2 more keys & 3 additons & excess markers", + assertInsertAdditionalMoreKeys("2 more keys & 3 additons & excess markers", new String[] { "%", "A", "%", "%", "B", "%" }, new String[] { "1", "2", "3" }, new String[] { "1", "A", "2", "3", "B" }); } + + private static final String HAS_LABEL = "!hasLabel!"; + private static final String NEEDS_DIVIDER = "!needsDividers!"; + private static final String AUTO_COLUMN_ORDER = "!autoColumnOrder!"; + private static final String FIXED_COLUMN_ORDER = "!fixedColumnOrder!"; + + private static void assertGetBooleanValue(String message, String key, String[] moreKeys, + String[] expected, boolean expectedValue) { + final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length); + final boolean actualValue = KeySpecParser.getBooleanValue(actual, key); + assertEquals(message + " [value]", expectedValue, actualValue); + assertArrayEquals(message, expected, actual); + } + + public void testGetBooleanValue() { + assertGetBooleanValue("Has label", HAS_LABEL, + new String[] { HAS_LABEL, "a", "b", "c" }, + new String[] { null, "a", "b", "c" }, true); + assertGetBooleanValue("HAS LABEL", HAS_LABEL, + new String[] { HAS_LABEL.toUpperCase(), "a", "b", "c" }, + new String[] { null, "a", "b", "c" }, true); + + assertGetBooleanValue("No has label", HAS_LABEL, + new String[] { "a", "b", "c" }, + new String[] { "a", "b", "c" }, false); + assertGetBooleanValue("No has label with fixed clumn order", HAS_LABEL, + new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, + new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, false); + + assertGetBooleanValue("Multiple has label", HAS_LABEL, + new String[] { + "a", HAS_LABEL.toUpperCase(), "b", "c", HAS_LABEL, "d" }, + new String[] { + "a", null, "b", "c", null, "d" }, true); + assertGetBooleanValue("Multiple has label with needs dividers", HAS_LABEL, + new String[] { + "a", HAS_LABEL, "b", NEEDS_DIVIDER, HAS_LABEL.toUpperCase(), "d" }, + new String[] { + "a", null, "b", NEEDS_DIVIDER, null, "d" }, true); + } + + private static void assertGetIntValue(String message, String key, int defaultValue, + String[] moreKeys, String[] expected, int expectedValue) { + final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length); + final int actualValue = KeySpecParser.getIntValue(actual, key, defaultValue); + assertEquals(message + " [value]", expectedValue, actualValue); + assertArrayEquals(message, expected, actual); + } + + public void testGetIntValue() { + assertGetIntValue("Fixed column order 3", FIXED_COLUMN_ORDER, -1, + new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, + new String[] { null, "a", "b", "c" }, 3); + assertGetIntValue("FIXED COLUMN ORDER 3", FIXED_COLUMN_ORDER, -1, + new String[] { FIXED_COLUMN_ORDER.toUpperCase() + "3", "a", "b", "c" }, + new String[] { null, "a", "b", "c" }, 3); + + assertGetIntValue("No fixed column order", FIXED_COLUMN_ORDER, -1, + new String[] { "a", "b", "c" }, + new String[] { "a", "b", "c" }, -1); + assertGetIntValue("No fixed column order with auto column order", FIXED_COLUMN_ORDER, -1, + new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" }, + new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" }, -1); + + assertGetIntValue("Multiple fixed column order 3,5", FIXED_COLUMN_ORDER, -1, + new String[] { FIXED_COLUMN_ORDER + "3", "a", FIXED_COLUMN_ORDER + "5", "b" }, + new String[] { null, "a", null, "b" }, 3); + assertGetIntValue("Multiple fixed column order 5,3 with has label", FIXED_COLUMN_ORDER, -1, + new String[] { + FIXED_COLUMN_ORDER.toUpperCase() + "5", HAS_LABEL, "a", + FIXED_COLUMN_ORDER + "3", "b" }, + new String[] { null, HAS_LABEL, "a", null, "b" }, 5); + } } diff --git a/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl b/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl index 0e887e494..72f4edda7 100644 --- a/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl +++ b/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl @@ -31,7 +31,8 @@ public final class KeyboardLabelsSet { // Language to labels map. private static final HashMap sLocaleToLabelsMap = new HashMap(); - private static final HashMap sNameToIdMap = new HashMap(); + private static final HashMap sLowerCaseNameToIdsMap = + new HashMap(); private String[] mLabels; // Resource name to label map. @@ -60,12 +61,21 @@ public final class KeyboardLabelsSet { } public String getLabel(final String name) { - if (mResourceNameToLabelsMap.containsKey(name)) { - return mResourceNameToLabelsMap.get(name); + String lowerCaseName = null; + String label = mResourceNameToLabelsMap.get(name); + if (label == null) { + lowerCaseName = name.toLowerCase(); + label = mResourceNameToLabelsMap.get(lowerCaseName); + } + if (label != null) { + return label; + } + Integer id = sLowerCaseNameToIdsMap.get(name); + if (id == null) { + id = sLowerCaseNameToIdsMap.get(lowerCaseName); // lowerCaseName != null } - final Integer id = sNameToIdMap.get(name); if (id == null) throw new RuntimeException("Unknown label: " + name); - final String label = (id < mLabels.length) ? mLabels[id] : null; + label = (id < mLabels.length) ? mLabels[id] : null; return (label == null) ? LANGUAGE_DEFAULT[id] : label; } @@ -105,7 +115,7 @@ public final class KeyboardLabelsSet { static { int id = 0; for (final String name : NAMES) { - sNameToIdMap.put(name, id++); + sLowerCaseNameToIdsMap.put(name, id++); } for (int i = 0; i < LANGUAGES_AND_LABELS.length; i += 2) {