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