1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.xnap.gui.component;
20
21 import java.util.Arrays;
22 import java.util.Stack;
23
24 import javax.swing.DefaultComboBoxModel;
25
26 import org.apache.log4j.Logger;
27
28
29 /***
30 * The DefaultComboBoxModel uses a ternary search tree for completion.
31 */
32 public class DefaultCompletionModel extends DefaultComboBoxModel
33 implements CompletionModel
34 {
35 private static Logger logger =
36 Logger.getLogger(DefaultCompletionModel.class);
37
38
39
40 private CharNode root = null;
41
42
43 /***
44 * Constructs a new model with no items.
45 */
46 public DefaultCompletionModel()
47 {
48 }
49
50 /***
51 * Constructs a new model using the given items for completion.
52 *
53 * @param items the objects used for completion
54 */
55 public DefaultCompletionModel(Object[] items)
56 {
57 this(items, false);
58 }
59
60 /***
61 * Constructs a new model using the given items for completion.
62 *
63 * @param items the array of objects used for completion
64 * @param sorted whether the array of items is sorted or not
65 */
66 public DefaultCompletionModel(Object[] items, boolean sorted)
67 {
68 if (!sorted) {
69 Arrays.sort(items);
70 }
71 insert(items, 0, items.length);
72 }
73
74 public boolean complete(String prefix)
75 {
76 removeAllElements();
77
78 if (prefix.length() == 0) {
79
80 }
81 else {
82 CharNode node = search(prefix);
83
84 if (node != null) {
85 if (node.obj != null) {
86 addElement(node.obj);
87 }
88 collectElements(node.eqkid);
89 }
90 }
91 return getSize() > 0;
92 }
93
94 public String completeUniquePrefix(String prefix)
95 {
96 CharNode node = search(prefix);
97
98 if (node != null && node.obj == null && node.eqkid != null) {
99 StringBuffer sb = new StringBuffer(prefix);
100 node = node.eqkid;
101
102 while ((node != null) && (node.lokid == null)
103 && (node.hikid == null)) {
104 sb.append(node.splitchar);
105 if (node.obj != null) {
106 break;
107 }
108 node = node.eqkid;
109 }
110 return sb.toString();
111 }
112 return prefix;
113 }
114
115
116
117 /***
118 * Adds an object to the completion tree.
119 *
120 * @param object the object which is completed using its {@link
121 * Object#toString()} method
122 */
123 public void insert(Object object)
124 {
125 char[] key = object.toString().toCharArray();
126 root = insert(root, key, 0, object);
127 }
128
129
130 public void remove(Object object)
131 {
132 CharNode node = root;
133 int index = 0;
134 Stack stack = new Stack();
135 String prefix = object.toString();
136
137 while ((node != null) && (index < prefix.length())) {
138 stack.push(node);
139 char c = prefix.charAt(index);
140
141 if (c < node.splitchar) {
142 node = node.lokid;
143 }
144 else if (c == node.splitchar) {
145 if (index == (prefix.length() - 1)) {
146 break;
147 }
148 else {
149 node = node.eqkid;
150 index++;
151 }
152 }
153 else {
154 node = node.hikid;
155 }
156 }
157
158 if (node != null && node.obj == object) {
159 node.obj = null;
160 stack.pop();
161 while (node.obj == null && node.eqkid == null && node.lokid == null
162 && node.hikid == null) {
163 CharNode parent = (CharNode)stack.pop();
164
165 }
166 }
167 }
168
169 /***
170 * Traverses the tree preorder wise and adds all elements to the model.
171 */
172 private void collectElements(CharNode node)
173 {
174 if (node != null) {
175 collectElements(node.lokid);
176
177 if (node.obj != null) {
178 addElement(node.obj);
179 }
180 collectElements(node.eqkid);
181 collectElements(node.hikid);
182 }
183 }
184
185 private void insert(Object[] items, int left, int right)
186 {
187 if (left < right) {
188 int m = left + (int)Math.floor((right - left) / 2);
189 insert(items[m]);
190 insert(items, left, m);
191 insert(items, m + 1, right);
192 }
193 }
194
195 private CharNode insert(CharNode node, char[] key, int index, Object object)
196 {
197 if (node == null) {
198 node = new CharNode(key[index]);
199
200 if (index == (key.length - 1)) {
201 node.obj = object;
202 }
203 }
204
205 if (key[index] < node.splitchar) {
206 node.lokid = insert(node.lokid, key, index, object);
207 }
208 else if (key[index] == node.splitchar) {
209 if (++index < key.length) {
210 node.eqkid = insert(node.eqkid, key, index, object);
211 }
212 else {
213
214 node.obj = object;
215 }
216 }
217 else {
218 node.hikid = insert(node.hikid, key, index, object);
219 }
220
221 return node;
222 }
223
224 /***
225 * Returns node.
226 */
227 private CharNode search(String prefix)
228 {
229 CharNode node = root;
230 int index = 0;
231
232 while ((node != null) && (index < prefix.length())) {
233 logger.debug("Visiting " + node.obj);
234
235 char c = prefix.charAt(index);
236 logger.debug("checking char " + c);
237
238 if (c < node.splitchar) {
239 node = node.lokid;
240 }
241 else if (c == node.splitchar) {
242 if (index == (prefix.length() - 1)) {
243 return node;
244 }
245 else {
246 node = node.eqkid;
247 index++;
248 }
249 }
250 else {
251 node = node.hikid;
252 }
253 }
254
255 return node;
256 }
257
258
259 /***
260 * Implementation of ternary search trees.
261 */
262 private class CharNode
263 {
264 public char splitchar;
265 public CharNode lokid;
266 public CharNode eqkid;
267 public CharNode hikid;
268 public Object obj;
269
270 public CharNode(char value)
271 {
272 splitchar = value;
273 }
274 }
275 }