View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      //--- Constant(s) ---
39      //--- Data field(s) ---
40      private CharNode root = null;
41  
42      //--- Constructor(s) ---
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              //  collectElements(root);
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     //--- Method(s) ---
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 	// TODO
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                 // the newly inserted object replaces the old one
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     //--- Inner class(es) ---
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 }