KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > WordMatch


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.editor;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import javax.swing.text.Position JavaDoc;
27 import javax.swing.text.BadLocationException JavaDoc;
28 import javax.swing.text.JTextComponent JavaDoc;
29
30 /** Word matching support enables to fill in the rest of the word
31 * when knowing the begining of the word. It is capable to search either
32 * only in current file or also in several or all open files.
33 *
34 * @author Miloslav Metelka
35 * @version 1.00
36 */

37
38 public class WordMatch extends FinderFactory.AbstractFinder
39     implements SettingsChangeListener, PropertyChangeListener JavaDoc {
40
41     private static final Object JavaDoc NULL_DOC = new Object JavaDoc();
42
43     /** Mapping of kit class to document with the static word */
44     private static final HashMap JavaDoc staticWordsDocs = new HashMap JavaDoc();
45
46     /** First part of matching word expressed as char[].
47     * Status of word matching support
48     * can be tested by looking if this variable is null. If it is,
49     * word matching was reset and it's not initialized yet.
50     */

51     char[] baseWord;
52
53     /** Found characters are accumulated here */
54     char[] word = new char[20];
55
56     /** Last word returned */
57     String JavaDoc lastWord;
58
59     /** Previous word returned */
60     String JavaDoc previousWord;
61
62     /** Current index in word */
63     int wordLen;
64
65     /** HashMap for already matched words */
66     StringMap wordsMap = new StringMap();
67
68     /** ArrayList holding already found words and their positions. */
69     ArrayList JavaDoc wordInfoList = new ArrayList JavaDoc();
70
71     /** Current index in word match vector. Reaching either first or last
72     * index of vector means searching backward or forward respectively from
73     * position stored in previous vector's element.
74     */

75     int wordsIndex;
76
77     /** Current search direction */
78     boolean forwardSearch;
79
80     /** Pointer to editorUI instance */
81     EditorUI editorUI;
82
83     /** Whether the search should be wrapped */
84     boolean wrapSearch;
85
86     /** Search with case matching */
87     boolean matchCase;
88
89     /** Search using smart case */
90     boolean smartCase;
91
92     /** This is the flag that really says whether the search is matching case
93     * or not. The value is (smartCase ? (is-there-capital-in-base-word?) : matchCase).
94     */

95     boolean realMatchCase;
96
97     /** Whether the match should be reported when word is found
98     * which is only one char long.
99     */

100     boolean matchOneChar;
101
102     /** Maximum lenght in chars of the search area.
103     * If the number is zero, no search is performed except the static words.
104     */

105     int maxSearchLen;
106
107     /** Document where to start from */
108     BaseDocument startDoc;
109
110     /** Construct new word match over given view manager */
111     public WordMatch(EditorUI editorUI) {
112         this.editorUI = editorUI;
113
114         Settings.addSettingsChangeListener(this);
115
116         synchronized (editorUI.getComponentLock()) {
117             // if component already installed in EditorUI simulate installation
118
JTextComponent JavaDoc component = editorUI.getComponent();
119             if (component != null) {
120                 propertyChange(new PropertyChangeEvent JavaDoc(editorUI,
121                                                        EditorUI.COMPONENT_PROPERTY, null, component));
122             }
123
124             editorUI.addPropertyChangeListener(this);
125         }
126     }
127
128     /** Called when settings were changed. The method is called
129     * by editorUI when settings were changed and from constructor.
130     */

131     public void settingsChange(SettingsChangeEvent evt) {
132         if (evt != null) { // real change event
133
staticWordsDocs.clear();
134         }
135
136         Class JavaDoc kitClass = Utilities.getKitClass(editorUI.getComponent());
137         if (kitClass != null) {
138             maxSearchLen = SettingsUtil.getInteger(kitClass, SettingsNames.WORD_MATCH_SEARCH_LEN,
139                                                    Integer.MAX_VALUE);
140             wrapSearch = SettingsUtil.getBoolean(kitClass, SettingsNames.WORD_MATCH_WRAP_SEARCH,
141                                                  true);
142             matchOneChar = SettingsUtil.getBoolean(kitClass, SettingsNames.WORD_MATCH_MATCH_ONE_CHAR,
143                                                    true);
144             matchCase = SettingsUtil.getBoolean(kitClass, SettingsNames.WORD_MATCH_MATCH_CASE,
145                                                 false);
146             smartCase = SettingsUtil.getBoolean(kitClass, SettingsNames.WORD_MATCH_SMART_CASE,
147                                                 false);
148         }
149     }
150
151     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
152         String JavaDoc propName = evt.getPropertyName();
153
154         if (EditorUI.COMPONENT_PROPERTY.equals(propName)) {
155             JTextComponent JavaDoc component = (JTextComponent JavaDoc)evt.getNewValue();
156             if (component != null) { // just installed
157

158                 settingsChange(null);
159
160             } else { // just deinstalled
161
// component = (JTextComponent)evt.getOldValue();
162

163             }
164
165         }
166     }
167
168     /** Clear word matching, so that it forgots the remembered
169     * matching words.
170     */

