pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / test / org / apache / lucene / facet / taxonomy / TestCategoryPath.java
1 package org.apache.lucene.facet.taxonomy;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.IOException;
6 import java.io.InputStreamReader;
7 import java.io.OutputStreamWriter;
8
9 import org.junit.Test;
10
11 import org.apache.lucene.util.LuceneTestCase;
12 import org.apache.lucene.facet.taxonomy.CategoryPath;
13
14 /**
15  * Licensed to the Apache Software Foundation (ASF) under one or more
16  * contributor license agreements.  See the NOTICE file distributed with
17  * this work for additional information regarding copyright ownership.
18  * The ASF licenses this file to You under the Apache License, Version 2.0
19  * (the "License"); you may not use this file except in compliance with
20  * the License.  You may obtain a copy of the License at
21  *
22  *     http://www.apache.org/licenses/LICENSE-2.0
23  *
24  * Unless required by applicable law or agreed to in writing, software
25  * distributed under the License is distributed on an "AS IS" BASIS,
26  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27  * See the License for the specific language governing permissions and
28  * limitations under the License.
29  */
30
31 public class TestCategoryPath extends LuceneTestCase {
32   
33   @Test 
34   public void testBasic() {
35     CategoryPath p = new CategoryPath(0,0);
36     assertEquals(0, p.length());
37     for (int i=0; i<1000; i++) {
38       p.add("hello");
39       assertEquals(i+1, p.length());
40     }
41   }
42   
43   @Test 
44   public void testConstructorCapacity() {
45     CategoryPath p = new CategoryPath(0,0);
46     assertEquals(0, p.capacityChars());
47     assertEquals(0, p.capacityComponents());
48     assertEquals(0, p.length());
49     p = new CategoryPath(5,18);
50     assertEquals(5, p.capacityChars());
51     assertEquals(18, p.capacityComponents());
52     assertEquals(0, p.length());
53     p = new CategoryPath(27,13);
54     assertEquals(27, p.capacityChars());
55     assertEquals(13, p.capacityComponents());
56     assertEquals(0, p.length());
57   }
58   
59   @Test 
60   public void testClear() {
61     CategoryPath p = new CategoryPath(0,0);
62     p.add("hi");
63     p.add("there");
64     assertEquals(2, p.length());
65     p.clear();
66     assertEquals(0, p.length());
67     p.add("yo!");
68     assertEquals(1, p.length());
69   }
70
71   @Test 
72   public void testTrim() {
73     CategoryPath p = new CategoryPath(0,0);
74     p.add("this");
75     p.add("message");
76     p.add("will");
77     p.add("self");
78     p.add("destruct");
79     p.add("in");
80     p.add("five");
81     p.add("seconds");
82     assertEquals(8, p.length());
83     p.trim(3);
84     assertEquals(5, p.length());
85     p.trim(0); // no-op
86     assertEquals(5, p.length());
87     p.trim(-3);  // no-op
88     assertEquals(5, p.length());
89     p.trim(1);
90     assertEquals(4, p.length());
91     p.trim(8); // clear
92     assertEquals(0, p.length());
93     p.add("yo!");
94     assertEquals(1, p.length());
95     p.trim(1); // clear
96     assertEquals(0, p.length());
97   }
98
99   @Test 
100   public void testComponentsLimit() {
101     // Test that we can add up to 2^15-1 components
102     CategoryPath p = new CategoryPath(0,0);
103     for (int i=0; i<32767; i++) {
104       p.add("");
105       assertEquals(i+1, p.length());
106     }
107     // Also see that in the current implementation, this is actually
108     // the limit: if we add one more component, things break (because
109     // we used a short to hold ncomponents). See that it breaks in the
110     // way we expect it to:
111     p.add(""); // this still works, but...
112     assertEquals(-32768, p.length()); // now the length is wrong and negative
113   }
114   
115   @Test 
116   public void testCharsLimit() {
117     // Test that we can add up to 2^15-1 characters
118     CategoryPath p = new CategoryPath(0,0);
119     for (int i=0; i<8192; i++) {
120       p.add("aaaa");
121     }
122     // Also see that in the current implementation, this is actually the
123     // limit: If we add one more character, things break (because ends[]
124     // is an array of shorts), and we actually get an exception.
125     try {
126       p.add("a");
127       fail("Should have thrown an exception");
128     } catch (ArrayIndexOutOfBoundsException e) {
129       // good.
130     }
131   }
132   
133   @Test 
134   public void testToString() {
135     CategoryPath p = new CategoryPath(0,0);
136     // When the category is empty, we expect an empty string
137     assertEquals("", p.toString('/'));
138     // This is (deliberately, in our implementation) indistinguishable
139     // from the case of a single empty component:
140     p.add("");
141     assertEquals("", p.toString('/'));
142     // Check just one category (so no delimiter needed):
143     p.clear();
144     p.add("hello");
145     assertEquals("hello", p.toString('/'));
146     // Now for two categories:
147     p.clear();
148     p.add("hello");
149     p.add("world");
150     assertEquals("hello/world", p.toString('/'));
151     // And for a thousand...
152     p.clear();
153     p.add("0");
154     StringBuilder expected = new StringBuilder("0");
155     for (int i=1; i<1000; i++) {
156       String num = Integer.toString(i);
157       p.add(num);
158       expected.append('/');
159       expected.append(num);
160     }
161     assertEquals(expected.toString(), p.toString('/'));
162     // Check that toString() without a parameter just defaults to '/':
163     assertEquals(expected.toString(), p.toString());
164   }
165
166   // testing toString() and its variants already test most of the appendTo()
167   // code, but not all of it (the "eclemma" code-coverage tool discovered
168   // this for us). Here we complete the coverage of the appendTo() methods:
169   @Test 
170   public void testAppendTo() throws IOException {
171     CategoryPath p = new CategoryPath(0,0);
172     StringBuilder sb = new StringBuilder();
173     p.appendTo(sb, '/');
174     assertEquals(0, sb.length());
175     p.appendTo(sb, '/', -1);
176     assertEquals(0, sb.length());
177     p.appendTo(sb, '/', 1);
178     assertEquals(0, sb.length());
179     p.appendTo(sb, '/', -1, 1);
180     assertEquals(0, sb.length());
181   }
182   
183   @Test 
184   public void testLastComponent() {
185     CategoryPath p = new CategoryPath(1000,1000);
186     // When the category is empty, we expect a null
187     assertNull(p.lastComponent());
188     for (int i=0; i<=100; i++) {
189       String num = Integer.toString(i);
190       p.add(num);
191       assertEquals(num, p.lastComponent());
192     }
193   }
194   
195   @Test 
196   public void testGetComponent() {
197     CategoryPath p = new CategoryPath(1000,1000);
198     // When the category is empty, we expect a null
199     assertNull(p.getComponent(0));
200     assertNull(p.getComponent(1));
201     assertNull(p.getComponent(-1));
202     for (int i=0; i<=100; i++) {
203       p.add(Integer.toString(i));
204       for (int j=0; j<=i; j++) {
205         assertEquals(j, Integer.parseInt(p.getComponent(j)));
206       }
207       assertNull(p.getComponent(-1));
208       assertNull(p.getComponent(i+1));
209     }
210   }
211
212   @Test 
213   public void testToStringPrefix() {
214     CategoryPath p = new CategoryPath(0,0);
215     p.add("hi");
216     p.add("there");
217     p.add("man");
218     assertEquals("hi/there/man", p.toString('/'));
219     assertEquals("", p.toString('/', 0));
220     assertEquals("hi", p.toString('/', 1));
221     assertEquals("hi/there", p.toString('/', 2));
222     assertEquals("hi/there/man", p.toString('/', 3));
223     assertEquals("hi/there/man", p.toString('/', 4));
224     assertEquals("hi/there/man", p.toString('/', -1));
225   }
226
227   @Test 
228   public void testToStringSubpath() {
229     CategoryPath p = new CategoryPath(0,0);
230     assertEquals("", p.toString('/', 0, 0));
231     p.add("hi");
232     p.add("there");
233     p.add("man");
234     assertEquals("", p.toString('/', 0, 0));
235     assertEquals("hi", p.toString('/', 0, 1));
236     assertEquals("hi/there", p.toString('/', 0, 2));
237     assertEquals("hi/there/man", p.toString('/', 0, 3));
238     assertEquals("hi/there/man", p.toString('/', 0, 4));
239     assertEquals("hi/there/man", p.toString('/', 0, -1));
240     assertEquals("hi/there/man", p.toString('/', -1, -1));
241     assertEquals("there/man", p.toString('/', 1, -1));
242     assertEquals("man", p.toString('/', 2, -1));
243     assertEquals("", p.toString('/', 3, -1));
244     assertEquals("there/man", p.toString('/', 1, 3));
245     assertEquals("there", p.toString('/', 1, 2));
246     assertEquals("", p.toString('/', 1, 1));
247   }
248
249   @Test 
250   public void testDelimiterConstructor() {
251     // Test that the constructor that takes a string and a delimiter
252     // works correctly. Also check that it allocates exactly the needed
253     // needed size for the array - not more.
254     CategoryPath p = new CategoryPath("", '/');
255     assertEquals(p.length(), 0);
256     assertEquals(p.capacityChars(), 0);
257     assertEquals(p.capacityComponents(), 0);
258     p = new CategoryPath("hello", '/');
259     assertEquals(p.length(), 1);
260     assertEquals(p.capacityChars(), 5);
261     assertEquals(p.capacityComponents(), 1);
262     assertEquals(p.toString('@'), "hello");
263     p = new CategoryPath("hi/there", '/');
264     assertEquals(p.length(), 2);
265     assertEquals(p.capacityChars(), 7);
266     assertEquals(p.capacityComponents(), 2);
267     assertEquals(p.toString('@'), "hi@there");
268     p = new CategoryPath("how/are/you/doing?", '/');
269     assertEquals(p.length(), 4);
270     assertEquals(p.capacityChars(), 15);
271     assertEquals(p.capacityComponents(), 4);
272     assertEquals(p.toString('@'), "how@are@you@doing?");
273   }
274   
275   @Test 
276   public void testDefaultConstructor() {
277     // test that the default constructor (no parameters) currently
278     // defaults to creating an object with a 0 initial capacity.
279     // If we change this default later, we also need to change this
280     // test.
281     CategoryPath p = new CategoryPath();
282     assertEquals(0, p.capacityChars());
283     assertEquals(0, p.capacityComponents());
284     assertEquals(0, p.length());
285     assertEquals("", p.toString('/'));
286   }
287   
288   @Test 
289   public void testAddEmpty() {
290     // In the current implementation, p.add("") should add en empty
291     // component (which is, admitingly, not a useful case. On the other
292     // hand, p.add("", delimiter) should add no components at all.
293     // Verify this:
294     CategoryPath p = new CategoryPath(0, 0);
295     p.add("");
296     assertEquals(1, p.length());
297     p.add("");
298     assertEquals(2, p.length());
299     p.add("", '/');
300     assertEquals(2, p.length());
301     p.clear();
302     p.add("", '/');
303     assertEquals(0, p.length());
304   }
305   
306   @Test 
307   public void testDelimiterAdd() {
308     // Test that the add() that takes a string and a delimiter
309     // works correctly. Note that unlike the constructor test above,
310     // we can't expect the capacity to grow to exactly the length of
311     // the given category, so we do not test this.
312     CategoryPath p = new CategoryPath(0, 0);
313     p.add("", '/');
314     assertEquals(0, p.length());
315     assertEquals("", p.toString('@'), "");
316     p.clear();
317     p.add("hello", '/');
318     assertEquals(p.length(), 1);
319     assertEquals(p.toString('@'), "hello");
320     p.clear();
321     p.add("hi/there", '/');
322     assertEquals(p.length(), 2);
323     assertEquals(p.toString('@'), "hi@there");
324     p.clear();
325     p.add("how/are/you/doing?", '/');
326     assertEquals(p.length(), 4);
327     assertEquals(p.toString('@'), "how@are@you@doing?");
328     // See that this is really an add, not replace:
329     p.clear();
330     p.add("hi/there", '/');
331     assertEquals(p.length(), 2);
332     assertEquals(p.toString('@'), "hi@there");
333     p.add("how/are/you/doing", '/');
334     assertEquals(p.length(), 6);
335     assertEquals(p.toString('@'), "hi@there@how@are@you@doing");
336   }
337   
338   @Test 
339   public void testCopyConstructor() {
340     CategoryPath p = new CategoryPath(0,0);
341     int expectedchars=0;
342     for (int i=0; i<1000; i++) {
343       CategoryPath clone = new CategoryPath(p);
344       assertEquals(p.length(), clone.length());
345       assertEquals(p.toString('/'), clone.toString('/'));
346       // verify that the newly created clone has exactly the right
347       // capacity, with no spare (while the original path p probably
348       // does have spare)
349       assertEquals(i, clone.capacityComponents());
350       assertEquals(expectedchars, clone.capacityChars());
351       // Finally, add another component to the path, for the next
352       // round of this loop
353       String num = Integer.toString(i);
354       p.add(num);
355       expectedchars+=num.length();
356     }
357   }
358
359   @Test 
360   public void testPrefixCopyConstructor() {
361     CategoryPath p = new CategoryPath(0,0);
362     p.add("hi");
363     p.add("there");
364     p.add("man");
365     assertEquals(p.length(), 3);
366     
367     CategoryPath p1 = new CategoryPath(p,2);
368     assertEquals(2, p1.length());
369     assertEquals("hi/there", p1.toString('/'));
370     // the new prefix object should only take the space it needs: 
371     assertEquals(2, p1.capacityComponents());
372     assertEquals(7, p1.capacityChars());
373
374     p1 = new CategoryPath(p,1);
375     assertEquals(1, p1.length());
376     assertEquals("hi", p1.toString('/'));
377     assertEquals(1, p1.capacityComponents());
378     assertEquals(2, p1.capacityChars());
379
380     p1 = new CategoryPath(p,0);
381     assertEquals(0, p1.length());
382     assertEquals("", p1.toString('/'));
383     assertEquals(0, p1.capacityComponents());
384     assertEquals(0, p1.capacityChars());
385
386     // with all the following lengths, the prefix should be the whole path: 
387     int[] lengths = { 3, -1, 4 };
388     for (int i=0; i<lengths.length; i++) {
389       p1 = new CategoryPath(p, lengths[i]);
390       assertEquals(3, p1.length());
391       assertEquals("hi/there/man", p1.toString('/'));
392       assertEquals(p, p1);
393       assertEquals(3, p1.capacityComponents());
394       assertEquals(10, p1.capacityChars());
395     }
396   }
397
398   @Test 
399   public void testEquals() {
400     // check that two empty paths are equal, even if they have different
401     // capacities:
402     CategoryPath p1 = new CategoryPath(0,0);
403     CategoryPath p2 = new CategoryPath(1000,300);
404     assertEquals(true, p1.equals(p2));
405     // If we make p2 different, it is no longer equals:
406     p2.add("hi");
407     assertEquals(false, p1.equals(p2));
408     // A categoryPath is definitely not equals to an object of some other
409     // type:
410     assertEquals(false, p1.equals(Integer.valueOf(3)));
411     // Build two paths separately, and compare them
412     p1.clear();
413     p1.add("hello");
414     p1.add("world");
415     p2.clear();
416     p2.add("hello");
417     p2.add("world");
418     assertEquals(true, p1.equals(p2));    
419     // Check that comparison really don't look at old data which might
420     // be stored in the array
421     p1.clear();
422     p1.add("averylongcategoryname");
423     p1.clear();
424     p1.add("hi");
425     p2.clear();
426     p2.add("hi");
427     assertEquals(true, p1.equals(p2));
428     // Being of the same length is obviously not enough to be equal
429     p1.clear();
430     p1.add("hi");
431     p2.clear();
432     p2.add("hello");
433     assertEquals(false, p1.equals(p2));
434     p1.clear();
435     p1.add("hi");
436     p2.clear();
437     p2.add("ho");
438     assertEquals(false, p1.equals(p2));
439   }
440   @Test 
441   public void testHashCode() {
442     // Note: in this test, we assume that if two paths are not equal,
443     // their hash codes should come out differently. This is *not*
444     // always the case, but in the examples we use below, it comes out
445     // fine, and unless we have some really bad luck in changing our
446     // hash function, this should also remain true in the future.
447     
448     // check that two empty paths are equal, even if they have different
449     // capacities:
450     CategoryPath p1 = new CategoryPath(0,0);
451     CategoryPath p2 = new CategoryPath(1000,300);
452     assertEquals(p1.hashCode(), p2.hashCode());
453     // If we make p2 different, it is no longer equals:
454     p2.add("hi");
455     assertEquals(false, p1.hashCode()==p2.hashCode());
456     // Build two paths separately, and compare them
457     p1.clear();
458     p1.add("hello");
459     p1.add("world");
460     p2.clear();
461     p2.add("hello");
462     p2.add("world");
463     assertEquals(p1.hashCode(), p2.hashCode());
464     // Check that comparison really don't look at old data which might
465     // be stored in the array
466     p1.clear();
467     p1.add("averylongcategoryname");
468     p1.clear();
469     p1.add("hi");
470     p2.clear();
471     p2.add("hi");
472     assertEquals(p1.hashCode(), p2.hashCode());
473     // Being of the same length is obviously not enough to be equal
474     p1.clear();
475     p1.add("hi");
476     p2.clear();
477     p2.add("hello");
478     assertEquals(false, p1.hashCode()==p2.hashCode());
479     p1.clear();
480     p1.add("hi");
481     p2.clear();
482     p2.add("ho");
483     assertEquals(false, p1.hashCode()==p2.hashCode());
484   }
485   
486   @Test 
487   public void testHashCodePrefix() {
488     // First, repeat the tests of testHashCode() using hashCode(-1)
489     // just to make sure nothing was broken in this variant:
490     CategoryPath p1 = new CategoryPath(0,0);
491     CategoryPath p2 = new CategoryPath(1000,300);
492     assertEquals(p1.hashCode(-1), p2.hashCode(-1));
493     p2.add("hi");
494     assertEquals(false, p1.hashCode(-1)==p2.hashCode(-1));
495     p1.clear();
496     p1.add("hello");
497     p1.add("world");
498     p2.clear();
499     p2.add("hello");
500     p2.add("world");
501     assertEquals(p1.hashCode(-1), p2.hashCode(-1));
502     p1.clear();
503     p1.add("averylongcategoryname");
504     p1.clear();
505     p1.add("hi");
506     p2.clear();
507     p2.add("hi");
508     assertEquals(p1.hashCode(-1), p2.hashCode(-1));
509     p1.clear();
510     p1.add("hi");
511     p2.clear();
512     p2.add("hello");
513     assertEquals(false, p1.hashCode(-1)==p2.hashCode(-1));
514     p1.clear();
515     p1.add("hi");
516     p2.clear();
517     p2.add("ho");
518     assertEquals(false, p1.hashCode(-1)==p2.hashCode(-1));
519     
520     // Now move to testing prefixes:
521     CategoryPath p = new CategoryPath();
522     p.add("this");
523     p.add("is");
524     p.add("a");
525     p.add("test");
526     assertEquals(p.hashCode(), p.hashCode(4));
527     assertEquals(new CategoryPath().hashCode(), p.hashCode(0));
528     assertEquals(new CategoryPath(p, 1).hashCode(), p.hashCode(1));
529     assertEquals(new CategoryPath(p, 2).hashCode(), p.hashCode(2));
530     assertEquals(new CategoryPath(p, 3).hashCode(), p.hashCode(3));
531   }
532
533   @Test 
534   public void testLongHashCode() {
535     // Note: in this test, we assume that if two paths are not equal,
536     // their hash codes should come out differently. This is *not*
537     // always the case, but in the examples we use below, it comes out
538     // fine, and unless we have some really bad luck in changing our
539     // hash function, this should also remain true in the future.
540     
541     // check that two empty paths are equal, even if they have different
542     // capacities:
543     CategoryPath p1 = new CategoryPath(0,0);
544     CategoryPath p2 = new CategoryPath(1000,300);
545     assertEquals(p1.longHashCode(), p2.longHashCode());
546     // If we make p2 different, it is no longer equals:
547     p2.add("hi");
548     assertEquals(false, p1.longHashCode()==p2.longHashCode());
549     // Build two paths separately, and compare them
550     p1.clear();
551     p1.add("hello");
552     p1.add("world");
553     p2.clear();
554     p2.add("hello");
555     p2.add("world");
556     assertEquals(p1.longHashCode(), p2.longHashCode());
557     // Check that comparison really don't look at old data which might
558     // be stored in the array
559     p1.clear();
560     p1.add("averylongcategoryname");
561     p1.clear();
562     p1.add("hi");
563     p2.clear();
564     p2.add("hi");
565     assertEquals(p1.longHashCode(), p2.longHashCode());
566     // Being of the same length is obviously not enough to be equal
567     p1.clear();
568     p1.add("hi");
569     p2.clear();
570     p2.add("hello");
571     assertEquals(false, p1.longHashCode()==p2.longHashCode());
572     p1.clear();
573     p1.add("hi");
574     p2.clear();
575     p2.add("ho");
576     assertEquals(false, p1.longHashCode()==p2.longHashCode());
577   }
578   
579   @Test 
580   public void testLongHashCodePrefix() {
581     // First, repeat the tests of testLongHashCode() using longHashCode(-1)
582     // just to make sure nothing was broken in this variant:
583     
584     // check that two empty paths are equal, even if they have different
585     // capacities:
586     CategoryPath p1 = new CategoryPath(0,0);
587     CategoryPath p2 = new CategoryPath(1000,300);
588     assertEquals(p1.longHashCode(-1), p2.longHashCode(-1));
589     // If we make p2 different, it is no longer equals:
590     p2.add("hi");
591     assertEquals(false, p1.longHashCode(-1)==p2.longHashCode(-1));
592     // Build two paths separately, and compare them
593     p1.clear();
594     p1.add("hello");
595     p1.add("world");
596     p2.clear();
597     p2.add("hello");
598     p2.add("world");
599     assertEquals(p1.longHashCode(-1), p2.longHashCode(-1));
600     // Check that comparison really don't look at old data which might
601     // be stored in the array
602     p1.clear();
603     p1.add("averylongcategoryname");
604     p1.clear();
605     p1.add("hi");
606     p2.clear();
607     p2.add("hi");
608     assertEquals(p1.longHashCode(-1), p2.longHashCode(-1));
609     // Being of the same length is obviously not enough to be equal
610     p1.clear();
611     p1.add("hi");
612     p2.clear();
613     p2.add("hello");
614     assertEquals(false, p1.longHashCode(-1)==p2.longHashCode(-1));
615     p1.clear();
616     p1.add("hi");
617     p2.clear();
618     p2.add("ho");
619     assertEquals(false, p1.longHashCode(-1)==p2.longHashCode(-1));
620     
621     // Now move to testing prefixes:
622     CategoryPath p = new CategoryPath();
623     p.add("this");
624     p.add("is");
625     p.add("a");
626     p.add("test");
627     assertEquals(p.longHashCode(), p.longHashCode(4));
628     assertEquals(new CategoryPath().longHashCode(), p.longHashCode(0));
629     assertEquals(new CategoryPath(p, 1).longHashCode(), p.longHashCode(1));
630     assertEquals(new CategoryPath(p, 2).longHashCode(), p.longHashCode(2));
631     assertEquals(new CategoryPath(p, 3).longHashCode(), p.longHashCode(3));
632   }
633   
634   @Test 
635   public void testArrayConstructor() {
636     CategoryPath p = new CategoryPath("hello", "world", "yo");
637     assertEquals(3, p.length());
638     assertEquals(12, p.capacityChars());
639     assertEquals(3, p.capacityComponents());
640     assertEquals("hello/world/yo", p.toString('/'));
641     
642     p = new CategoryPath(new String[0]);
643     assertEquals(0, p.length());
644     assertEquals(0, p.capacityChars());
645     assertEquals(0, p.capacityComponents());
646   }
647   
648   @Test 
649   public void testCharsNeededForFullPath() {
650     String[] components = { "hello", "world", "yo" };
651     CategoryPath p = new CategoryPath();
652     assertEquals(0, p.charsNeededForFullPath());
653     int expectedCharsNeeded = 0;
654     for (int i=0; i<components.length; i++) {
655       p.add(components[i]);
656       expectedCharsNeeded += components[i].length();
657       if (i>0) {
658         expectedCharsNeeded++;
659       }
660       assertEquals(expectedCharsNeeded, p.charsNeededForFullPath());
661     }
662   }
663   
664   @Test 
665   public void testCopyToCharArray() {
666     String[] components = { "hello", "world", "yo" };
667     CategoryPath p = new CategoryPath(components);
668     char[] charArray = new char[p.charsNeededForFullPath()];
669     int numCharsCopied = 0;
670     
671     numCharsCopied = p.copyToCharArray(charArray, 0, 0, '.');
672     assertEquals(0, numCharsCopied);
673     assertEquals("", new String(charArray, 0, numCharsCopied));
674     
675     numCharsCopied = p.copyToCharArray(charArray, 0, 1, '.');
676     assertEquals(5, numCharsCopied);
677     assertEquals("hello", new String(charArray, 0, numCharsCopied));
678     
679     numCharsCopied = p.copyToCharArray(charArray, 0, 3, '.');
680     assertEquals(14, numCharsCopied);
681     assertEquals("hello.world.yo", new String(charArray, 0, numCharsCopied));
682     
683     numCharsCopied = p.copyToCharArray(charArray, 0, -1, '.');
684     assertEquals(14, numCharsCopied);
685     assertEquals("hello.world.yo", new String(charArray, 0, numCharsCopied));
686     numCharsCopied = p.copyToCharArray(charArray, 0, 4, '.');
687     assertEquals(14, numCharsCopied);
688     assertEquals("hello.world.yo", new String(charArray, 0, numCharsCopied));
689   }
690   
691   @Test 
692   public void testCharSerialization() throws Exception {
693     CategoryPath[] testCategories = {
694         new CategoryPath("hi", "there", "man"),
695         new CategoryPath("hello"),
696         new CategoryPath("what's", "up"),
697         // See that an empty category, which generates a (char)0,
698         // doesn't cause any problems in the middle of the serialization:
699         new CategoryPath(),
700         new CategoryPath("another", "example"),
701         new CategoryPath(),
702         new CategoryPath()
703     };
704     StringBuilder sb = new StringBuilder();
705     for (int i=0; i<testCategories.length; i++) {
706       testCategories[i].serializeAppendTo(sb);
707     }
708     
709     CategoryPath tmp = new CategoryPath();
710     int offset=0;
711     for (int i=0; i<testCategories.length; i++) {
712       // check equalsToSerialized, in a equal and non-equal case:
713       assertTrue(testCategories[i].equalsToSerialized(sb, offset));
714       assertFalse(new CategoryPath("Hello", "world").equalsToSerialized(sb, offset));
715       assertFalse(new CategoryPath("world").equalsToSerialized(sb, offset));
716       // and check hashCodeFromSerialized:
717       assertEquals(testCategories[i].hashCode(), CategoryPath.hashCodeOfSerialized(sb, offset));
718       // and check setFromSerialized:
719       offset = tmp.setFromSerialized(sb, offset);
720       assertEquals(testCategories[i], tmp);
721     }
722     assertEquals(offset, sb.length());
723     // A similar test, for a much longer path (though not larger than the
724     // 2^15-1 character limit that CategoryPath allows:
725     sb = new StringBuilder();
726     CategoryPath p = new CategoryPath();
727     for (int i=0; i<1000; i++) {
728       p.add(Integer.toString(i));
729     }
730     p.serializeAppendTo(sb);
731     p.serializeAppendTo(sb);
732     p.serializeAppendTo(sb);
733     offset=0;
734     assertTrue(p.equalsToSerialized(sb, offset));
735     assertEquals(p.hashCode(), CategoryPath.hashCodeOfSerialized(sb, offset));
736     offset = tmp.setFromSerialized(sb, offset);
737     assertEquals(p, tmp);
738     assertTrue(p.equalsToSerialized(sb, offset));
739     assertEquals(p.hashCode(), CategoryPath.hashCodeOfSerialized(sb, offset));
740     offset = tmp.setFromSerialized(sb, offset);
741     assertEquals(p, tmp);
742     assertTrue(p.equalsToSerialized(sb, offset));
743     assertEquals(p.hashCode(), CategoryPath.hashCodeOfSerialized(sb, offset));
744     offset = tmp.setFromSerialized(sb, offset);
745     assertEquals(p, tmp);
746     assertEquals(offset, sb.length());
747     
748     // Test the serializeAppendTo variant with a prefixLen
749     p = new CategoryPath();
750     for (int i=0; i<783; i++) {
751       p.add(Integer.toString(i));
752     }
753     int[] prefixLengths = { 0, 574, 782, 783, 784, -1 };
754     for (int prefixLen : prefixLengths) {
755       sb = new StringBuilder();
756       p.serializeAppendTo(prefixLen, sb);
757       assertTrue(new CategoryPath(p, prefixLen).equalsToSerialized(sb, 0));
758     }
759     
760     // Test the equalsToSerialized variant with a prefixLen
761     // We use p and prefixLengths set above.
762     for (int prefixLen : prefixLengths) {
763       sb = new StringBuilder();
764       new CategoryPath(p, prefixLen).serializeAppendTo(sb);
765       assertTrue(p.equalsToSerialized(prefixLen, sb, 0));
766     }
767     
768     // Check also the false case of equalsToSerialized with prefixLen:
769     sb = new StringBuilder();
770     new CategoryPath().serializeAppendTo(sb);
771     assertTrue(new CategoryPath().equalsToSerialized(0, sb, 0));
772     assertTrue(new CategoryPath("a", "b").equalsToSerialized(0, sb, 0));
773     assertFalse(new CategoryPath("a", "b").equalsToSerialized(1, sb, 0));
774     sb = new StringBuilder();
775     new CategoryPath("a", "b").serializeAppendTo(sb);
776     assertFalse(new CategoryPath().equalsToSerialized(0, sb, 0));
777     assertFalse(new CategoryPath("a").equalsToSerialized(0, sb, 0));
778     assertFalse(new CategoryPath("a").equalsToSerialized(1, sb, 0));
779     assertFalse(new CategoryPath("a", "b").equalsToSerialized(0, sb, 0));
780     assertFalse(new CategoryPath("a", "b").equalsToSerialized(1, sb, 0));
781     assertTrue(new CategoryPath("a", "b").equalsToSerialized(2, sb, 0));
782     assertTrue(new CategoryPath("a", "b", "c").equalsToSerialized(2, sb, 0));
783     assertFalse(new CategoryPath("z", "b", "c").equalsToSerialized(2, sb, 0));
784     assertFalse(new CategoryPath("aa", "b", "c").equalsToSerialized(2, sb, 0));
785   }
786
787   @Test 
788   public void testStreamWriterSerialization() throws Exception {
789     CategoryPath[] testPaths = {
790         new CategoryPath("hi", "there", "man"),
791         new CategoryPath("hello"),
792         new CategoryPath("date", "2009", "May", "13", "14", "59", "00"),
793         // See that an empty category, which generates a (char)0,
794         // doesn't cause any problems in the middle of the serialization:
795         new CategoryPath(),
796         new CategoryPath("another", "example")
797     };
798     ByteArrayOutputStream baos = new ByteArrayOutputStream();
799     OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");  // UTF-8 is always supported.
800     for (CategoryPath cp : testPaths) {
801       cp.serializeToStreamWriter(osw);
802     }
803     osw.flush();
804     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
805     InputStreamReader isr = new InputStreamReader(bais, "UTF-8");
806     CategoryPath[] checkPaths = {
807         new CategoryPath(), new CategoryPath(), new CategoryPath(), new CategoryPath(), new CategoryPath()
808     };
809     for (int j = 0; j < checkPaths.length; j++) {
810       checkPaths[j].deserializeFromStreamReader(isr);
811       assertEquals("Paths not equal", testPaths[j], checkPaths[j]);
812     }
813   }
814
815   @Test 
816   public void testCharSequenceCtor() throws Exception {
817     CategoryPath[] testPaths = {
818         new CategoryPath(new CS("hi"), new CS("there"), new CS("man")),
819         new CategoryPath(new CS("hello")),
820         new CategoryPath(new CS("date"), new CS("2009"), new CS("May"), new CS("13"),
821             new CS("14"), new CS("59"), new CS("00")),
822         new CategoryPath(),
823         new CategoryPath(new CS("another"), new CS("example"))
824     };
825     assertEquals("Wrong capacity", 10, testPaths[0].capacityChars());
826     assertEquals("Wrong capacity", 5, testPaths[1].capacityChars());
827     assertEquals("Wrong capacity", 19, testPaths[2].capacityChars());
828     assertEquals("Wrong capacity", 0, testPaths[3].capacityChars());
829     assertEquals("Wrong capacity", 14, testPaths[4].capacityChars());
830
831     assertEquals("Wrong component", "hi", testPaths[0].getComponent(0));
832     assertEquals("Wrong component", "there", testPaths[0].getComponent(1));
833     assertEquals("Wrong component", "man", testPaths[0].getComponent(2));
834     assertEquals("Wrong component", "hello", testPaths[1].getComponent(0));
835     assertEquals("Wrong component", "date", testPaths[2].getComponent(0));
836     assertEquals("Wrong component", "2009", testPaths[2].getComponent(1));
837     assertEquals("Wrong component", "May", testPaths[2].getComponent(2));
838     assertEquals("Wrong component", "13", testPaths[2].getComponent(3));
839     assertEquals("Wrong component", "14", testPaths[2].getComponent(4));
840     assertEquals("Wrong component", "59", testPaths[2].getComponent(5));
841     assertEquals("Wrong component", "00", testPaths[2].getComponent(6));
842     assertNull("Not null component", testPaths[3].getComponent(0));
843     assertEquals("Wrong component", "another", testPaths[4].getComponent(0));
844     assertEquals("Wrong component", "example", testPaths[4].getComponent(1));
845   }
846
847   @Test 
848   public void testIsDescendantOf() throws Exception {
849     CategoryPath[] testPaths = {
850         new CategoryPath(new CS("hi"), new CS("there")),
851         new CategoryPath(new CS("hi"), new CS("there"), new CS("man")),
852         new CategoryPath(new CS("hithere"), new CS("man")),
853         new CategoryPath(new CS("hi"), new CS("there"), new CS("mano")),
854         new CategoryPath(),
855     };
856     assertTrue(testPaths[0].isDescendantOf(testPaths[0]));
857     assertTrue(testPaths[0].isDescendantOf(testPaths[4]));
858     assertFalse(testPaths[4].isDescendantOf(testPaths[0]));
859     assertTrue(testPaths[1].isDescendantOf(testPaths[0]));
860     assertTrue(testPaths[1].isDescendantOf(testPaths[1]));
861     assertTrue(testPaths[3].isDescendantOf(testPaths[0]));
862     assertFalse(testPaths[2].isDescendantOf(testPaths[0]));
863     assertFalse(testPaths[2].isDescendantOf(testPaths[1]));
864     assertFalse(testPaths[3].isDescendantOf(testPaths[1]));
865   }
866
867   @Test 
868   public void testCompareTo() {
869     CategoryPath p = new CategoryPath("a/b/c/d", '/');
870     CategoryPath pother = new CategoryPath("a/b/c/d", '/');
871     assertTrue(pother.compareTo(p) == 0);
872     pother = new CategoryPath("", '/');
873     assertTrue(pother.compareTo(p) < 0);
874     pother = new CategoryPath("a/b_/c/d", '/');
875     assertTrue(pother.compareTo(p) > 0);
876     pother = new CategoryPath("a/b/c", '/');
877     assertTrue(pother.compareTo(p) < 0);
878     pother = new CategoryPath("a/b/c/e", '/');
879     assertTrue(pother.compareTo(p) > 0);
880     pother = new CategoryPath("a/b/c//e", '/');
881     assertTrue(pother.compareTo(p) < 0);
882   }
883   
884   private static class CS implements CharSequence {
885     public CS(String s) {
886       this.ca = new char[s.length()];
887       s.getChars(0, s.length(), this.ca, 0);
888     }
889     public char charAt(int index) {
890       return this.ca[index];
891     }
892     public int length() {
893       return this.ca.length;
894     }
895     public CharSequence subSequence(int start, int end) {
896       return null; // not used.
897     }
898     private char[] ca;
899   }
900
901 }