| /* |
| * 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)); |
| } |
| } |