171     public synchronized void clear() {
172         if (baseWord != null) {
173             baseWord = null;
174             wordsMap.clear();
175             wordInfoList.clear();
176             wordsIndex = 0;
177         }
178     }
179
180     /** Reset this finder before each search */
181     public void reset() {
182         super.reset();
183         wordLen = 0;
184     }
185
186     /** Find next matching word and replace it on current cursor position
187     * @param forward in which direction should the search be done
188     */

189     public synchronized String JavaDoc getMatchWord(int startPos, boolean forward) {
190         int listSize = wordInfoList.size();
191         boolean searchNext = (listSize == 0)
192                              || (wordsIndex == (forward ? (listSize - 1) : 0));
193         startDoc = (BaseDocument)editorUI.getComponent().getDocument();
194         String JavaDoc ret = null;
195
196         // initialize base word if necessary
197
if (baseWord == null) {
198             try {
199                 String JavaDoc baseWordString = Utilities.getIdentifierBefore(startDoc, startPos);
200                 if (baseWordString == null) {
201                     baseWordString = ""; // NOI18N
202
}
203                 lastWord = baseWordString;
204                 baseWord = baseWordString.toCharArray();
205
206                 WordInfo info = new WordInfo(baseWordString,
207                                              startDoc.createPosition(startPos - baseWord.length), startDoc);
208                 wordsMap.put(info.word, info);
209                 wordInfoList.add(info);
210             } catch (BadLocationException JavaDoc e) {
211                 Utilities.annotateLoggable(e);
212             }
213             if (smartCase && !matchCase) {
214                 realMatchCase = false;
215                 for (int i = 0; i < baseWord.length; i++) {
216                     if (Character.isUpperCase(baseWord[i])) {
217                         realMatchCase = true;
218                     }
219                 }
220             } else {
221                 realMatchCase = matchCase;
222             }
223             // make lowercase if not matching case
224
if (!realMatchCase) {
225                 for (int i = 0; i < baseWord.length; i++) {
226                     baseWord[i] = Character.toLowerCase(baseWord[i]);
227                 }
228             }
229         }
230
231         // possibly search next word
232
if (searchNext) {
233             try {
234                 // determine start document and position
235
BaseDocument doc; // actual document
236
int pos; // actual position
237
if (listSize > 0) {
238                     WordInfo info = (WordInfo)wordInfoList.get(wordsIndex);
239                     doc = info.doc;
240                     pos = info.pos.getOffset();
241                     if (forward) {
242                         pos += info.word.length();
243                     }
244                 } else {
245                     doc = startDoc;
246                     pos = startPos;
247                 }
248
249                 // search for next occurence
250
while (doc != null) {
251                     if (doc.getLength() > 0) {
252                         int endPos;
253                         if (doc == startDoc) {
254                             if (forward) {
255                                 endPos = (pos >= startPos) ? -1 : startPos;
256                             } else { // bwd
257
endPos = (pos == -1 || pos > startPos) ? startPos : 0;
258                             }
259                         } else { // not starting doc
260
endPos = -1;
261                         }
262
263                         this.forwardSearch = !(!forward && (doc == startDoc));
264                         int foundPos = doc.find(this, pos, endPos);
265                         if (foundPos != -1) { // found
266
if (forward) {
267                                 wordsIndex++;
268                             }
269                             WordInfo info = new WordInfo(new String JavaDoc(word, 0, wordLen),
270                                                          doc.createPosition(foundPos), doc);
271                             wordsMap.put(info.word, info);
272                             wordInfoList.add(wordsIndex, info);
273                             previousWord = lastWord;
274                             lastWord = info.word;
275                             return lastWord;
276                         }
277                         if (doc == startDoc) {
278                             if (forward) {
279                                 pos = 0;
280                                 if (endPos != -1 || !wrapSearch) {
281                                     doc = getNextDoc(doc);
282                                 }
283                             } else { // bwd
284
if (pos == -1 || !wrapSearch) {
285                                     doc = getNextDoc(doc);
286                                     pos = 0;
287                                 } else {
288                                     pos = -1; // stay on the same document
289
}
290                             }
291                         } else { // not starting doc
292
doc = getNextDoc(doc);
293                             pos = 0;
294                         }
295                     } else { // empty document
296
doc = getNextDoc(doc);
297                         pos = 0; // should be anyway
298
}
299                 }
300                 // Return null in this case
301
} catch (BadLocationException JavaDoc e) {
302                 Utilities.annotateLoggable(e);
303             }
304         } else { // use word from the list
305
wordsIndex += (forward ? 1 : -1);
306             previousWord = lastWord;
307             lastWord = ((WordInfo)wordInfoList.get(wordsIndex)).word;
308             ret = lastWord;
309         }
310
311         startDoc = null;
312         return ret;
313     }
314
315     public String JavaDoc getPreviousWord() {
316         return previousWord;
317     }
318
319     private void doubleWordSize() {
320         char[] tmp = new char[word.length * 2];
321         System.arraycopy(word, 0, tmp, 0, word.length);
322         word = tmp;
323     }
324
325     private boolean checkWord() {
326         // check matching of one-char string
327
if (!matchOneChar && wordLen == 1) {
328             return false;
329         }
330
331         // check word start
332
if (baseWord.length > 0) {
333             if (wordLen < baseWord.length) {
334                 return false;
335             }
336             for (int i = 0; i < baseWord.length; i++) {
337                 if (realMatchCase) {
338                     if (word[i] != baseWord[i]) {
339                         return false;
340                     }
341                 } else { // case-insensitive
342
if (Character.toLowerCase(word[i]) != baseWord[i]) {
343                         return false;
344                     }
345                 }
346             }
347         }
348
349         // check existing words
350
if (wordsMap.containsKey(word, 0, wordLen)) {
351             return false;
352         }
353         return true; // new word found
354
}
355
356
357     public int find(int bufferStartPos, char buffer[],
358                     int offset1, int offset2, int reqPos, int limitPos) {
359         int offset = reqPos - bufferStartPos;
360         if (forwardSearch) {
361             int limitOffset = limitPos - bufferStartPos - 1;
362             while (offset < offset2) {
363                 char ch = buffer[offset];
364                 boolean wp = startDoc.isIdentifierPart(ch);
365                 if (wp) { // append the char
366
if (wordLen == word.length) {
367                         doubleWordSize();
368                     }
369                     word[wordLen++] = ch;
370                 }
371
372                 if (!wp) {
373                     if (wordLen > 0) {
374                         if (checkWord()) {
375                             found = true;
376                             return bufferStartPos + offset - wordLen;
377                             
378                         } else {
379                             wordLen = 0;
380                         }
381                     }
382
383                 } else { // current char is word part
384
if (limitOffset == offset) {
385                         if (checkWord()) {
386                             found = true;
387                             // differs in one char because current is part of word
388
return bufferStartPos + offset - wordLen + 1;
389
390                         } else {
391                             wordLen = 0;
392                         }
393                     }
394                 }
395
396                 offset++;
397             }
398         } else { // bwd search
399
int limitOffset = limitPos - bufferStartPos;
400             while (offset >= offset1) {
401                 char ch = buffer[offset];
402                 boolean wp = startDoc.isIdentifierPart(ch);
403                 if (wp) {
404                     if (wordLen == word.length) {
405                         doubleWordSize();
406                     }
407                     word[wordLen++] = ch;
408                 }
409                 if (!wp || (limitOffset == offset)) {
410                     if (wordLen > 0) {
411                         Analyzer.reverse(word, wordLen); // reverse word chars
412
if (checkWord()) {
413                             found = true;
414                             return (wp) ? bufferStartPos + offset + 1
415                                 : bufferStartPos + offset;
416                         } else {
417                             wordLen = 0;
418                         }
419                     }
420                 }
421                 offset--;
422             }
423         }
424         return bufferStartPos + offset;
425     }
426
427
428     private BaseDocument getNextDoc(BaseDocument doc) {
429         if (doc == getStaticWordsDoc()) {
430             return null;
431         }
432         BaseDocument nextDoc = Registry.getLessActiveDocument(doc);
433         if (nextDoc == null) {
434             nextDoc = getStaticWordsDoc();
435         }
436         return nextDoc;
437     }
438
439     private BaseDocument getStaticWordsDoc() {
440         Class JavaDoc kitClass = Utilities.getKitClass(editorUI.getComponent());
441         Object JavaDoc val = staticWordsDocs.get(kitClass);
442         if (val == NULL_DOC) {
443             return null;
444         }
445         BaseDocument doc = (BaseDocument)val;
446         if (doc == null) {
447             String JavaDoc staticWords = (String JavaDoc)Settings.getValue(kitClass,
448                                  SettingsNames.WORD_MATCH_STATIC_WORDS);
449             if (staticWords != null) {
450                 doc = new BaseDocument(BaseKit.class, false); // don't add to registry
451
try {
452                     doc.insertString(0, staticWords, null);
453                 } catch (BadLocationException JavaDoc e) {
454                     Utilities.annotateLoggable(e);
455                 }
456                 staticWordsDocs.put(kitClass, doc);
457             } else { // null static words
458
staticWordsDocs.put(kitClass, NULL_DOC);
459             }
460         }
461         return doc;
462     }
463
464     /** Word match info - used in previous/next word matching.
465     * It contains info found word and next matching position.
466     */

