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