// A = type of element in keys.
// B = type of value in nodes.
// Can be case-sensitive or case-insensitive
// values can't be null
sclass ListTrie {
B value;
Comparator comparator;
TreeMap, ListTrie> children;
*() {}
*(Comparator *comparator) {}
void add(L string, B value) {
ifdef ListTrie_debug printFunctionCall("ListTrie add", string, value); endifdef
// case 1: empty string
if (empty(string)) ret with this.value = value;
// case 2: some prefix of new string exists
L existingPrefix = longestCommonPrefixOfNavigableSetAndList(string, navigableKeys(children), comparator);
ifdef ListTrie_debug print(+existingPrefix); endifdef
if (nempty(existingPrefix)) {
ListTrie child = children.get(existingPrefix);
L substring = subList(string, l(existingPrefix));
// case 2a: new string is complete suffix of existing child
ifdef ListTrie_debug print(+child); endifdef
if (child != null)
ret with child.add(substring, value);
// case 2b: make new branch
ListTrie branch = newChild(null);
Map.Entry, ListTrie> e = children.higherEntry(existingPrefix);
L key = e.getKey();
child = e.getValue();
children.remove(e.getKey());
children.put(existingPrefix, branch);
branch.makeChildren().put(subList(key, l(existingPrefix)), child);
branch.children.put(substring, newChild(value));
ret;
}
// case 3: no existing prefix, just add new child
makeChildren().put(string, newChild(value));
}
Map, B> asMap() {
Map, B> map = new TreeMap(listComparator(comparator));
new L buf;
collectMap(buf, map);
ret map;
}
private void collectMap(L buf, Map, B> map) {
if (value != null) map.put(cloneList(buf), value);
int n = l(buf);
for (L string, ListTrie child : children) {
buf.addAll(string);
child.collectMap(buf, map);
truncateList(buf, n);
}
}
toString {
new StringBuilder buf;
if (value != null) {
buf.append("\"\" => ").append(value).append("\n");
childrenToString(buf, 2);
} else
childrenToString(buf, 0);
ret str(buf);
}
void toString(StringBuilder buf, int indent) {
if (value != null)
buf.append(" => ").append(value);
buf.append("\n");
childrenToString(buf, indent+2);
}
void childrenToString(StringBuilder buf, int indent) {
for (L string, ListTrie child : children) {
buf.append(spaces(indent)).append(string);
child.toString(buf, indent);
}
}
private ListTrie newChild(B value) {
ListTrie child = new(comparator);
child.value = value;
ret child;
}
private TreeMap, ListTrie> makeChildren() {
if (children == null) children = new TreeMap(listComparator(comparator));
ret children;
}
Set> childStrings() { ret keys(children); }
ListTrie getChild(L string) { ret children.get(string); }
NavigableMap, ListTrie> children() { ret children; }
}