KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > JavaTagger


1 /*
2  * JavaTagger.java
3  *
4  * Copyright (C) 1998-2002 Peter Graves
5  * $Id: JavaTagger.java,v 1.6 2003/12/30 19:24:02 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import java.util.Stack JavaDoc;
25 import java.util.ArrayList JavaDoc;
26
27 public class JavaTagger extends Tagger implements Constants
28 {
29     // States.
30
private static final int NEUTRAL = 0;
31     private static final int INTERFACE_NAME = 1;
32     private static final int CLASS_NAME = 2;
33     private static final int CLASS_PROLOG = 3; // After name, before opening brace.
34
private static final int EXTENDS = 4;
35     private static final int IMPLEMENTS = 5;
36     private static final int METHOD_NAME = 6;
37     private static final int METHOD_PROLOG = 7;
38     private static final int STATIC_INITIALIZER = 8;
39     private static final int NEW = 9;
40     private static final int FIELD_INITIALIZER = 10; // After '=' in a variable declaration.
41

42     protected Position pos;
43     protected String JavaDoc token;
44     protected Position tokenStart;
45
46     private ArrayList JavaDoc tags;
47     private JavaClass currentClass;
48     private int visibility; // TAG_PUBLIC, TAG_PRIVATE, TAG_PROTECTED
49

50     public JavaTagger(SystemBuffer buffer)
51     {
52         super(buffer);
53     }
54
55     public synchronized void run()
56     {
57         pos = new Position(buffer.getFirstLine(), 0);
58         token = null;
59         tokenStart = null;
60         tags = new ArrayList JavaDoc();
61         currentClass = null;
62         visibility = 0;
63         final boolean beanShell = buffer.getModeId() == BEANSHELL_MODE;
64         final boolean javaScript = buffer.getModeId() == JAVASCRIPT_MODE;
65         final Stack JavaDoc stack = new Stack JavaDoc();
66         int state = NEUTRAL;
67         while (!pos.atEnd()) {
68             char c = pos.getChar();
69             if (Character.isWhitespace(c)) {
70                 pos.skipWhitespace();
71                 continue;
72             }
73             if (c == '\'' || c == '"') {
74                 pos.skipQuote();
75                 continue;
76             }
77             if (pos.lookingAt("/*")) {
78                 skipComment(pos);
79                 continue;
80             }
81             if (pos.lookingAt("//")) {
82                 skipSingleLineComment(pos);
83                 continue;
84             }
85             if (state == STATIC_INITIALIZER) {
86                 if (c == '{') {
87                     skipBrace();
88                     state = NEUTRAL;
89                     continue;
90                 }
91                 // Anything else...
92
state = NEUTRAL;
93                 // Fall through...
94
}
95             if (state == METHOD_NAME) {
96                 if (c == '{') {
97                     addTag(TAG_METHOD);
98                     visibility = 0;
99                     skipBrace();
100                     state = NEUTRAL;
101                     continue;
102                 }
103                 if (c == ';') {
104                     if (beanShell) {
105                         ; // It's just a function call.
106
} else {
107                         // Abstract or native method.
108
addTag(TAG_METHOD);
109                         visibility = 0;
110                     }
111                     state = NEUTRAL;
112                     pos.next();
113                     continue;
114                 }
115                 if (pos.lookingAt("throws")) {
116                     addTag(TAG_METHOD);
117                     // Set token to null here so we don't try to add this method again.
118
token = null;
119                     visibility = 0;
120                     state = METHOD_PROLOG;
121                     pos.skip(6); // Skip over "throws".
122
continue;
123                 }
124                 state = NEUTRAL;
125                 // Fall through...
126
}
127             if (state == CLASS_PROLOG) {
128                 if (pos.lookingAt("extends")) {
129                      state = EXTENDS;
130                      pos.skip(7); // Skip over "extends".
131
continue;
132                 }
133                 if (pos.lookingAt("implements")) {
134                     state = IMPLEMENTS;
135                     pos.skip(10);
136                     continue;
137                 }
138                 if (c == '{')
139                     state = NEUTRAL;
140                 pos.next();
141                 continue;
142             }
143             if (state == IMPLEMENTS) {
144                 if (c == '{') {
145                     state = NEUTRAL;
146                     pos.next();
147                     continue;
148                 }
149                 // else fall through...
150
}
151             if (state == METHOD_PROLOG) {
152                 // Wait for a semicolon or the opening brace of the method body.
153
if (c == '{') {
154                     skipBrace();
155                     state = NEUTRAL;
156                 } else if (c == ';') {
157                     // Abstract or native method. If token is null, we've
158
// added it already.
159
if (token != null) {
160                         addTag(TAG_METHOD);
161                         visibility = 0;
162                     }
163                     state = NEUTRAL;
164                     pos.next();
165                 } else
166                     pos.next();
167                 continue;
168             }
169             if (state == NEW) {
170                 if (c == '(') {
171                     skipParen();
172                     continue;
173                 } else if (c == '{') {
174                     skipBrace();
175                     continue;
176                 } else if (c == ';')
177                     state = NEUTRAL;
178                 pos.next();
179                 continue;
180             }
181             if (state == FIELD_INITIALIZER) {
182                 if (c == '(') {
183                     skipParen();
184                     continue;
185                 }
186                 if (c == '{') {
187                     skipBrace();
188                     if (javaScript)
189                         state = NEUTRAL;
190                     continue;
191                 }
192                 if (c == ',')
193                     state = NEUTRAL;
194                 else if (c == ';') {
195                     state = NEUTRAL;
196                     visibility = 0;
197                 }
198                 pos.next();
199                 continue;
200             }
201             if (state == IMPLEMENTS) {
202                 if (c == ',') {
203                     pos.next();
204                     continue;
205                 }
206             }
207             if (c == '}') {
208                 if (!stack.empty())
209                     currentClass = (JavaClass) stack.pop();
210                 else
211                     currentClass = null;
212                 pos.next();
213                 continue;
214             }
215             if (Character.isJavaIdentifierStart(c)) {
216                 // If we're in an "extends" or "implements" clause, the token
217
// may be a canonical class name (like "java.lang.String").
218
// Otherwise the token is a simple identifier.
219
gatherToken(state == EXTENDS || state == IMPLEMENTS);
220                 if (state == INTERFACE_NAME) {
221                     if (currentClass != null)
222                         stack.push(currentClass);
223                     final JavaClass parentClass = currentClass;
224                     currentClass = new JavaClass(token, TAG_INTERFACE);
225                     state = CLASS_PROLOG;
226                     // Add a tag for the class itself.
227
tags.add(new JavaTag("interface ".concat(token),
228                         tokenStart,TAG_INTERFACE, visibility, parentClass));
229                     visibility = 0;
230                 } else if (state == CLASS_NAME) {
231                     if (currentClass != null)
232                         stack.push(currentClass);
233                     final JavaClass parentClass = currentClass;
234                     currentClass = new JavaClass(token, TAG_CLASS);
235                     state = CLASS_PROLOG;
236                     // Add a tag for the class itself.
237
tags.add(new JavaTag("class ".concat(token), tokenStart,
238                         TAG_CLASS, visibility, parentClass));
239                     visibility = 0;
240                 } else if (state == EXTENDS) {
241                     tags.add(new JavaTag(token, tokenStart, TAG_EXTENDS,
242                         visibility, currentClass));
243                     state = CLASS_PROLOG;
244                 } else if (state == IMPLEMENTS)
245                     tags.add(new JavaTag(token, tokenStart, TAG_IMPLEMENTS,
246                         visibility, currentClass));
247                 else if (token.equals("package") || token.equals("import"))
248                     skipSemi();
249                 else if (token.equals("interface"))
250                     state = INTERFACE_NAME;
251                 else if (token.equals("class"))
252                     state = CLASS_NAME;
253                 else if (token.equals("static"))
254                     state = STATIC_INITIALIZER;
255                 else if (token.equals("new"))
256                     // Don't be confused by lines like "Runnable r = new Runnable() { ... };"
257
state = NEW;
258                 else if (token.equals("public"))
259                     visibility |= TAG_PUBLIC;
260                 else if (token.equals("protected"))
261                     visibility |= TAG_PROTECTED;
262                 else if (token.equals("private"))
263                     visibility |= TAG_PRIVATE;
264                 continue;
265             }
266             if (c == '(') {
267                 skipParen();
268                 state = METHOD_NAME;
269                 continue;
270             }
271             if (c == '=') {
272                 addTag(TAG_FIELD);
273                 state = FIELD_INITIALIZER;
274                 pos.next();
275                 continue;
276             }
277             // "int x, y;"
278
if (c == ';' || c == ',') {
279                 if (token != null)
280                     addTag(TAG_FIELD);
281                 // Don't reset the visibility until we see the semicolon.
282
if (c == ';')
283                     visibility = 0;
284             }
285             pos.next();
286         }
287         buffer.setTags(tags);
288     }
289
290     private void addTag(int type)
291     {
292         if (currentClass != null) {
293             FastStringBuffer sb = new FastStringBuffer(currentClass.getName());
294             sb.append('.');
295             sb.append(token);
296             tags.add(new JavaTag(sb.toString(), tokenStart, type, visibility, currentClass));
297         } else
298             tags.add(new JavaTag(token, tokenStart, type, visibility));
299     }
300
301     protected static final void skipComment(Position pos)
302     {
303         while (true) {
304             if (pos.lookingAt("*/")) {
305                 pos.skip(2);
306                 return;
307             }
308             if (!pos.next())
309                 return;
310         }
311     }
312
313     protected final void skipSingleLineComment(Position pos)
314     {
315         checkForExplicitTag(pos);
316         Line next = pos.getNextLine();
317         if (next != null)
318             pos.moveTo(next, 0);
319         else
320             pos.setOffset(pos.getLineLength());
321     }
322
323     private final void checkForExplicitTag(Position pos)
324     {
325         if (tags == null)
326             return; // Only supported for Java and JavaScript for now.
327
final String JavaDoc explicitTag =
328             Editor.preferences().getStringProperty(Property.EXPLICIT_TAG);
329         if (explicitTag != null && explicitTag.length() > 0) {
330             pos = pos.copy();
331             String JavaDoc s = pos.getString(); // Substring to end of line.
332
int index = s.indexOf(explicitTag);
333             if (index >= 0) {
334                 pos.skip(index + explicitTag.length());
335                 pos.skipWhitespace();
336                 // Now we're looking at the first character of the tag.
337
FastStringBuffer sb = new FastStringBuffer();
338                 char c = pos.getChar();
339                 if (c == '"') {
340                     while (pos.next()) {
341                         c = pos.getChar();
342                         if (c == '"')
343                             break;
344                         sb.append(c);
345                     }
346                 } else {
347                     // By default, explicit tags are whitespace-delimited, so
348
// they can contain characters that are not legal in Java
349
// identifiers ("symbol-value").
350
sb.append(c);
351                     while (pos.next()) {
352                         c = pos.getChar();
353                         if (Character.isWhitespace(c))
354                             break;
355                         sb.append(c);
356                     }
357                 }
358                 final String JavaDoc tag = sb.toString();
359                 // Exact location of tag is beginning of text on line
360
// containing tag.
361
pos.setOffset(0);
362                 pos.skipWhitespace();
363                 tags.add(new JavaTag(tag, pos, TAG_EXPLICIT, 0, currentClass));
364             }
365         }
366     }
367
368     // If canonical is true, strings like "java.lang.String" are considered to
369
// be a single token. Used for "extends" and "implements" clauses.
370
private void gatherToken(boolean canonical)
371     {
372         tokenStart = new Position(pos);
373         FastStringBuffer sb = new FastStringBuffer();
374         char c;
375         if (canonical) {
376             while (Character.isJavaIdentifierPart(c = pos.getChar()) || c == '.') {
377                 sb.append(c);
378                 if (!pos.next())
379                     break;
380             }
381         } else {
382             while (Character.isJavaIdentifierPart(c = pos.getChar())) {
383                 sb.append(c);
384                 if (!pos.next())
385                     break;
386             }
387         }
388         token = sb.toString();
389     }
390
391     protected void skipParen()
392     {
393         if (pos.next()) {
394             int count = 1;
395             while (true) {
396                 if (pos.lookingAt("/*")) {
397                     skipComment(pos);
398                     continue;
399                 }
400                 if (pos.lookingAt("//")) {
401                     skipSingleLineComment(pos);
402                     continue;
403                 }
404                 char c = pos.getChar();
405                 if (c == '"' || c == '\'') {
406                     pos.skipQuote();
407                     continue;
408                 }
409                 if (c == '(')
410                     ++count;
411                 else if (c == ')')
412                     --count;
413                 if (!pos.next())
414                     break;
415                 if (count == 0)
416                     break;
417             }
418         }
419     }
420
421     protected void skipBrace()
422     {
423         if (pos.next()) {
424             int count = 1;
425             while (true) {
426                 if (pos.lookingAt("/*")) {
427                     skipComment(pos);
428                     continue;
429                 }
430                 if (pos.lookingAt("//")) {
431                     skipSingleLineComment(pos);
432                     continue;
433                 }
434                 char c = pos.getChar();
435                 if (c == '"' || c == '\'') {
436                     pos.skipQuote();
437                     continue;
438                 }
439                 if (c == '\\') {
440                     // Escape. Ignore this char and the next.
441
if (!pos.next())
442                         break;
443                     if (!pos.next())
444                         break;
445                     continue;
446                 }
447                 if (c == '{')
448                     ++count;
449                 else if (c == '}')
450                     --count;
451                 if (!pos.next())
452                     break;
453                 if (count == 0)
454                     break;
455             }
456         }
457     }
458
459     private void skipSemi()
460     {
461         while (!pos.atEnd()) {
462             if (pos.lookingAt("/*")) {
463                 skipComment(pos);
464                 continue;
465             }
466             if (pos.lookingAt("//")) {
467                 skipSingleLineComment(pos);
468                 continue;
469             }
470             char c = pos.getChar();
471             if (c == '"' || c == '\'') {
472                 pos.skipQuote();
473                 continue;
474             } else if (c == ';') {
475                 pos.next();
476                 break;
477             } else
478                 pos.next();
479         }
480     }
481
482     // Used by the C, C++ and Objective C taggers.
483
protected static final void skipPreprocessor(Position pos)
484     {
485         while (true) {
486             Line line = pos.getLine();
487             Line nextLine = line.next();
488             if (nextLine == null) {
489                 pos.setOffset(line.length());
490                 return;
491             }
492             pos.moveTo(nextLine, 0);
493             if (line.length() == 0 || line.charAt(line.length()-1) != '\\')
494                 return;
495         }
496     }
497 }
498
Popular Tags