KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > FindReplaceDocumentAdapter


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jface.text;
13
14 import java.util.regex.Matcher JavaDoc;
15 import java.util.regex.Pattern JavaDoc;
16 import java.util.regex.PatternSyntaxException JavaDoc;
17
18 import org.eclipse.core.runtime.Assert;
19
20
21 /**
22  * Provides search and replace operations on
23  * {@link org.eclipse.jface.text.IDocument}.
24  * <p>
25  * Replaces
26  * {@link org.eclipse.jface.text.IDocument#search(int, String, boolean, boolean, boolean)}.
27  *
28  * @since 3.0
29  */

30 public class FindReplaceDocumentAdapter implements CharSequence JavaDoc {
31
32     /**
33      * Internal type for operation codes.
34      */

35     private static class FindReplaceOperationCode {
36     }
37
38     // Find/replace operation codes.
39
private static final FindReplaceOperationCode FIND_FIRST= new FindReplaceOperationCode();
40     private static final FindReplaceOperationCode FIND_NEXT= new FindReplaceOperationCode();
41     private static final FindReplaceOperationCode REPLACE= new FindReplaceOperationCode();
42     private static final FindReplaceOperationCode REPLACE_FIND_NEXT= new FindReplaceOperationCode();
43
44     /**
45      * The adapted document.
46      */

47     private IDocument fDocument;
48
49     /**
50      * State for findReplace.
51      */

52     private FindReplaceOperationCode fFindReplaceState= null;
53
54     /**
55      * The matcher used in findReplace.
56      */

57     private Matcher JavaDoc fFindReplaceMatcher;
58
59     /**
60      * The match offset from the last findReplace call.
61      */

62     private int fFindReplaceMatchOffset;
63
64     /**
65      * Constructs a new find replace document adapter.
66      *
67      * @param document the adapted document
68      */

69     public FindReplaceDocumentAdapter(IDocument document) {
70         Assert.isNotNull(document);
71         fDocument= document;
72     }
73
74     /**
75      * Returns the location of a given string in this adapter's document based on a set of search criteria.
76      *
77      * @param startOffset document offset at which search starts
78      * @param findString the string to find
79      * @param forwardSearch the search direction
80      * @param caseSensitive indicates whether lower and upper case should be distinguished
81      * @param wholeWord indicates whether the findString should be limited by white spaces as
82      * defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>.
83      * @param regExSearch if <code>true</code> findString represents a regular expression
84      * Must not be used in combination with <code>wholeWord</code>.
85      * @return the find or replace region or <code>null</code> if there was no match
86      * @throws BadLocationException if startOffset is an invalid document offset
87      * @throws PatternSyntaxException if a regular expression has invalid syntax
88      */

89     public IRegion find(int startOffset, String JavaDoc findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, boolean regExSearch) throws BadLocationException {
90         Assert.isTrue(!(regExSearch && wholeWord));
91
92         // Adjust offset to special meaning of -1
93
if (startOffset == -1 && forwardSearch)
94             startOffset= 0;
95         if (startOffset == -1 && !forwardSearch)
96             startOffset= length() - 1;
97
98         return findReplace(FIND_FIRST, startOffset, findString, null, forwardSearch, caseSensitive, wholeWord, regExSearch);
99     }
100
101     /**
102      * Stateful findReplace executes a FIND, REPLACE, REPLACE_FIND or FIND_FIRST operation.
103      * In case of REPLACE and REPLACE_FIND it sends a <code>DocumentEvent</code> to all
104      * registered <code>IDocumentListener</code>.
105      *
106      * @param startOffset document offset at which search starts
107      * this value is only used in the FIND_FIRST operation and otherwise ignored
108      * @param findString the string to find
109      * this value is only used in the FIND_FIRST operation and otherwise ignored
110      * @param replaceText the string to replace the current match
111      * this value is only used in the REPLACE and REPLACE_FIND operations and otherwise ignored
112      * @param forwardSearch the search direction
113      * @param caseSensitive indicates whether lower and upper case should be distinguished
114      * @param wholeWord indicates whether the findString should be limited by white spaces as
115      * defined by Character.isWhiteSpace. Must not be used in combination with <code>regExSearch</code>.
116      * @param regExSearch if <code>true</code> this operation represents a regular expression
117      * Must not be used in combination with <code>wholeWord</code>.
118      * @param operationCode specifies what kind of operation is executed
119      * @return the find or replace region or <code>null</code> if there was no match
120      * @throws BadLocationException if startOffset is an invalid document offset
121      * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation
122      * @throws PatternSyntaxException if a regular expression has invalid syntax
123      */

124     private IRegion findReplace(final FindReplaceOperationCode operationCode, int startOffset, String JavaDoc findString, String JavaDoc replaceText, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, boolean regExSearch) throws BadLocationException {
125
126         // Validate option combinations
127
Assert.isTrue(!(regExSearch && wholeWord));
128
129         // Validate state
130
if ((operationCode == REPLACE || operationCode == REPLACE_FIND_NEXT) && (fFindReplaceState != FIND_FIRST && fFindReplaceState != FIND_NEXT))
131             throw new IllegalStateException JavaDoc("illegal findReplace state: cannot replace without preceding find"); //$NON-NLS-1$
132

133         if (operationCode == FIND_FIRST) {
134             // Reset
135

136             if (findString == null || findString.length() == 0)
137                 return null;
138
139             // Validate start offset
140
if (startOffset < 0 || startOffset >= length())
141                 throw new BadLocationException();
142
143             int patternFlags= 0;
144
145             if (regExSearch)
146                 patternFlags |= Pattern.MULTILINE;
147
148             if (!caseSensitive)
149                 patternFlags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
150
151             if (wholeWord)
152                 findString= "\\b" + findString + "\\b"; //$NON-NLS-1$ //$NON-NLS-2$
153

154             if (!regExSearch && !wholeWord)
155                 findString= asRegPattern(findString);
156
157             fFindReplaceMatchOffset= startOffset;
158             if (fFindReplaceMatcher != null && fFindReplaceMatcher.pattern().pattern().equals(findString) && fFindReplaceMatcher.pattern().flags() == patternFlags) {
159                 /*
160                  * Commented out for optimization:
161                  * The call is not needed since FIND_FIRST uses find(int) which resets the matcher
162                  */

163                 // fFindReplaceMatcher.reset();
164
} else {
165                 Pattern JavaDoc pattern= Pattern.compile(findString, patternFlags);
166                 fFindReplaceMatcher= pattern.matcher(this);
167             }
168         }
169
170         // Set state
171
fFindReplaceState= operationCode;
172
173         if (operationCode == REPLACE || operationCode == REPLACE_FIND_NEXT) {
174             if (regExSearch) {
175                 Pattern JavaDoc pattern= fFindReplaceMatcher.pattern();
176                 Matcher JavaDoc replaceTextMatcher= pattern.matcher(fFindReplaceMatcher.group());
177                 try {
178                     replaceText= replaceTextMatcher.replaceFirst(replaceText);
179                 } catch (IndexOutOfBoundsException JavaDoc ex) {
180                     throw new PatternSyntaxException JavaDoc(ex.getLocalizedMessage(), replaceText, -1);
181                 }
182             }
183
184             int offset= fFindReplaceMatcher.start();
185             fDocument.replace(offset, fFindReplaceMatcher.group().length(), replaceText);
186
187             if (operationCode == REPLACE) {
188                 return new Region(offset, replaceText.length());
189             }
190         }
191
192         if (operationCode != REPLACE) {
193             if (forwardSearch) {
194
195                 boolean found= false;
196                 if (operationCode == FIND_FIRST)
197                     found= fFindReplaceMatcher.find(startOffset);
198                 else
199                     found= fFindReplaceMatcher.find();
200
201                 if (operationCode == REPLACE_FIND_NEXT)
202                     fFindReplaceState= FIND_NEXT;
203
204                 if (found && fFindReplaceMatcher.group().length() > 0)
205                     return new Region(fFindReplaceMatcher.start(), fFindReplaceMatcher.group().length());
206                 return null;
207             }
208
209             // backward search
210
boolean found= fFindReplaceMatcher.find(0);
211             int index= -1;
212             int length= -1;
213             while (found && fFindReplaceMatcher.start() + fFindReplaceMatcher.group().length() <= fFindReplaceMatchOffset + 1) {
214                 index= fFindReplaceMatcher.start();
215                 length= fFindReplaceMatcher.group().length();
216                 found= fFindReplaceMatcher.find(index + 1);
217             }
218             fFindReplaceMatchOffset= index;
219             if (index > -1) {
220                 // must set matcher to correct position
221
fFindReplaceMatcher.find(index);
222                 return new Region(index, length);
223             }
224             return null;
225         }
226
227         return null;
228     }
229
230     /**
231      * Converts a non-regex string to a pattern
232      * that can be used with the regex search engine.
233      *
234      * @param string the non-regex pattern
235      * @return the string converted to a regex pattern
236      */

237     private String JavaDoc asRegPattern(String JavaDoc string) {
238         StringBuffer JavaDoc out= new StringBuffer JavaDoc(string.length());
239         boolean quoting= false;
240
241         for (int i= 0, length= string.length(); i < length; i++) {
242             char ch= string.charAt(i);
243             if (ch == '\\') {
244                 if (quoting) {
245                     out.append("\\E"); //$NON-NLS-1$
246
quoting= false;
247                 }
248                 out.append("\\\\"); //$NON-NLS-1$
249
continue;
250             }
251             if (!quoting) {
252                 out.append("\\Q"); //$NON-NLS-1$
253
quoting= true;
254             }
255             out.append(ch);
256         }
257         if (quoting)
258             out.append("\\E"); //$NON-NLS-1$
259

260         return out.toString();
261     }
262
263     /**
264      * Substitutes the previous match with the given text.
265      * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
266      *
267      * @param text the substitution text
268      * @param regExReplace if <code>true</code> <code>text</code> represents a regular expression
269      * @return the replace region or <code>null</code> if there was no match
270      * @throws BadLocationException if startOffset is an invalid document offset
271      * @throws IllegalStateException if a REPLACE or REPLACE_FIND operation is not preceded by a successful FIND operation
272      * @throws PatternSyntaxException if a regular expression has invalid syntax
273      *
274      * @see DocumentEvent
275      * @see IDocumentListener
276      */

277     public IRegion replace(String JavaDoc text, boolean regExReplace) throws BadLocationException {
278         return findReplace(REPLACE, -1, null, text, false, false, false, regExReplace);
279     }
280
281     // ---------- CharSequence implementation ----------
282

283     /*
284      * @see java.lang.CharSequence#length()
285      */

286     public int length() {
287         return fDocument.getLength();
288     }
289
290     /*
291      * @see java.lang.CharSequence#charAt(int)
292      */

293     public char charAt(int index) {
294         try {
295             return fDocument.getChar(index);
296         } catch (BadLocationException e) {
297             throw new IndexOutOfBoundsException JavaDoc();
298         }
299     }
300
301     /*
302      * @see java.lang.CharSequence#subSequence(int, int)
303      */

304     public CharSequence JavaDoc subSequence(int start, int end) {
305         try {
306             return fDocument.get(start, end - start);
307         } catch (BadLocationException e) {
308             throw new IndexOutOfBoundsException JavaDoc();
309         }
310     }
311
312     /*
313      * @see java.lang.Object#toString()
314      */

315     public String JavaDoc toString() {
316         return fDocument.get();
317     }
318 }
319
Popular Tags