KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > rules > PatternRule


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  * Christopher Lenz (cmlenz@gmx.de) - support for line continuation
11  *******************************************************************************/

12
13 package org.eclipse.jface.text.rules;
14
15 import java.util.Arrays JavaDoc;
16 import java.util.Comparator JavaDoc;
17
18 import org.eclipse.core.runtime.Assert;
19
20
21
22
23
24 /**
25  * Standard implementation of <code>IPredicateRule</code>.
26  * Is is capable of detecting a pattern which begins with a given start
27  * sequence and ends with a given end sequence. If the end sequence is
28  * not specified, it can be either end of line, end or file, or both. Additionally,
29  * the pattern can be constrained to begin in a certain column. The rule can also
30  * be used to check whether the text to scan covers half of the pattern, i.e. contains
31  * the end sequence required by the rule.
32  */

33 public class PatternRule implements IPredicateRule {
34
35     /**
36      * Comparator that orders <code>char[]</code> in decreasing array lengths.
37      *
38      * @since 3.1
39      */

40     private static class DecreasingCharArrayLengthComparator implements Comparator JavaDoc {
41         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
42             return ((char[]) o2).length - ((char[]) o1).length;
43         }
44     }
45
46     /** Internal setting for the un-initialized column constraint */
47     protected static final int UNDEFINED= -1;
48
49     /** The token to be returned on success */
50     protected IToken fToken;
51     /** The pattern's start sequence */
52     protected char[] fStartSequence;
53     /** The pattern's end sequence */
54     protected char[] fEndSequence;
55     /** The pattern's column constrain */
56     protected int fColumn= UNDEFINED;
57     /** The pattern's escape character */
58     protected char fEscapeCharacter;
59     /**
60      * Indicates whether the escape character continues a line
61      * @since 3.0
62      */

63     protected boolean fEscapeContinuesLine;
64     /** Indicates whether end of line terminates the pattern */
65     protected boolean fBreaksOnEOL;
66     /** Indicates whether end of file terminates the pattern */
67     protected boolean fBreaksOnEOF;
68
69     /**
70      * Line delimiter comparator which orders according to decreasing delimiter length.
71      * @since 3.1
72      */

73     private Comparator JavaDoc fLineDelimiterComparator= new DecreasingCharArrayLengthComparator();
74     /**
75      * Cached line delimiters.
76      * @since 3.1
77      */

78     private char[][] fLineDelimiters;
79     /**
80      * Cached sorted {@linkplain #fLineDelimiters}.
81      * @since 3.1
82      */

83     private char[][] fSortedLineDelimiters;
84
85     /**
86      * Creates a rule for the given starting and ending sequence.
87      * When these sequences are detected the rule will return the specified token.
88      * Alternatively, the sequence can also be ended by the end of the line.
89      * Any character which follows the given escapeCharacter will be ignored.
90      *
91      * @param startSequence the pattern's start sequence
92      * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
93      * @param token the token which will be returned on success
94      * @param escapeCharacter any character following this one will be ignored
95      * @param breaksOnEOL indicates whether the end of the line also terminates the pattern
96      */

97     public PatternRule(String JavaDoc startSequence, String JavaDoc endSequence, IToken token, char escapeCharacter, boolean breaksOnEOL) {
98         Assert.isTrue(startSequence != null && startSequence.length() > 0);
99         Assert.isTrue(endSequence != null || breaksOnEOL);
100         Assert.isNotNull(token);
101
102         fStartSequence= startSequence.toCharArray();
103         fEndSequence= (endSequence == null ? new char[0] : endSequence.toCharArray());
104         fToken= token;
105         fEscapeCharacter= escapeCharacter;
106         fBreaksOnEOL= breaksOnEOL;
107     }
108
109     /**
110      * Creates a rule for the given starting and ending sequence.
111      * When these sequences are detected the rule will return the specified token.
112      * Alternatively, the sequence can also be ended by the end of the line or the end of the file.
113      * Any character which follows the given escapeCharacter will be ignored.
114      *
115      * @param startSequence the pattern's start sequence
116      * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
117      * @param token the token which will be returned on success
118      * @param escapeCharacter any character following this one will be ignored
119      * @param breaksOnEOL indicates whether the end of the line also terminates the pattern
120      * @param breaksOnEOF indicates whether the end of the file also terminates the pattern
121      * @since 2.1
122      */

