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