KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > java > JavaCodeScanner


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  * Philippe Ombredanne <pombredanne@nexb.com> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=150989
11  *******************************************************************************/

12
13 package org.eclipse.jdt.internal.ui.text.java;
14
15
16 import java.util.ArrayList JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19
20 import org.eclipse.core.runtime.Assert;
21
22 import org.eclipse.jface.preference.IPreferenceStore;
23 import org.eclipse.jface.util.PropertyChangeEvent;
24
25 import org.eclipse.jface.text.rules.ICharacterScanner;
26 import org.eclipse.jface.text.rules.IRule;
27 import org.eclipse.jface.text.rules.IToken;
28 import org.eclipse.jface.text.rules.IWhitespaceDetector;
29 import org.eclipse.jface.text.rules.IWordDetector;
30 import org.eclipse.jface.text.rules.SingleLineRule;
31 import org.eclipse.jface.text.rules.Token;
32 import org.eclipse.jface.text.rules.WhitespaceRule;
33
34 import org.eclipse.jdt.core.JavaCore;
35
36 import org.eclipse.jdt.ui.PreferenceConstants;
37 import org.eclipse.jdt.ui.text.IColorManager;
38 import org.eclipse.jdt.ui.text.IJavaColorConstants;
39
40 import org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightings;
41 import org.eclipse.jdt.internal.ui.text.AbstractJavaScanner;
42 import org.eclipse.jdt.internal.ui.text.CombinedWordRule;
43 import org.eclipse.jdt.internal.ui.text.JavaWhitespaceDetector;
44 import org.eclipse.jdt.internal.ui.text.JavaWordDetector;
45 import org.eclipse.jdt.internal.ui.text.ISourceVersionDependent;
46
47
48 /**
49  * A Java code scanner.
50  */