123     public PatternRule(String JavaDoc startSequence, String JavaDoc endSequence, IToken token, char escapeCharacter, boolean breaksOnEOL, boolean breaksOnEOF) {
124         this(startSequence, endSequence, token, escapeCharacter, breaksOnEOL);
125         fBreaksOnEOF= breaksOnEOF;
126     }
127
128     /**
129      * Creates a rule for the given starting and ending sequence.
130      * When these sequences are detected the rule will return the specified token.
131      * Alternatively, the sequence can also be ended by the end of the line or the end of the file.
132      * Any character which follows the given escapeCharacter will be ignored. An end of line
133      * immediately after the given <code>lineContinuationCharacter</code> will not cause the
134      * pattern to terminate even if <code>breakOnEOL</code> is set to true.
135      *
136      * @param startSequence the pattern's start sequence
137      * @param endSequence the pattern's end sequence, <code>null</code> is a legal value
138      * @param token the token which will be returned on success
139      * @param escapeCharacter any character following this one will be ignored
140      * @param breaksOnEOL indicates whether the end of the line also terminates the pattern
141      * @param breaksOnEOF indicates whether the end of the file also terminates the pattern
142      * @param escapeContinuesLine indicates whether the specified escape character is used for line
143      * continuation, so that an end of line immediately after the escape character does not
144      * terminate the pattern, even if <code>breakOnEOL</code> is set
145      * @since 3.0
146      */

147     public PatternRule(String JavaDoc startSequence, String JavaDoc endSequence, IToken token, char escapeCharacter, boolean breaksOnEOL, boolean breaksOnEOF, boolean escapeContinuesLine) {
148         this(startSequence, endSequence, token, escapeCharacter, breaksOnEOL, breaksOnEOF);
149         fEscapeContinuesLine= escapeContinuesLine;
150     }
151
152     /**
153      * Sets a column constraint for this rule. If set, the rule's token
154      * will only be returned if the pattern is detected starting at the
155      * specified column. If the column is smaller then 0, the column
156      * constraint is considered removed.
157      *
158      * @param column the column in which the pattern starts
159      */

160     public void setColumnConstraint(int column) {
161         if (column < 0)
162             column= UNDEFINED;
163         fColumn= column;
164     }
165
166
167     /**
168      * Evaluates this rules without considering any column constraints.
169      *
170      * @param scanner the character scanner to be used
171      * @return the token resulting from this evaluation
172      */

173     protected IToken doEvaluate(ICharacterScanner scanner) {
174         return doEvaluate(scanner, false);
175     }
176
177     /**
178      * Evaluates this rules without considering any column constraints. Resumes
179      * detection, i.e. look sonly for the end sequence required by this rule if the
180      * <code>resume</code> flag is set.
181      *
182      * @param scanner the character scanner to be used
183      * @param resume <code>true</code> if detection should be resumed, <code>false</code> otherwise
184      * @return the token resulting from this evaluation
185      * @since 2.0
186      */

187     protected IToken doEvaluate(ICharacterScanner scanner, boolean resume) {
188
189         if (resume) {
190
191             if (endSequenceDetected(scanner))
192                 return fToken;
193
194         } else {
195
196             int c= scanner.read();
197             if (c == fStartSequence[0]) {
198                 if (sequenceDetected(scanner, fStartSequence, false)) {
199                     if (endSequenceDetected(scanner))
200                         return fToken;
201                 }
202             }
203         }
204
205         scanner.unread();
206         return Token.UNDEFINED;
207     }
208
209     /*
210      * @see IRule#evaluate(ICharacterScanner)
211      */

212     public IToken evaluate(ICharacterScanner scanner) {
213         return evaluate(scanner, false);
214     }
215
216     /**
217      * Returns whether the end sequence was detected. As the pattern can be considered
218      * ended by a line delimiter, the result of this method is <code>true</code> if the
219      * rule breaks on the end of the line, or if the EOF character is read.
220      *
221      * @param scanner the character scanner to be used
222      * @return <code>true</code> if the end sequence has been detected
223      */

