KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > lib2 > search > DocumentFinder


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.modules.editor.lib2.search;
21
22 import java.util.Map JavaDoc;
23 import java.util.regex.Matcher JavaDoc;
24 import java.util.regex.Pattern JavaDoc;
25 import java.util.regex.PatternSyntaxException JavaDoc;
26 import javax.swing.text.BadLocationException JavaDoc;
27 import javax.swing.text.Document JavaDoc;
28 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
29 import org.netbeans.modules.editor.lib2.DocUtils;
30 import org.openide.DialogDisplayer;
31 import org.openide.NotifyDescriptor;
32 import org.openide.util.NbBundle;
33
34 /**
35  *
36  * @author Martin Roskanin
37  */

38 public class DocumentFinder
39 {
40     
41     private static FalseBlocksFinder falseBlocksFinder;
42     private static FalseFinder falseFinder;
43     private static WholeWordsBlocksFinder wholeWordsBlocksFinder;
44     private static RegExpBlocksFinder regExpBlocksFinder;
45     private static StringBlocksFinder stringBlocksFinder;
46     private static WholeWordsBwdFinder wholeWordsBwdFinder;
47     private static WholeWordsFwdFinder wholeWordsFwdFinder;
48     private static RegExpBwdFinder regExpBwdFinder;
49     private static RegExpFwdFinder regExpFwdFinder;
50     private static StringBwdFinder stringBwdFinder;
51     private static StringFwdFinder stringFwdFinder;
52     
53     /** Creates a new instance of DocumentFinder */
54     private DocumentFinder()
55     {
56     }
57
58
59     private static DocFinder getFinder(Document JavaDoc doc, Map JavaDoc searchProps, boolean oppositeDir, boolean blocksFinder){
60         String JavaDoc text = (String JavaDoc)searchProps.get(EditorFindSupport.FIND_WHAT);
61         if (text == null || text.length() == 0) {
62             if (blocksFinder) {
63                 if (falseBlocksFinder == null){
64                     falseBlocksFinder = new FalseBlocksFinder();
65                 }
66                 return falseBlocksFinder;
67             } else {
68                 if (falseFinder == null){
69                     falseFinder = new FalseFinder();
70                 }
71                 return falseFinder;
72             }
73         }
74
75         Boolean JavaDoc b = (Boolean JavaDoc)searchProps.get(EditorFindSupport.FIND_BACKWARD_SEARCH);
76         boolean bwdSearch = (b != null && b.booleanValue());
77         if (oppositeDir) { // negate for opposite direction search
78
bwdSearch = !bwdSearch;
79         }
80
81         b = (Boolean JavaDoc)searchProps.get(EditorFindSupport.FIND_MATCH_CASE);
82         boolean matchCase = (b != null && b.booleanValue());
83         b = (Boolean JavaDoc)searchProps.get(EditorFindSupport.FIND_SMART_CASE);
84         boolean smartCase = (b != null && b.booleanValue());
85         b = (Boolean JavaDoc)searchProps.get(EditorFindSupport.FIND_WHOLE_WORDS);
86         boolean wholeWords = (b != null && b.booleanValue());
87
88         if (smartCase && !matchCase) {
89             int cnt = text.length();
90             for (int i = 0; i < cnt; i++) {
91                 if (Character.isUpperCase(text.charAt(i))) {
92                     matchCase = true;
93                 }
94             }
95         }
96
97         b = (Boolean JavaDoc) searchProps.get(EditorFindSupport.FIND_REG_EXP);
98         boolean regExpSearch = (b!=null && b.booleanValue());
99         
100         Pattern JavaDoc pattern = null;
101         if (regExpSearch){
102             try{
103                 pattern = PatternCache.getPattern(text, matchCase);
104                 if (pattern == null){
105                     pattern = (matchCase) ? Pattern.compile(text, Pattern.MULTILINE) : Pattern.compile(text, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // NOI18N
106
PatternCache.putPattern(text, matchCase, pattern);
107                 }
108             }catch(PatternSyntaxException JavaDoc pse){
109                 if (!blocksFinder){
110                     NotifyDescriptor msg = new NotifyDescriptor.Message(
111                             pse.getDescription(), NotifyDescriptor.ERROR_MESSAGE);
112                     msg.setTitle(NbBundle.getBundle(DocumentFinder.class).getString("pattern-error-dialog-title")); //NOI18N
113
DialogDisplayer.getDefault().notify(msg);
114                 }
115                 PatternCache.putPattern(text, matchCase, null);
116                 return null;
117             }
118         }else{
119             PatternCache.clear();
120         }
121         
122         if (blocksFinder) {
123             if (wholeWords && !regExpSearch) {
124                 if (wholeWordsBlocksFinder == null){
125                     wholeWordsBlocksFinder = new WholeWordsBlocksFinder();
126                 }
127                 wholeWordsBlocksFinder.setParams(doc, text, matchCase);
128                 return wholeWordsBlocksFinder;
129             } else {
130                 if (regExpSearch){
131                     if (regExpBlocksFinder == null){
132                         regExpBlocksFinder = new RegExpBlocksFinder();
133                     }
134                     regExpBlocksFinder.setParams(pattern, matchCase);
135                     return regExpBlocksFinder;
136                 }else{
137                     if (stringBlocksFinder == null){
138                         stringBlocksFinder = new StringBlocksFinder();
139                     }
140                     stringBlocksFinder.setParams(text, matchCase);
141                     return stringBlocksFinder;
142                 }
143             }
144         } else {
145             if (wholeWords && !regExpSearch) {
146                 if (bwdSearch) {
147                     if (wholeWordsBwdFinder == null){
148                         wholeWordsBwdFinder = new WholeWordsBwdFinder();
149                     }
150                     wholeWordsBwdFinder.setParams(doc, text, matchCase);
151                     return wholeWordsBwdFinder;
152                 } else {
153                     if (wholeWordsFwdFinder == null){
154                         wholeWordsFwdFinder = new WholeWordsFwdFinder();
155                     }
156                     wholeWordsFwdFinder.setParams(doc, text, matchCase);
157                     return wholeWordsFwdFinder;
158                 }
159             } else {
160                 if (regExpSearch){
161                     if (bwdSearch) {
162                         if (regExpBwdFinder == null){
163                             regExpBwdFinder = new RegExpBwdFinder();
164                         }
165                         regExpBwdFinder.setParams(pattern, matchCase);
166                         return regExpBwdFinder;
167                     } else {
168                         if (regExpFwdFinder == null){
169                             regExpFwdFinder = new RegExpFwdFinder();
170                         }
171                         regExpFwdFinder.setParams(pattern, matchCase);
172                         return regExpFwdFinder;
173                     }
174                 }else{
175                     if (bwdSearch) {
176                         if (stringBwdFinder == null){
177                             stringBwdFinder = new StringBwdFinder();
178                         }
179                         stringBwdFinder.setParams(text, matchCase);
180                         return stringBwdFinder;
181                     } else {
182                         if (stringFwdFinder == null){
183                             stringFwdFinder = new StringFwdFinder();
184                         }
185                         stringFwdFinder.setParams(text, matchCase);
186                         return stringFwdFinder;
187                     }
188                 }
189             }
190         }
191     }
192     
193
194     private static FindReplaceResult findReplaceImpl(String JavaDoc replaceText, Document JavaDoc doc, int startOffset, int endOffset, Map JavaDoc props,
195                              boolean oppositeDir) throws BadLocationException JavaDoc{
196         int ret[] = new int[2];
197         if (endOffset == -1){
198             endOffset = doc.getLength();
199         }
200         if (startOffset>endOffset){
201             int temp = startOffset;
202             startOffset = endOffset;
203             endOffset = temp;
204         }
205         DocFinder finder = getFinder(doc, props, oppositeDir, false);
206         if (finder == null){
207             return null;
208         }
209         finder.reset();
210         CharSequence JavaDoc cs = DocumentUtilities.getText(doc, startOffset, endOffset - startOffset);
211         if (cs==null) return null;
212         int findRet = finder.find(startOffset, cs);
213         if (!finder.isFound()){
214             ret[0] = -1;
215             return new FindReplaceResult(ret, replaceText);
216         }
217         ret[0] = startOffset + findRet;
218         
219         if (finder instanceof StringFinder){
220             int length = ((StringFinder)finder).getFoundLength();
221             ret[1] = ret [0] + length;
222         }
223         
224         if (finder instanceof RegExpFinder){
225             Matcher JavaDoc matcher = ((RegExpFinder)finder).getMatcher();
226             if (matcher != null && replaceText != null){
227                 CharSequence JavaDoc foundString = cs.subSequence(ret[0]-startOffset, ret[1]-startOffset);
228                 matcher.reset(foundString);
229                 if (matcher.find()){
230                     try{
231                         replaceText = matcher.replaceFirst(replaceText);
232                     }catch(IndexOutOfBoundsException JavaDoc ioobe){
233                         NotifyDescriptor msg = new NotifyDescriptor.Message(
234                                 ioobe.getLocalizedMessage(), NotifyDescriptor.ERROR_MESSAGE);
235                         msg.setTitle(NbBundle.getBundle(DocumentFinder.class).getString("pattern-error-dialog-title")); //NOI18N
236
DialogDisplayer.getDefault().notify(msg);
237                         return null;
238                     }
239                 }
240             }
241         }
242         return new FindReplaceResult(ret, replaceText);
243     }
244     
245     /**
246      * Finds in document
247      *
248      * @param doc document where to find
249      * @param startOffset offset in the document where the search will start
250      * @param endOffset offset where the search will end with reporting
251      * that nothing was found.
252      * @param props find properties
253      */

254     public static int[] find(Document JavaDoc doc, int startOffset, int endOffset, Map JavaDoc props,
255                              boolean oppositeDir) throws BadLocationException JavaDoc{
256         FindReplaceResult result = findReplaceImpl(null, doc, startOffset, endOffset, props, oppositeDir);
257         if (result == null){
258             return null;
259         }
260
261         return result.getFoundPositions();
262     }
263
264     public static int[] findBlocks(Document JavaDoc doc, int startOffset, int endOffset,
265                     Map JavaDoc props, int blocks[]) throws BadLocationException JavaDoc{
266         BlocksFinder finder =(BlocksFinder) getFinder(doc, props, false, true);
267         if (finder == null){
268             return blocks;
269         }
270         finder.reset();
271         finder.setBlocks(blocks);
272         CharSequence JavaDoc cs = DocumentUtilities.getText(doc, startOffset, endOffset - startOffset);
273         if (cs==null){
274             return null;
275         }
276         finder.find(startOffset, cs);
277         int ret [] = finder.getBlocks();
278         return ret;
279     }
280     
281     /**
282      * Finds the searching string and substitute replace expression in case of
283      * regexp backreferences.
284      * @return FindReplaceResult, that contains positions of found string and substituted replace expression
285      */

286     public static FindReplaceResult findReplaceResult(String JavaDoc replaceString, Document JavaDoc doc, int startOffset, int endOffset, Map JavaDoc props,
287                              boolean oppositeDir) throws BadLocationException JavaDoc{
288         return findReplaceImpl(replaceString, doc, startOffset, endOffset, props, oppositeDir);
289     }
290     
291     private interface DocFinder{
292         
293         public int find(int initOffset, CharSequence JavaDoc data);
294         
295         public boolean isFound();
296         
297         public void reset();
298     }
299
300     
301     private static final class FalseBlocksFinder extends AbstractBlocksFinder {
302
303         public int find(int initOffset, CharSequence JavaDoc data) {
304             return -1;
305         }
306         
307     }
308     
309     /** Request non-existent position immediately */
310     private static class FalseFinder extends AbstractFinder
311         implements StringFinder {
312
313         public int find(int initOffset, CharSequence JavaDoc data) {
314             return -1;
315         }
316
317         public int getFoundLength() {
318             return 0;
319         }
320
321     }
322     
323
324     
325     private static abstract class AbstractBlocksFinder extends AbstractFinder
326         implements BlocksFinder {
327
328         private static int[] EMPTY_INT_ARRAY = new int[0];
329
330         private int[] blocks = EMPTY_INT_ARRAY;
331
332         private int blocksInd;
333
334         private boolean closed;
335
336         public void reset() {
337             blocksInd = 0;
338             closed = false;
339         }
340
341         public int[] getBlocks() {
342             if (!closed) { // not closed yet
343
closeBlocks();
344                 closed = true;
345             }
346             return blocks;
347         }
348
349         public void setBlocks(int[] blocks) {
350             this.blocks = blocks;
351             closed = false;
352         }
353
354         protected void addBlock(int blkStartPos, int blkEndPos) {
355             if (blocksInd == blocks.length) {
356                 int[] dbl = new int[blocks.length * 2];
357                 System.arraycopy(blocks, 0, dbl, 0, blocks.length);
358                 blocks = dbl;
359             }
360             blocks[blocksInd++] = blkStartPos;
361             blocks[blocksInd++] = blkEndPos;
362         }
363
364         /** Insert closing sequence [-1, -1] */
365         protected void closeBlocks() {
366             addBlock(-1, -1);
367         }
368
369         public String JavaDoc debugBlocks() {
370             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
371             int ind = 0;
372             while (blocks[ind] != -1) {
373                 buf.append((ind/2 + 1) + ": [" + blocks[ind] + ", " + blocks[ind + 1] + "]\n"); // NOI18N
374
ind+= 2;
375             }
376             return buf.toString();
377         }
378
379     }
380     
381     /** Finder that constructs [begin-pos, end-pos] blocks.
382     * This is useful for highlight-search draw layer.
383     * The block-finders are always forward-search finders.
384     */

385     private interface BlocksFinder extends DocFinder {
386
387         /** Set the array into which the finder puts
388         * the position blocks. If the length of array is not sufficient
389         * the finder extends the array. The last block is set to [-1, -1].
390         */

391         public void setBlocks(int[] blocks);
392
393         /** Get the array filled with position blocks. It is either
394         * original array passed to setBlocks() or the new array
395         * if the finder extended the array.
396         */

397         public int[] getBlocks();
398
399     }
400
401
402     /** Abstract finder implementation. The only <CODE>find()</CODE>
403     * method must be redefined.
404     */

405     private static abstract class AbstractFinder implements DocFinder {
406
407         /** Was the string found? */
408         protected boolean found;
409
410         /** Was the string found? */
411         public final boolean isFound() {
412             return found;
413         }
414
415         /** Reset the finder */
416         public void reset() {
417             found = false;
418         }
419
420     }
421
422     
423     /** Finder that looks for some search expression expressed by string.
424     * It can be either simple string
425     * or some form of regular expression expressed by string.
426     */

427     private interface StringFinder extends DocFinder {
428
429         /** Get the length of the found string. This is useful
430         * for regular expressions, because the length of the regular
431         * expression can be different than the length of the string
432         * that matched the expression.
433         */

434         public int getFoundLength();
435
436     }
437
438     
439     /** String forward finder that finds whole words only
440     * and that creates position blocks.
441     * There are some speed optimizations attempted.
442     */

443     private static final class WholeWordsBlocksFinder extends AbstractBlocksFinder {
444
445         char chars[];
446
447         int stringInd;
448
449         boolean matchCase;
450
451         boolean insideWord;
452
453         boolean firstCharWordPart;
454
455         boolean wordFound;
456
457         Document JavaDoc doc;
458
459         public WholeWordsBlocksFinder() {
460         }
461
462         public void setParams(Document JavaDoc doc, String JavaDoc s, boolean matchCase){
463             this.matchCase = matchCase;
464             this.doc = doc;
465             chars = (matchCase ? s : s.toLowerCase()).toCharArray();
466             firstCharWordPart = DocUtils.isIdentifierPart(doc, chars[0]);
467         }
468         
469         public void reset() {
470             super.reset();
471             insideWord = false;
472             wordFound = false;
473             stringInd = 0;
474         }
475
476         public int find(int initOffset, CharSequence JavaDoc data) {
477             int offset = 0;
478             int limitPos = data.length();
479             int limitOffset = limitPos - 1;
480             while (offset >= 0 && offset < limitPos) {
481                 char ch = data.charAt(offset);
482
483                 if (!matchCase) {
484                     ch = Character.toLowerCase(ch);
485                 }
486
487                 // whole word already found but must verify next char
488
if (wordFound) {
489                     if (DocUtils.isIdentifierPart(doc, ch)) { // word continues
490
insideWord = firstCharWordPart;
491                         offset -= chars.length - 1;
492                     } else {
493                         int blkEnd = initOffset + offset;
494                         addBlock(blkEnd - chars.length, blkEnd);
495                         insideWord = false;
496                         offset++;
497                     }
498                     wordFound = false;
499                     stringInd = 0;
500                     continue;
501                 }
502
503                 if (stringInd == 0) { // special case for first char
504
if (ch != chars[0] || insideWord) { // first char doesn't match
505
insideWord = DocUtils.isIdentifierPart(doc, ch);
506                         offset++;
507                     } else { // first char matches
508
stringInd = 1; // matched and not inside word
509
if (chars.length == 1) {
510                             if (offset == limitOffset) {
511                                 int blkStart = initOffset + offset;
512                                 addBlock(blkStart, blkStart + 1);
513                             } else {
514                                 wordFound = true;
515                             }
516                         }
517                         offset++;
518                     }
519                 } else { // already matched at least one char
520
if (ch == chars[stringInd]) { // matches current char
521
stringInd++;
522                         if (stringInd == chars.length) { // found whole string
523
if (offset == limitOffset) {
524                                 int blkEnd = initOffset + 1;
525                                 addBlock(blkEnd - stringInd, blkEnd);
526                             } else {
527                                 wordFound = true;
528                             }
529                         }
530                         offset++;
531                     } else { // current char doesn't match, stringInd > 0
532
offset += 1 - stringInd;
533                         stringInd = 0;
534                         insideWord = firstCharWordPart;
535                     }
536                 }
537
538             }
539             return offset;
540         }
541
542     }
543
544     
545     /** String forward finder that creates position blocks */
546     private static final class StringBlocksFinder
547         extends AbstractBlocksFinder {
548
549         char chars[];
550
551         int stringInd;
552
553         boolean matchCase;
554
555         public StringBlocksFinder() {
556         }
557
558         public void setParams(String JavaDoc s, boolean matchCase){
559             this.matchCase = matchCase;
560             chars = (matchCase ? s : s.toLowerCase()).toCharArray();
561         }
562         
563         public void reset() {
564             super.reset();
565             stringInd = 0;
566         }
567
568         public int find(int initOffset, CharSequence JavaDoc data) {
569             int offset = 0;
570             int endPos = data.length();
571             while (offset >= 0 && offset < endPos) {
572                 char ch = data.charAt(offset);
573
574                 if (!matchCase) {
575                     ch = Character.toLowerCase(ch);
576                 }
577                 if (ch == chars[stringInd]) {
578                     
579                     stringInd++;
580                     if (stringInd == chars.length) {
581                         int blkEnd = initOffset + offset + 1;
582                         addBlock(blkEnd - stringInd, blkEnd);
583                         stringInd = 0;
584                     }
585                     offset++;
586                 } else {
587                     offset += 1 - stringInd;
588                     stringInd = 0;
589                 }
590
591             }
592             return offset;
593         }
594
595     }
596    
597     
598     private static final class WholeWordsBwdFinder extends GenericBwdFinder
599         implements StringFinder {
600
601         char chars[];
602
603         int stringInd;
604
605         boolean matchCase;
606
607         boolean insideWord;
608
609         boolean lastCharWordPart;
610
611         boolean wordFound;
612
613         int endInd;
614
615         Document JavaDoc doc;
616
617         public WholeWordsBwdFinder() {
618         }
619         
620         public void setParams(Document JavaDoc doc, String JavaDoc s, boolean matchCase){
621             this.doc = doc;
622             this.matchCase = matchCase;
623             chars = (matchCase ? s : s.toLowerCase()).toCharArray();
624             endInd = chars.length - 1;
625             DocUtils.isIdentifierPart(doc, chars[endInd]);
626         }
627
628         public int getFoundLength() {
629             return chars.length;
630         }
631
632         public void reset() {
633             super.reset();
634             insideWord = false;
635             wordFound = false;
636             stringInd = endInd;
637         }
638
639         protected int scan(char ch, boolean lastChar) {
640             if (!matchCase) {
641                 ch = Character.toLowerCase(ch);
642             }
643
644             // whole word already found but must verify next char
645
if (wordFound) {
646                 if (DocUtils.isIdentifierPart(doc, ch)) { // word continues
647
wordFound = false;
648                     insideWord = lastCharWordPart;
649                     stringInd = endInd;
650                     return endInd;
651                 } else {
652                     found = true;
653                     return 1;
654                 }
655             }
656
657             if (stringInd == endInd) { // special case for last char
658
if (ch != chars[endInd] || insideWord) { // first char doesn't match
659
insideWord = DocUtils.isIdentifierPart(doc, ch);
660                     return -1;
661                 } else { // first char matches
662
stringInd = endInd - 1; // matched and not inside word
663
if (chars.length == 1) {
664                         if (lastChar) {
665                             found = true;
666                             return 0;
667                         } else {
668                             wordFound = true;
669                             return -1;
670                         }
671                     }
672                     return -1;
673                 }
674             } else { // already matched at least one char
675
if (ch == chars[stringInd]) { // matches current char
676
stringInd--;
677                     if (stringInd == -1) { // found whole string
678
if (lastChar) {
679                             found = true;
680                             return 0;
681                         } else {
682                             wordFound = true;
683                             return -1;
684                         }
685                     }
686                     return -1; // successfully matched char, go to next char
687
} else { // current char doesn't match, stringInd > 0
688
int back = chars.length - 2 - stringInd;
689                     stringInd = endInd;
690                     insideWord = lastCharWordPart;
691                     return back;
692                 }
693             }
694         }
695     }
696
697     
698     /** Generic forward finder that simplifies the search process. */
699     private static abstract class GenericFwdFinder extends AbstractFinder {
700
701         public final int find(int initOffset, CharSequence JavaDoc chars) {
702             int offset = 0;
703             int limitPos = chars.length();
704             int limitOffset = limitPos - 1;
705             while (offset >= 0 && offset < limitPos) {
706                 offset += scan(chars.charAt(offset), (offset == limitOffset));
707                 if (found) {
708                     break;
709                 }
710             }
711             return offset;
712         }
713
714         /** This function decides if it found a desired string or not.
715         * The function receives currently searched character and flag if it's
716         * the last one that is searched or not.
717         * @return if the function decides that
718         * it found a desired string it sets <CODE>found = true</CODE> and returns
719         * how many characters back the searched string begins in forward
720         * direction (0 stands for current character).
721         * For example if the function looks for word 'yes' and it gets
722         * 's' as parameter it sets found = true and returns -2.
723         * If the string is not yet found it returns how many characters it should go
724         * in forward direction (in this case it would usually be 1).
725         * The next searched character will be that one requested.
726         */

727         protected abstract int scan(char ch, boolean lastChar);
728
729     }
730
731     /** Generic backward finder that simplifies the search process. */
732     private static abstract class GenericBwdFinder extends AbstractFinder {
733
734         public final int find(int initOffset, CharSequence JavaDoc chars) {
735             int offset = chars.length() - 1;
736             int offset2;
737             int limitPos = 0;
738             int limitOffset = chars.length();
739             while (offset >= 0 && offset < limitOffset) {
740                 offset += scan(chars.charAt(offset), (offset == limitOffset));
741                 if (found) {
742                     break;
743                 }
744             }
745             return offset;
746         }
747
748         /** This function decides if it found a desired string or not.
749         * The function receives currently searched character and flag if it's
750         * the last one that is searched or not.
751         * @return if the function decides that
752         * it found a desired string it sets <CODE>found = true</CODE> and returns
753         * how many characters back the searched string begins in backward
754         * direction (0 stands for current character). It is usually 0 as the
755         * finder usually decides after the last required character but it's
756         * not always the case e.g. for whole-words-only search it can be 1 or so.
757         * If the string is not yet found it returns how many characters it should go
758         * in backward direction (in this case it would usually be -1).
759         * The next searched character will be that one requested.
760         */

761         protected abstract int scan(char ch, boolean lastChar);
762
763     }
764
765     
766     private static final class WholeWordsFwdFinder extends GenericFwdFinder
767         implements StringFinder {
768
769         char chars[];
770
771         int stringInd;
772
773         boolean matchCase;
774
775         Document JavaDoc doc;
776
777         boolean insideWord;
778
779         boolean firstCharWordPart;
780
781         boolean wordFound;
782
783         public WholeWordsFwdFinder() {
784         }
785
786         public void setParams(Document JavaDoc doc, String JavaDoc s, boolean matchCase){
787             this.doc = doc;
788             this.matchCase = matchCase;
789             chars = (matchCase ? s : s.toLowerCase()).toCharArray();
790             firstCharWordPart = DocUtils.isIdentifierPart(doc, chars[0]);
791         }
792         
793         public int getFoundLength() {
794             return chars.length;
795         }
796
797         public void reset() {
798             super.reset();
799             insideWord = false;
800             wordFound = false;
801             stringInd = 0;
802         }
803
804         protected int scan(char ch, boolean lastChar) {
805             if (!matchCase) {
806                 ch = Character.toLowerCase(ch);
807             }
808
809             // whole word already found but must verify next char
810
if (wordFound) {
811                 if (DocUtils.isIdentifierPart(doc, ch)) { // word continues
812
wordFound = false;
813                     insideWord = firstCharWordPart;
814                     stringInd = 0;
815                     return 1 - chars.length;
816                 } else {
817                     found = true;
818                     return -chars.length;
819                 }
820             }
821
822             if (stringInd == 0) { // special case for first char
823
if (ch != chars[0] || insideWord) { // first char doesn't match
824
insideWord = DocUtils.isIdentifierPart(doc, ch);
825                     return 1;
826                 } else { // first char matches
827
stringInd = 1; // matched and not inside word
828
if (chars.length == 1) {
829                         if (lastChar) {
830                             found = true;
831                             return 0;
832                         } else {
833                             wordFound = true;
834                             return 1;
835                         }
836                     }
837                     return 1;
838                 }
839             } else { // already matched at least one char
840
if (ch == chars[stringInd]) { // matches current char
841
stringInd++;
842                     if (stringInd == chars.length) { // found whole string
843
if (lastChar) {
844                             found = true;
845                             return 1 - chars.length; // how many chars back the string starts
846
} else {
847                             wordFound = true;
848                             return 1;
849                         }
850                     }
851                     return 1; // successfully matched char, go to next char
852
} else { // current char doesn't match, stringInd > 0
853
int back = 1 - stringInd;
854                     stringInd = 0;
855                     insideWord = firstCharWordPart;
856                     return back; // go back to search from the next to first char
857
}
858             }
859         }
860
861     }
862     
863     private static class StringBwdFinder extends GenericBwdFinder
864         implements StringFinder {
865
866         char chars[];
867
868         int stringInd;
869
870         boolean matchCase;
871
872         int endInd;
873
874         public StringBwdFinder() {
875         }
876
877         public void setParams(String JavaDoc s, boolean matchCase){
878             this.matchCase = matchCase;
879             chars = (matchCase ? s : s.toLowerCase()).toCharArray();
880             endInd = chars.length - 1;
881         }
882         
883         public int getFoundLength() {
884             return chars.length;
885         }
886
887         public void reset() {
888             super.reset();
889             stringInd = endInd;
890         }
891
892         protected int scan(char ch, boolean lastChar) {
893             if (!matchCase) {
894                 ch = Character.toLowerCase(ch);
895             }
896             if (ch == chars[stringInd]) {
897                 stringInd--;
898                 if (stringInd == -1) {
899                     found = true;
900                     return 0;
901                 }
902                 return -1;
903             } else {
904                 if (stringInd == endInd) {
905                     return -1;
906                 } else {
907                     int back = chars.length - 2 - stringInd;
908                     stringInd = endInd;
909                     return back;
910                 }
911             }
912         }
913
914     }
915
916     private static final class StringFwdFinder extends GenericFwdFinder
917         implements StringFinder {
918
919         char chars[];
920
921         int stringInd;
922
923         boolean matchCase;
924
925         public StringFwdFinder() {
926         }
927         
928         public void setParams(String JavaDoc s, boolean matchCase){
929             this.matchCase = matchCase;
930             chars = (matchCase ? s : s.toLowerCase()).toCharArray();
931         }
932
933         public int getFoundLength() {
934             return chars.length;
935         }
936
937         public void reset() {
938             super.reset();
939             stringInd = 0;
940         }
941
942         protected int scan(char ch, boolean lastChar) {
943             if (!matchCase) {
944                 ch = Character.toLowerCase(ch);
945             }
946             if (ch == chars[stringInd]) {
947                 stringInd++;
948                 if (stringInd == chars.length) { // found whole string
949
found = true;
950                     return 1 - stringInd; // how many chars back the string starts
951
}
952                 return 1; // successfully matched char, go to next char
953
} else {
954                 if (stringInd == 0) {
955                     return 1;
956                 } else {
957                     int back = 1 - stringInd;
958                     stringInd = 0;
959                     return back;
960                 }
961             }
962         }
963
964     }
965     
966     
967     private abstract static class RegExpFinder extends AbstractFinder implements StringFinder{
968         public abstract Matcher JavaDoc getMatcher();
969     }
970     
971     // ----------------- regexp ----------------------
972
private static class RegExpBwdFinder extends RegExpFinder{
973
974         boolean matchCase;
975         Pattern JavaDoc pattern;
976         int length = 0;
977         Matcher JavaDoc matcher;
978         
979
980         public RegExpBwdFinder() {
981         }
982
983         public Matcher JavaDoc getMatcher(){
984             return matcher;
985         }
986         
987         public void setParams(Pattern JavaDoc pattern, boolean matchCase){
988             this.matchCase = matchCase;
989             this.pattern = pattern;
990         }
991         
992         public int getFoundLength() {
993             return length;
994         }
995
996         public void reset() {
997             super.reset();
998             length = 0;
999         }
1000
1001        private int lineFind (int lineStart, int lineEnd, CharSequence JavaDoc chars){
1002            matcher = pattern.matcher(chars.subSequence(lineStart, lineEnd));
1003            int ret = -1;
1004            while (matcher.find()){
1005                int start = matcher.start();
1006                int end = matcher.end();
1007                length = end - start;
1008                if (length <= 0){
1009                    found = false;
1010                    return -1;
1011                }
1012                ret = start;
1013            }
1014            return ret;
1015        }
1016        
1017        public int find(int initOffset, CharSequence JavaDoc chars) {
1018            char ch;
1019            
1020            int charsEnd = chars.length() - 1;
1021            int lineEnd = charsEnd;
1022            int lineStart = charsEnd;
1023            for (int i = charsEnd; i>=0; i--){
1024                ch = chars.charAt(i);
1025                if (ch == '\n' || i==0){
1026                    int retFind = lineFind (lineStart+((i==0)?0:1), lineEnd+1, chars);
1027                    if (retFind!=-1){
1028                        found = true;
1029                        return i + retFind + ((i==0)?0:1);
1030                    }
1031                    lineStart--;
1032                    lineEnd = lineStart;
1033                }else{
1034                    lineStart--;
1035                }
1036            }
1037            return -1;
1038        }
1039
1040    }
1041
1042    private static final class RegExpFwdFinder extends RegExpFinder{
1043
1044        Pattern JavaDoc pattern;
1045        boolean matchCase;
1046        int length = 0;
1047        Matcher JavaDoc matcher;
1048
1049        public RegExpFwdFinder() {
1050        }
1051
1052        public Matcher JavaDoc getMatcher(){
1053            return matcher;
1054        }
1055        
1056        public void setParams(Pattern JavaDoc pattern, boolean matchCase){
1057            this.matchCase = matchCase;
1058            this.pattern = pattern;
1059        }
1060        
1061        public int getFoundLength() {
1062            return length;
1063        }
1064
1065        public void reset() {
1066            super.reset();
1067            length = 0;
1068        }
1069
1070        public int find(int initOffset, CharSequence JavaDoc chars) {
1071            matcher = pattern.matcher(chars);
1072            if (matcher.find()){
1073                found = true;
1074                int start = matcher.start();
1075                int end = matcher.end();
1076                length = end - start;
1077                if (length <= 0){
1078                    found = false;
1079                    return -1;
1080                }
1081                return start;
1082            }else{
1083                return -1;
1084            }
1085            
1086        }
1087    }
1088
1089    /** String forward finder that creates position blocks */
1090    private static final class RegExpBlocksFinder
1091        extends AbstractBlocksFinder {
1092
1093        Pattern JavaDoc pattern;
1094        
1095        int stringInd;
1096
1097        boolean matchCase;
1098
1099        public RegExpBlocksFinder() {
1100        }
1101        
1102        public void setParams(Pattern JavaDoc pattern, boolean matchCase){
1103            this.pattern = pattern;
1104            this.matchCase = matchCase;
1105        }
1106
1107        public void reset() {
1108            super.reset();
1109            stringInd = 0;
1110        }
1111
1112        public int find(int initOffset, CharSequence JavaDoc data) {
1113            Matcher JavaDoc matcher = pattern.matcher(data);
1114            int ret = 0;
1115            while (matcher.find()){
1116                int start = initOffset + matcher.start();
1117                int end = initOffset + matcher.end();
1118                addBlock(start, end);
1119                ret = start;
1120            }
1121            return ret;
1122        }
1123
1124    }
1125    
1126    private static class PatternCache{
1127        
1128        private static String JavaDoc cache_str;
1129        private static boolean cache_matchCase;
1130        private static Pattern JavaDoc cache_pattern;
1131        
1132        private PatternCache(){
1133        }
1134        
1135        public static void putPattern(String JavaDoc str, boolean matchCase, Pattern JavaDoc pattern){
1136            cache_str = str;
1137            cache_matchCase = matchCase;
1138            cache_pattern = pattern;
1139        }
1140        
1141        public static Pattern JavaDoc getPattern(String JavaDoc str, boolean matchCase){
1142            if (str == null) return null;
1143            if (str.equals(cache_str) && matchCase == cache_matchCase){
1144                return cache_pattern;
1145            }
1146            return null;
1147        }
1148        
1149        public static void clear(){
1150            cache_str = null;
1151            cache_matchCase = false;
1152            cache_pattern = null;
1153        }
1154    }
1155
1156    public static class FindReplaceResult{
1157        private int[] positions;
1158        private String JavaDoc replacedString;
1159        
1160        public FindReplaceResult(int[] positions, String JavaDoc replacedString){
1161            this.positions = positions;
1162            this.replacedString = replacedString;
1163        }
1164        
1165        public String JavaDoc getReplacedString(){
1166            return replacedString;
1167        }
1168        
1169        public int[] getFoundPositions(){
1170            return positions;
1171        }
1172    }
1173    
1174}
1175
1176
1177
Popular Tags