1 package net
.kezvh
.collections
.text
;
3 import java
.util
.AbstractMap
;
4 import java
.util
.AbstractSet
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Arrays
;
7 import java
.util
.Collection
;
8 import java
.util
.Comparator
;
9 import java
.util
.Iterator
;
10 import java
.util
.LinkedList
;
12 import java
.util
.NoSuchElementException
;
14 import java
.util
.SortedMap
;
15 import java
.util
.SortedSet
;
17 import net
.kezvh
.collections
.TreeIterator
;
18 import net
.kezvh
.collections
.views
.IteratorView
;
19 import net
.kezvh
.development
.UnimplementedException
;
20 import net
.kezvh
.functional
.lambda
.L1
;
26 public class TrieMap
<T
> extends AbstractMap
<String
, T
> implements SortedMap
<String
, T
> {
27 private final TreeIterator
.ChildrenAccessor
<TrieNode
<T
>> childrenAccessor
= new TreeIterator
.ChildrenAccessor
<TrieNode
<T
>>() {
29 * @see net.kezvh.collections.TreeIterator.ChildrenAccessor#getChildren(java.lang.Object)
34 public Set
<TrieNode
<T
>> getChildren(final TrieNode
<T
> node
) {
35 return new AbstractSet
<TrieNode
<T
>>() {
38 public Iterator
<TrieNode
<T
>> iterator() {
39 return node
.suffices
.iterator();
44 return node
.suffices
.size();
51 private static final class TrieNode
<V
> {
52 private char[] keys
= new char[] {};
53 private final ArrayList
<TrieNode
<V
>> suffices
= new ArrayList
<TrieNode
<V
>>() {
55 * @see java.util.AbstractList#iterator()
59 public Iterator
<TrieNode
<V
>> iterator() {
60 final Iterator
<TrieNode
<V
>> iterator
= super.iterator();
61 return new Iterator
<TrieNode
<V
>>() {
63 * @see java.util.Iterator#hasNext()
67 public boolean hasNext() {
68 return iterator
.hasNext();
72 * @see java.util.Iterator#next()
76 public TrieNode
<V
> next() {
77 return iterator
.next();
81 * @see java.util.Iterator#remove()
84 public void remove() {
86 throw new UnimplementedException("need to invoke remove on the TriENode"); // FIXME
94 * @param index COMMENT
97 public char getSuffixKey(final int index
) {
98 return this.keys
[index
];
102 * @param index COMMENT
105 public TrieNode
<V
> getSuffixNode(final int index
) {
106 return this.suffices
.get(index
);
113 public TrieNode
<V
> find(final char key
) {
114 return this.getSuffixNode(this.keyIndex(key
));
121 public int keyIndex(final char key
) {
122 return Arrays
.binarySearch(this.keys
, key
);
127 * @param newValue COMMENT
128 * @param insertionPoint COMMENT
130 public void insert(final char key
, final TrieNode
<V
> newValue
, final int insertionPoint
) {
131 final char[] newKeys
= new char[this.keys
.length
+ 1];
132 System
.arraycopy(this.keys
, 0, newKeys
, 0, insertionPoint
);
133 newKeys
[insertionPoint
] = key
;
134 System
.arraycopy(this.keys
, insertionPoint
, newKeys
, insertionPoint
+ 1, this.keys
.length
- insertionPoint
);
136 this.suffices
.add(insertionPoint
, newValue
);
137 this.suffices
.trimToSize();
143 public int getNumKeys() {
144 return this.keys
.length
;
150 public int getNumEntries() {
152 if (this.value
!= null)
155 for (final TrieNode
<V
> t
: this.suffices
)
156 numEntries
+= t
.getNumEntries();
162 * @param newValue COMMENT
165 public TrieNode
<V
> put(final char key
, final TrieNode
<V
> newValue
) {
166 final int index
= this.keyIndex(key
);
168 return this.suffices
.set(index
, newValue
);
170 final int insertionPoint
= -(index
+ 1);
171 this.insert(key
, newValue
, insertionPoint
);
178 public boolean hasValue() {
179 return this.value
!= null;
185 public V
getValue() {
190 * @param value COMMENT
193 public V
setValue(final V value
) {
202 private final TrieNode
<T
> rootTrieNode
= new TrieNode
<T
>();
205 * @see java.util.AbstractMap#put(java.lang.Object, java.lang.Object)
207 * @param value COMMENT
211 public T
put(final String key
, final T value
) {
212 // final long[] longs = TrieMap.getLongs(key);
213 final char[] chars
= key
.toCharArray();
214 TrieNode
<T
> t
= this.rootTrieNode
;
216 for (final char element
: chars
) {
217 final int index
= t
.keyIndex(element
);
219 t
= t
.getSuffixNode(index
);
222 final int insertionPoint
= -(index
+ 1);
223 final TrieNode
<T
> tn
= new TrieNode
<T
>();
224 t
.insert(element
, tn
, insertionPoint
);
229 return t
.setValue(value
);
233 * @see java.util.AbstractMap#containsKey(java.lang.Object)
238 public boolean containsKey(final Object key
) {
239 if (key
== null || String
.class != key
.getClass())
242 final String string
= (String
) key
;
243 // final long[] longs = TrieMap.getLongs(string);
244 final char[] chars
= string
.toCharArray();
245 TrieNode
<T
> t
= this.rootTrieNode
;
246 for (final char element
: chars
) {
247 final int index
= t
.keyIndex(element
);
249 t
= t
.getSuffixNode(index
);
258 * @see java.util.AbstractMap#get(java.lang.Object)
263 public T
get(final Object key
) {
264 if (key
== null || String
.class != key
.getClass())
267 final String string
= (String
) key
;
268 // final long[] longs = TrieMap.getLongs(string);
269 final char[] chars
= string
.toCharArray();
270 TrieNode
<T
> t
= this.rootTrieNode
;
271 for (final char element
: chars
) {
272 final int index
= t
.keyIndex(element
);
274 t
= t
.getSuffixNode(index
);
283 * TODO: this MUST implement SortedSet
287 private final class EntrySet
extends AbstractSet
<Map
.Entry
<String
, T
>> implements SortedSet
<Map
.Entry
<String
, T
>> {
289 private final L1
<Map
.Entry
<String
, T
>, TrieNode
<T
>> map
= new L1
<Entry
<String
, T
>, TrieNode
<T
>>() {
292 * @param param COMMENT
296 public Entry
<String
, T
> op(final TrieNode
<T
> param
) {
297 return new Map
.Entry
<String
, T
>() {
299 * @see java.util.Map.Entry#getKey()
303 public String
getKey() {
305 throw new UnimplementedException();
307 // AAAAAAAAAAAAARRRRRRRRRRRRRGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHH
312 * @see java.util.Map.Entry#getValue()
316 public T
getValue() {
317 return param
.getValue();
321 * @see java.util.Map.Entry#setValue(java.lang.Object)
322 * @param value COMMENT
326 public T
setValue(final T value
) {
327 return param
.setValue(value
);
334 * @see java.util.AbstractCollection#iterator()
338 public Iterator
<java
.util
.Map
.Entry
<String
, T
>> iterator() {
339 return new IteratorView
<TrieNode
<T
>, Entry
<String
, T
>>(this.map
, new TreeIterator
<TrieNode
<T
>>(TrieMap
.this.rootTrieNode
, TrieMap
.this.childrenAccessor
));
343 * @see java.util.AbstractCollection#size()
348 return TrieMap
.this.size();
352 public Comparator
<?
super java
.util
.Map
.Entry
<String
, T
>> comparator() {
353 throw new UnimplementedException(); // FIXME
357 public java
.util
.Map
.Entry
<String
, T
> first() {
358 throw new UnimplementedException(); // FIXME
362 public SortedSet
<java
.util
.Map
.Entry
<String
, T
>> headSet(final java
.util
.Map
.Entry
<String
, T
> toElement
) {
363 throw new UnimplementedException(); // FIXME
367 public java
.util
.Map
.Entry
<String
, T
> last() {
368 throw new UnimplementedException(); // FIXME
372 public SortedSet
<java
.util
.Map
.Entry
<String
, T
>> subSet(final java
.util
.Map
.Entry
<String
, T
> fromElement
, final java
.util
.Map
.Entry
<String
, T
> toElement
) {
373 throw new UnimplementedException(); // FIXME
377 public SortedSet
<java
.util
.Map
.Entry
<String
, T
>> tailSet(final java
.util
.Map
.Entry
<String
, T
> fromElement
) {
378 throw new UnimplementedException(); // FIXME
383 private EntrySet entrySet
= null;
386 * @see java.util.AbstractMap#entrySet()
390 public Set
<java
.util
.Map
.Entry
<String
, T
>> entrySet() {
391 if (this.entrySet
== null)
392 this.entrySet
= new EntrySet();
393 return this.entrySet
;
397 * @see java.util.SortedMap#comparator()
400 public Comparator
<?
super String
> comparator() {
401 return new Comparator
<String
>() {
403 * @see java.util.Comparator#compare(java.lang.Object,
407 * @return o1.compareTo(o2)
410 public int compare(final String o1
, final String o2
) {
411 return o1
.compareTo(o2
);
417 * @see java.util.SortedMap#firstKey()
420 public String
firstKey() {
422 throw new NoSuchElementException();
428 * @see java.util.SortedMap#headMap(java.lang.Object)
429 * @param toKey COMMENT
432 public SortedMap
<String
, T
> headMap(final String toKey
) {
437 * @see java.util.SortedMap#lastKey()
440 public String
lastKey() {
442 throw new NoSuchElementException();
448 * @see java.util.SortedMap#subMap(java.lang.Object, java.lang.Object)
449 * @param fromKey COMMENT
450 * @param toKey COMMENT
453 public SortedMap
<String
, T
> subMap(final String fromKey
, final String toKey
) {
458 * @see java.util.SortedMap#tailMap(java.lang.Object)
459 * @param fromKey COMMENT
462 public SortedMap
<String
, T
> tailMap(final String fromKey
) {
466 private boolean hasSuffix(final TrieNode
<T
> trieNode
, final char[] chars
, final int firstCharIndex
) {
467 TrieNode
<T
> currentNode
= trieNode
;
468 for (int i
= firstCharIndex
; i
< chars
.length
; i
++) {
469 final int keyIndex
= currentNode
.keyIndex(chars
[i
]);
471 currentNode
= currentNode
.getSuffixNode(keyIndex
);
476 return currentNode
.hasValue();
479 private ArrayList
<TrieNode
<T
>> getTrieNodes(final char[] chars
) {
480 TrieNode
<T
> currentTrieNode
= this.rootTrieNode
;
481 final ArrayList
<TrieNode
<T
>> trieNodes
= new ArrayList
<TrieNode
<T
>>(chars
.length
+ 1);
483 trieNodes
.add(this.rootTrieNode
);
485 for (final char element
: chars
) {
486 final TrieNode
<T
> nextTrieNode
= currentTrieNode
.find(element
);
487 trieNodes
.add(nextTrieNode
);
488 currentTrieNode
= nextTrieNode
;
490 trieNodes
.trimToSize();
494 private Collection
<String
> getSingleChars() {
495 final Collection
<String
> similar
= new LinkedList
<String
>();
496 for (int i
= 0; i
< this.rootTrieNode
.getNumKeys(); i
++)
497 if (this.rootTrieNode
.getSuffixNode(i
).hasValue())
498 similar
.add(String
.valueOf(this.rootTrieNode
.getSuffixKey(i
)));
506 public Collection
<String
> getKeysSimilarTo(final String key
) {
507 // FIXME refactor this nasty motherfucker.
508 final int len
= key
.length();
510 return this.getSingleChars();
511 final Collection
<String
> similar
= new LinkedList
<String
>();
513 final StringBuffer add
= new StringBuffer(key
+ 'X'); // add
514 final StringBuffer rem
= new StringBuffer(key
.subSequence(0, len
- 1)); // remove
515 final StringBuffer del
= new StringBuffer(key
); // change
517 final char[] chars
= key
.toCharArray();
518 final ArrayList
<TrieNode
<T
>> trieNodes
= this.getTrieNodes(chars
); // len
526 { // first iteration, last character, only care about adds
527 final TrieNode
<T
> lastNode
= trieNodes
.get(len
);
528 for (int i
= 0; i
< lastNode
.getNumKeys(); i
++)
529 if (lastNode
.getSuffixNode(i
).hasValue()) {
530 add
.setCharAt(len
, lastNode
.getSuffixKey(i
));
531 similar
.add(add
.toString());
534 add
.setCharAt(len
, chars
[len
- 1]); // set last character of add
537 for (int k
= len
- 1; k
>= 0; k
--) {
538 final TrieNode
<T
> cur
= trieNodes
.get(k
);
539 if (this.hasSuffix(cur
, chars
, k
+ 1)) // removes
540 similar
.add(rem
.toString());
542 for (int i
= 0; i
< cur
.getNumKeys(); i
++) {
543 final char c
= cur
.getSuffixKey(i
);
548 final TrieNode
<T
> node
= cur
.getSuffixNode(i
);
550 if (this.hasSuffix(node
, chars
, k
+ 1)) { // changes
552 similar
.add(del
.toString());
555 if (this.hasSuffix(node
, chars
, k
)) { // adds
557 similar
.add(add
.toString());
561 del
.setCharAt(k
, chars
[k
]);
563 add
.setCharAt(k
, chars
[k
- 1]);
564 rem
.setCharAt(k
- 1, chars
[k
]);