51 public final class JavaCodeScanner extends AbstractJavaScanner {
52
53     /**
54      * Rule to detect java operators.
55      *
56      * @since 3.0
57      */

58     private static final class OperatorRule implements IRule {
59
60         /** Java operators */
61         private final char[] JAVA_OPERATORS= { ';', '.', '=', '/', '\\', '+', '-', '*', '<', '>', ':', '?', '!', ',', '|', '&', '^', '%', '~'};
62         /** Token to return for this rule */
63         private final IToken fToken;
64
65         /**
66          * Creates a new operator rule.
67          *
68          * @param token Token to use for this rule
69          */

70         public OperatorRule(IToken token) {
71             fToken= token;
72         }
73
74         /**
75          * Is this character an operator character?
76          *
77          * @param character Character to determine whether it is an operator character
78          * @return <code>true</code> iff the character is an operator, <code>false</code> otherwise.
79          */

80         public boolean isOperator(char character) {
81             for (int index= 0; index < JAVA_OPERATORS.length; index++) {
82                 if (JAVA_OPERATORS[index] == character)
83                     return true;
84             }
85             return false;
86         }
87
88         /*
89          * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
90          */

91         public IToken evaluate(ICharacterScanner scanner) {
92
93             int character= scanner.read();
94             if (isOperator((char) character)) {
95                 do {
96                     character= scanner.read();
97                 } while (isOperator((char) character));
98                 scanner.unread();
99                 return fToken;
100             } else {
101                 scanner.unread();
102                 return Token.UNDEFINED;
103             }
104         }
105     }
106
107     /**
108      * Rule to detect java brackets.
109      *
110      * @since 3.3
111      */

112     private static final class BracketRule implements IRule {
113
114         /** Java brackets */
115         private final char[] JAVA_BRACKETS= { '(', ')', '{', '}', '[', ']' };
116         /** Token to return for this rule */
117         private final IToken fToken;
118
119         /**
120          * Creates a new bracket rule.
121          *
122          * @param token Token to use for this rule
123          */

124         public BracketRule(IToken token) {
125             fToken= token;
126         }
127
128         /**
129          * Is this character a bracket character?
130          *
131          * @param character Character to determine whether it is a bracket character
132          * @return <code>true</code> iff the character is a bracket, <code>false</code> otherwise.
133          */

134         public boolean isBracket(char character) {
135             for (int index= 0; index < JAVA_BRACKETS.length; index++) {
136                 if (JAVA_BRACKETS[index] == character)
137                     return true;
138             }
139             return false;
140         }
141
142         /*
143          * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
144          */

145         public IToken evaluate(ICharacterScanner scanner) {
146
147             int character= scanner.read();
148             if (isBracket((char) character)) {
149                 do {
150                     character= scanner.read();
151                 } while (isBracket((char) character));
152                 scanner.unread();
153                 return fToken;
154             } else {
155                 scanner.unread();
156                 return Token.UNDEFINED;
157             }
158         }
159     }
160
161
162     private static class VersionedWordMatcher extends CombinedWordRule.WordMatcher implements ISourceVersionDependent {
163
164         private final IToken fDefaultToken;
165         private final String JavaDoc fVersion;
166         private boolean fIsVersionMatch;
167
168         public VersionedWordMatcher(IToken defaultToken, String JavaDoc version, String JavaDoc currentVersion) {
169             fDefaultToken= defaultToken;
170             fVersion= version;
171             setSourceVersion(currentVersion);
172         }
173
174         /*
175          * @see org.eclipse.jdt.internal.ui.text.ISourceVersionDependent#setSourceVersion(java.lang.String)
176          */

177         public void setSourceVersion(String JavaDoc version) {
178             fIsVersionMatch= fVersion.compareTo(version) <= 0;
179         }
180
181         /*
182          * @see org.eclipse.jdt.internal.ui.text.CombinedWordRule.WordMatcher#evaluate(org.eclipse.jface.text.rules.ICharacterScanner, org.eclipse.jdt.internal.ui.text.CombinedWordRule.CharacterBuffer)
183          */

184         public IToken evaluate(ICharacterScanner scanner, CombinedWordRule.CharacterBuffer word) {
185             IToken token= super.evaluate(scanner, word);
186
187             if (fIsVersionMatch || token.isUndefined())
188                 return token;
189
190             return fDefaultToken;
191         }
192     }
193
194     /**
195      * An annotation rule matches the '@' symbol, any following whitespace and
196      * optionally a following <code>interface</code> keyword.
197      *
198      * It does not match if there is a comment between the '@' symbol and
199      * the identifier. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=82452
200      *
201      * @since 3.1
202      */

203     private static class AnnotationRule implements IRule, ISourceVersionDependent {
204         /**
205          * A resettable scanner supports marking a position in a scanner and
206          * unreading back to the marked position.
207          */

208         private static final class ResettableScanner implements ICharacterScanner {
209             private final ICharacterScanner fDelegate;
210             private int fReadCount;
211
212             /**
213              * Creates a new resettable scanner that will forward calls
214              * to <code>scanner</code>, but store a marked position.
215              *
216              * @param scanner the delegate scanner
217              */

218             public ResettableScanner(final ICharacterScanner scanner) {
219                 Assert.isNotNull(scanner);
220                 fDelegate= scanner;
221                 mark();
222             }
223
224             /*
225              * @see org.eclipse.jface.text.rules.ICharacterScanner#getColumn()
226              */

227             public int getColumn() {
228                 return fDelegate.getColumn();
229             }
230
231             /*
232              * @see org.eclipse.jface.text.rules.ICharacterScanner#getLegalLineDelimiters()
233              */

234             public char[][] getLegalLineDelimiters() {
235                 return fDelegate.getLegalLineDelimiters();
236             }
237
238             /*
239              * @see org.eclipse.jface.text.rules.ICharacterScanner#read()
240              */

241             public int read() {
242                 int ch= fDelegate.read();
243                 if (ch != ICharacterScanner.EOF)
244                     fReadCount++;
245                 return ch;
246             }
247
248             /*
249              * @see org.eclipse.jface.text.rules.ICharacterScanner#unread()
250              */

251             public void unread() {
252                 if (fReadCount > 0)
253                     fReadCount--;
254                 fDelegate.unread();
255             }
256
257             /**
258              * Marks an offset in the scanned content.
259              */

260             public void mark() {
261                 fReadCount= 0;
262             }
263
264             /**
265              * Resets the scanner to the marked position.
266              */

267             public void reset() {
268                 while (fReadCount > 0)
269                     unread();
270
271                 while (fReadCount < 0)
272                     read();
273             }
274         }
275
276         private final IWhitespaceDetector fWhitespaceDetector= new JavaWhitespaceDetector();
277         private final IWordDetector fWordDetector= new JavaWordDetector();
278         private final IToken fInterfaceToken;
279         private final IToken fAtToken;
280         private final String JavaDoc fVersion;
281         private boolean fIsVersionMatch;
282
283         /**
284          * Creates a new rule.
285          *
286          * @param interfaceToken the token to return if
287          * <code>'@\s*interface'</code> is matched
288          * @param atToken the token to return if <code>'@'</code>
289          * is matched, but not <code>'@\s*interface'</code>
290          * @param version the lowest <code>JavaCore.COMPILER_SOURCE</code>
291          * version that this rule is enabled
292          * @param currentVersion the current
293          * <code>JavaCore.COMPILER_SOURCE</code> version
294          */

295         public AnnotationRule(IToken interfaceToken, Token atToken, String JavaDoc version, String JavaDoc currentVersion) {
296             fInterfaceToken= interfaceToken;
297             fAtToken= atToken;
298             fVersion= version;
299             setSourceVersion(currentVersion);
300         }
301
302         /*
303          * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner)
304          */

305         public IToken evaluate(ICharacterScanner scanner) {
306             if (!fIsVersionMatch)
307                 return Token.UNDEFINED;
308
309             ResettableScanner resettable= new ResettableScanner(scanner);
310             if (resettable.read() == '@')
311                 return readAnnotation(resettable);
312
313             resettable.reset();
314             return Token.UNDEFINED;
315         }
316
317         private IToken readAnnotation(ResettableScanner scanner) {
318             scanner.mark();
319             skipWhitespace(scanner);
320             if (readInterface(scanner)) {
321                 return fInterfaceToken;
322             } else {
323                 scanner.reset();
324                 return fAtToken;
325             }
326         }
327
328         private boolean readInterface(ICharacterScanner scanner) {
329             int ch= scanner.read();
330             int i= 0;
331             while (i < INTERFACE.length() && INTERFACE.charAt(i) == ch) {
332                 i++;
333                 ch= scanner.read();
334             }
335             if (i < INTERFACE.length())
336                 return false;
337             
338             if (fWordDetector.isWordPart((char) ch))
339                 return false;
340             
341             if (ch != ICharacterScanner.EOF)
342                 scanner.unread();
343             
344             return true;
345         }
346
347         private boolean skipWhitespace(ICharacterScanner scanner) {
348             while (fWhitespaceDetector.isWhitespace((char) scanner.read())) {
349                 // do nothing
350
}
351
352             scanner.unread();
353             return true;
354         }
355
356         /*
357          * @see org.eclipse.jdt.internal.ui.text.ISourceVersionDependent#setSourceVersion(java.lang.String)
358          */

359         public void setSourceVersion(String JavaDoc version) {
360             fIsVersionMatch= fVersion.compareTo(version) <= 0;
361         }
362
363     }
364
365     private static final String JavaDoc SOURCE_VERSION= JavaCore.COMPILER_SOURCE;
366
367     static String JavaDoc[] fgKeywords= {
368         "abstract", //$NON-NLS-1$
369
"break", //$NON-NLS-1$
370
"case", "catch", "class", "const", "continue", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
371
"default", "do", //$NON-NLS-2$ //$NON-NLS-1$
372
"else", "extends", //$NON-NLS-2$ //$NON-NLS-1$
373
"final", "finally", "for", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
374
"goto", //$NON-NLS-1$
375
"if", "implements", "import", "instanceof", "interface", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
376
"native", "new", //$NON-NLS-2$ //$NON-NLS-1$
377
"package", "private", "protected", "public", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
378
"static", "super", "switch", "synchronized", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
379
"this", "throw", "throws", "transient", "try", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
380
"volatile", //$NON-NLS-1$
381
"while" //$NON-NLS-1$
382
};
383
384     private static final String JavaDoc INTERFACE= "interface"; //$NON-NLS-1$
385
private static final String JavaDoc RETURN= "return"; //$NON-NLS-1$
386
private static String JavaDoc[] fgJava14Keywords= { "assert" }; //$NON-NLS-1$
387
private static String JavaDoc[] fgJava15Keywords= { "enum" }; //$NON-NLS-1$
388

389     private static String JavaDoc[] fgTypes= { "void", "boolean", "char", "byte", "short", "strictfp", "int", "long", "float", "double" }; //$NON-NLS-1$ //$NON-NLS-5$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-2$
390

391     private static String JavaDoc[] fgConstants= { "false", "null", "true" }; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
392

393     private static final String JavaDoc ANNOTATION_BASE_KEY= PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + SemanticHighlightings.ANNOTATION;
394     private static final String JavaDoc ANNOTATION_COLOR_KEY= ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX;
395
396     private static String JavaDoc[] fgTokenProperties= {
397         IJavaColorConstants.JAVA_KEYWORD,
398         IJavaColorConstants.JAVA_STRING,
399         IJavaColorConstants.JAVA_DEFAULT,
400         IJavaColorConstants.JAVA_KEYWORD_RETURN,
401         IJavaColorConstants.JAVA_OPERATOR,
402         IJavaColorConstants.JAVA_BRACKET,
403         ANNOTATION_COLOR_KEY,
404     };
405
406     private List JavaDoc fVersionDependentRules= new ArrayList JavaDoc(3);
407
408     /**
409      * Creates a Java code scanner
410      *
411      * @param manager the color manager
412      * @param store the preference store
413      */

414     public JavaCodeScanner(IColorManager manager, IPreferenceStore store) {
415         super(manager, store);
416         initialize();
417     }
418
419     /*
420      * @see AbstractJavaScanner#getTokenProperties()
421      */

422     protected String JavaDoc[] getTokenProperties() {
423         return fgTokenProperties;
424     }
425
426     /*
427      * @see AbstractJavaScanner#createRules()
428      */

429     protected List JavaDoc createRules() {
430
431         List JavaDoc rules= new ArrayList JavaDoc();
432
433         // Add rule for character constants.
434
Token token= getToken(IJavaColorConstants.JAVA_STRING);
435         rules.add(new SingleLineRule("'", "'", token, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
436

437
438         // Add generic whitespace rule.
439
rules.add(new WhitespaceRule(new JavaWhitespaceDetector()));
440
441         String JavaDoc version= getPreferenceStore().getString(SOURCE_VERSION);
442
443         // Add JLS3 rule for /@\s*interface/ and /@\s*\w+/
444
token= getToken(ANNOTATION_COLOR_KEY);
445         AnnotationRule atInterfaceRule= new AnnotationRule(getToken(IJavaColorConstants.JAVA_KEYWORD), token, JavaCore.VERSION_1_5, version);
446         rules.add(atInterfaceRule);
447         fVersionDependentRules.add(atInterfaceRule);
448
449         // Add word rule for new keywords, 4077
450
JavaWordDetector wordDetector= new JavaWordDetector();
451         token= getToken(IJavaColorConstants.JAVA_DEFAULT);
452         CombinedWordRule combinedWordRule= new CombinedWordRule(wordDetector, token);
453
454         token= getToken(IJavaColorConstants.JAVA_DEFAULT);
455         VersionedWordMatcher j14Matcher= new VersionedWordMatcher(token, JavaCore.VERSION_1_4, version);
456
457         token= getToken(IJavaColorConstants.JAVA_KEYWORD);
458         for (int i=0; i<fgJava14Keywords.length; i++)
459             j14Matcher.addWord(fgJava14Keywords[i], token);
460
461         combinedWordRule.addWordMatcher(j14Matcher);
462         fVersionDependentRules.add(j14Matcher);
463
464         token= getToken(IJavaColorConstants.JAVA_DEFAULT);
465         VersionedWordMatcher j15Matcher= new VersionedWordMatcher(token, JavaCore.VERSION_1_5, version);
466         token= getToken(IJavaColorConstants.JAVA_KEYWORD);
467         for (int i=0; i<fgJava15Keywords.length; i++)
468             j15Matcher.addWord(fgJava15Keywords[i], token);
469
470         combinedWordRule.addWordMatcher(j15Matcher);
471         fVersionDependentRules.add(j15Matcher);
472
473         // Add rule for operators
474
token= getToken(IJavaColorConstants.JAVA_OPERATOR);
475         rules.add(new OperatorRule(token));
476
477         // Add rule for brackets
478
token= getToken(IJavaColorConstants.JAVA_BRACKET);
479         rules.add(new BracketRule(token));
480
481         // Add word rule for keyword 'return'.
482
CombinedWordRule.WordMatcher returnWordRule= new CombinedWordRule.WordMatcher();
483         token= getToken(IJavaColorConstants.JAVA_KEYWORD_RETURN);
484         returnWordRule.addWord(RETURN, token);
485         combinedWordRule.addWordMatcher(returnWordRule);
486
487         // Add word rule for keywords, types, and constants.
488
CombinedWordRule.WordMatcher wordRule= new CombinedWordRule.WordMatcher();
489         token= getToken(IJavaColorConstants.JAVA_KEYWORD);
490         for (int i=0; i<fgKeywords.length; i++)
491             wordRule.addWord(fgKeywords[i], token);
492         for (int i=0; i<fgTypes.length; i++)
493             wordRule.addWord(fgTypes[i], token);
494         for (int i=0; i<fgConstants.length; i++)
495             wordRule.addWord(fgConstants[i], token);
496
497         combinedWordRule.addWordMatcher(wordRule);
498
499         rules.add(combinedWordRule);
500
501         setDefaultReturnToken(getToken(IJavaColorConstants.JAVA_DEFAULT));
502         return rules;
503     }
504     
505     /*
506      * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getBoldKey(java.lang.String)
507      */

508     protected String JavaDoc getBoldKey(String JavaDoc colorKey) {
509         if ((ANNOTATION_COLOR_KEY).equals(colorKey))
510             return ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_BOLD_SUFFIX;
511         return super.getBoldKey(colorKey);
512     }
513     
514     /*
515      * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getItalicKey(java.lang.String)
516      */

517     protected String JavaDoc getItalicKey(String JavaDoc colorKey) {
518         if ((ANNOTATION_COLOR_KEY).equals(colorKey))
519             return ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ITALIC_SUFFIX;
520         return super.getItalicKey(colorKey);
521     }
522     
523     /*
524      * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getStrikethroughKey(java.lang.String)
525      */

526     protected String JavaDoc getStrikethroughKey(String JavaDoc colorKey) {
527         if ((ANNOTATION_COLOR_KEY).equals(colorKey))
528             return ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_STRIKETHROUGH_SUFFIX;
529         return super.getStrikethroughKey(colorKey);
530     }
531     
532     /*
533      * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getUnderlineKey(java.lang.String)
534      */

535     protected String JavaDoc getUnderlineKey(String JavaDoc colorKey) {
536         if ((ANNOTATION_COLOR_KEY).equals(colorKey))
537             return ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_UNDERLINE_SUFFIX;
538         return super.getUnderlineKey(colorKey);
539     }
540
541     /*
542      * @see AbstractJavaScanner#affectsBehavior(PropertyChangeEvent)
543      */

544     public boolean affectsBehavior(PropertyChangeEvent event) {
545         return event.getProperty().equals(SOURCE_VERSION) || super.affectsBehavior(event);
546     }
547
548     /*
549      * @see AbstractJavaScanner#adaptToPreferenceChange(PropertyChangeEvent)
550      */

551     public void adaptToPreferenceChange(PropertyChangeEvent event) {
552
553         if (event.getProperty().equals(SOURCE_VERSION)) {
554             Object JavaDoc value= event.getNewValue();
555
556             if (value instanceof String JavaDoc) {
557                 String JavaDoc s= (String JavaDoc) value;
558
559                 for (Iterator JavaDoc it= fVersionDependentRules.iterator(); it.hasNext();) {
560                     ISourceVersionDependent dependent= (ISourceVersionDependent) it.next();
561                     dependent.setSourceVersion(s);
562                 }
563             }
564
565         } else if (super.affectsBehavior(event)) {
566             super.adaptToPreferenceChange(event);
567         }
568     }
569 }
570
Popular Tags