/** * A character tree map. */ static class Trie { private final char start; private final Trie[] arr; public final T value; private Trie(char start, Trie[] arr, @Nullable T value) { this.start = start; this.arr = arr; this.value = value; } @Nullable Trie get(char ch) { int i = ch - start; return 0 <= i && i < arr.length ? arr[i] : null; } @Nullable Trie getIgnoreCase(char ch) { int i = lower(ch) - start; return 0 <= i && i < arr.length ? arr[i] : null; } /** * Returns the value corresponding to s[off:end]. */ @Nullable T get(String s, int off, int end) { Trie t = this; while (off < end) { t = t.get(s.charAt(off++)); if (t == null) { return null; } } return t.value; } @Nullable T get(char[] s, int off, int end) { Trie t = this; while (off < end) { t = t.get(s[off++]); if (t == null) { return null; } } return t.value; } /** * Returns the value corresponding to s[off:end] ignoring the case of ASCII * characters and matching only against lowercase keys. */ @Nullable T getIgnoreCase(String s, int off, int end) { Trie t = this; while (off < end) { t = t.getIgnoreCase(s.charAt(off++)); if (t == null) { return null; } } return t.value; } @Nullable T getIgnoreCase(char[] s, int off, int end) { Trie t = this; while (off < end) { t = t.getIgnoreCase(s[off++]); if (t == null) { return null; } } return t.value; } @Override public String toString() { StringBuilder sb = new StringBuilder(); toString(sb, 0); return sb.toString(); } private void toString(StringBuilder sb, int depth) { sb.append(value).append(" {"); ++depth; for (int i = 0; i < arr.length; ++i) { if (arr[i] == null) { continue; } sb.append('\n'); for (int j = depth; --j >= 0;) { sb.append(" "); } sb.append('\'').append((char) (i + start)).append("':"); arr[i].toString(sb, depth); } sb.append("}"); } static Builder builder() { return new Builder(null, (char) 0); } static final class Builder { A value; final char ch; final List> children = new ArrayList>(); Builder(@Nullable A value, char ch) { this.value = value; this.ch = ch; } Builder put(String s, @Nullable A newValue) { put(this, s, newValue); return this; } static void put(Builder b, String s, @Nullable A newValue) { for (int i = 0, n = s.length(); i < n; ++i) { char ch = s.charAt(i); Builder next = null; for (Builder child : b.children) { if (child.ch == ch) { next = child; break; } } if (next == null) { next = new Builder(null, ch); b.children.add(next); } b = next; } b.value = newValue; } @SuppressWarnings("unchecked") Trie build() { Trie[] arr; int n = children.size(); char min = 0; if (n == 0) { arr = (Trie[]) NO_TRIES; } else { Collections.sort(children, new Comparator>() { public int compare(Builder a, Builder b) { return a.ch - b.ch; } }); min = children.get(0).ch; int max = children.get(n-1).ch; arr = (Trie[]) new Trie[max - min + 1]; for (int i = 0; i < n; ++i) { Builder b = children.get(i); arr[b.ch - min] = b.build(); } } return new Trie(min, arr, value); } } private static final Trie[] NO_TRIES = new Trie[0]; }