467     private static final class WordInfo {
468
469         public WordInfo(String JavaDoc word, Position JavaDoc pos, BaseDocument doc) {
470             this.word = word;
471             this.pos = pos;
472             this.doc = doc;
473         }
474
475         /** Found word */
476         String JavaDoc word;
477
478         /** Position of the word in document.
479         * Positions are used so that the marks are removed
480         * when they are no longer necessary.
481         */

482         Position JavaDoc pos;
483
484         /** Document where the word resides */
485         BaseDocument doc;
486
487         public boolean equals(Object JavaDoc o) {
488             if (this == o) {
489                 return true;
490             }
491             if (o instanceof WordMatch) {
492                 WordMatch wm = (WordMatch)o;
493                 return Analyzer.equals(word, wm.word, 0, wm.wordLen);
494             }
495             if (o instanceof WordInfo) {
496                 return word.equals(((WordInfo)o).word);
497             }
498             if (o instanceof String JavaDoc) {
499                 return word.equals(o);
500             }
501             return false;
502         }
503
504         public int hashCode() {
505             return word.hashCode();
506         }
507
508         public String JavaDoc toString() {
509             return "{word='" + word + "', pos=" + pos.getOffset() // NOI18N
510
+ ", doc=" + Registry.getID(doc) + "}"; // NOI18N
511
}
512
513     }
514
515     public String JavaDoc toString() {
516         return "baseWord=" + ((baseWord != null) ? ("'" + baseWord.toString() + "'") // NOI18N
517
: "null") + ", wrapSearch=" + wrapSearch // NOI18N
518
+ ", matchCase=" + matchCase + ", smartCase=" + smartCase // NOI18N
519
+ ", matchOneChar=" + matchOneChar + ", maxSearchLen=" + maxSearchLen // NOI18N
520
+ ", wordsMap=" + wordsMap + "\nwordInfoList=" + wordInfoList // NOI18N
521
+ "\nwordsIndex=" + wordsIndex; // NOI18N
522
}
523
524 }
525
Popular Tags