2 *******************************************************************************
3 * Copyright (C) 2011, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
6 * created on: 2011jan10
7 * created by: Markus W. Scherer
8 * ported from ICU4C ucharstrietest.h/.cpp
11 package com.ibm.icu.dev.test.util;
13 import java.util.NoSuchElementException;
15 import com.ibm.icu.dev.test.TestFmwk;
16 import com.ibm.icu.text.UnicodeSet;
17 import com.ibm.icu.util.BytesTrie;
18 import com.ibm.icu.util.CharsTrie;
19 import com.ibm.icu.util.CharsTrieBuilder;
20 import com.ibm.icu.util.StringTrieBuilder;
22 public class CharsTrieTest extends TestFmwk {
23 public static void main(String[] args) throws Exception {
24 new CharsTrieTest().run(args);
26 public CharsTrieTest() {}
28 // All test functions have a TestNN prefix where NN is a double-digit number.
29 // This is so that when tests are run in sorted order
30 // the simpler ones are run first.
31 // If there is a problem, the simpler ones are easier to step through.
33 public void Test00Builder() {
36 builder_.build(StringTrieBuilder.Option.FAST);
37 errln("CharsTrieBuilder().build() did not throw IndexOutOfBoundsException");
39 } catch(IndexOutOfBoundsException e) {
43 builder_.add("=", 0).add("=", 1);
44 errln("CharsTrieBuilder.add() did not detect duplicates");
46 } catch(IllegalArgumentException e) {
51 private static final class StringAndValue {
52 public StringAndValue(String str, int val) {
60 // Note: C++ StringAndValue initializers converted to Java syntax
61 // with Eclipse Find/Replace regular expressions:
62 // Find: \{ (".*", [-0-9xa-fA-F]+) \}
63 // Replace with: new StringAndValue($1)
65 public void Test10Empty() {
66 final StringAndValue[] data={
67 new StringAndValue("", 0)
72 public void Test11_a() {
73 final StringAndValue[] data={
74 new StringAndValue("a", 1)
79 public void Test12_a_ab() {
80 final StringAndValue[] data={
81 new StringAndValue("a", 1),
82 new StringAndValue("ab", 100)
87 public void Test20ShortestBranch() {
88 final StringAndValue[] data={
89 new StringAndValue("a", 1000),
90 new StringAndValue("b", 2000)
95 public void Test21Branches() {
96 final StringAndValue[] data={
97 new StringAndValue("a", 0x10),
98 new StringAndValue("cc", 0x40),
99 new StringAndValue("e", 0x100),
100 new StringAndValue("ggg", 0x400),
101 new StringAndValue("i", 0x1000),
102 new StringAndValue("kkkk", 0x4000),
103 new StringAndValue("n", 0x10000),
104 new StringAndValue("ppppp", 0x40000),
105 new StringAndValue("r", 0x100000),
106 new StringAndValue("sss", 0x200000),
107 new StringAndValue("t", 0x400000),
108 new StringAndValue("uu", 0x800000),
109 new StringAndValue("vv", 0x7fffffff),
110 new StringAndValue("zz", 0x80000000)
112 for(int length=2; length<=data.length; ++length) {
113 logln("TestBranches length="+length);
114 checkData(data, length);
118 public void Test22LongSequence() {
119 final StringAndValue[] data={
120 new StringAndValue("a", -1),
121 // sequence of linear-match nodes
122 new StringAndValue("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", -2),
123 // more than 256 units
125 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"+
126 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"+
127 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"+
128 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"+
129 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"+
130 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", -3)
135 public void Test23LongBranch() {
136 // Split-branch and interesting compact-integer values.
137 final StringAndValue[] data={
138 new StringAndValue("a", -2),
139 new StringAndValue("b", -1),
140 new StringAndValue("c", 0),
141 new StringAndValue("d2", 1),
142 new StringAndValue("f", 0x3f),
143 new StringAndValue("g", 0x40),
144 new StringAndValue("h", 0x41),
145 new StringAndValue("j23", 0x1900),
146 new StringAndValue("j24", 0x19ff),
147 new StringAndValue("j25", 0x1a00),
148 new StringAndValue("k2", 0x1a80),
149 new StringAndValue("k3", 0x1aff),
150 new StringAndValue("l234567890", 0x1b00),
151 new StringAndValue("l234567890123", 0x1b01),
152 new StringAndValue("nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn", 0x10ffff),
153 new StringAndValue("oooooooooooooooooooooooooooooooooooooooooooooooooooooo", 0x110000),
154 new StringAndValue("pppppppppppppppppppppppppppppppppppppppppppppppppppppp", 0x120000),
155 new StringAndValue("r", 0x333333),
156 new StringAndValue("s2345", 0x4444444),
157 new StringAndValue("t234567890", 0x77777777),
158 new StringAndValue("z", 0x80000001)
163 public void Test24ValuesForState() {
164 // Check that saveState() and resetToState() interact properly
165 // with next() and current().
166 final StringAndValue[] data={
167 new StringAndValue("a", -1),
168 new StringAndValue("ab", -2),
169 new StringAndValue("abc", -3),
170 new StringAndValue("abcd", -4),
171 new StringAndValue("abcde", -5),
172 new StringAndValue("abcdef", -6)
177 public void Test30Compact() {
178 // Duplicate trailing strings and values provide opportunities for compacting.
179 final StringAndValue[] data={
180 new StringAndValue("+", 0),
181 new StringAndValue("+august", 8),
182 new StringAndValue("+december", 12),
183 new StringAndValue("+july", 7),
184 new StringAndValue("+june", 6),
185 new StringAndValue("+november", 11),
186 new StringAndValue("+october", 10),
187 new StringAndValue("+september", 9),
188 new StringAndValue("-", 0),
189 new StringAndValue("-august", 8),
190 new StringAndValue("-december", 12),
191 new StringAndValue("-july", 7),
192 new StringAndValue("-june", 6),
193 new StringAndValue("-november", 11),
194 new StringAndValue("-october", 10),
195 new StringAndValue("-september", 9),
196 // The l+n branch (with its sub-nodes) is a duplicate but will be written
197 // both times because each time it follows a different linear-match node.
198 new StringAndValue("xjuly", 7),
199 new StringAndValue("xjune", 6)
204 public void Test31FirstForCodePoint() {
205 final StringAndValue[] data={
206 new StringAndValue("a", 1),
207 new StringAndValue("a\ud800", 2),
208 new StringAndValue("a\ud800\udc00", 3), // "a\\U00010000"
209 new StringAndValue("\ud840", 4),
210 new StringAndValue("\ud840\udc00\udbff", 5), // "\\U00020000\udbff"
211 new StringAndValue("\ud840\udc00\udbff\udfff", 6), // "\\U00020000\\U0010ffff"
212 new StringAndValue("\ud840\udc00\udbff\udfffz", 7), // "\\U00020000\\U0010ffffz"
213 new StringAndValue("\ud900\udc00xy", 8), // "\\U00050000xy"
214 new StringAndValue("\ud900\udc00xyz", 9) // "\\U00050000xyz"
219 public void Test32NextForCodePoint() {
220 final StringAndValue[] data={
221 // "\u4dff\\U00010000\u9999\\U00020000\udfff\\U0010ffff"
222 new StringAndValue("\u4dff\ud800\udc00\u9999\ud840\udc00\udfff\udbff\udfff", 2000000000),
223 // "\u4dff\\U00010000\u9999\\U00020002"
224 new StringAndValue("\u4dff\ud800\udc00\u9999\ud840\udc02", 44444),
225 // "\u4dff\\U000103ff"
226 new StringAndValue("\u4dff\ud800\udfff", 99999)
228 CharsTrie trie=buildTrie(data, data.length, StringTrieBuilder.Option.FAST);
229 BytesTrie.Result result;
230 if( (result=trie.nextForCodePoint(0x4dff))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
231 (result=trie.nextForCodePoint(0x10000))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
232 (result=trie.nextForCodePoint(0x9999))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
233 (result=trie.nextForCodePoint(0x20000))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
234 (result=trie.nextForCodePoint(0xdfff))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
235 (result=trie.nextForCodePoint(0x10ffff))!=BytesTrie.Result.FINAL_VALUE || result!=trie.current() ||
236 trie.getValue()!=2000000000
238 errln("CharsTrie.nextForCodePoint() fails for "+data[0].s);
240 if( (result=trie.firstForCodePoint(0x4dff))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
241 (result=trie.nextForCodePoint(0x10000))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
242 (result=trie.nextForCodePoint(0x9999))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
243 (result=trie.nextForCodePoint(0x20002))!=BytesTrie.Result.FINAL_VALUE || result!=trie.current() ||
244 trie.getValue()!=44444
246 errln("CharsTrie.nextForCodePoint() fails for "+data[1].s);
248 if( (result=trie.reset().nextForCodePoint(0x4dff))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
249 (result=trie.nextForCodePoint(0x10000))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
250 (result=trie.nextForCodePoint(0x9999))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
251 (result=trie.nextForCodePoint(0x20222))!=BytesTrie.Result.NO_MATCH || result!=trie.current() // no match for trail surrogate
253 errln("CharsTrie.nextForCodePoint() fails for \u4dff\\U00010000\u9999\\U00020222");
255 if( (result=trie.reset().nextForCodePoint(0x4dff))!=BytesTrie.Result.NO_VALUE || result!=trie.current() ||
256 (result=trie.nextForCodePoint(0x103ff))!=BytesTrie.Result.FINAL_VALUE || result!=trie.current() ||
257 trie.getValue()!=99999
259 errln("CharsTrie.nextForCodePoint() fails for "+data[2].s);
263 // Generate (string, value) pairs.
264 // The first string (before next()) will be empty.
265 private static final class Generator {
273 s.append(c=(char)(value>>16));
274 s.append((char)(value>>4));
276 s.append((char)value);
279 value+=((value>>5)&0x7ff)*3+1;
282 public CharSequence getString() { return s; }
283 public int getValue() { return value; }
284 public int countUniqueFirstChars() { return set.size(); }
285 public int getIndex() { return num; }
287 private StringBuilder s=new StringBuilder();
288 private UnicodeSet set=new UnicodeSet();
293 private CharsTrie buildLargeTrie(int numUniqueFirst) {
294 Generator gen=new Generator();
296 while(gen.countUniqueFirstChars()<numUniqueFirst) {
297 builder_.add(gen.getString(), gen.getValue());
300 logln("buildLargeTrie("+numUniqueFirst+") added "+gen.getIndex()+" strings");
301 CharSequence trieChars=builder_.buildCharSequence(StringTrieBuilder.Option.FAST);
302 logln("serialized trie size: "+trieChars.length()+" chars\n");
303 return new CharsTrie(trieChars, 0);
306 // Exercise a large branch node.
307 public void Test37LargeTrie() {
308 CharsTrie trie=buildLargeTrie(1111);
309 Generator gen=new Generator();
310 while(gen.countUniqueFirstChars()<1111) {
311 CharSequence x=gen.getString();
312 int value=gen.getValue();
317 if(trie.first(x.charAt(0))==BytesTrie.Result.NO_MATCH) {
318 errln(String.format("first(first char U+%04X)=BytesTrie.Result.NO_MATCH for string %d\n",
319 x.charAt(0), gen.getIndex()));
324 BytesTrie.Result result=trie.next(x, index, x.length());
325 if(!result.hasValue() || result!=trie.current() || value!=trie.getValue()) {
326 errln(String.format("next("+prettify(x)+")!=hasValue or "+
327 "next()!=current() or getValue() wrong "+
328 "for string "+gen.getIndex()));
335 private CharsTrie buildMonthsTrie(StringTrieBuilder.Option buildOption) {
336 // All types of nodes leading to the same value,
337 // for code coverage of recursive functions.
338 // In particular, we need a lot of branches on some single level
339 // to exercise a split-branch node.
340 final StringAndValue[] data={
341 new StringAndValue("august", 8),
342 new StringAndValue("jan", 1),
343 new StringAndValue("jan.", 1),
344 new StringAndValue("jana", 1),
345 new StringAndValue("janbb", 1),
346 new StringAndValue("janc", 1),
347 new StringAndValue("janddd", 1),
348 new StringAndValue("janee", 1),
349 new StringAndValue("janef", 1),
350 new StringAndValue("janf", 1),
351 new StringAndValue("jangg", 1),
352 new StringAndValue("janh", 1),
353 new StringAndValue("janiiii", 1),
354 new StringAndValue("janj", 1),
355 new StringAndValue("jankk", 1),
356 new StringAndValue("jankl", 1),
357 new StringAndValue("jankmm", 1),
358 new StringAndValue("janl", 1),
359 new StringAndValue("janm", 1),
360 new StringAndValue("jannnnnnnnnnnnnnnnnnnnnnnnnnnnn", 1),
361 new StringAndValue("jano", 1),
362 new StringAndValue("janpp", 1),
363 new StringAndValue("janqqq", 1),
364 new StringAndValue("janr", 1),
365 new StringAndValue("januar", 1),
366 new StringAndValue("january", 1),
367 new StringAndValue("july", 7),
368 new StringAndValue("jun", 6),
369 new StringAndValue("jun.", 6),
370 new StringAndValue("june", 6)
372 return buildTrie(data, data.length, buildOption);
375 public void Test40GetUniqueValue() {
376 CharsTrie trie=buildMonthsTrie(StringTrieBuilder.Option.FAST);
378 if((uniqueValue=trie.getUniqueValue())!=0) {
379 errln("unique value at root");
384 // getUniqueValue() directly after next()
385 if((uniqueValue=trie.getUniqueValue())!=((1<<1)|1)) {
386 errln("not unique value 1 after \"jan\": instead "+uniqueValue);
390 if((uniqueValue=trie.getUniqueValue())!=0) {
391 errln("unique value after \"ju\"");
393 if(trie.next('n')!=BytesTrie.Result.INTERMEDIATE_VALUE || 6!=trie.getValue()) {
394 errln("not normal value 6 after \"jun\"");
396 // getUniqueValue() after getValue()
397 if((uniqueValue=trie.getUniqueValue())!=((6<<1)|1)) {
398 errln("not unique value 6 after \"jun\"");
400 // getUniqueValue() from within a linear-match node
403 if((uniqueValue=trie.getUniqueValue())!=((8<<1)|1)) {
404 errln("not unique value 8 after \"au\"");
408 public void Test41GetNextChars() {
409 CharsTrie trie=buildMonthsTrie(StringTrieBuilder.Option.SMALL);
410 StringBuilder buffer=new StringBuilder();
411 int count=trie.getNextChars(buffer);
412 if(count!=2 || !"aj".contentEquals(buffer)) {
413 errln("months getNextChars()!=[aj] at root");
418 // getNextChars() directly after next()
420 count=trie.getNextChars(buffer);
421 if(count!=20 || !".abcdefghijklmnopqru".contentEquals(buffer)) {
422 errln("months getNextChars()!=[.abcdefghijklmnopqru] after \"jan\"");
424 // getNextChars() after getValue()
425 trie.getValue(); // next() had returned BytesTrie.Result.INTERMEDIATE_VALUE.
427 count=trie.getNextChars(buffer);
428 if(count!=20 || !".abcdefghijklmnopqru".contentEquals(buffer)) {
429 errln("months getNextChars()!=[.abcdefghijklmnopqru] after \"jan\"+getValue()");
431 // getNextChars() from a linear-match node
434 count=trie.getNextChars(buffer);
435 if(count!=1 || !"a".contentEquals(buffer)) {
436 errln("months getNextChars()!=[a] after \"janu\"");
440 count=trie.getNextChars(buffer);
441 if(count!=1 || !"r".contentEquals(buffer)) {
442 errln("months getNextChars()!=[r] after \"janua\"");
446 // getNextChars() after a final match
448 count=trie.getNextChars(buffer);
449 if(count!=0 || buffer.length()!=0) {
450 errln("months getNextChars()!=[] after \"january\"");
454 public void Test50IteratorFromBranch() {
455 CharsTrie trie=buildMonthsTrie(StringTrieBuilder.Option.FAST);
456 // Go to a branch node.
460 CharsTrie.Iterator iter=trie.iterator();
461 // Expected data: Same as in buildMonthsTrie(), except only the suffixes
463 final StringAndValue[] data={
464 new StringAndValue("", 1),
465 new StringAndValue(".", 1),
466 new StringAndValue("a", 1),
467 new StringAndValue("bb", 1),
468 new StringAndValue("c", 1),
469 new StringAndValue("ddd", 1),
470 new StringAndValue("ee", 1),
471 new StringAndValue("ef", 1),
472 new StringAndValue("f", 1),
473 new StringAndValue("gg", 1),
474 new StringAndValue("h", 1),
475 new StringAndValue("iiii", 1),
476 new StringAndValue("j", 1),
477 new StringAndValue("kk", 1),
478 new StringAndValue("kl", 1),
479 new StringAndValue("kmm", 1),
480 new StringAndValue("l", 1),
481 new StringAndValue("m", 1),
482 new StringAndValue("nnnnnnnnnnnnnnnnnnnnnnnnnnnn", 1),
483 new StringAndValue("o", 1),
484 new StringAndValue("pp", 1),
485 new StringAndValue("qqq", 1),
486 new StringAndValue("r", 1),
487 new StringAndValue("uar", 1),
488 new StringAndValue("uary", 1)
490 checkIterator(iter, data);
491 // Reset, and we should get the same result.
492 logln("after iter.reset()");
493 checkIterator(iter.reset(), data);
496 public void Test51IteratorFromLinearMatch() {
497 CharsTrie trie=buildMonthsTrie(StringTrieBuilder.Option.SMALL);
498 // Go into a linear-match node.
504 CharsTrie.Iterator iter=trie.iterator();
505 // Expected data: Same as in buildMonthsTrie(), except only the suffixes
506 // following "janua".
507 final StringAndValue[] data={
508 new StringAndValue("r", 1),
509 new StringAndValue("ry", 1)
511 checkIterator(iter, data);
512 // Reset, and we should get the same result.
513 logln("after iter.reset()");
514 checkIterator(iter.reset(), data);
517 public void Test52TruncatingIteratorFromRoot() {
518 CharsTrie trie=buildMonthsTrie(StringTrieBuilder.Option.FAST);
519 CharsTrie.Iterator iter=trie.iterator(4);
520 // Expected data: Same as in buildMonthsTrie(), except only the first 4 characters
521 // of each string, and no string duplicates from the truncation.
522 final StringAndValue[] data={
523 new StringAndValue("augu", -1),
524 new StringAndValue("jan", 1),
525 new StringAndValue("jan.", 1),
526 new StringAndValue("jana", 1),
527 new StringAndValue("janb", -1),
528 new StringAndValue("janc", 1),
529 new StringAndValue("jand", -1),
530 new StringAndValue("jane", -1),
531 new StringAndValue("janf", 1),
532 new StringAndValue("jang", -1),
533 new StringAndValue("janh", 1),
534 new StringAndValue("jani", -1),
535 new StringAndValue("janj", 1),
536 new StringAndValue("jank", -1),
537 new StringAndValue("janl", 1),
538 new StringAndValue("janm", 1),
539 new StringAndValue("jann", -1),
540 new StringAndValue("jano", 1),
541 new StringAndValue("janp", -1),
542 new StringAndValue("janq", -1),
543 new StringAndValue("janr", 1),
544 new StringAndValue("janu", -1),
545 new StringAndValue("july", 7),
546 new StringAndValue("jun", 6),
547 new StringAndValue("jun.", 6),
548 new StringAndValue("june", 6)
550 checkIterator(iter, data);
551 // Reset, and we should get the same result.
552 logln("after iter.reset()");
553 checkIterator(iter.reset(), data);
556 public void Test53TruncatingIteratorFromLinearMatchShort() {
557 final StringAndValue[] data={
558 new StringAndValue("abcdef", 10),
559 new StringAndValue("abcdepq", 200),
560 new StringAndValue("abcdeyz", 3000)
562 CharsTrie trie=buildTrie(data, data.length, StringTrieBuilder.Option.FAST);
563 // Go into a linear-match node.
566 // Truncate within the linear-match node.
567 CharsTrie.Iterator iter=trie.iterator(2);
568 final StringAndValue[] expected={
569 new StringAndValue("cd", -1)
571 checkIterator(iter, expected);
572 // Reset, and we should get the same result.
573 logln("after iter.reset()");
574 checkIterator(iter.reset(), expected);
577 public void Test54TruncatingIteratorFromLinearMatchLong() {
578 final StringAndValue[] data={
579 new StringAndValue("abcdef", 10),
580 new StringAndValue("abcdepq", 200),
581 new StringAndValue("abcdeyz", 3000)
583 CharsTrie trie=buildTrie(data, data.length, StringTrieBuilder.Option.FAST);
584 // Go into a linear-match node.
588 // Truncate after the linear-match node.
589 CharsTrie.Iterator iter=trie.iterator(3);
590 final StringAndValue[] expected={
591 new StringAndValue("def", 10),
592 new StringAndValue("dep", -1),
593 new StringAndValue("dey", -1)
595 checkIterator(iter, expected);
596 // Reset, and we should get the same result.
597 logln("after iter.reset()");
598 checkIterator(iter.reset(), expected);
601 public void Test59IteratorFromChars() {
602 final StringAndValue[] data={
603 new StringAndValue("mm", 3),
604 new StringAndValue("mmm", 33),
605 new StringAndValue("mmnop", 333)
608 for(StringAndValue item : data) {
609 builder_.add(item.s, item.value);
611 CharSequence trieChars=builder_.buildCharSequence(StringTrieBuilder.Option.FAST);
612 checkIterator(CharsTrie.iterator(trieChars, 0, 0), data);
615 private void checkData(StringAndValue data[]) {
616 checkData(data, data.length);
619 private void checkData(StringAndValue data[], int dataLength) {
620 logln("checkData(dataLength="+dataLength+", fast)");
621 checkData(data, dataLength, StringTrieBuilder.Option.FAST);
622 logln("checkData(dataLength="+dataLength+", small)");
623 checkData(data, dataLength, StringTrieBuilder.Option.SMALL);
626 private void checkData(StringAndValue[] data, int dataLength, StringTrieBuilder.Option buildOption) {
627 CharsTrie trie=buildTrie(data, dataLength, buildOption);
628 checkFirst(trie, data, dataLength);
629 checkNext(trie, data, dataLength);
630 checkNextWithState(trie, data, dataLength);
631 checkNextString(trie, data, dataLength);
632 checkIterator(trie, data, dataLength);
635 private CharsTrie buildTrie(StringAndValue data[], int dataLength,
636 StringTrieBuilder.Option buildOption) {
637 // Add the items to the trie builder in an interesting (not trivial, not random) order.
639 if((dataLength&1)!=0) {
640 // Odd number of items.
643 } else if((dataLength%3)!=0) {
644 // Not a multiple of 3.
652 for(int i=0; i<dataLength; ++i) {
653 builder_.add(data[index].s, data[index].value);
654 index=(index+step)%dataLength;
656 CharsTrie trie=builder_.build(buildOption);
658 builder_.add("zzz", 999);
659 errln("builder.build().add(zzz) did not throw IllegalStateException");
660 } catch(IllegalStateException e) {
663 CharSequence trieChars=builder_.buildCharSequence(buildOption);
664 logln("serialized trie size: "+trieChars.length()+" chars");
665 // Tries from either build() method should be identical but
666 // CharsTrie does not implement equals().
667 // We just return either one.
668 if((dataLength&1)!=0) {
671 return new CharsTrie(trieChars, 0);
675 private void checkFirst(CharsTrie trie, StringAndValue[] data, int dataLength) {
676 for(int i=0; i<dataLength; ++i) {
677 if(data[i].s.length()==0) {
678 continue; // skip empty string
680 String expectedString=data[i].s;
681 int c=expectedString.charAt(0);
682 int nextCp=expectedString.length()>1 ? expectedString.charAt(1) : 0;
683 BytesTrie.Result firstResult=trie.first(c);
684 int firstValue=firstResult.hasValue() ? trie.getValue() : -1;
685 BytesTrie.Result nextResult=trie.next(nextCp);
686 if(firstResult!=trie.reset().next(c) ||
687 firstResult!=trie.current() ||
688 firstValue!=(firstResult.hasValue() ? trie.getValue() : -1) ||
689 nextResult!=trie.next(nextCp)
691 errln(String.format("trie.first(U+%04X)!=trie.reset().next(same) for %s",
694 c=expectedString.codePointAt(0);
695 int cLength=Character.charCount(c);
696 nextCp=expectedString.length()>cLength ? expectedString.codePointAt(cLength) : 0;
697 firstResult=trie.firstForCodePoint(c);
698 firstValue=firstResult.hasValue() ? trie.getValue() : -1;
699 nextResult=trie.nextForCodePoint(nextCp);
700 if(firstResult!=trie.reset().nextForCodePoint(c) ||
701 firstResult!=trie.current() ||
702 firstValue!=(firstResult.hasValue() ? trie.getValue() : -1) ||
703 nextResult!=trie.nextForCodePoint(nextCp)
705 errln(String.format("trie.firstForCodePoint(U+%04X)!=trie.reset().nextForCodePoint(same) for %s",
712 private void checkNext(CharsTrie trie, StringAndValue[] data, int dataLength) {
713 CharsTrie.State state=new CharsTrie.State();
714 for(int i=0; i<dataLength; ++i) {
715 String expectedString=data[i].s;
716 int stringLength=expectedString.length();
717 BytesTrie.Result result;
718 if( !(result=trie.next(expectedString, 0, stringLength)).hasValue() ||
719 result!=trie.current()
721 errln("trie does not seem to contain "+data[i].s);
722 } else if(trie.getValue()!=data[i].value) {
723 errln(String.format("trie value for %s is %d=0x%x instead of expected %d=0x%x",
725 trie.getValue(), trie.getValue(),
726 data[i].value, data[i].value));
727 } else if(result!=trie.current() || trie.getValue()!=data[i].value) {
728 errln("trie value for "+data[i].s+" changes when repeating current()/getValue()");
731 result=trie.current();
732 for(int j=0; j<stringLength; ++j) {
733 if(!result.hasNext()) {
734 errln(String.format("trie.current()!=hasNext before end of %s (at index %d)",
738 if(result==BytesTrie.Result.INTERMEDIATE_VALUE) {
740 if(trie.current()!=BytesTrie.Result.INTERMEDIATE_VALUE) {
741 errln(String.format("trie.getValue().current()!=BytesTrie.Result.INTERMEDIATE_VALUE "+
742 "before end of %s (at index %d)", data[i].s, j));
746 result=trie.next(expectedString.charAt(j));
747 if(!result.matches()) {
748 errln(String.format("trie.next()=BytesTrie.Result.NO_MATCH "+
749 "before end of %s (at index %d)", data[i].s, j));
752 if(result!=trie.current()) {
753 errln(String.format("trie.next()!=following current() "+
754 "before end of %s (at index %d)", data[i].s, j));
758 if(!result.hasValue()) {
759 errln("trie.next()!=hasValue at the end of "+data[i].s);
763 if(result!=trie.current()) {
764 errln("trie.current() != current()+getValue()+current() after end of "+
767 // Compare the final current() with whether next() can actually continue.
768 trie.saveState(state);
769 boolean nextContinues=false;
770 for(int c=0x20; c<0xe000; ++c) {
772 c=0xd800; // Check for ASCII and surrogates but not all of the BMP.
774 if(trie.resetToState(state).next(c).matches()) {
779 if((result==BytesTrie.Result.INTERMEDIATE_VALUE)!=nextContinues) {
780 errln("(trie.current()==BytesTrie.Result.INTERMEDIATE_VALUE) contradicts "+
781 "(trie.next(some char)!=BytesTrie.Result.NO_MATCH) after end of "+data[i].s);
787 private void checkNextWithState(CharsTrie trie, StringAndValue[] data, int dataLength) {
788 CharsTrie.State noState=new CharsTrie.State(), state=new CharsTrie.State();
789 for(int i=0; i<dataLength; ++i) {
792 trie.resetToState(noState);
793 errln("trie.resetToState(noState) should throw an IllegalArgumentException");
794 } catch(IllegalArgumentException e) {
798 String expectedString=data[i].s;
799 int stringLength=expectedString.length();
800 int partialLength=stringLength/3;
801 for(int j=0; j<partialLength; ++j) {
802 if(!trie.next(expectedString.charAt(j)).matches()) {
803 errln("trie.next()=BytesTrie.Result.NO_MATCH for a prefix of "+data[i].s);
807 trie.saveState(state);
808 BytesTrie.Result resultAtState=trie.current();
809 BytesTrie.Result result;
810 int valueAtState=-99;
811 if(resultAtState.hasValue()) {
812 valueAtState=trie.getValue();
814 result=trie.next(0); // mismatch
815 if(result!=BytesTrie.Result.NO_MATCH || result!=trie.current()) {
816 errln("trie.next(0) matched after part of "+data[i].s);
818 if( resultAtState!=trie.resetToState(state).current() ||
819 (resultAtState.hasValue() && valueAtState!=trie.getValue())
821 errln("trie.next(part of "+data[i].s+") changes current()/getValue() after "+
822 "saveState/next(0)/resetToState");
823 } else if(!(result=trie.next(expectedString, partialLength, stringLength)).hasValue() ||
824 result!=trie.current()) {
825 errln("trie.next(rest of "+data[i].s+") does not seem to contain "+data[i].s+" after "+
826 "saveState/next(0)/resetToState");
827 } else if(!(result=trie.resetToState(state).
828 next(expectedString, partialLength, stringLength)).hasValue() ||
829 result!=trie.current()) {
830 errln("trie does not seem to contain "+data[i].s+
831 " after saveState/next(rest)/resetToState");
832 } else if(trie.getValue()!=data[i].value) {
833 errln(String.format("trie value for %s is %d=0x%x instead of expected %d=0x%x",
835 trie.getValue(), trie.getValue(),
836 data[i].value, data[i].value));
842 // next(string) is also tested in other functions,
843 // but here we try to go partway through the string, and then beyond it.
844 private void checkNextString(CharsTrie trie, StringAndValue[] data, int dataLength) {
845 for(int i=0; i<dataLength; ++i) {
846 String expectedString=data[i].s;
847 int stringLength=expectedString.length();
848 if(!trie.next(expectedString, 0, stringLength/2).matches()) {
849 errln("trie.next(up to middle of string)=BytesTrie.Result.NO_MATCH for "+data[i].s);
852 // Test that we stop properly at the end of the string.
853 trie.next(expectedString, stringLength/2, stringLength);
854 if(trie.next(0).matches()) {
855 errln("trie.next(string+NUL)!=BytesTrie.Result.NO_MATCH for "+data[i].s);
861 private void checkIterator(CharsTrie trie, StringAndValue[] data, int dataLength) {
862 checkIterator(trie.iterator(), data, dataLength);
865 private void checkIterator(CharsTrie.Iterator iter, StringAndValue data[]) {
866 checkIterator(iter, data, data.length);
869 private void checkIterator(CharsTrie.Iterator iter, StringAndValue[] data, int dataLength) {
870 for(int i=0; i<dataLength; ++i) {
871 if(!iter.hasNext()) {
872 errln("trie iterator hasNext()=false for item "+i+": "+data[i].s);
875 CharsTrie.Entry entry=iter.next();
876 String expectedString=data[i].s;
877 if(!expectedString.contentEquals(entry.chars)) {
878 errln(String.format("trie iterator next().getString()=%s but expected %s for item %d",
879 entry.chars, data[i].s, i));
881 if(entry.value!=data[i].value) {
882 errln(String.format("trie iterator next().getValue()=%d=0x%x but expected %d=0x%x for item %d: %s",
883 entry.value, entry.value,
884 data[i].value, data[i].value,
889 errln("trie iterator hasNext()=true after all items");
893 errln("trie iterator next() did not throw NoSuchElementException after all items");
894 } catch(NoSuchElementException e) {
899 private CharsTrieBuilder builder_=new CharsTrieBuilder();