Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.inputmethod.keyboard.internal;
     18 
     19 import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
     20 import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
     21 import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
     22 
     23 import android.test.AndroidTestCase;
     24 import android.test.suitebuilder.annotation.SmallTest;
     25 
     26 import com.android.inputmethod.latin.Constants;
     27 
     28 import java.util.Arrays;
     29 import java.util.Locale;
     30 
     31 @SmallTest
     32 public class KeySpecParserTests extends AndroidTestCase {
     33     private final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
     34     private final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
     35 
     36     private static final String CODE_SETTINGS = "!code/key_settings";
     37     private static final String ICON_SETTINGS = "!icon/settings_key";
     38     private static final String CODE_SETTINGS_UPPERCASE = CODE_SETTINGS.toUpperCase();
     39     private static final String ICON_SETTINGS_UPPERCASE = ICON_SETTINGS.toUpperCase();
     40     private static final String CODE_NON_EXISTING = "!code/non_existing";
     41     private static final String ICON_NON_EXISTING = "!icon/non_existing";
     42 
     43     private int mCodeSettings;
     44     private int mCodeActionNext;
     45     private int mSettingsIconId;
     46 
     47     @Override
     48     protected void setUp() throws Exception {
     49         super.setUp();
     50 
     51         final String language = Locale.ENGLISH.getLanguage();
     52         mCodesSet.setLanguage(language);
     53         mTextsSet.setLanguage(language);
     54         mTextsSet.loadStringResources(getContext());
     55 
     56         mCodeSettings = KeySpecParser.parseCode(
     57                 CODE_SETTINGS, mCodesSet, CODE_UNSPECIFIED);
     58         mCodeActionNext = KeySpecParser.parseCode(
     59                 "!code/key_action_next", mCodesSet, CODE_UNSPECIFIED);
     60         mSettingsIconId = KeySpecParser.getIconId(ICON_SETTINGS);
     61     }
     62 
     63     private void assertParser(String message, String moreKeySpec, String expectedLabel,
     64             String expectedOutputText, int expectedIcon, int expectedCode) {
     65         final String labelResolved = KeySpecParser.resolveTextReference(moreKeySpec, mTextsSet);
     66         final MoreKeySpec spec = new MoreKeySpec(labelResolved, false /* needsToUpperCase */,
     67                 Locale.US, mCodesSet);
     68         assertEquals(message + " [label]", expectedLabel, spec.mLabel);
     69         assertEquals(message + " [ouptputText]", expectedOutputText, spec.mOutputText);
     70         assertEquals(message + " [icon]",
     71                 KeyboardIconsSet.getIconName(expectedIcon),
     72                 KeyboardIconsSet.getIconName(spec.mIconId));
     73         assertEquals(message + " [code]",
     74                 Constants.printableCode(expectedCode),
     75                 Constants.printableCode(spec.mCode));
     76     }
     77 
     78     private void assertParserError(String message, String moreKeySpec, String expectedLabel,
     79             String expectedOutputText, int expectedIcon, int expectedCode) {
     80         try {
     81             assertParser(message, moreKeySpec, expectedLabel, expectedOutputText, expectedIcon,
     82                     expectedCode);
     83             fail(message);
     84         } catch (Exception pcpe) {
     85             // success.
     86         }
     87     }
     88 
     89     // \U001d11e: MUSICAL SYMBOL G CLEF
     90     private static final String PAIR1 = "\ud834\udd1e";
     91     private static final int CODE1 = PAIR1.codePointAt(0);
     92     // \U001d122: MUSICAL SYMBOL F CLEF
     93     private static final String PAIR2 = "\ud834\udd22";
     94     private static final int CODE2 = PAIR2.codePointAt(0);
     95     // \U002f8a6: CJK COMPATIBILITY IDEOGRAPH-2F8A6; variant character of \u6148.
     96     private static final String PAIR3 = "\ud87e\udca6";
     97     private static final String SURROGATE1 = PAIR1 + PAIR2;
     98     private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3;
     99 
    100     public void testSingleLetter() {
    101         assertParser("Single letter", "a",
    102                 "a", null, ICON_UNDEFINED, 'a');
    103         assertParser("Single surrogate", PAIR1,
    104                 PAIR1, null, ICON_UNDEFINED, CODE1);
    105         assertParser("Single escaped bar", "\\|",
    106                 "|", null, ICON_UNDEFINED, '|');
    107         assertParser("Single escaped escape", "\\\\",
    108                 "\\", null, ICON_UNDEFINED, '\\');
    109         assertParser("Single comma", ",",
    110                 ",", null, ICON_UNDEFINED, ',');
    111         assertParser("Single escaped comma", "\\,",
    112                 ",", null, ICON_UNDEFINED, ',');
    113         assertParser("Single escaped letter", "\\a",
    114                 "a", null, ICON_UNDEFINED, 'a');
    115         assertParser("Single escaped surrogate", "\\" + PAIR2,
    116                 PAIR2, null, ICON_UNDEFINED, CODE2);
    117         assertParser("Single bang", "!",
    118                 "!", null, ICON_UNDEFINED, '!');
    119         assertParser("Single escaped bang", "\\!",
    120                 "!", null, ICON_UNDEFINED, '!');
    121         assertParser("Single output text letter", "a|a",
    122                 "a", null, ICON_UNDEFINED, 'a');
    123         assertParser("Single surrogate pair outputText", "G Clef|" + PAIR1,
    124                 "G Clef", null, ICON_UNDEFINED, CODE1);
    125         assertParser("Single letter with outputText", "a|abc",
    126                 "a", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    127         assertParser("Single letter with surrogate outputText", "a|" + SURROGATE1,
    128                 "a", SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    129         assertParser("Single surrogate with outputText", PAIR3 + "|abc",
    130                 PAIR3, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    131         assertParser("Single letter with escaped outputText", "a|a\\|c",
    132                 "a", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    133         assertParser("Single letter with escaped surrogate outputText",
    134                 "a|" + PAIR1 + "\\|" + PAIR2,
    135                 "a", PAIR1 + "|" + PAIR2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    136         assertParser("Single letter with comma outputText", "a|a,b",
    137                 "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    138         assertParser("Single letter with escaped comma outputText", "a|a\\,b",
    139                 "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    140         assertParser("Single letter with outputText starts with bang", "a|!bc",
    141                 "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    142         assertParser("Single letter with surrogate outputText starts with bang", "a|!" + SURROGATE2,
    143                 "a", "!" + SURROGATE2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    144         assertParser("Single letter with outputText contains bang", "a|a!c",
    145                 "a", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    146         assertParser("Single letter with escaped bang outputText", "a|\\!bc",
    147                 "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    148         assertParser("Single escaped escape with single outputText", "\\\\|\\\\",
    149                 "\\", null, ICON_UNDEFINED, '\\');
    150         assertParser("Single escaped bar with single outputText", "\\||\\|",
    151                 "|", null, ICON_UNDEFINED, '|');
    152         assertParser("Single letter with code", "a|" + CODE_SETTINGS,
    153                 "a", null, ICON_UNDEFINED, mCodeSettings);
    154     }
    155 
    156     public void testLabel() {
    157         assertParser("Simple label", "abc",
    158                 "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    159         assertParser("Simple surrogate label", SURROGATE1,
    160                 SURROGATE1, SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    161         assertParser("Label with escaped bar", "a\\|c",
    162                 "a|c", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    163         assertParser("Surrogate label with escaped bar", PAIR1 + "\\|" + PAIR2,
    164                 PAIR1 + "|" + PAIR2, PAIR1 + "|" + PAIR2,
    165                 ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    166         assertParser("Label with escaped escape", "a\\\\c",
    167                 "a\\c", "a\\c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    168         assertParser("Label with comma", "a,c",
    169                 "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    170         assertParser("Label with escaped comma", "a\\,c",
    171                 "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    172         assertParser("Label starts with bang", "!bc",
    173                 "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    174         assertParser("Surrogate label starts with bang", "!" + SURROGATE1,
    175                 "!" + SURROGATE1, "!" + SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    176         assertParser("Label contains bang", "a!c",
    177                 "a!c", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    178         assertParser("Label with escaped bang", "\\!bc",
    179                 "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    180         assertParser("Label with escaped letter", "\\abc",
    181                 "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    182         assertParser("Label with outputText", "abc|def",
    183                 "abc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    184         assertParser("Label with comma and outputText", "a,c|def",
    185                 "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    186         assertParser("Escaped comma label with outputText", "a\\,c|def",
    187                 "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    188         assertParser("Escaped label with outputText", "a\\|c|def",
    189                 "a|c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    190         assertParser("Label with escaped bar outputText", "abc|d\\|f",
    191                 "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    192         assertParser("Escaped escape label with outputText", "a\\\\|def",
    193                 "a\\", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    194         assertParser("Label starts with bang and outputText", "!bc|def",
    195                 "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    196         assertParser("Label contains bang label and outputText", "a!c|def",
    197                 "a!c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    198         assertParser("Escaped bang label with outputText", "\\!bc|def",
    199                 "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    200         assertParser("Label with comma outputText", "abc|a,b",
    201                 "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    202         assertParser("Label with escaped comma outputText", "abc|a\\,b",
    203                 "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    204         assertParser("Label with outputText starts with bang", "abc|!bc",
    205                 "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    206         assertParser("Label with outputText contains bang", "abc|a!c",
    207                 "abc", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    208         assertParser("Label with escaped bang outputText", "abc|\\!bc",
    209                 "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    210         assertParser("Label with escaped bar outputText", "abc|d\\|f",
    211                 "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    212         assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f",
    213                 "a|c", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    214         assertParser("Label with code", "abc|" + CODE_SETTINGS,
    215                 "abc", null, ICON_UNDEFINED, mCodeSettings);
    216         assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS,
    217                 "a|c", null, ICON_UNDEFINED, mCodeSettings);
    218     }
    219 
    220     public void testIconAndCode() {
    221         assertParser("Icon with outputText", ICON_SETTINGS + "|abc",
    222                 null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT);
    223         assertParser("Icon with outputText starts with bang", ICON_SETTINGS + "|!bc",
    224                 null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
    225         assertParser("Icon with outputText contains bang", ICON_SETTINGS + "|a!c",
    226                 null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT);
    227         assertParser("Icon with escaped bang outputText", ICON_SETTINGS + "|\\!bc",
    228                 null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
    229         assertParser("Label starts with bang and code", "!bc|" + CODE_SETTINGS,
    230                 "!bc", null, ICON_UNDEFINED, mCodeSettings);
    231         assertParser("Label contains bang and code", "a!c|" + CODE_SETTINGS,
    232                 "a!c", null, ICON_UNDEFINED, mCodeSettings);
    233         assertParser("Escaped bang label with code", "\\!bc|" + CODE_SETTINGS,
    234                 "!bc", null, ICON_UNDEFINED, mCodeSettings);
    235         assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS,
    236                 null, null, mSettingsIconId, mCodeSettings);
    237     }
    238 
    239     public void testResourceReference() {
    240         assertParser("Settings as more key", "!text/settings_as_more_key",
    241                 null, null, mSettingsIconId, mCodeSettings);
    242 
    243         assertParser("Action next as more key", "!text/label_next_key|!code/key_action_next",
    244                 "Next", null, ICON_UNDEFINED, mCodeActionNext);
    245 
    246         assertParser("Popular domain",
    247                 "!text/keylabel_for_popular_domain|!text/keylabel_for_popular_domain ",
    248                 ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    249     }
    250 
    251     public void testFormatError() {
    252         assertParserError("Empty spec", "", null,
    253                 null, ICON_UNDEFINED, CODE_UNSPECIFIED);
    254         assertParserError("Empty label with outputText", "|a",
    255                 null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED);
    256         assertParserError("Empty label with code", "|" + CODE_SETTINGS,
    257                 null, null, ICON_UNDEFINED, mCodeSettings);
    258         assertParserError("Empty outputText with label", "a|",
    259                 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
    260         assertParserError("Empty outputText with icon", ICON_SETTINGS + "|",
    261                 null, null, mSettingsIconId, CODE_UNSPECIFIED);
    262         assertParserError("Empty icon and code", "|",
    263                 null, null, ICON_UNDEFINED, CODE_UNSPECIFIED);
    264         assertParserError("Icon without code", ICON_SETTINGS,
    265                 null, null, mSettingsIconId, CODE_UNSPECIFIED);
    266         assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc",
    267                 null, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    268         assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING,
    269                 "abc", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
    270         assertParserError("Third bar at end", "a|b|",
    271                 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
    272         assertParserError("Multiple bar", "a|b|c",
    273                 "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
    274         assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c",
    275                 "a", null, ICON_UNDEFINED, mCodeSettings);
    276         assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c",
    277                 null, null, mSettingsIconId, CODE_UNSPECIFIED);
    278         assertParserError("Multiple bar with icon and code",
    279                 ICON_SETTINGS + "|" + CODE_SETTINGS + "|c",
    280                 null, null, mSettingsIconId, mCodeSettings);
    281     }
    282 
    283     public void testUselessUpperCaseSpecifier() {
    284         assertParser("Single letter with CODE", "a|" + CODE_SETTINGS_UPPERCASE,
    285                 "a", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    286         assertParser("Label with CODE", "abc|" + CODE_SETTINGS_UPPERCASE,
    287                 "abc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    288         assertParser("Escaped label with CODE", "a\\|c|" + CODE_SETTINGS_UPPERCASE,
    289                 "a|c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    290         assertParser("ICON with outputText", ICON_SETTINGS_UPPERCASE + "|abc",
    291                 "!ICON/SETTINGS_KEY", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    292         assertParser("ICON with outputText starts with bang", ICON_SETTINGS_UPPERCASE + "|!bc",
    293                 "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    294         assertParser("ICON with outputText contains bang", ICON_SETTINGS_UPPERCASE + "|a!c",
    295                 "!ICON/SETTINGS_KEY", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    296         assertParser("ICON with escaped bang outputText", ICON_SETTINGS_UPPERCASE + "|\\!bc",
    297                 "!ICON/SETTINGS_KEY", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    298         assertParser("Label starts with bang and CODE", "!bc|" + CODE_SETTINGS_UPPERCASE,
    299                 "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    300         assertParser("Label contains bang and CODE", "a!c|" + CODE_SETTINGS_UPPERCASE,
    301                 "a!c", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    302         assertParser("Escaped bang label with CODE", "\\!bc|" + CODE_SETTINGS_UPPERCASE,
    303                 "!bc", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    304         assertParser("ICON with CODE", ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE,
    305                 "!ICON/SETTINGS_KEY", "!CODE/KEY_SETTINGS", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    306         assertParser("SETTINGS AS MORE KEY", "!TEXT/SETTINGS_AS_MORE_KEY",
    307                 "!TEXT/SETTINGS_AS_MORE_KEY", "!TEXT/SETTINGS_AS_MORE_KEY", ICON_UNDEFINED,
    308                 CODE_OUTPUT_TEXT);
    309         assertParser("ACTION NEXT AS MORE KEY", "!TEXT/LABEL_NEXT_KEY|!CODE/KEY_ACTION_NEXT",
    310                 "!TEXT/LABEL_NEXT_KEY", "!CODE/KEY_ACTION_NEXT", ICON_UNDEFINED,
    311                 CODE_OUTPUT_TEXT);
    312         assertParser("POPULAR DOMAIN",
    313                 "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN|!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ",
    314                 "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN", "!TEXT/KEYLABEL_FOR_POPULAR_DOMAIN ",
    315                 ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    316         assertParserError("Empty label with CODE", "|" + CODE_SETTINGS_UPPERCASE,
    317                 null, null, ICON_UNDEFINED, mCodeSettings);
    318         assertParserError("Empty outputText with ICON", ICON_SETTINGS_UPPERCASE + "|",
    319                 null, null, mSettingsIconId, CODE_UNSPECIFIED);
    320         assertParser("ICON without code", ICON_SETTINGS_UPPERCASE,
    321                 "!ICON/SETTINGS_KEY", "!ICON/SETTINGS_KEY", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
    322         assertParserError("Multiple bar with label and CODE", "a|" + CODE_SETTINGS_UPPERCASE + "|c",
    323                 "a", null, ICON_UNDEFINED, mCodeSettings);
    324         assertParserError("Multiple bar with ICON and outputText", ICON_SETTINGS_UPPERCASE + "|b|c",
    325                 null, null, mSettingsIconId, CODE_UNSPECIFIED);
    326         assertParserError("Multiple bar with ICON and CODE",
    327                 ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE + "|c",
    328                 null, null, mSettingsIconId, mCodeSettings);
    329     }
    330 
    331     private static void assertArrayEquals(String message, Object[] expected, Object[] actual) {
    332         if (expected == actual) {
    333             return;
    334         }
    335         if (expected == null || actual == null) {
    336             assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
    337             return;
    338         }
    339         if (expected.length != actual.length) {
    340             assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual));
    341             return;
    342         }
    343         for (int i = 0; i < expected.length; i++) {
    344             assertEquals(message + " [" + i + "]",
    345                     Arrays.toString(expected), Arrays.toString(actual));
    346         }
    347     }
    348 
    349     private static void assertInsertAdditionalMoreKeys(String message, String[] moreKeys,
    350             String[] additionalMoreKeys, String[] expected) {
    351         final String[] actual =
    352                 KeySpecParser.insertAdditionalMoreKeys( moreKeys, additionalMoreKeys);
    353         assertArrayEquals(message, expected, actual);
    354     }
    355 
    356     public void testEmptyEntry() {
    357         assertInsertAdditionalMoreKeys("null more keys and null additons",
    358                 null,
    359                 null,
    360                 null);
    361         assertInsertAdditionalMoreKeys("null more keys and empty additons",
    362                 null,
    363                 new String[0],
    364                 null);
    365         assertInsertAdditionalMoreKeys("empty more keys and null additons",
    366                 new String[0],
    367                 null,
    368                 null);
    369         assertInsertAdditionalMoreKeys("empty more keys and empty additons",
    370                 new String[0],
    371                 new String[0],
    372                 null);
    373 
    374         assertInsertAdditionalMoreKeys("filter out empty more keys",
    375                 new String[] { null, "a", "", "b", null },
    376                 null,
    377                 new String[] { "a", "b" });
    378         assertInsertAdditionalMoreKeys("filter out empty additons",
    379                 new String[] { "a", "%", "b", "%", "c", "%", "d" },
    380                 new String[] { null, "A", "", "B", null },
    381                 new String[] { "a", "A", "b", "B", "c", "d" });
    382     }
    383 
    384     public void testInsertAdditionalMoreKeys() {
    385         // Escaped marker.
    386         assertInsertAdditionalMoreKeys("escaped marker",
    387                 new String[] { "\\%", "%-)" },
    388                 new String[] { "1", "2" },
    389                 new String[] { "1", "2", "\\%", "%-)" });
    390 
    391         // 0 more key.
    392         assertInsertAdditionalMoreKeys("null & null", null, null, null);
    393         assertInsertAdditionalMoreKeys("null & 1 additon",
    394                 null,
    395                 new String[] { "1" },
    396                 new String[] { "1" });
    397         assertInsertAdditionalMoreKeys("null & 2 additons",
    398                 null,
    399                 new String[] { "1", "2" },
    400                 new String[] { "1", "2" });
    401 
    402         // 0 additional more key.
    403         assertInsertAdditionalMoreKeys("1 more key & null",
    404                 new String[] { "A" },
    405                 null,
    406                 new String[] { "A" });
    407         assertInsertAdditionalMoreKeys("2 more keys & null",
    408                 new String[] { "A", "B" },
    409                 null,
    410                 new String[] { "A", "B" });
    411 
    412         // No marker.
    413         assertInsertAdditionalMoreKeys("1 more key & 1 addtional & no marker",
    414                 new String[] { "A" },
    415                 new String[] { "1" },
    416                 new String[] { "1", "A" });
    417         assertInsertAdditionalMoreKeys("1 more key & 2 addtionals & no marker",
    418                 new String[] { "A" },
    419                 new String[] { "1", "2" },
    420                 new String[] { "1", "2", "A" });
    421         assertInsertAdditionalMoreKeys("2 more keys & 1 addtional & no marker",
    422                 new String[] { "A", "B" },
    423                 new String[] { "1" },
    424                 new String[] { "1", "A", "B" });
    425         assertInsertAdditionalMoreKeys("2 more keys & 2 addtionals & no marker",
    426                 new String[] { "A", "B" },
    427                 new String[] { "1", "2" },
    428                 new String[] { "1", "2", "A", "B" });
    429 
    430         // 1 marker.
    431         assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at head",
    432                 new String[] { "%", "A" },
    433                 new String[] { "1" },
    434                 new String[] { "1", "A" });
    435         assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at tail",
    436                 new String[] { "A", "%" },
    437                 new String[] { "1" },
    438                 new String[] { "A", "1" });
    439         assertInsertAdditionalMoreKeys("2 more keys & 1 additon & marker at middle",
    440                 new String[] { "A", "%", "B" },
    441                 new String[] { "1" },
    442                 new String[] { "A", "1", "B" });
    443 
    444         // 1 marker & excess additional more keys.
    445         assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at head",
    446                 new String[] { "%", "A", "B" },
    447                 new String[] { "1", "2" },
    448                 new String[] { "1", "A", "B", "2" });
    449         assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at tail",
    450                 new String[] { "A", "B", "%" },
    451                 new String[] { "1", "2" },
    452                 new String[] { "A", "B", "1", "2" });
    453         assertInsertAdditionalMoreKeys("2 more keys & 2 additons & marker at middle",
    454                 new String[] { "A", "%", "B" },
    455                 new String[] { "1", "2" },
    456                 new String[] { "A", "1", "B", "2" });
    457 
    458         // 2 markers.
    459         assertInsertAdditionalMoreKeys("0 more key & 2 addtional & 2 markers",
    460                 new String[] { "%", "%" },
    461                 new String[] { "1", "2" },
    462                 new String[] { "1", "2" });
    463         assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at head",
    464                 new String[] { "%", "%", "A" },
    465                 new String[] { "1", "2" },
    466                 new String[] { "1", "2", "A" });
    467         assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at tail",
    468                 new String[] { "A", "%", "%" },
    469                 new String[] { "1", "2" },
    470                 new String[] { "A", "1", "2" });
    471         assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle",
    472                 new String[] { "A", "%", "%", "B" },
    473                 new String[] { "1", "2" },
    474                 new String[] { "A", "1", "2", "B" });
    475         assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle",
    476                 new String[] { "%", "A", "%", "B" },
    477                 new String[] { "1", "2" },
    478                 new String[] { "1", "A", "2", "B" });
    479         assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail",
    480                 new String[] { "%", "A", "B", "%" },
    481                 new String[] { "1", "2" },
    482                 new String[] { "1", "A", "B", "2" });
    483         assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail",
    484                 new String[] { "A", "%", "B", "%" },
    485                 new String[] { "1", "2" },
    486                 new String[] { "A", "1", "B", "2" });
    487 
    488         // 2 markers & excess additional more keys.
    489         assertInsertAdditionalMoreKeys("0 more key & 2 additons & 2 markers",
    490                 new String[] { "%", "%" },
    491                 new String[] { "1", "2", "3" },
    492                 new String[] { "1", "2", "3" });
    493         assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at head",
    494                 new String[] { "%", "%", "A" },
    495                 new String[] { "1", "2", "3" },
    496                 new String[] { "1", "2", "A", "3" });
    497         assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at tail",
    498                 new String[] { "A", "%", "%" },
    499                 new String[] { "1", "2", "3" },
    500                 new String[] { "A", "1", "2", "3" });
    501         assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle",
    502                 new String[] { "A", "%", "%", "B" },
    503                 new String[] { "1", "2", "3" },
    504                 new String[] { "A", "1", "2", "B", "3" });
    505         assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & middle",
    506                 new String[] { "%", "A", "%", "B" },
    507                 new String[] { "1", "2", "3" },
    508                 new String[] { "1", "A", "2", "B", "3" });
    509         assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & tail",
    510                 new String[] { "%", "A", "B", "%" },
    511                 new String[] { "1", "2", "3" },
    512                 new String[] { "1", "A", "B", "2", "3" });
    513         assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail",
    514                 new String[] { "A", "%", "B", "%" },
    515                 new String[] { "1", "2", "3" },
    516                 new String[] { "A", "1", "B", "2", "3" });
    517 
    518         // 0 addtional more key and excess markers.
    519         assertInsertAdditionalMoreKeys("0 more key & null & excess marker",
    520                 new String[] { "%" },
    521                 null,
    522                 null);
    523         assertInsertAdditionalMoreKeys("1 more key & null & excess marker at head",
    524                 new String[] { "%", "A" },
    525                 null,
    526                 new String[] { "A" });
    527         assertInsertAdditionalMoreKeys("1 more key & null & excess marker at tail",
    528                 new String[] { "A", "%" },
    529                 null,
    530                 new String[] { "A" });
    531         assertInsertAdditionalMoreKeys("2 more keys & null & excess marker at middle",
    532                 new String[] { "A", "%", "B" },
    533                 null,
    534                 new String[] { "A", "B" });
    535         assertInsertAdditionalMoreKeys("2 more keys & null & excess markers",
    536                 new String[] { "%", "A", "%", "B", "%" },
    537                 null,
    538                 new String[] { "A", "B" });
    539 
    540         // Excess markers.
    541         assertInsertAdditionalMoreKeys("0 more key & 1 additon & excess marker",
    542                 new String[] { "%", "%" },
    543                 new String[] { "1" },
    544                 new String[] { "1" });
    545         assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at head",
    546                 new String[] { "%", "%", "A" },
    547                 new String[] { "1" },
    548                 new String[] { "1", "A" });
    549         assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at tail",
    550                 new String[] { "A", "%", "%" },
    551                 new String[] { "1" },
    552                 new String[] { "A", "1" });
    553         assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess marker at middle",
    554                 new String[] { "A", "%", "%", "B" },
    555                 new String[] { "1" },
    556                 new String[] { "A", "1", "B" });
    557         assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess markers",
    558                 new String[] { "%", "A", "%", "B", "%" },
    559                 new String[] { "1" },
    560                 new String[] { "1", "A", "B" });
    561         assertInsertAdditionalMoreKeys("2 more keys & 2 additons & excess markers",
    562                 new String[] { "%", "A", "%", "B", "%" },
    563                 new String[] { "1", "2" },
    564                 new String[] { "1", "A", "2", "B" });
    565         assertInsertAdditionalMoreKeys("2 more keys & 3 additons & excess markers",
    566                 new String[] { "%", "A", "%", "%", "B", "%" },
    567                 new String[] { "1", "2", "3" },
    568                 new String[] { "1", "A", "2", "3", "B" });
    569     }
    570 
    571     private static final String HAS_LABEL = "!hasLabel!";
    572     private static final String NEEDS_DIVIDER = "!needsDividers!";
    573     private static final String AUTO_COLUMN_ORDER = "!autoColumnOrder!";
    574     private static final String FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
    575 
    576     private static void assertGetBooleanValue(String message, String key, String[] moreKeys,
    577             String[] expected, boolean expectedValue) {
    578         final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
    579         final boolean actualValue = KeySpecParser.getBooleanValue(actual, key);
    580         assertEquals(message + " [value]", expectedValue, actualValue);
    581         assertArrayEquals(message, expected, actual);
    582     }
    583 
    584     public void testGetBooleanValue() {
    585         assertGetBooleanValue("Has label", HAS_LABEL,
    586                 new String[] { HAS_LABEL, "a", "b", "c" },
    587                 new String[] { null, "a", "b", "c" }, true);
    588         // Upper case specification will not work.
    589         assertGetBooleanValue("HAS LABEL", HAS_LABEL,
    590                 new String[] { HAS_LABEL.toUpperCase(), "a", "b", "c" },
    591                 new String[] { "!HASLABEL!", "a", "b", "c" }, false);
    592 
    593         assertGetBooleanValue("No has label", HAS_LABEL,
    594                 new String[] { "a", "b", "c" },
    595                 new String[] { "a", "b", "c" }, false);
    596         assertGetBooleanValue("No has label with fixed clumn order", HAS_LABEL,
    597                 new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
    598                 new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, false);
    599 
    600         // Upper case specification will not work.
    601         assertGetBooleanValue("Multiple has label", HAS_LABEL,
    602                 new String[] {
    603                     "a", HAS_LABEL.toUpperCase(), "b", "c", HAS_LABEL, "d" },
    604                 new String[] {
    605                     "a", "!HASLABEL!", "b", "c", null, "d" }, true);
    606         // Upper case specification will not work.
    607         assertGetBooleanValue("Multiple has label with needs dividers", HAS_LABEL,
    608                 new String[] {
    609                     "a", HAS_LABEL, "b", NEEDS_DIVIDER, HAS_LABEL.toUpperCase(), "d" },
    610                 new String[] {
    611                     "a", null, "b", NEEDS_DIVIDER, "!HASLABEL!", "d" }, true);
    612     }
    613 
    614     private static void assertGetIntValue(String message, String key, int defaultValue,
    615             String[] moreKeys, String[] expected, int expectedValue) {
    616         final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
    617         final int actualValue = KeySpecParser.getIntValue(actual, key, defaultValue);
    618         assertEquals(message + " [value]", expectedValue, actualValue);
    619         assertArrayEquals(message, expected, actual);
    620     }
    621 
    622     public void testGetIntValue() {
    623         assertGetIntValue("Fixed column order 3", FIXED_COLUMN_ORDER, -1,
    624                 new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
    625                 new String[] { null, "a", "b", "c" }, 3);
    626         // Upper case specification will not work.
    627         assertGetIntValue("FIXED COLUMN ORDER 3", FIXED_COLUMN_ORDER, -1,
    628                 new String[] { FIXED_COLUMN_ORDER.toUpperCase() + "3", "a", "b", "c" },
    629                 new String[] { "!FIXEDCOLUMNORDER!3", "a", "b", "c" }, -1);
    630 
    631         assertGetIntValue("No fixed column order", FIXED_COLUMN_ORDER, -1,
    632                 new String[] { "a", "b", "c" },
    633                 new String[] { "a", "b", "c" }, -1);
    634         assertGetIntValue("No fixed column order with auto column order", FIXED_COLUMN_ORDER, -1,
    635                 new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" },
    636                 new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" }, -1);
    637 
    638         assertGetIntValue("Multiple fixed column order 3,5", FIXED_COLUMN_ORDER, -1,
    639                 new String[] { FIXED_COLUMN_ORDER + "3", "a", FIXED_COLUMN_ORDER + "5", "b" },
    640                 new String[] { null, "a", null, "b" }, 3);
    641         // Upper case specification will not work.
    642         assertGetIntValue("Multiple fixed column order 5,3 with has label", FIXED_COLUMN_ORDER, -1,
    643                 new String[] {
    644                     FIXED_COLUMN_ORDER.toUpperCase() + "5", HAS_LABEL, "a",
    645                     FIXED_COLUMN_ORDER + "3", "b" },
    646                 new String[] { "!FIXEDCOLUMNORDER!5", HAS_LABEL, "a", null, "b" }, 3);
    647     }
    648 }
    649