blob: 96c166256de4ecff37e12dba8861539241d7a27f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.collections4.trie;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Map.Entry;
import org.apache.commons.collections4.Trie.Cursor;
import org.apache.commons.collections4.trie.analyzer.CharacterKeyAnalyzer;
import org.apache.commons.collections4.trie.analyzer.IntegerKeyAnalyzer;
import org.apache.commons.collections4.trie.analyzer.StringKeyAnalyzer;
import org.junit.Assert;
import org.junit.Test;
/**
* JUnit tests.
*
* FIXME: add serialization support
* TODO: integrate into test framework, utilize existing map tests
*
* @since 4.0
* @version $Id$
*/
public class PatriciaTrieTest {
@Test
@SuppressWarnings("boxing") // OK in test code
public void testSimple() {
final PatriciaTrie<Integer, String> intTrie = new PatriciaTrie<Integer, String>(new IntegerKeyAnalyzer());
Assert.assertTrue(intTrie.isEmpty());
Assert.assertEquals(0, intTrie.size());
intTrie.put(1, "One");
Assert.assertFalse(intTrie.isEmpty());
Assert.assertEquals(1, intTrie.size());
Assert.assertEquals("One", intTrie.remove(1));
Assert.assertNull(intTrie.remove(1));
Assert.assertTrue(intTrie.isEmpty());
Assert.assertEquals(0, intTrie.size());
intTrie.put(1, "One");
Assert.assertEquals("One", intTrie.get(1));
Assert.assertEquals("One", intTrie.put(1, "NotOne"));
Assert.assertEquals(1, intTrie.size());
Assert.assertEquals("NotOne", intTrie.get(1));
Assert.assertEquals("NotOne", intTrie.remove(1));
Assert.assertNull(intTrie.put(1, "One"));
}
@Test
@SuppressWarnings("boxing") // OK in test code
public void testCeilingEntry() {
final PatriciaTrie<Character, String> charTrie
= new PatriciaTrie<Character, String>(new CharacterKeyAnalyzer());
charTrie.put('c', "c");
charTrie.put('p', "p");
charTrie.put('l', "l");
charTrie.put('t', "t");
charTrie.put('k', "k");
charTrie.put('a', "a");
charTrie.put('y', "y");
charTrie.put('r', "r");
charTrie.put('u', "u");
charTrie.put('o', "o");
charTrie.put('w', "w");
charTrie.put('i', "i");
charTrie.put('e', "e");
charTrie.put('x', "x");
charTrie.put('q', "q");
charTrie.put('b', "b");
charTrie.put('j', "j");
charTrie.put('s', "s");
charTrie.put('n', "n");
charTrie.put('v', "v");
charTrie.put('g', "g");
charTrie.put('h', "h");
charTrie.put('m', "m");
charTrie.put('z', "z");
charTrie.put('f', "f");
charTrie.put('d', "d");
final Object[] results = new Object[] {
'a', "a", 'b', "b", 'c', "c", 'd', "d", 'e', "e",
'f', "f", 'g', "g", 'h', "h", 'i', "i", 'j', "j",
'k', "k", 'l', "l", 'm', "m", 'n', "n", 'o', "o",
'p', "p", 'q', "q", 'r', "r", 's', "s", 't', "t",
'u', "u", 'v', "v", 'w', "w", 'x', "x", 'y', "y",
'z', "z"
};
for(int i = 0; i < results.length; i++) {
final Map.Entry<Character, String> found = charTrie.ceilingEntry((Character)results[i]);
Assert.assertNotNull(found);
Assert.assertEquals(results[i], found.getKey());
Assert.assertEquals(results[++i], found.getValue());
}
// Remove some & try again...
charTrie.remove('a');
charTrie.remove('z');
charTrie.remove('q');
charTrie.remove('l');
charTrie.remove('p');
charTrie.remove('m');
charTrie.remove('u');
Map.Entry<Character, String> found = charTrie.ceilingEntry('u');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'v', found.getKey());
found = charTrie.ceilingEntry('a');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'b', found.getKey());
found = charTrie.ceilingEntry('z');
Assert.assertNull(found);
found = charTrie.ceilingEntry('q');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'r', found.getKey());
found = charTrie.ceilingEntry('l');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'n', found.getKey());
found = charTrie.ceilingEntry('p');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'r', found.getKey());
found = charTrie.ceilingEntry('m');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'n', found.getKey());
found = charTrie.ceilingEntry('\0');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'b', found.getKey());
charTrie.put('\0', "");
found = charTrie.ceilingEntry('\0');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'\0', found.getKey());
}
@Test
@SuppressWarnings("boxing") // OK in test code
public void testLowerEntry() {
final PatriciaTrie<Character, String> charTrie = new PatriciaTrie<Character, String>(new CharacterKeyAnalyzer());
charTrie.put('c', "c");
charTrie.put('p', "p");
charTrie.put('l', "l");
charTrie.put('t', "t");
charTrie.put('k', "k");
charTrie.put('a', "a");
charTrie.put('y', "y");
charTrie.put('r', "r");
charTrie.put('u', "u");
charTrie.put('o', "o");
charTrie.put('w', "w");
charTrie.put('i', "i");
charTrie.put('e', "e");
charTrie.put('x', "x");
charTrie.put('q', "q");
charTrie.put('b', "b");
charTrie.put('j', "j");
charTrie.put('s', "s");
charTrie.put('n', "n");
charTrie.put('v', "v");
charTrie.put('g', "g");
charTrie.put('h', "h");
charTrie.put('m', "m");
charTrie.put('z', "z");
charTrie.put('f', "f");
charTrie.put('d', "d");
final Object[] results = new Object[] {
'a', "a", 'b', "b", 'c', "c", 'd', "d", 'e', "e",
'f', "f", 'g', "g", 'h', "h", 'i', "i", 'j', "j",
'k', "k", 'l', "l", 'm', "m", 'n', "n", 'o', "o",
'p', "p", 'q', "q", 'r', "r", 's', "s", 't', "t",
'u', "u", 'v', "v", 'w', "w", 'x', "x", 'y', "y",
'z', "z"
};
for(int i = 0; i < results.length; i+=2) {
//System.out.println("Looking for: " + results[i]);
final Map.Entry<Character, String> found = charTrie.lowerEntry((Character)results[i]);
if(i == 0) {
Assert.assertNull(found);
} else {
Assert.assertNotNull(found);
Assert.assertEquals(results[i-2], found.getKey());
Assert.assertEquals(results[i-1], found.getValue());
}
}
Map.Entry<Character, String> found = charTrie.lowerEntry((char)('z' + 1));
Assert.assertNotNull(found);
Assert.assertEquals((Character)'z', found.getKey());
// Remove some & try again...
charTrie.remove('a');
charTrie.remove('z');
charTrie.remove('q');
charTrie.remove('l');
charTrie.remove('p');
charTrie.remove('m');
charTrie.remove('u');
found = charTrie.lowerEntry('u');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'t', found.getKey());
found = charTrie.lowerEntry('v');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'t', found.getKey());
found = charTrie.lowerEntry('a');
Assert.assertNull(found);
found = charTrie.lowerEntry('z');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'y', found.getKey());
found = charTrie.lowerEntry((char)('z'+1));
Assert.assertNotNull(found);
Assert.assertEquals((Character)'y', found.getKey());
found = charTrie.lowerEntry('q');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'o', found.getKey());
found = charTrie.lowerEntry('r');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'o', found.getKey());
found = charTrie.lowerEntry('p');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'o', found.getKey());
found = charTrie.lowerEntry('l');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'k', found.getKey());
found = charTrie.lowerEntry('m');
Assert.assertNotNull(found);
Assert.assertEquals((Character)'k', found.getKey());
found = charTrie.lowerEntry('\0');
Assert.assertNull(found);
charTrie.put('\0', "");
found = charTrie.lowerEntry('\0');
Assert.assertNull(found);
}
@Test
@SuppressWarnings("boxing") // OK in test code
public void testIteration() {
final PatriciaTrie<Integer, String> intTrie = new PatriciaTrie<Integer, String>(new IntegerKeyAnalyzer());
intTrie.put(1, "One");
intTrie.put(5, "Five");
intTrie.put(4, "Four");
intTrie.put(2, "Two");
intTrie.put(3, "Three");
intTrie.put(15, "Fifteen");
intTrie.put(13, "Thirteen");
intTrie.put(14, "Fourteen");
intTrie.put(16, "Sixteen");
TestCursor cursor = new TestCursor(
1, "One", 2, "Two", 3, "Three", 4, "Four", 5, "Five", 13, "Thirteen",
14, "Fourteen", 15, "Fifteen", 16, "Sixteen");
cursor.starting();
intTrie.traverse(cursor);
cursor.finished();
cursor.starting();
for (final Map.Entry<Integer, String> entry : intTrie.entrySet()) {
cursor.select(entry);
}
cursor.finished();
cursor.starting();
for (final Integer integer : intTrie.keySet()) {
cursor.checkKey(integer);
}
cursor.finished();
cursor.starting();
for (final String string : intTrie.values()) {
cursor.checkValue(string);
}
cursor.finished();
final PatriciaTrie<Character, String> charTrie = new PatriciaTrie<Character, String>(new CharacterKeyAnalyzer());
charTrie.put('c', "c");
charTrie.put('p', "p");
charTrie.put('l', "l");
charTrie.put('t', "t");
charTrie.put('k', "k");
charTrie.put('a', "a");
charTrie.put('y', "y");
charTrie.put('r', "r");
charTrie.put('u', "u");
charTrie.put('o', "o");
charTrie.put('w', "w");
charTrie.put('i', "i");
charTrie.put('e', "e");
charTrie.put('x', "x");
charTrie.put('q', "q");
charTrie.put('b', "b");
charTrie.put('j', "j");
charTrie.put('s', "s");
charTrie.put('n', "n");
charTrie.put('v', "v");
charTrie.put('g', "g");
charTrie.put('h', "h");
charTrie.put('m', "m");
charTrie.put('z', "z");
charTrie.put('f', "f");
charTrie.put('d', "d");
cursor = new TestCursor('a', "a", 'b', "b", 'c', "c", 'd', "d", 'e', "e",
'f', "f", 'g', "g", 'h', "h", 'i', "i", 'j', "j",
'k', "k", 'l', "l", 'm', "m", 'n', "n", 'o', "o",
'p', "p", 'q', "q", 'r', "r", 's', "s", 't', "t",
'u', "u", 'v', "v", 'w', "w", 'x', "x", 'y', "y",
'z', "z");
cursor.starting();
charTrie.traverse(cursor);
cursor.finished();
cursor.starting();
for (final Map.Entry<Character, String> entry : charTrie.entrySet()) {
cursor.select(entry);
}
cursor.finished();
cursor.starting();
for (final Character character : charTrie.keySet()) {
cursor.checkKey(character);
}
cursor.finished();
cursor.starting();
for (final String string : charTrie.values()) {
cursor.checkValue(string);
}
cursor.finished();
}
@Test
@SuppressWarnings("boxing") // OK in test code
public void testSelect() {
final PatriciaTrie<Character, String> charTrie = new PatriciaTrie<Character, String>(new CharacterKeyAnalyzer());
charTrie.put('c', "c");
charTrie.put('p', "p");
charTrie.put('l', "l");
charTrie.put('t', "t");
charTrie.put('k', "k");
charTrie.put('a', "a");
charTrie.put('y', "y");
charTrie.put('r', "r");
charTrie.put('u', "u");
charTrie.put('o', "o");
charTrie.put('w', "w");
charTrie.put('i', "i");
charTrie.put('e', "e");
charTrie.put('x', "x");
charTrie.put('q', "q");
charTrie.put('b', "b");
charTrie.put('j', "j");
charTrie.put('s', "s");
charTrie.put('n', "n");
charTrie.put('v', "v");
charTrie.put('g', "g");
charTrie.put('h', "h");
charTrie.put('m', "m");
charTrie.put('z', "z");
charTrie.put('f', "f");
charTrie.put('d', "d");
final TestCursor cursor = new TestCursor(
'd', "d", 'e', "e", 'f', "f", 'g', "g",
'a', "a", 'b', "b", 'c', "c",
'l', "l", 'm', "m", 'n', "n", 'o', "o",
'h', "h", 'i', "i", 'j', "j", 'k', "k",
't', "t", 'u', "u", 'v', "v", 'w', "w",
'p', "p", 'q', "q", 'r', "r", 's', "s",
'x', "x", 'y', "y", 'z', "z");
Assert.assertEquals(26, charTrie.size());
cursor.starting();
charTrie.select('d', cursor);
cursor.finished();
}
@Test
@SuppressWarnings("boxing") // OK in test code
public void testTraverseCursorRemove() {
final PatriciaTrie<Character, String> charTrie = new PatriciaTrie<Character, String>(new CharacterKeyAnalyzer());
charTrie.put('c', "c");
charTrie.put('p', "p");
charTrie.put('l', "l");
charTrie.put('t', "t");
charTrie.put('k', "k");
charTrie.put('a', "a");
charTrie.put('y', "y");
charTrie.put('r', "r");
charTrie.put('u', "u");
charTrie.put('o', "o");
charTrie.put('w', "w");
charTrie.put('i', "i");
charTrie.put('e', "e");
charTrie.put('x', "x");
charTrie.put('q', "q");
charTrie.put('b', "b");
charTrie.put('j', "j");
charTrie.put('s', "s");
charTrie.put('n', "n");
charTrie.put('v', "v");
charTrie.put('g', "g");
charTrie.put('h', "h");
charTrie.put('m', "m");
charTrie.put('z', "z");
charTrie.put('f', "f");
charTrie.put('d', "d");
final TestCursor cursor = new TestCursor('a', "a", 'b', "b", 'c', "c", 'd', "d", 'e', "e",
'f', "f", 'g', "g", 'h', "h", 'i', "i", 'j', "j",
'k', "k", 'l', "l", 'm', "m", 'n', "n", 'o', "o",
'p', "p", 'q', "q", 'r', "r", 's', "s", 't', "t",
'u', "u", 'v', "v", 'w', "w", 'x', "x", 'y', "y",
'z', "z");
cursor.starting();
charTrie.traverse(cursor);
cursor.finished();
// Test removing both an internal & external node.
// 'm' is an example External node in this Trie, and 'p' is an internal.
Assert.assertEquals(26, charTrie.size());
final Object[] toRemove = new Object[] { 'g', 'd', 'e', 'm', 'p', 'q', 'r', 's' };
cursor.addToRemove(toRemove);
cursor.starting();
charTrie.traverse(cursor);
cursor.finished();
Assert.assertEquals(26 - toRemove.length, charTrie.size());
cursor.starting();
charTrie.traverse(cursor);
cursor.finished();
cursor.starting();
for (final Entry<Character, String> entry : charTrie.entrySet()) {
cursor.select(entry);
if (Arrays.asList(toRemove).contains(entry.getKey())) {
Assert.fail("got an: " + entry);
}
}
cursor.finished();
}
@Test
@SuppressWarnings("boxing") // OK in test code
public void testIteratorRemove() {
final PatriciaTrie<Character, String> charTrie = new PatriciaTrie<Character, String>(new CharacterKeyAnalyzer());
charTrie.put('c', "c");
charTrie.put('p', "p");
charTrie.put('l', "l");
charTrie.put('t', "t");
charTrie.put('k', "k");
charTrie.put('a', "a");
charTrie.put('y', "y");
charTrie.put('r', "r");
charTrie.put('u', "u");
charTrie.put('o', "o");
charTrie.put('w', "w");
charTrie.put('i', "i");
charTrie.put('e', "e");
charTrie.put('x', "x");
charTrie.put('q', "q");
charTrie.put('b', "b");
charTrie.put('j', "j");
charTrie.put('s', "s");
charTrie.put('n', "n");
charTrie.put('v', "v");
charTrie.put('g', "g");
charTrie.put('h', "h");
charTrie.put('m', "m");
charTrie.put('z', "z");
charTrie.put('f', "f");
charTrie.put('d', "d");
final TestCursor cursor = new TestCursor('a', "a", 'b', "b", 'c', "c", 'd', "d", 'e', "e",
'f', "f", 'g', "g", 'h', "h", 'i', "i", 'j', "j",
'k', "k", 'l', "l", 'm', "m", 'n', "n", 'o', "o",
'p', "p", 'q', "q", 'r', "r", 's', "s", 't', "t",
'u', "u", 'v', "v", 'w', "w", 'x', "x", 'y', "y",
'z', "z");
// Test removing both an internal & external node.
// 'm' is an example External node in this Trie, and 'p' is an internal.
Assert.assertEquals(26, charTrie.size());
final Object[] toRemove = new Object[] { 'e', 'm', 'p', 'q', 'r', 's' };
cursor.starting();
for(final Iterator<Map.Entry<Character, String>> i = charTrie.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<Character,String> entry = i.next();
cursor.select(entry);
if(Arrays.asList(toRemove).contains(entry.getKey())) {
i.remove();
}
}
cursor.finished();
Assert.assertEquals(26 - toRemove.length, charTrie.size());
cursor.remove(toRemove);
cursor.starting();
for (final Entry<Character, String> entry : charTrie.entrySet()) {
cursor.select(entry);
if (Arrays.asList(toRemove).contains(entry.getKey())) {
Assert.fail("got an: " + entry);
}
}
cursor.finished();
}
@Test
public void testHamlet() throws Exception {
// Make sure that Hamlet is read & stored in the same order as a SortedSet.
final List<String> original = new ArrayList<String>();
final List<String> control = new ArrayList<String>();
final SortedMap<String, String> sortedControl = new TreeMap<String, String>();
final PatriciaTrie<String, String> trie = new PatriciaTrie<String, String>(new StringKeyAnalyzer());
final InputStream in = getClass().getResourceAsStream("hamlet.txt");
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String read = null;
while( (read = reader.readLine()) != null) {
final StringTokenizer st = new StringTokenizer(read);
while(st.hasMoreTokens()) {
final String token = st.nextToken();
original.add(token);
sortedControl.put(token, token);
trie.put(token, token);
}
}
control.addAll(sortedControl.values());
Assert.assertEquals(control.size(), sortedControl.size());
Assert.assertEquals(sortedControl.size(), trie.size());
Iterator<String> iter = trie.values().iterator();
for (final String aControl : control) {
Assert.assertEquals(aControl, iter.next());
}
final Random rnd = new Random();
int item = 0;
iter = trie.values().iterator();
int removed = 0;
for(; item < control.size(); item++) {
Assert.assertEquals(control.get(item), iter.next());
if(rnd.nextBoolean()) {
iter.remove();
removed++;
}
}
Assert.assertEquals(control.size(), item);
Assert.assertTrue(removed > 0);
Assert.assertEquals(control.size(), trie.size() + removed);
// reset hamlet
trie.clear();
for (final String anOriginal : original) {
trie.put(anOriginal, anOriginal);
}
assertEqualArrays(sortedControl.values().toArray(), trie.values().toArray());
assertEqualArrays(sortedControl.keySet().toArray(), trie.keySet().toArray());
assertEqualArrays(sortedControl.entrySet().toArray(), trie.entrySet().toArray());
Assert.assertEquals(sortedControl.firstKey(), trie.firstKey());
Assert.assertEquals(sortedControl.lastKey(), trie.lastKey());
SortedMap<String, String> sub = trie.headMap(control.get(523));
Assert.assertEquals(523, sub.size());
for(int i = 0; i < control.size(); i++) {
if(i < 523) {
Assert.assertTrue(sub.containsKey(control.get(i)));
} else {
Assert.assertFalse(sub.containsKey(control.get(i)));
}
}
// Too slow to check values on all, so just do a few.
Assert.assertTrue(sub.containsValue(control.get(522)));
Assert.assertFalse(sub.containsValue(control.get(523)));
Assert.assertFalse(sub.containsValue(control.get(524)));
try {
sub.headMap(control.get(524));
Assert.fail("should have thrown IAE");
} catch(final IllegalArgumentException expected) {}
Assert.assertEquals(sub.lastKey(), control.get(522));
Assert.assertEquals(sub.firstKey(), control.get(0));
sub = sub.tailMap(control.get(234));
Assert.assertEquals(289, sub.size());
Assert.assertEquals(control.get(234), sub.firstKey());
Assert.assertEquals(control.get(522), sub.lastKey());
for(int i = 0; i < control.size(); i++) {
if(i < 523 && i > 233) {
Assert.assertTrue(sub.containsKey(control.get(i)));
} else {
Assert.assertFalse(sub.containsKey(control.get(i)));
}
}
try {
sub.tailMap(control.get(232));
Assert.fail("should have thrown IAE");
} catch(final IllegalArgumentException expected) {}
sub = sub.subMap(control.get(300), control.get(400));
Assert.assertEquals(100, sub.size());
Assert.assertEquals(control.get(300), sub.firstKey());
Assert.assertEquals(control.get(399), sub.lastKey());
for(int i = 0; i < control.size(); i++) {
if(i < 400 && i > 299) {
Assert.assertTrue(sub.containsKey(control.get(i)));
} else {
Assert.assertFalse(sub.containsKey(control.get(i)));
}
}
}
@Test
public void testPrefixedBy() {
final PatriciaTrie<String, String> trie
= new PatriciaTrie<String, String>(new StringKeyAnalyzer());
final String[] keys = new String[]{
"",
"Albert", "Xavier", "XyZ", "Anna", "Alien", "Alberto",
"Alberts", "Allie", "Alliese", "Alabama", "Banane",
"Blabla", "Amber", "Ammun", "Akka", "Akko", "Albertoo",
"Amma"
};
for (final String key : keys) {
trie.put(key, key);
}
SortedMap<String, String> map;
Iterator<String> iterator;
Iterator<Map.Entry<String, String>> entryIterator;
Map.Entry<String, String> entry;
map = trie.getPrefixedBy("Al");
Assert.assertEquals(8, map.size());
Assert.assertEquals("Alabama", map.firstKey());
Assert.assertEquals("Alliese", map.lastKey());
Assert.assertEquals("Albertoo", map.get("Albertoo"));
Assert.assertNotNull(trie.get("Xavier"));
Assert.assertNull(map.get("Xavier"));
Assert.assertNull(trie.get("Alice"));
Assert.assertNull(map.get("Alice"));
iterator = map.values().iterator();
Assert.assertEquals("Alabama", iterator.next());
Assert.assertEquals("Albert", iterator.next());
Assert.assertEquals("Alberto", iterator.next());
Assert.assertEquals("Albertoo", iterator.next());
Assert.assertEquals("Alberts", iterator.next());
Assert.assertEquals("Alien", iterator.next());
Assert.assertEquals("Allie", iterator.next());
Assert.assertEquals("Alliese", iterator.next());
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("Albert");
iterator = map.keySet().iterator();
Assert.assertEquals("Albert", iterator.next());
Assert.assertEquals("Alberto", iterator.next());
Assert.assertEquals("Albertoo", iterator.next());
Assert.assertEquals("Alberts", iterator.next());
Assert.assertFalse(iterator.hasNext());
Assert.assertEquals(4, map.size());
Assert.assertEquals("Albert", map.firstKey());
Assert.assertEquals("Alberts", map.lastKey());
Assert.assertNull(trie.get("Albertz"));
map.put("Albertz", "Albertz");
Assert.assertEquals("Albertz", trie.get("Albertz"));
Assert.assertEquals(5, map.size());
Assert.assertEquals("Albertz", map.lastKey());
iterator = map.keySet().iterator();
Assert.assertEquals("Albert", iterator.next());
Assert.assertEquals("Alberto", iterator.next());
Assert.assertEquals("Albertoo", iterator.next());
Assert.assertEquals("Alberts", iterator.next());
Assert.assertEquals("Albertz", iterator.next());
Assert.assertFalse(iterator.hasNext());
Assert.assertEquals("Albertz", map.remove("Albertz"));
map = trie.getPrefixedBy("Alberto");
Assert.assertEquals(2, map.size());
Assert.assertEquals("Alberto", map.firstKey());
Assert.assertEquals("Albertoo", map.lastKey());
entryIterator = map.entrySet().iterator();
entry = entryIterator.next();
Assert.assertEquals("Alberto", entry.getKey());
Assert.assertEquals("Alberto", entry.getValue());
entry = entryIterator.next();
Assert.assertEquals("Albertoo", entry.getKey());
Assert.assertEquals("Albertoo", entry.getValue());
Assert.assertFalse(entryIterator.hasNext());
trie.put("Albertoad", "Albertoad");
Assert.assertEquals(3, map.size());
Assert.assertEquals("Alberto", map.firstKey());
Assert.assertEquals("Albertoo", map.lastKey());
entryIterator = map.entrySet().iterator();
entry = entryIterator.next();
Assert.assertEquals("Alberto", entry.getKey());
Assert.assertEquals("Alberto", entry.getValue());
entry = entryIterator.next();
Assert.assertEquals("Albertoad", entry.getKey());
Assert.assertEquals("Albertoad", entry.getValue());
entry = entryIterator.next();
Assert.assertEquals("Albertoo", entry.getKey());
Assert.assertEquals("Albertoo", entry.getValue());
Assert.assertFalse(entryIterator.hasNext());
Assert.assertEquals("Albertoo", trie.remove("Albertoo"));
Assert.assertEquals("Alberto", map.firstKey());
Assert.assertEquals("Albertoad", map.lastKey());
Assert.assertEquals(2, map.size());
entryIterator = map.entrySet().iterator();
entry = entryIterator.next();
Assert.assertEquals("Alberto", entry.getKey());
Assert.assertEquals("Alberto", entry.getValue());
entry = entryIterator.next();
Assert.assertEquals("Albertoad", entry.getKey());
Assert.assertEquals("Albertoad", entry.getValue());
Assert.assertFalse(entryIterator.hasNext());
Assert.assertEquals("Albertoad", trie.remove("Albertoad"));
trie.put("Albertoo", "Albertoo");
map = trie.getPrefixedBy("X");
Assert.assertEquals(2, map.size());
Assert.assertFalse(map.containsKey("Albert"));
Assert.assertTrue(map.containsKey("Xavier"));
Assert.assertFalse(map.containsKey("Xalan"));
iterator = map.values().iterator();
Assert.assertEquals("Xavier", iterator.next());
Assert.assertEquals("XyZ", iterator.next());
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("An");
Assert.assertEquals(1, map.size());
Assert.assertEquals("Anna", map.firstKey());
Assert.assertEquals("Anna", map.lastKey());
iterator = map.keySet().iterator();
Assert.assertEquals("Anna", iterator.next());
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("Ban");
Assert.assertEquals(1, map.size());
Assert.assertEquals("Banane", map.firstKey());
Assert.assertEquals("Banane", map.lastKey());
iterator = map.keySet().iterator();
Assert.assertEquals("Banane", iterator.next());
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("Am");
Assert.assertFalse(map.isEmpty());
Assert.assertEquals(3, map.size());
Assert.assertEquals("Amber", trie.remove("Amber"));
iterator = map.keySet().iterator();
Assert.assertEquals("Amma", iterator.next());
Assert.assertEquals("Ammun", iterator.next());
Assert.assertFalse(iterator.hasNext());
iterator = map.keySet().iterator();
map.put("Amber", "Amber");
Assert.assertEquals(3, map.size());
try {
iterator.next();
Assert.fail("CME expected");
} catch(final ConcurrentModificationException expected) {}
Assert.assertEquals("Amber", map.firstKey());
Assert.assertEquals("Ammun", map.lastKey());
map = trie.getPrefixedBy("Ak\0");
Assert.assertTrue(map.isEmpty());
map = trie.getPrefixedBy("Ak");
Assert.assertEquals(2, map.size());
Assert.assertEquals("Akka", map.firstKey());
Assert.assertEquals("Akko", map.lastKey());
map.put("Ak", "Ak");
Assert.assertEquals("Ak", map.firstKey());
Assert.assertEquals("Akko", map.lastKey());
Assert.assertEquals(3, map.size());
trie.put("Al", "Al");
Assert.assertEquals(3, map.size());
Assert.assertEquals("Ak", map.remove("Ak"));
Assert.assertEquals("Akka", map.firstKey());
Assert.assertEquals("Akko", map.lastKey());
Assert.assertEquals(2, map.size());
iterator = map.keySet().iterator();
Assert.assertEquals("Akka", iterator.next());
Assert.assertEquals("Akko", iterator.next());
Assert.assertFalse(iterator.hasNext());
Assert.assertEquals("Al", trie.remove("Al"));
map = trie.getPrefixedBy("Akka");
Assert.assertEquals(1, map.size());
Assert.assertEquals("Akka", map.firstKey());
Assert.assertEquals("Akka", map.lastKey());
iterator = map.keySet().iterator();
Assert.assertEquals("Akka", iterator.next());
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("Ab");
Assert.assertTrue(map.isEmpty());
Assert.assertEquals(0, map.size());
try {
final Object o = map.firstKey();
Assert.fail("got a first key: " + o);
} catch(final NoSuchElementException nsee) {}
try {
final Object o = map.lastKey();
Assert.fail("got a last key: " + o);
} catch(final NoSuchElementException nsee) {}
iterator = map.values().iterator();
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("Albertooo");
Assert.assertTrue(map.isEmpty());
Assert.assertEquals(0, map.size());
try {
final Object o = map.firstKey();
Assert.fail("got a first key: " + o);
} catch(final NoSuchElementException nsee) {}
try {
final Object o = map.lastKey();
Assert.fail("got a last key: " + o);
} catch(final NoSuchElementException nsee) {}
iterator = map.values().iterator();
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("");
Assert.assertSame(trie, map); // stricter than necessary, but a good check
map = trie.getPrefixedBy("\0");
Assert.assertTrue(map.isEmpty());
Assert.assertEquals(0, map.size());
try {
final Object o = map.firstKey();
Assert.fail("got a first key: " + o);
} catch(final NoSuchElementException nsee) {}
try {
final Object o = map.lastKey();
Assert.fail("got a last key: " + o);
} catch(final NoSuchElementException nsee) {}
iterator = map.values().iterator();
Assert.assertFalse(iterator.hasNext());
}
@Test
public void testPrefixByOffsetAndLength() {
final PatriciaTrie<String, String> trie
= new PatriciaTrie<String, String>(new StringKeyAnalyzer());
final String[] keys = new String[]{
"Albert", "Xavier", "XyZ", "Anna", "Alien", "Alberto",
"Alberts", "Allie", "Alliese", "Alabama", "Banane",
"Blabla", "Amber", "Ammun", "Akka", "Akko", "Albertoo",
"Amma"
};
for (final String key : keys) {
trie.put(key, key);
}
SortedMap<String, String> map;
Iterator<String> iterator;
map = trie.getPrefixedBy("Alice", 2);
Assert.assertEquals(8, map.size());
Assert.assertEquals("Alabama", map.firstKey());
Assert.assertEquals("Alliese", map.lastKey());
Assert.assertEquals("Albertoo", map.get("Albertoo"));
Assert.assertNotNull(trie.get("Xavier"));
Assert.assertNull(map.get("Xavier"));
Assert.assertNull(trie.get("Alice"));
Assert.assertNull(map.get("Alice"));
iterator = map.values().iterator();
Assert.assertEquals("Alabama", iterator.next());
Assert.assertEquals("Albert", iterator.next());
Assert.assertEquals("Alberto", iterator.next());
Assert.assertEquals("Albertoo", iterator.next());
Assert.assertEquals("Alberts", iterator.next());
Assert.assertEquals("Alien", iterator.next());
Assert.assertEquals("Allie", iterator.next());
Assert.assertEquals("Alliese", iterator.next());
Assert.assertFalse(iterator.hasNext());
map = trie.getPrefixedBy("BAlice", 1, 2);
Assert.assertEquals(8, map.size());
Assert.assertEquals("Alabama", map.firstKey());
Assert.assertEquals("Alliese", map.lastKey());
Assert.assertEquals("Albertoo", map.get("Albertoo"));
Assert.assertNotNull(trie.get("Xavier"));
Assert.assertNull(map.get("Xavier"));
Assert.assertNull(trie.get("Alice"));
Assert.assertNull(map.get("Alice"));
iterator = map.values().iterator();
Assert.assertEquals("Alabama", iterator.next());
Assert.assertEquals("Albert", iterator.next());
Assert.assertEquals("Alberto", iterator.next());
Assert.assertEquals("Albertoo", iterator.next());
Assert.assertEquals("Alberts", iterator.next());
Assert.assertEquals("Alien", iterator.next());
Assert.assertEquals("Allie", iterator.next());
Assert.assertEquals("Alliese", iterator.next());
Assert.assertFalse(iterator.hasNext());
}
@Test
public void testPrefixedByRemoval() {
final PatriciaTrie<String, String> trie
= new PatriciaTrie<String, String>(new StringKeyAnalyzer());
final String[] keys = new String[]{
"Albert", "Xavier", "XyZ", "Anna", "Alien", "Alberto",
"Alberts", "Allie", "Alliese", "Alabama", "Banane",
"Blabla", "Amber", "Ammun", "Akka", "Akko", "Albertoo",
"Amma"
};
for (final String key : keys) {
trie.put(key, key);
}
SortedMap<String, String> map = trie.getPrefixedBy("Al");
Assert.assertEquals(8, map.size());
Iterator<String> iter = map.keySet().iterator();
Assert.assertEquals("Alabama", iter.next());
Assert.assertEquals("Albert", iter.next());
Assert.assertEquals("Alberto", iter.next());
Assert.assertEquals("Albertoo", iter.next());
Assert.assertEquals("Alberts", iter.next());
Assert.assertEquals("Alien", iter.next());
iter.remove();
Assert.assertEquals(7, map.size());
Assert.assertEquals("Allie", iter.next());
Assert.assertEquals("Alliese", iter.next());
Assert.assertFalse(iter.hasNext());
map = trie.getPrefixedBy("Ak");
Assert.assertEquals(2, map.size());
iter = map.keySet().iterator();
Assert.assertEquals("Akka", iter.next());
iter.remove();
Assert.assertEquals(1, map.size());
Assert.assertEquals("Akko", iter.next());
if(iter.hasNext()) {
Assert.fail("shouldn't have next (but was: " + iter.next() + ")");
}
Assert.assertFalse(iter.hasNext());
}
@Test
public void testTraverseWithAllNullBitKey() {
final PatriciaTrie<String, String> trie
= new PatriciaTrie<String, String>(new StringKeyAnalyzer());
//
// One entry in the Trie
// Entry is stored at the root
//
// trie.put("", "All Bits Are Zero");
trie.put("\0", "All Bits Are Zero");
//
// / ("") <-- root
// \_/ \
// null
//
final List<String> strings = new ArrayList<String>();
trie.traverse(new Cursor<String, String>() {
public Decision select(final Entry<? extends String, ? extends String> entry) {
strings.add(entry.getValue());
return Decision.CONTINUE;
}
});
Assert.assertEquals(1, strings.size());
strings.clear();
for (final String s : trie.values()) {
strings.add(s);
}
Assert.assertEquals(1, strings.size());
}
@Test
public void testSelectWithAllNullBitKey() {
final PatriciaTrie<String, String> trie
= new PatriciaTrie<String, String>(new StringKeyAnalyzer());
// trie.put("", "All Bits Are Zero");
trie.put("\0", "All Bits Are Zero");
final List<String> strings = new ArrayList<String>();
trie.select("Hello", new Cursor<String, String>() {
public Decision select(final Entry<? extends String, ? extends String> entry) {
strings.add(entry.getValue());
return Decision.CONTINUE;
}
});
Assert.assertEquals(1, strings.size());
}
private static class TestCursor implements Cursor<Object, Object> {
private final List<Object> keys;
private final List<Object> values;
private Object selectFor;
private List<Object> toRemove;
private int index = 0;
TestCursor(final Object... objects) {
if(objects.length % 2 != 0) {
throw new IllegalArgumentException("must be * 2");
}
keys = new ArrayList<Object>(objects.length / 2);
values = new ArrayList<Object>(keys.size());
toRemove = Collections.emptyList();
for(int i = 0; i < objects.length; i++) {
keys.add(objects[i]);
values.add(objects[++i]);
}
}
@SuppressWarnings("unused")
void selectFor(final Object object) {
selectFor = object;
}
void addToRemove(final Object... objects) {
toRemove = new ArrayList<Object>(Arrays.asList(objects));
}
void remove(final Object... objects) {
for (final Object object : objects) {
final int idx = keys.indexOf(object);
keys.remove(idx);
values.remove(idx);
}
}
void starting() {
index = 0;
}
public void checkKey(final Object k) {
Assert.assertEquals(keys.get(index++), k);
}
public void checkValue(final Object o) {
Assert.assertEquals(values.get(index++), o);
}
public Decision select(final Entry<?, ?> entry) {
// System.out.println("Scanning: " + entry.getKey());
Assert.assertEquals(keys.get(index), entry.getKey());
Assert.assertEquals(values.get(index), entry.getValue());
index++;
if(toRemove.contains(entry.getKey())) {
// System.out.println("Removing: " + entry.getKey());
index--;
keys.remove(index);
values.remove(index);
toRemove.remove(entry.getKey());
return Decision.REMOVE;
}
if(selectFor != null && selectFor.equals(entry.getKey())) {
return Decision.EXIT;
} else {
return Decision.CONTINUE;
}
}
void finished() {
Assert.assertEquals(keys.size(), index);
}
}
private static void assertEqualArrays(final Object[] a, final Object[] b) {
Assert.assertTrue(Arrays.equals(a, b));
}
}