package org.limewire.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.SortedMap;
/**
* Provides a set-like interface designed specifically for <code>String</code>s.
* Uses a Trie as the backing map and provides an implementation specific to
* <code>String</code>s. Has the same retrieval/insertion times as the backing
* Trie. Stores the value as the string, for easier retrieval.
* The goal is to efficiently find Strings that can branch off a prefix.
* <p>
* Primarily designed as an {@link AutoCompleteDictionary}.
* <p>
* See <a href="http://en.wikipedia.org/wiki/Trie">Trie</a> for more information.
* <p>
* @modified David Soh (yunharla00@hotmail.com)
* <pre>
* 1. added getIterator() & getIterator(String) for enhanced AutoCompleteTextField use.
* 2. disallowed adding duplicates
*</pre>
*/
public class StringTrieSet implements AutoCompleteDictionary, Iterable<String> {
/**
* The backing map. A binary-sorted Trie.
*/
private final transient Trie<String, String> map;
public StringTrieSet(boolean ignoreCase) {
if(ignoreCase) {
map = new CaseIgnoredTrie<String>();
} else {
map = new PatriciaTrie<String, String>(new CharSequenceKeyAnalyzer());
}
}
/**
* Adds a value to the set. Different letter case of values is always
* kept and significant. If the TrieSet is made case-insensitive,
* it will not store two Strings with different case but will update
* the stored values with the case of the last entry.
*/
public void addEntry(String data) {
if (!contains(data)) //disallow adding duplicates
map.put(data, data);
}
/**
* Determines whether or not the Set contains this String.
*/
public boolean contains(String data) {
return map.get(data) != null;
}
/**
* Removes a value from the Set.
*
* @return <tt>true</tt> if a value was actually removed.
*/
public boolean removeEntry(String data) {
return map.remove(data) != null;
}
/**
* Return all the Strings that can be prefixed by this String.
* All values returned by the iterator have their case preserved.
*/
public Collection<String> getPrefixedBy(String data) {
return map.getPrefixedBy(data).values();
}
/**
* Return the last String in the set that can be prefixed by this String
* (Trie's are stored in alphabetical order).
* Return null if no such String exist in the current set.
*/
public String lookup(String data) {
Iterator<String> it = map.getPrefixedBy(data).values().iterator();
if (!it.hasNext())
return null;
return it.next();
}
/**
* Returns all values (entire TrieSet).
*/
public Iterator<String> iterator() {
return map.values().iterator();
}
/**
* Clears all items in the dictionary.
*/
public void clear() {
List<String> l = new ArrayList<String>(map.size());
for (String string : this) {
l.add(string);
}
for (String string : l) {
removeEntry(string);
}
}
private static class CaseIgnoredTrie<V> extends PatriciaTrie<String, V> {
public CaseIgnoredTrie() {
super(new CharSequenceKeyAnalyzer());
}
private String canonicalize(final String s) {
return s.toUpperCase(Locale.US).toLowerCase(Locale.US);
}
@Override
public boolean containsKey(Object k) {
return super.containsKey(canonicalize((String)k));
}
@Override
public V get(Object k) {
return super.get(canonicalize((String)k));
}
@Override
public SortedMap<String, V> getPrefixedBy(String key, int offset, int length) {
return super.getPrefixedBy(canonicalize(key), offset, length);
}
@Override
public SortedMap<String, V> getPrefixedBy(String key, int length) {
return super.getPrefixedBy(canonicalize(key), length);
}
@Override
public SortedMap<String, V> getPrefixedBy(String key) {
return super.getPrefixedBy(canonicalize(key));
}
@Override
public SortedMap<String, V> getPrefixedByBits(String key, int bitLength) {
return super.getPrefixedByBits(canonicalize(key), bitLength);
}
@Override
public SortedMap<String, V> headMap(String toKey) {
return super.headMap(canonicalize(toKey));
}
@Override
public V put(String key, V value) {
return super.put(canonicalize(key), value);
}
@Override
public V remove(Object k) {
return super.remove(canonicalize((String)k));
}
@Override
public Entry<String, V> select(String key, Cursor<? super String, ? super V> cursor) {
return super.select(canonicalize(key), cursor);
}
@Override
public V select(String key) {
return super.select(canonicalize(key));
}
@Override
public SortedMap<String, V> subMap(String fromKey, String toKey) {
return super.subMap(canonicalize(fromKey), canonicalize(toKey));
}
@Override
public SortedMap<String, V> tailMap(String fromKey) {
return super.tailMap(canonicalize(fromKey));
}
}
}