KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * CFormatter.java
3  *
4  * Copyright (C) 1998-2003 Peter Graves
5  * $Id: CFormatter.java,v 1.3 2003/12/29 19:25:24 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 gnu.regexp.RE;
25 import gnu.regexp.UncheckedRE;
26 import java.util.HashSet JavaDoc;
27
28 public final class CFormatter extends Formatter implements Constants
29 {
30     private static final int C_FORMAT_TEXT = 0;
31     private static final int C_FORMAT_COMMENT = 1;
32     private static final int C_FORMAT_STRING = 2;
33     private static final int C_FORMAT_IDENTIFIER = 3;
34     private static final int C_FORMAT_KEYWORD = 4;
35     private static final int C_FORMAT_FUNCTION = 5;
36     private static final int C_FORMAT_OPERATOR = 6;
37     private static final int C_FORMAT_BRACE = 7;
38     private static final int C_FORMAT_NUMBER = 8;
39     private static final int C_FORMAT_PREPROCESSOR = 9;
40     private static final int C_FORMAT_DISABLED = 10;
41
42     private static final RE lynxArgsRE = new UncheckedRE("ARGS[0-9][0-9]?");
43
44     private final Mode mode;
45
46     public CFormatter(Buffer buffer, int language)
47     {
48         this.buffer = buffer;
49         switch (language) {
50             case LANGUAGE_C:
51                 mode = CMode.getMode();
52                 break;
53             case LANGUAGE_CPP:
54                 mode = CppMode.getMode();
55                 break;
56             case LANGUAGE_OBJC:
57                 mode = ObjCMode.getMode();
58                 break;
59             default:
60                 Debug.assertTrue(false);
61                 mode = null;
62                 break;
63         }
64     }
65
66     private int tokenBegin = 0;
67
68     private void endToken(String JavaDoc text, int tokenEnd, int state)
69     {
70         if (tokenEnd - tokenBegin > 0) {
71             int format = C_FORMAT_TEXT;
72             switch (state) {
73                 case STATE_NEUTRAL:
74                     format = C_FORMAT_TEXT;
75                     break;
76                 case STATE_QUOTE:
77                     format = C_FORMAT_STRING;
78                     break;
79                 case STATE_IDENTIFIER:
80                     format = C_FORMAT_IDENTIFIER;
81                     break;
82                 case STATE_COMMENT:
83                     format = C_FORMAT_COMMENT;
84                     break;
85                 case STATE_OPERATOR:
86                     format = C_FORMAT_OPERATOR;
87                     break;
88                 case STATE_BRACE:
89                     format = C_FORMAT_BRACE;
90                     break;
91                 case STATE_NUMBER:
92                 case STATE_HEXNUMBER:
93                     format = C_FORMAT_NUMBER;
94                     break;
95                 case STATE_PREPROCESSOR:
96                     format = C_FORMAT_PREPROCESSOR;
97                     break;
98             }
99             addSegment(text, tokenBegin, tokenEnd, format);
100             tokenBegin = tokenEnd;
101         }
102     }
103
104     private void parseLine(Line line)
105     {
106         if (line == null) {
107             addSegment("", C_FORMAT_TEXT);
108             return;
109         }
110         String JavaDoc text;
111         if (Editor.tabsAreVisible())
112             text = Utilities.makeTabsVisible(line.getText(), buffer.getTabWidth());
113         else
114             text = Utilities.detab(line.getText(), buffer.getTabWidth());
115         if (line.flags() == STATE_DISABLED) {
116             addSegment(text, C_FORMAT_DISABLED);
117             return;
118         }
119         tokenBegin = 0;
120         boolean isPreprocessorLine = false;
121         char quoteChar = '\0';
122         int state = line.flags();
123         if (state == STATE_QUOTE)
124             quoteChar = '"';
125         int i = 0;
126         final int limit = text.length();
127
128         // Skip whitespace at start of line.
129
while (i < limit) {
130             if (Character.isWhitespace(text.charAt(i))) {
131                 ++i;
132             } else {
133                 endToken(text, i, state);
134                 break;
135             }
136         }
137
138         char c;
139
140         // Test for preprocessor directive. Must be first non-whitespace character.
141
if (i < limit && state == STATE_NEUTRAL) {
142             c = text.charAt(i);
143             if (c == '#') {
144                 state = STATE_PREPROCESSOR;
145                 isPreprocessorLine = true;
146                 ++i;
147                 while (i < limit && (Character.isWhitespace(c = text.charAt(i)) || c == '#'))
148                     ++i;
149                 while (i < limit && (c = text.charAt(i)) >= 'a' && c <= 'z')
150                     ++i;
151                 endToken(text, i, state);
152                 state = STATE_NEUTRAL;
153             }
154         }
155
156         while (i < limit) {
157             c = text.charAt(i);
158             if (state == STATE_COMMENT) {
159                 if (i < limit-1 && c == '*' && text.charAt(i+1) == '/') {
160                     endToken(text, i + 2, state);
161                     state = STATE_NEUTRAL;
162                     i += 2;
163                 } else
164                     ++i;
165                 continue;
166             }
167             if (state == STATE_QUOTE) {
168                 if (c == quoteChar) {
169                     endToken(text, i+1, state);
170                     state = STATE_NEUTRAL;
171                 } else if (c == '\\' && i < limit-1) {
172                     // Escape char.
173
++i;
174                 }
175                 ++i;
176                 continue;
177             }
178
179             // Reaching here, we're not in a comment or a quoted string.
180
if (c == '"' || c == '\'') {
181                 endToken(text, i, state);
182                 state = STATE_QUOTE;
183                 quoteChar = c;
184                 ++i;
185                 continue;
186             }
187             if (c == '/') {
188                 if (i < limit-1) {
189                     if (text.charAt(i+1) == '*') {
190                         endToken(text, i, state);
191                         state = STATE_COMMENT;
192                         i += 2;
193                     } else if (text.charAt(i+1) == '/') {
194                         endToken(text, i, state);
195                         endToken(text, limit, STATE_COMMENT);
196                         return;
197                     } else
198                         ++i;
199                 } else
200                     ++i;
201                 continue;
202             }
203             if (!isPreprocessorLine && isOperatorChar(c)) {
204                 if (state != STATE_OPERATOR) {
205                     endToken(text, i, state);
206                     // Check for keyword (as in e.g. "char*").
207
LineSegment segment = getLastSegment();
208                     if (segment != null && isKeyword(segment.getText()))
209                         segment.setFormat(C_FORMAT_KEYWORD);
210                     state = STATE_OPERATOR;
211                 }
212                 ++i;
213                 continue;
214             }
215             if (c == '{' || c == '}') {
216                 if (state != STATE_BRACE) {
217                     endToken(text, i, state);
218                     if (!isPreprocessorLine) {
219                         // Check for keyword.
220
LineSegment segment = getLastSegment();
221                         if (segment != null && isKeyword(segment.getText()))
222                             segment.setFormat(C_FORMAT_KEYWORD);
223                     }
224                     state = STATE_BRACE;
225                 }
226                 ++i;
227                 continue;
228             }
229             if (state == STATE_OPERATOR || state == STATE_BRACE) {
230                 if (mode.isIdentifierStart(c)) {
231                     endToken(text, i, state);
232                     state = STATE_IDENTIFIER;
233                 } else if (Character.isDigit(c)) {
234                     endToken(text, i, state);
235                     state = STATE_NUMBER;
236                 } else {
237                     endToken(text, i, state);
238                     state = STATE_NEUTRAL;
239                 }
240                 ++i;
241                 continue;
242             }
243             if (state == STATE_IDENTIFIER) {
244                 if (!mode.isIdentifierPart(c)) {
245                     endToken(text, i, state);
246                     // Check for keyword or function.
247
LineSegment segment = getLastSegment();
248                     if (segment != null) {
249                         final String JavaDoc segmentText = segment.getText();
250                         if (segmentText.startsWith("ARGS") &&
251                             lynxArgsRE.isMatch(segmentText)) {
252                             // Lynx source "ARGSnn" macro.
253
;
254                         } else if (!isPreprocessorLine && isKeyword(segmentText)) {
255                             segment.setFormat(C_FORMAT_KEYWORD);
256                         } else if (c == '(') {
257                             segment.setFormat(C_FORMAT_FUNCTION);
258                         } else if (Character.isWhitespace(c)) {
259                             // Look ahead to see if next non-whitespace char is '('.
260
int j = i + 1;
261                             while (j < limit && Character.isWhitespace(c = text.charAt(j)))
262                                 ++j;
263                             if (c == '(') {
264                                 segment.setFormat(C_FORMAT_FUNCTION);
265                             } else if (c == 'A' && text.regionMatches(j, "ARGS", 0, 4)) {
266                                 // Lynx "ARGSnn" macro.
267
if (lynxArgsRE.getMatch(text.substring(j)) != null)
268                                     segment.setFormat(C_FORMAT_FUNCTION);
269                             } else if (c == 'N' && text.regionMatches(j, "NOARGS", 0, 6)) {
270                                 // Lynx macro.
271
segment.setFormat(C_FORMAT_FUNCTION);
272                             }
273                         }
274                     }
275                     state = STATE_NEUTRAL;
276                 }
277                 ++i;
278                 continue;
279             }
280             if (state == STATE_NUMBER) {
281                 if (Character.isDigit(c))
282                     ;
283                 else if (c == 'u' || c == 'U' || c == 'l' || c == 'L')
284                     ;
285                 else if (i - tokenBegin == 1 && c == 'x' || c == 'X')
286                     state = STATE_HEXNUMBER;
287                 else {
288                     endToken(text, i, state);
289                     if (mode.isIdentifierStart(c))
290                         state = STATE_IDENTIFIER;
291                     else
292                         state = STATE_NEUTRAL;
293                 }
294                 ++i;
295                 continue;
296             }
297             if (state == STATE_HEXNUMBER) {
298                 if (Character.isDigit(c))
299                     ;
300                 else if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
301                     ;
302                 else if (c == 'u' || c == 'U' || c == 'l' || c == 'L')
303                     ;
304                 else {
305                     endToken(text, i, state);
306                     if (mode.isIdentifierStart(c))
307                         state = STATE_IDENTIFIER;
308                     else
309                         state = STATE_NEUTRAL;
310                 }
311                 ++i;
312                 continue;
313             }
314             if (state == STATE_NEUTRAL) {
315                 if (mode.isIdentifierStart(c)) {
316                     endToken(text, i, state);
317                     state = STATE_IDENTIFIER;
318                 } else if (Character.isDigit(c)) {
319                     endToken(text, i, state);
320                     state = STATE_NUMBER;
321                 }
322             }
323             ++i;
324         }
325
326         // Reached end of line.
327
endToken(text, i, state);
328         if (state == STATE_IDENTIFIER && !isPreprocessorLine) {
329             // Last token might be a keyword.
330
LineSegment segment = getLastSegment();
331             if (segment != null && isKeyword(segment.getText()))
332                 segment.setFormat(C_FORMAT_KEYWORD);
333         }
334     }
335
336     public LineSegmentList formatLine(Line line)
337     {
338         clearSegmentList();
339         parseLine(line);
340         return segmentList;
341     }
342
343     public boolean parseBuffer()
344     {
345         int state = STATE_NEUTRAL;
346         boolean continued = false;
347         Line line = buffer.getFirstLine();
348         boolean changed = false;
349         while (line != null) {
350             int oldflags = line.flags();
351             // Quoted strings can't span lines in C.
352
if (state == STATE_QUOTE && !continued)
353                 state = STATE_NEUTRAL;
354             if (state != oldflags) {
355                 line.setFlags(state);
356                 changed = true;
357             }
358             if (state == STATE_NEUTRAL) {
359                 if (line.getText().startsWith("#if 0")) {
360                     // Might be null.
361
Line match = CMode.findMatchPreprocessor(line);
362                     while (line != null && line != match) {
363                         oldflags = line.flags();
364                         if (oldflags != STATE_DISABLED) {
365                             line.setFlags(STATE_DISABLED);
366                             changed = true;
367                         }
368                         line = line.next();
369                     }
370                     if (line != null && line.getText().startsWith("#en")) {
371                         oldflags = line.flags();
372                         if (oldflags != STATE_DISABLED) {
373                             line.setFlags(STATE_DISABLED);
374                             changed = true;
375                         }
376                         line = line.next();
377                     }
378                     continue;
379                 }
380             }
381             char quoteChar = '\0';
382             final int limit = line.length();
383             char c = '\0';
384             continued = false;
385             for (int i = 0; i < limit; i++) {
386                 c = line.charAt(i);
387                 if (c == '\\' && i < limit-1) {
388                     // Escape.
389
++i;
390                     continue;
391                 }
392                 if (state == STATE_COMMENT) {
393                     if (c == '*' && i < limit-1) {
394                         c = line.charAt(i+1);
395                         if (c == '/') {
396                             ++i;
397                             state = STATE_NEUTRAL;
398                         }
399                     }
400                     continue;
401                 }
402                 if (state == STATE_QUOTE) {
403                     if (c == quoteChar) {
404                         state = STATE_NEUTRAL;
405                         quoteChar = '\0';
406                     }
407                     continue;
408                 }
409                 // Not in comment or quoted string.
410
if (c == '/' && i < limit-1) {
411                     c = line.charAt(++i);
412                     if (c == '/') {
413                         // Single-line comment beginning.
414
// Ignore rest of line.
415
break;
416                     } else if (c == '*')
417                         state = STATE_COMMENT;
418                 } else if (c == '"' || c == '\'') {
419                     state = STATE_QUOTE;
420                     quoteChar = c;
421                 }
422             }
423             if (c == '\\')
424                 continued = true;
425             line = line.next();
426         }
427         buffer.setNeedsParsing(false);
428         return changed;
429     }
430
431     private static final boolean isOperatorChar(char c)
432     {
433         return "!&|<>=+/*-".indexOf(c) >= 0;
434     }
435
436     public FormatTable getFormatTable()
437     {
438         if (formatTable == null) {
439             formatTable = new FormatTable("CMode");
440             formatTable.addEntryFromPrefs(C_FORMAT_TEXT, "text");
441             formatTable.addEntryFromPrefs(C_FORMAT_COMMENT, "comment");
442             formatTable.addEntryFromPrefs(C_FORMAT_STRING, "string");
443             formatTable.addEntryFromPrefs(C_FORMAT_IDENTIFIER, "identifier", "text");
444             formatTable.addEntryFromPrefs(C_FORMAT_KEYWORD, "keyword");
445             formatTable.addEntryFromPrefs(C_FORMAT_FUNCTION, "function");
446             formatTable.addEntryFromPrefs(C_FORMAT_OPERATOR, "operator");
447             formatTable.addEntryFromPrefs(C_FORMAT_BRACE, "brace");
448             formatTable.addEntryFromPrefs(C_FORMAT_NUMBER, "number");
449             formatTable.addEntryFromPrefs(C_FORMAT_PREPROCESSOR, "preprocessor");
450             formatTable.addEntryFromPrefs(C_FORMAT_DISABLED, "disabled");
451         }
452         return formatTable;
453     }
454 }
455
Popular Tags