KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > FindReplaceMachine


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32 END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model;
35
36 import edu.rice.cs.drjava.model.definitions.reducedmodel.ReducedModelStates;
37   
38 import edu.rice.cs.util.UnexpectedException;
39 import edu.rice.cs.util.swing.DocumentIterator;
40 import edu.rice.cs.util.swing.Utilities;
41 import edu.rice.cs.util.text.AbstractDocumentInterface;
42 import edu.rice.cs.util.Lambda;
43 import edu.rice.cs.util.Log;
44 import edu.rice.cs.util.StringOps;
45
46 import java.awt.EventQueue JavaDoc;
47
48 import javax.swing.text.BadLocationException JavaDoc;
49 import javax.swing.text.Position JavaDoc;
50
51 /** Implementation of logic of find/replace over a document.
52  * @version $Id: FindReplaceMachine.java 4035 2006-11-17 15:19:13Z rcartwright $
53  */

54 public class FindReplaceMachine {
55   
56   // TODO: is _start still used in any way that matters?
57

58   static private Log _log = new Log("FindReplace.txt", false);
59   
60   /* Visible machine state; manipulated directly or indirectly by FindReplacePanel. */
61   private OpenDefinitionsDocument _doc; // Current search document
62
private OpenDefinitionsDocument _firstDoc; // First document where searching started (when searching all documents)
63
private Position JavaDoc _current; // Position of the cursor in _doc when machine is stopped
64
// private Position _start; // Position in _doc from which searching started or will start.
65
private String JavaDoc _findWord; // Word to find. */
66
private String JavaDoc _replaceWord; // Word to replace _findword.
67
private boolean _matchCase;
68   private boolean _matchWholeWord;
69   private boolean _searchAllDocuments; // Whether to search all documents (or just the current document)
70
private boolean _isForward; // Whether search direction is forward (false means backward)
71
private boolean _ignoreCommentsAndStrings; // Whether to ignore matches in comments and strings
72
private String JavaDoc _lastFindWord; // Last word found; set to null by FindReplacePanel if caret is updated
73
private boolean _skipText; // Whether to skip over the current match if direction is reversed
74
private DocumentIterator _docIterator; // An iterator of open documents; _doc is current
75
private SingleDisplayModel _model;
76
77   /** Standard Constructor.
78    * Creates new machine to perform find/replace operations on a particular document starting from a given position.
79    * @param docIterator an object that allows navigation through open Swing documents (it is DefaultGlobalModel)
80    * @exception BadLocationException
81    */

82   public FindReplaceMachine(SingleDisplayModel model, DocumentIterator docIterator) {
83     _skipText = false;
84 // _checkAllDocsWrapped = false;
85
// _allDocsWrapped = false;
86
_model = model;
87     _docIterator = docIterator;
88     setFindAnyOccurrence();
89     setFindWord("");
90     setReplaceWord("");
91     setSearchBackwards(false);
92     setMatchCase(true);
93     setSearchAllDocuments(false);
94     setIgnoreCommentsAndStrings(false);
95   }
96   
97   public void cleanUp() {
98     _docIterator = null;
99     setFindWord("");
100     _doc = null;
101   }
102   
103   /** Called when the current position is updated in the document implying _skipText should not be set
104    * if the user toggles _searchBackwards
105    */

106   public void positionChanged() {
107     _lastFindWord = null;
108     _skipText = false;
109   }
110
111   public void setLastFindWord() { _lastFindWord = _findWord; }
112
113   public boolean getSearchBackwards() { return ! _isForward; }
114
115   public void setSearchBackwards(boolean searchBackwards) {
116     if (_isForward == searchBackwards) {
117       // If we switch from searching forward to searching backwards or viceversa, isOnMatch is true, and _findword is the
118
// same as the _lastFindWord, we know the user just found _findWord, so skip over this match.
119
if (onMatch() && _findWord.equals(_lastFindWord)) _skipText = true;
120       else _skipText = false;
121     }
122     _isForward = ! searchBackwards;
123   }
124
125   public void setMatchCase(boolean matchCase) { _matchCase = matchCase; }
126   
127   public void setMatchWholeWord() { _matchWholeWord = true; }
128   
129   public void setFindAnyOccurrence() { _matchWholeWord = false; }
130
131   public void setSearchAllDocuments(boolean searchAllDocuments) { _searchAllDocuments = searchAllDocuments; }
132   
133   public void setIgnoreCommentsAndStrings(boolean ignoreCommentsAndStrings) {
134     _ignoreCommentsAndStrings = ignoreCommentsAndStrings;
135   }
136
137   public void setDocument(OpenDefinitionsDocument doc) { _doc = doc; }
138   
139   public void setFirstDoc(OpenDefinitionsDocument firstDoc) { _firstDoc = firstDoc; }
140  
141   public void setPosition(int pos) {
142 // System.err.println("Setting position " + pos + " in doc [" + _doc.getText() + "]");
143
// assert (pos >= 0) && (pos <= _doc.getLength());
144
try { _current = _doc.createPosition(pos); }
145     catch (BadLocationException JavaDoc ble) { throw new UnexpectedException(ble); }
146   }
147
148   /** Gets the character offset to which this machine is currently pointing. */
149   public int getCurrentOffset() { return _current.getOffset(); }
150
151   public String JavaDoc getFindWord() { return _findWord; }
152
153   public String JavaDoc getReplaceWord() { return _replaceWord; }
154
155   public boolean getSearchAllDocuments() { return _searchAllDocuments; }
156
157   public OpenDefinitionsDocument getDocument() { return _doc; }
158   
159   public OpenDefinitionsDocument getFirstDoc() { return _firstDoc; }
160
161   /** Change the word being sought.
162    * @param word the new word to seek
163    */

164   public void setFindWord(String JavaDoc word) {
165     _findWord = StringOps.replace(word, System.getProperty("line.separator"), "\n");
166   }
167
168   /** Change the replacing word.
169    * @param word the new replacing word
170    */

171   public void setReplaceWord(String JavaDoc word) {
172     _replaceWord = StringOps.replace(word, System.getProperty("line.separator"),"\n");
173   }
174
175   /** Determine if the machine is on an instance of the find word. Only executes in event thread except for
176     * initialization.
177     * @return true if the current position is right after an instance of the find word.
178     */

179   public boolean onMatch() {
180     
181 // assert EventQueue.isDispatchThread();
182

183     String JavaDoc findWord = _findWord;
184     int wordLen, off;
185     
186     if(_current == null) return false;
187     
188     wordLen = findWord.length();
189     if (_isForward) off = getCurrentOffset() - wordLen;
190     else off = getCurrentOffset();
191
192     if (off < 0) return false;
193     
194     String JavaDoc matchSpace;
195     try {
196       if (off + wordLen > _doc.getLength()) return false;
197       matchSpace = _doc.getText(off, wordLen);
198     }
199     catch (BadLocationException JavaDoc e) { throw new UnexpectedException(e); }
200
201     if (!_matchCase) {
202       matchSpace = matchSpace.toLowerCase();
203       findWord = findWord.toLowerCase();
204     }
205     return matchSpace.equals(findWord);
206   }
207   
208   /** If we're on a match for the find word, replace it with the replace word. Only executes in event thread. */
209   public boolean replaceCurrent() {
210     
211     assert EventQueue.isDispatchThread();
212
213     if (! onMatch()) return false;
214     _doc.acquireWriteLock();
215     try {
216 // boolean atStart = false;
217
int offset = getCurrentOffset();
218       if (_isForward) offset -= _findWord.length(); // position is now on left edge of match
219
// assert _findWord.equals(_doc.getText(offset, _findWord.length()));
220

221 // Utilities.show("ReplaceCurrent called. _doc = " + _doc.getText() + " offset = " + offset + " _findWord = " + _findWord);
222

223       _doc.remove(offset, _findWord.length());
224
225 // if (position == 0) atStart = true;
226
_doc.insertString(getCurrentOffset(), _replaceWord, null);
227       
228       // update _current Position
229
if (_isForward) setPosition(offset + _replaceWord.length());
230       else setPosition(offset);
231       
232       return true;
233     }
234     catch (BadLocationException JavaDoc e) { throw new UnexpectedException(e); }
235     finally { _doc.releaseWriteLock(); }
236   }
237
238   /** Replaces all occurences of the find word with the replace word in the current document of in all documents
239    * depending the value of the machine register _searchAllDocuments.
240    * @return the number of replacements
241    */

242   public int replaceAll() { return replaceAll(_searchAllDocuments); }
243   
244   /** Replaces all occurences of the find word with the replace word in the current document of in all documents
245    * depending the value of the flag searchAll.
246    * @return the number of replacements
247    */

248   private int replaceAll(boolean searchAll) {
249     if (searchAll) {
250       OpenDefinitionsDocument startDoc = _doc;
251       int count = 0; // the number of replacements done so farr
252
int n = _docIterator.getDocumentCount();
253       for (int i = 0; i < n; i++) {
254         // replace all in the rest of the documents
255
count += _replaceAllInCurrentDoc();
256         _doc = _docIterator.getNextDocument(_doc);
257       }
258       
259       // update display (adding "*") in navigatgorPane
260
_model.getDocumentNavigator().repaint();
261       
262       return count;
263     }
264     else return _replaceAllInCurrentDoc();
265   }
266   
267   /** Replaces all occurences of _findWord with _replaceWord in _doc. Never searches in other documents. Starts at
268    * the beginning or the end of the document (depending on find direction). This convention ensures that matches
269    * created by string replacement will not be replaced as in the following example:<p>
270    * findString: "hello"<br>
271    * replaceString: "e"<br>
272    * document text: "hhellollo"<p>
273    * Depending on the cursor position, clicking replace all could either make the document text read "hello"
274    * (which is correct) or "e". This is because of the behavior of findNext(), and it would be incorrect
275    * to change that behavior. Only executes in event thread.
276    * @return the number of replacements
277    */

278   private int _replaceAllInCurrentDoc() {
279
280     assert EventQueue.isDispatchThread();
281     
282     if (_isForward) setPosition(0);
283     else setPosition(_doc.getLength());
284     
285     int count = 0;
286     FindResult fr = findNext(false); // find next match in current doc
287
// Utilities.show(fr + " returned by call on findNext()");
288

289     while (! fr.getWrapped()) {
290       replaceCurrent(); // sets writeLock so that other threads do not see inconsistent state
291
count++;
292 // Utilities.show("Found " + count + " occurrences. Calling findNext() inside loop");
293
fr = findNext(false); // find next match in current doc
294
// Utilities.show("Call on findNext() returned " + fr.toString() + "in doc '" + _doc.getText() + "'");
295
}
296     return count;
297   }
298   
299   /** Processes all occurences of the find word with the replace word in the current document or in all documents
300    * depending the value of the machine register _searchAllDocuments.
301    * @param findAction action to perform on the occurrences; input is the FindResult, output is ignored
302    * @return the number of processed occurrences
303    */

304   public int processAll(Lambda<Void JavaDoc, FindResult> findAction) { return processAll(findAction, _searchAllDocuments); }
305   
306   /** Processes all occurences of the find word with the replace word in the current document of in all documents
307    * depending the value of the flag searchAll. Only executes in event thread.
308    * @param findAction action to perform on the occurrences; input is the FindResult, output is ignored
309    * @return the number of replacements
310    */

311   private int processAll(Lambda<Void JavaDoc, FindResult> findAction, boolean searchAll) {
312     
313     assert EventQueue.isDispatchThread();
314     
315     if (searchAll) {
316       OpenDefinitionsDocument startDoc = _doc;
317       int count = 0; // the number of replacements done so farr
318
int n = _docIterator.getDocumentCount();
319       for (int i = 0; i < n; i++) {
320         // process all in the rest of the documents
321
count += _processAllInCurrentDoc(findAction);
322         _doc = _docIterator.getNextDocument(_doc);
323       }
324       
325       // update display (perhaps adding "*") in navigatgorPane
326
_model.getDocumentNavigator().repaint();
327       
328       return count;
329     }
330     else return _processAllInCurrentDoc(findAction);
331   }
332   
333   /** Processes all occurences of _findWord in _doc. Never processes other documents. Starts at
334    * the beginning or the end of the document (depending on find direction). This convention ensures that matches
335    * created by string replacement will not be replaced as in the following example:<p>
336    * findString: "hello"<br>
337    * replaceString: "e"<br>
338    * document text: "hhellollo"<p>
339    * Only executes in event thread.
340    * @param findAction action to perform on the occurrences; input is the FindResult, output is ignored
341    * @return the number of replacements
342    */

343   private int _processAllInCurrentDoc(Lambda<Void JavaDoc, FindResult> findAction) {
344     
345       if (_isForward) setPosition(0);
346       else setPosition(_doc.getLength());
347       
348       int count = 0;
349       FindResult fr = findNext(false); // find next match in current doc
350

351       while (! fr.getWrapped()) {
352         findAction.apply(fr);
353         count++;
354         fr = findNext(false); // find next match in current doc
355
}
356       return count;
357   }
358   
359   public FindResult findNext() { return findNext(_searchAllDocuments); }
360
361   /** Finds the next occurrence of the find word and returns an offset at the end of that occurrence or -1 if the word
362    * was not found. Selectors should select backwards the length of the find word from the find offset. This
363    * position is stored in the current offset of the machine, and that is why it is after: in subsequent searches, the
364    * same instance won't be found twice. In a backward search, the position returned is at the beginning of the word.
365    * Also returns a flag indicating whether the end of the document was reached and wrapped around. This is done
366    * using the FindResult class which contains the matching document, an integer offset and two flag indicated whether
367    * the search wrapped (within _doc and across all documents). Only executes in the event thread.
368    * @param searchAll whether to search all documents (or just _doc)
369    * @return a FindResult object containing foundOffset and a flag indicating wrapping to the beginning during a search
370    */

371   private FindResult findNext(boolean searchAll) {
372     
373     assert EventQueue.isDispatchThread();
374     
375     // Find next match, if any, in _doc.
376
FindResult fr;
377     int start;
378     int len;
379       
380     // If the user just found a match and toggled the "Search Backwards" option, we should skip the matched text.
381
if (_skipText) { // adjust position (offset)
382
// System.err.println("Skip text is true! Last find word = " + _lastFindWord);
383
int wordLen = _lastFindWord.length();
384       if (_isForward) setPosition(getCurrentOffset() + wordLen);
385       else setPosition(getCurrentOffset() - wordLen);
386       positionChanged();
387     }
388     
389 // System.err.println("findNext(" + searchAll + ") called with _doc = [" + _doc.getText() + "] and offset = " + _current.getOffset());
390

391     int offset = getCurrentOffset();
392 // System.err.println("findNext(" + searchAll + ") called; initial offset is " + offset);
393
// System.err.println("_doc = [" + _doc.getText() + "], _doc.getLength() = " + _doc.getLength());
394
if (_isForward) {
395       start = offset;
396       len = _doc.getLength() - offset;
397     }
398     else {
399       start = 0;
400       len = offset;
401     }
402     fr = _findNextInDoc(_doc, start, len, searchAll);
403     if ((fr.getFoundOffset() >= 0) || ! searchAll) return fr; // match found in _doc or search is local
404

405     // find match in other docs
406
return _findNextInOtherDocs(_doc, start, len);
407   }
408
409  
410   /** Finds next match in specified doc only. If searching forward, len must be doc.getLength(). If searching backward,
411    * start must be 0. If searchAll, suppress executing in-document wrapped search, because it must be deferred. Assumes
412    * acquireReadLock is already held. Note than this method does a wrapped search if specified search fails.
413    */

414   private FindResult _findNextInDoc(OpenDefinitionsDocument doc, int start, int len, boolean searchAll) {
415     // search from current position to "end" of document ("end" is start if searching backward)
416
_log.log("_findNextInDoc([" + doc.getText() + "], " + start + ", " + len + ", " + searchAll + ")");
417     FindResult fr = _findNextInDocSegment(doc, start, len);
418     if (fr.getFoundOffset() >= 0 || searchAll) return fr;
419     
420     return _findWrapped(doc, start, len, false); // last arg is false because search has not wrapped through all docs
421
}
422   
423   /** Helper method for findNext that looks for a match after searching has wrapped off the "end" (start if searching
424    * backward) of the document. Assumes acquireReadLock is already held!
425    * INVARIANT (! _isForward => start = 0) && (_isForward => start + len = doc.getLength()).
426    * @param doc the document in which search wrapped
427    * @param start location of preceding text segment where search FAILED.
428    * @param len length of text segment previously searched
429    * @param allWrapped whether this wrapped search is being performed after an all document search has wrapped
430    * @return the offset where the instance was found. Returns -1 if no instance was found between start and end
431    */

432   private FindResult _findWrapped(OpenDefinitionsDocument doc, int start, int len, boolean allWrapped) {
433     
434     assert (_isForward && start + len == doc.getLength()) || (! _isForward && start == 0);
435     
436     _log.log("_findWrapped(" + doc + ", " + start + ", " + len + ", " + allWrapped + ") docLength = " +
437                        doc.getLength() + ", _isForward = " + _isForward);
438
439     if (doc.getLength() == 0) return new FindResult(doc, -1, true, allWrapped);
440     
441     final int newLen, newStart;
442     if (_isForward) {
443       newStart = 0;
444       newLen = start;
445     }
446     else {
447       newStart = len;
448       newLen = doc.getLength() - len;
449     }
450       _log.log("Calling _findNextInDocSegment(" + doc.getText() + ", newStart = " + newStart + ", newLen = " +
451                      newLen + ", allWrapped = " + allWrapped + ") and _isForward = " + _isForward);
452     return _findNextInDocSegment(doc, newStart, newLen, true, allWrapped);
453   }
454      
455   /** Find first valid match withing specified segment of doc. */
456   private FindResult _findNextInDocSegment(OpenDefinitionsDocument doc, int start, int len) {
457     return _findNextInDocSegment(doc, start, len, false, false);
458   }
459   
460   /** Main helper method for findNext... that searches for _findWord inside the specified document segment. Assumes
461    * acquireReadLock is already held!
462    * @param doc document to be searched
463    * @param start the location (offset) of the text segment to be searched
464    * @param len the length of the text segment to be searched
465    * @param whether this search should span all documents
466    * @param wrapped whether this search is after wrapping around the document
467    * @param allWrapped whether this seach is after wrapping around all documents
468    * @return a FindResult object with foundOffset and a flag indicating wrapping to the beginning during a search. The
469    * foundOffset returned insided the FindResult is -1 if no instance was found.
470    */

471   private FindResult _findNextInDocSegment(final OpenDefinitionsDocument doc, final int start, final int origLen,
472                                            final boolean wrapped, final boolean allWrapped) {
473 // Utilities.show("called _findNextInDocSegment(" + doc.getText() + ",\n" + start + ", " + len + ", " + wrapped + " ...)");
474

475     final int docLen = doc.getLength();; // The length of the segment to be searched
476

477     final int len = (origLen < 0) ? docLen - start : origLen; // set len for end of doc if origLen < 0
478

479     if (len == 0 || docLen == 0) return new FindResult(doc, -1, wrapped, allWrapped);
480    
481     String JavaDoc text; // The text segment to be searched
482
final String JavaDoc findWord; // copy of word being searched (so it can converted to lower case if necessary
483
final int wordLen = _findWord.length(); // length of search key (word being searched fo
484

485     try {
486
487 // if (wrapped && allWrapped) Utilities.show(start +", " + len + ", " + docLen + ", doc = '" + doc.getText() + "'");
488
doc.acquireReadLock();
489       try { text = doc.getText(start, len); }
490       finally { doc.releaseReadLock(); }
491       
492       if (! _matchCase) {
493         text = text.toLowerCase();
494         findWord = _findWord.toLowerCase(); // does not affect wordLen
495
}
496       else findWord = _findWord;
497 // if (wrapped && allWrapped) Utilities.show("Executing loop with findWord = " + findWord + "; text = " + text + "; len = " + len);
498

499       // loop to find first valid (not ignored) occurrence of findWord
500
// loop carried variables are rem, foundOffset;
501
// loop invariant variables are _doc, docLen, _isForward, findWord, wordLen, start, len.
502
// Invariant: on forwardsearch, foundOffset + rem == len; on backward search foundOffset == rem.
503
// loop exits by returning match (as FindResult) or by falling through with no match.
504
// if match is returned, _current has been updated to match location
505
int foundOffset = _isForward? 0 : len;
506       int rem = len;
507 // _log.log("Starting search loop; text = '" + text + "' findWord = '" + findWord + "' forward? = " + _isForward + " rem = " + rem + " foundOffset = " + foundOffset);
508
while (rem >= wordLen) {
509
510         // Find next match in text
511
foundOffset = _isForward ? text.indexOf(findWord, foundOffset) : text.lastIndexOf(findWord, foundOffset);
512 // _log.log("foundOffset = " + foundOffset);
513
if (foundOffset < 0) break; // no valid match in this document
514
int foundLocation = start + foundOffset;
515         int matchLocation;
516         
517         if (_isForward) {
518           foundOffset += wordLen; // skip over matched word
519
// text = text.substring(adjustedOffset, len); // len is length of text before update
520
rem = len - foundOffset; // len is updated to length of remaining text to search
521
matchLocation = foundLocation + wordLen; // matchLocation is index in _doc of right edge of match
522
// _current = docToSearch.createPosition(start); // put caret at beginning of found word
523
}
524         else {
525           foundOffset -= wordLen; // skip over matched word
526
rem = foundOffset; // rem is adjusted to match foundOffset
527
matchLocation = foundLocation; // matchLocation is index in _doc of left edge of match
528
// text = text.substring(0, len); // len is length of text after update
529
// _current = docToSearch.createPosition(foundLocation); // put caret at end of found word
530
}
531 // _log.log("rem = " + rem);
532
doc.setCurrentLocation(foundLocation); // _shouldIgnore below uses reduced model
533

534 // Utilities.show("Finished iteration with text = " + text + "; len = " + len);
535
if (_shouldIgnore(foundLocation, doc)) continue;
536         
537         _current = doc.createPosition(matchLocation); // formerly doc.createPosition(...)
538

539 // System.err.println("Returning result = " + new FindResult(doc, matchLocation, wrapped, allWrapped));
540

541         return new FindResult(doc, matchLocation, wrapped, allWrapped); // return valid match
542
}
543     }
544     catch (BadLocationException JavaDoc e) { throw new UnexpectedException(e); }
545     
546     // loop fell through; search failed in doc segment
547
return new FindResult(doc, -1, wrapped, allWrapped);
548   }
549   
550   /** Searches all documents following startDoc for _findWord, cycling through the documents in the direction specified
551    * by _isForward. If the search cycles back to doc without finding a match, performs a wrapped search on doc.
552    * @param startDoc document where searching started and just failed
553    * @param start location in startDoc of the document segment where search failed.
554    * @param len length of the text segment where search failed.
555    * @return the FindResult containing the information for where we found _findWord or a dummy FindResult.
556    */

557   private FindResult _findNextInOtherDocs(final OpenDefinitionsDocument startDoc, int start, int len) {
558     
559 // System.err.println("_findNextInOtherDocs(" + startDoc.getText() + ", " + start + ", " + len + ")");
560

561     boolean allWrapped = false;
562     _doc = _isForward ? _docIterator.getNextDocument(startDoc) : _docIterator.getPrevDocument(startDoc);
563
564     while (_doc != startDoc) {
565       if (_doc == _firstDoc) allWrapped = true;
566       
567 // System.err.println("_doc = [" + _doc.getText() + "]");
568

569 // if (_isForward) setPosition(0);
570
// else setPosition(_doc.getLength());
571

572
573       // find next match in _doc
574
_doc.acquireReadLock();
575       FindResult fr;
576       try { fr = _findNextInDocSegment(_doc, 0, _doc.getLength(), false, allWrapped); }
577       finally { _doc.releaseReadLock(); }
578       
579       if (fr.getFoundOffset() >= 0) return fr;
580       
581 // System.err.println("Advancing from '" + _doc.getText() + "' to next doc");
582
_doc = _isForward ? _docIterator.getNextDocument(_doc) : _docIterator.getPrevDocument(_doc);
583 // System.err.println("Next doc is: '" + _doc.getText() + "'");
584
}
585     
586     // No valid match found; perform wrapped search. _findWrapped assumes acquireReadLock is held.
587
startDoc.acquireReadLock();
588     try { return _findWrapped(startDoc, start, len, true); } // last arg is true because searching all docs has wrapped
589
finally { startDoc.releaseReadLock(); }
590   }
591   
592   /** Determines whether the whole find word is found at the input position
593     * @param doc - the document where an instance of the find word was found
594     * @param foundOffset - the position where that instance was found
595     * @return true if the whole word is found at foundOffset, false otherwise
596     */

597   private boolean wholeWordFoundAtCurrent(OpenDefinitionsDocument doc, int foundOffset) {
598
599     char leftOfMatch = 0; // forced initialization
600
char rightOfMatch = 0; // forced initialization
601
int leftLoc = foundOffset - 1;
602     int rightLoc = foundOffset + _findWord.length();
603     boolean leftOutOfBounds = false;
604     boolean rightOutOfBounds = false;
605
606     doc.acquireReadLock();
607     try {
608       try { leftOfMatch = doc.getText(leftLoc, 1).charAt(0); }
609       catch (BadLocationException JavaDoc e) { leftOutOfBounds = true; }
610       try { rightOfMatch = doc.getText(rightLoc, 1).charAt(0); }
611       catch (BadLocationException JavaDoc e) { rightOutOfBounds = true; }
612     }
613     finally {doc.releaseReadLock();}
614     
615     if (! leftOutOfBounds && ! rightOutOfBounds) return isDelimiter(rightOfMatch) && isDelimiter(leftOfMatch);
616     if (! leftOutOfBounds) return isDelimiter(leftOfMatch);
617     if (! rightOutOfBounds) return isDelimiter(rightOfMatch);
618     return true;
619   }
620
621   /** Determines whether a character is a delimiter (not a letter or digit) as a helper to wholeWordFoundAtCurrent
622     *
623     * @param ch - a character
624     * @return true if ch is a delimiter, false otherwise
625     */

626   private boolean isDelimiter(char ch) { return ! Character.isLetterOrDigit(ch); }
627   
628   /** Returns true if the currently found instance should be ignored (either because it is inside a string or comment or
629     * because it does not match the whole word when either or both of those conditions are set to true). Only executes
630     * in event thread.
631     * @param foundOffset the location of the instance found
632     * @param doc the current document where the instance was found
633     * @return true if the location should be ignored, false otherwise
634     */

635   private boolean _shouldIgnore(int foundOffset, OpenDefinitionsDocument odd) {
636     
637     assert EventQueue.isDispatchThread();
638     
639     return (_matchWholeWord && ! wholeWordFoundAtCurrent(odd, foundOffset)) ||
640       (_ignoreCommentsAndStrings && odd.getStateAtCurrent() != ReducedModelStates.FREE);
641   }
642 }
Popular Tags