224     protected boolean endSequenceDetected(ICharacterScanner scanner) {
225
226         char[][] originalDelimiters= scanner.getLegalLineDelimiters();
227         int count= originalDelimiters.length;
228         if (fLineDelimiters == null || originalDelimiters.length != count) {
229             fSortedLineDelimiters= new char[count][];
230         } else {
231             while (count > 0 && fLineDelimiters[count-1] == originalDelimiters[count-1])
232                 count--;
233         }
234         if (count != 0) {
235             fLineDelimiters= originalDelimiters;
236             System.arraycopy(fLineDelimiters, 0, fSortedLineDelimiters, 0, fLineDelimiters.length);
237             Arrays.sort(fSortedLineDelimiters, fLineDelimiterComparator);
238         }
239
240         int readCount= 1;
241         int c;
242         while ((c= scanner.read()) != ICharacterScanner.EOF) {
243             if (c == fEscapeCharacter) {
244                 // Skip escaped character(s)
245
if (fEscapeContinuesLine) {
246                     c= scanner.read();
247                     for (int i= 0; i < fSortedLineDelimiters.length; i++) {
248                         if (c == fSortedLineDelimiters[i][0] && sequenceDetected(scanner, fSortedLineDelimiters[i], true))
249                             break;
250                     }
251                 } else
252                     scanner.read();
253
254             } else if (fEndSequence.length > 0 && c == fEndSequence[0]) {
255                 // Check if the specified end sequence has been found.
256
if (sequenceDetected(scanner, fEndSequence, true))
257                     return true;
258             } else if (fBreaksOnEOL) {
259                 // Check for end of line since it can be used to terminate the pattern.
260
for (int i= 0; i < fSortedLineDelimiters.length; i++) {
261                     if (c == fSortedLineDelimiters[i][0] && sequenceDetected(scanner, fSortedLineDelimiters[i], true))
262                         return true;
263                 }
264             }
265             readCount++;
266         }
267         
268         if (fBreaksOnEOF)
269             return true;
270
271         for (; readCount > 0; readCount--)
272             scanner.unread();
273
274         return false;
275     }
276
277     /**
278      * Returns whether the next characters to be read by the character scanner
279      * are an exact match with the given sequence. No escape characters are allowed
280      * within the sequence. If specified the sequence is considered to be found
281      * when reading the EOF character.
282      *
283      * @param scanner the character scanner to be used
284      * @param sequence the sequence to be detected
285      * @param eofAllowed indicated whether EOF terminates the pattern
286      * @return <code>true</code> if the given sequence has been detected
287      */

288     protected boolean sequenceDetected(ICharacterScanner scanner, char[] sequence, boolean eofAllowed) {
289         for (int i= 1; i < sequence.length; i++) {
290             int c= scanner.read();
291             if (c == ICharacterScanner.EOF && eofAllowed) {
292                 return true;
293             } else if (c != sequence[i]) {
294                 // Non-matching character detected, rewind the scanner back to the start.
295
// Do not unread the first character.
296
scanner.unread();
297                 for (int j= i-1; j > 0; j--)
298                     scanner.unread();
299                 return false;
300             }
301         }
302
303         return true;
304     }
305
306     /*
307      * @see IPredicateRule#evaluate(ICharacterScanner, boolean)
308      * @since 2.0
309      */

310     public IToken evaluate(ICharacterScanner scanner, boolean resume) {
311         if (fColumn == UNDEFINED)
312             return doEvaluate(scanner, resume);
313
314         int c= scanner.read();
315         scanner.unread();
316         if (c == fStartSequence[0])
317             return (fColumn == scanner.getColumn() ? doEvaluate(scanner, resume) : Token.UNDEFINED);
318         return Token.UNDEFINED;
319     }
320
321     /*
322      * @see IPredicateRule#getSuccessToken()
323      * @since 2.0
324      */

325     public IToken getSuccessToken() {
326         return fToken;
327     }
328 }
329
Popular Tags