2 * ********************************************************************************
\r
3 * Copyright (C) 2007-2009, International Business Machines Corporation and others.
\r
4 * All Rights Reserved.
\r
5 * ********************************************************************************
\r
7 package com.ibm.icu.impl;
\r
9 import java.util.ArrayList;
\r
10 import java.util.Iterator;
\r
11 import java.util.LinkedList;
\r
12 import java.util.List;
\r
14 import com.ibm.icu.lang.UCharacter;
\r
15 import com.ibm.icu.text.UTF16;
\r
18 * TextTrieMap is a trie implementation for supporting
\r
19 * fast prefix match for the key.
\r
21 public class TextTrieMap<V> {
\r
23 * Constructs a TextTrieMap object.
\r
25 * @param ignoreCase true to use case insensitive match
\r
27 public TextTrieMap(boolean ignoreCase) {
\r
28 this.ignoreCase = ignoreCase;
\r
32 * Adds the text key and its associated object in this object.
\r
34 * @param text The text.
\r
35 * @param o The object associated with the text.
\r
37 public synchronized void put(String text, V o) {
\r
38 CharacterNode node = root;
\r
39 for (int i = 0; i < text.length(); i++) {
\r
40 int ch = UTF16.charAt(text, i);
\r
41 node = node.addChildNode(ch);
\r
42 if (UTF16.getCharCount(ch) == 2) {
\r
50 * Gets an iterator of the objects associated with the
\r
51 * longest prefix matching string key.
\r
53 * @param text The text to be matched with prefixes.
\r
54 * @return An iterator of the objects associated with
\r
55 * the longest prefix matching matching key, or null
\r
56 * if no matching entry is found.
\r
58 public Iterator<V> get(String text) {
\r
59 return get(text, 0);
\r
63 * Gets an iterator of the objects associated with the
\r
64 * longest prefix matching string key starting at the
\r
65 * specified position.
\r
67 * @param text The text to be matched with prefixes.
\r
68 * @param start The start index of of the text
\r
69 * @return An iterator of the objects associated with the
\r
70 * longest prefix matching matching key, or null if no
\r
71 * matching entry is found.
\r
73 public Iterator<V> get(String text, int start) {
\r
74 LongestMatchHandler<V> handler = new LongestMatchHandler<V>();
\r
75 find(text, start, handler);
\r
76 return handler.getMatches();
\r
79 public void find(String text, ResultHandler<V> handler) {
\r
80 find(text, 0, handler);
\r
83 public void find(String text, int start, ResultHandler<V> handler) {
\r
84 find(root, text, start, start, handler);
\r
88 * Find an iterator of the objects associated with the
\r
89 * longest prefix matching string key under the specified node.
\r
91 * @param node The character node in this trie.
\r
92 * @param text The text to be matched with prefixes.
\r
93 * @param start The start index within the text.
\r
94 * @param index The current index within the text.
\r
95 * @param handler The result handler, ResultHandler#handlePrefixMatch
\r
96 * is called when any prefix match is found.
\r
98 private synchronized void find(CharacterNode node, String text,
\r
99 int start, int index, ResultHandler<V> handler) {
\r
100 Iterator<V> itr = node.iterator();
\r
102 if (!handler.handlePrefixMatch(index - start, itr)) {
\r
106 if (index < text.length()) {
\r
107 List<CharacterNode> childNodes = node.getChildNodes();
\r
108 if (childNodes == null) {
\r
111 int ch = UTF16.charAt(text, index);
\r
112 int chLen = UTF16.getCharCount(ch);
\r
113 for (int i = 0; i < childNodes.size(); i++) {
\r
114 CharacterNode child = childNodes.get(i);
\r
115 if (compare(ch, child.getCharacter())) {
\r
116 find(child, text, start, index + chLen, handler);
\r
124 * A private method used for comparing two characters.
\r
126 * @param ch1 The first character.
\r
127 * @param ch2 The second character.
\r
128 * @return true if the first character matches the second.
\r
130 private boolean compare(int ch1, int ch2) {
\r
134 else if (ignoreCase) {
\r
135 if (UCharacter.toLowerCase(ch1) == UCharacter.toLowerCase(ch2)) {
\r
138 else if (UCharacter.toUpperCase(ch1) == UCharacter.toUpperCase(ch2)) {
\r
145 // The root node of this trie
\r
146 private CharacterNode root = new CharacterNode(0);
\r
148 // Character matching option
\r
149 boolean ignoreCase;
\r
152 * Inner class representing a character node in the trie.
\r
154 private class CharacterNode {
\r
156 List<CharacterNode> children;
\r
160 * Constructs a node for the character.
\r
162 * @param ch The character associated with this node.
\r
164 public CharacterNode(int ch) {
\r
169 * Gets the character associated with this node.
\r
171 * @return The character
\r
173 public int getCharacter() {
\r
178 * Adds the object to the node.
\r
180 * @param obj The object set in the leaf node.
\r
182 public void addObject(V obj) {
\r
183 if (objlist == null) {
\r
184 objlist = new LinkedList<V>();
\r
190 * Gets an iterator of the objects associated with
\r
193 * @return The iterator or null if no objects are
\r
194 * associated with this node.
\r
196 public Iterator<V> iterator() {
\r
197 if (objlist == null) {
\r
200 return objlist.iterator();
\r
204 * Adds a child node for the character under this character
\r
205 * node in the trie. When the matching child node already
\r
206 * exists, the reference of the existing child node is
\r
209 * @param ch The character associated with a child node.
\r
210 * @return The child node.
\r
212 public CharacterNode addChildNode(int ch) {
\r
213 if (children == null) {
\r
214 children = new ArrayList<CharacterNode>();
\r
215 CharacterNode newNode = new CharacterNode(ch);
\r
216 children.add(newNode);
\r
219 CharacterNode node = null;
\r
220 for (int i = 0; i < children.size(); i++) {
\r
221 CharacterNode cur = children.get(i);
\r
222 if (compare(ch, cur.getCharacter())) {
\r
227 if (node == null) {
\r
228 node = new CharacterNode(ch);
\r
229 children.add(node);
\r
235 * Gets the list of child nodes under this node.
\r
237 * @return The list of child nodes.
\r
239 public List<CharacterNode> getChildNodes() {
\r
245 * Callback handler for processing prefix matches used by
\r
248 public interface ResultHandler<V> {
\r
250 * Handles a prefix key match
\r
252 * @param matchLength Matched key's length
\r
253 * @param values An iterator of the objects associated with the matched key
\r
254 * @return Return true to continue the search in the trie, false to quit.
\r
256 public boolean handlePrefixMatch(int matchLength, Iterator<V> values);
\r
259 private static class LongestMatchHandler<V> implements ResultHandler<V> {
\r
260 private Iterator<V> matches = null;
\r
261 private int length = 0;
\r
263 public boolean handlePrefixMatch(int matchLength, Iterator<V> values) {
\r
264 if (matchLength > length) {
\r
265 length = matchLength;
\r
271 public Iterator<V> getMatches() {
\r