KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > nemesis > forum > util > tool > CodeViewer


1 /*
2  * NEMESIS-FORUM.
3  * Copyright (C) 2002 David Laurent(lithium2@free.fr). All rights reserved.
4  *
5  * Copyright (c) 2000 The Apache Software Foundation. All rights reserved.
6  *
7  * Copyright (C) 2001 Yasna.com. All rights reserved.
8  *
9  * Copyright (C) 2000 CoolServlets.com. All rights reserved.
10  *
11  * NEMESIS-FORUM. is free software; you can redistribute it and/or
12  * modify it under the terms of the Apache Software License, Version 1.1,
13  * or (at your option) any later version.
14  *
15  * NEMESIS-FORUM core framework, NEMESIS-FORUM backoffice, NEMESIS-FORUM frontoffice
16  * application are parts of NEMESIS-FORUM and are distributed under
17  * same terms of licence.
18  *
19  *
20  * NEMESIS-FORUM includes software developed by the Apache Software Foundation (http://www.apache.org/)
21  * and software developed by CoolServlets.com (http://www.coolservlets.com).
22  * and software developed by Yasna.com (http://www.yasna.com).
23  *
24  */

25
26 package org.nemesis.forum.util.tool;
27
28 import java.util.Hashtable JavaDoc;
29
30 /**
31  * A class that syntax highlights Java code into html.
32  * <p>
33  * A CodeViewer object is created and then keeps state as
34  * lines are passed in. Each line passed in as java text, is returned as syntax
35  * highlighted html text.
36  * <p>
37  * Users of the class can set how the java code will be highlighted with
38  * setter methods.
39  * <p>
40  * Only valid java lines should be passed in since the object maintains
41  * state and may not handle illegal code gracefully.
42  * <p>
43  * The actual system is implemented as a series of filters that deal with
44  * specific portions of the java code. The filters are as follows:
45  * <p>
46  * <pre>
47  * htmlFilter
48  * |__
49  * multiLineCommentFilter
50  * |___
51  * inlineCommentFilter
52  * |___
53  * stringFilter
54  * |__
55  * keywordFilter
56  * </pre>
57  *
58  */

59 public class CodeViewer {
60
61     //private static HashMap reservedWords = new HashMap(80); // >= Java2 only (also, not thread-safe)
62
private static Hashtable JavaDoc reservedWords = new Hashtable JavaDoc(80); // < Java2 (thread-safe)
63
private boolean inMultiLineComment = false;
64     private String JavaDoc backgroundColor = "#ffffff";
65     private String JavaDoc commentStart = "<font color=\"#aa0000\"><i>";
66     private String JavaDoc commentEnd = "</font></i>";
67     private String JavaDoc stringStart = "<font color=\"#000099\">";
68     private String JavaDoc stringEnd = "</font>";
69     private String JavaDoc reservedWordStart = "<b>";
70     private String JavaDoc reservedWordEnd = "</b>";
71
72     /**
73      * Load all keywords at class loading time.
74      */

75     static {
76         loadKeywords();
77     }
78
79     /**
80      * Gets the html for the start of a comment block.
81      */

82     public String JavaDoc getCommentStart() {
83         return commentStart;
84     }
85
86     /**
87      * Sets the html for the start of a comment block.
88      */

89     public void setCommentStart(String JavaDoc commentStart) {
90         this.commentStart = commentStart;
91     }
92
93     /**
94      * Gets the html for the end of a comment block.
95      */

96     public String JavaDoc getCommentEnd() {
97         return commentEnd;
98     }
99
100     /**
101      * Sets the html for the end of a comment block.
102      */

103     public void setCommentEnd(String JavaDoc commentEnd) {
104         this.commentEnd = commentEnd;
105     }
106
107     /**
108      * Gets the html for the start of a String.
109      */

110     public String JavaDoc getStringStart() {
111         return stringStart;
112     }
113
114     /**
115      * Sets the html for the start of a String.
116      */

117     public void setStringStart(String JavaDoc stringStart) {
118         this.stringStart = stringStart;
119     }
120
121     /**
122      * Gets the html for the end of a String.
123      */

124     public String JavaDoc getStringEnd() {
125         return stringEnd;
126     }
127
128     /**
129      * Sets the html for the end of a String.
130      */

131     public void setStringEnd(String JavaDoc stringEnd) {
132         this.stringEnd = stringEnd;
133     }
134
135     /**
136      * Gets the html for the start of a reserved word.
137      */

138     public String JavaDoc getReservedWordStart() {
139         return reservedWordStart;
140     }
141
142     /**
143      * Sets the html for the start of a reserved word.
144      */

145     public void setReservedWordStart(String JavaDoc reservedWordStart) {
146         this.reservedWordStart = reservedWordStart;
147     }
148
149     /**
150      * Gets the html for the end of a reserved word.
151      */

152     public String JavaDoc getReservedWordEnd() {
153         return reservedWordEnd;
154     }
155
156     /**
157      * Sets the html for the end of a reserved word.
158      */

159     public void setReservedWordEnd(String JavaDoc reservedWordEnd) {
160         this.reservedWordEnd = reservedWordEnd;
161     }
162
163     /**
164      * Passes off each line to the first filter.
165      * @param line The line of Java code to be highlighted.
166      */

167     public String JavaDoc syntaxHighlight( String JavaDoc line ) {
168        return htmlFilter(line);
169     }
170
171     /*
172      * Filter html tags that appear in the java source into more benign text
173      * that won't disrupt the output.
174      */

175     private String JavaDoc htmlFilter( String JavaDoc line ) {
176         if( line == null || line.equals("") ) {
177             return "";
178         }
179         // replace ampersands with HTML escape sequence for ampersand;
180
line = replace(line, "&", "&#38;");
181
182         // replace \" sequences with HTML escape sequences;
183
line = replace(line, "\\\"", "&#92;&#34");
184
185         // replace the \\ with HTML escape sequences. fixes a problem when
186
// backslashes preceed quotes.
187
line = replace(line, "\\\\", "&#92;&#92;" );
188
189         // replace less-than signs which might be confused
190
// by HTML as tag angle-brackets;
191
line = replace(line, "<", "&#60;");
192         // replace greater-than signs which might be confused
193
// by HTML as tag angle-brackets;
194
line = replace(line, ">", "&#62;");
195
196         return multiLineCommentFilter(line);
197     }
198
199     /*
200      * Filter out multiLine comments. State is kept with a private boolean
201      * variable.
202      */

203     private String JavaDoc multiLineCommentFilter(String JavaDoc line) {
204         if (line == null || line.equals("")) {
205             return "";
206         }
207         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
208         int index;
209         //First, check for the end of a multi-line comment.
210
if (inMultiLineComment && (index = line.indexOf("*/")) > -1 && !isInsideString(line,index)) {
211             inMultiLineComment = false;
212             buf.append(line.substring(0,index));
213             buf.append("*/").append(commentEnd);
214             if (line.length() > index+2) {
215                 buf.append(inlineCommentFilter(line.substring(index+2)));
216             }
217             return buf.toString();
218         }
219         //If there was no end detected and we're currently in a multi-line
220
//comment, we don't want to do anymore work, so return line.
221
else if (inMultiLineComment) {
222             return line;
223         }
224         //We're not currently in a comment, so check to see if the start
225
//of a multi-line comment is in this line.
226
else if ((index = line.indexOf("/*")) > -1 && !isInsideString(line,index)) {
227             inMultiLineComment = true;
228             //Return result of other filters + everything after the start
229
//of the multiline comment. We need to pass the through the
230
//to the multiLineComment filter again in case the comment ends
231
//on the same line.
232
buf.append(inlineCommentFilter(line.substring(0,index)));
233             buf.append(commentStart).append("/*");
234             buf.append(multiLineCommentFilter(line.substring(index+2)));
235             return buf.toString();
236         }
237         //Otherwise, no useful multi-line comment information was found so
238
//pass the line down to the next filter for processesing.
239
else {
240             return inlineCommentFilter(line);
241         }
242     }
243
244     /*
245      * Filter inline comments from a line and formats them properly.
246      */

247     private String JavaDoc inlineCommentFilter(String JavaDoc line) {
248         if (line == null || line.equals("")) {
249             return "";
250         }
251         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
252         int index;
253         if ((index = line.indexOf("//")) > -1 && !isInsideString(line,index)) {
254             buf.append(stringFilter(line.substring(0,index)));
255             buf.append(commentStart);
256             buf.append(line.substring(index));
257             buf.append(commentEnd);
258         }
259         else {
260             buf.append(stringFilter(line));
261         }
262         return buf.toString();
263     }
264
265     /*
266      * Filters strings from a line of text and formats them properly.
267      */

268     private String JavaDoc stringFilter(String JavaDoc line) {
269         if (line == null || line.equals("")) {
270             return "";
271         }
272         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
273         if (line.indexOf("\"") <= -1) {
274             return keywordFilter(line);
275         }
276         int start = 0;
277         int startStringIndex = -1;
278         int endStringIndex = -1;
279         int tempIndex;
280         //Keep moving through String characters until we want to stop...
281
while ((tempIndex = line.indexOf("\"")) > -1) {
282             //We found the beginning of a string
283
if (startStringIndex == -1) {
284                 startStringIndex = 0;
285                 buf.append( stringFilter(line.substring(start,tempIndex)) );
286                 buf.append(stringStart).append("\"");
287                 line = line.substring(tempIndex+1);
288             }
289             //Must be at the end
290
else {
291                 startStringIndex = -1;
292                 endStringIndex = tempIndex;
293                 buf.append(line.substring(0,endStringIndex+1));
294                 buf.append(stringEnd);
295                 line = line.substring(endStringIndex+1);
296             }
297         }
298         buf.append( keywordFilter(line) );
299         return buf.toString();
300     }
301
302     /*
303      * Filters keywords from a line of text and formats them properly.
304      */

305     private String JavaDoc keywordFilter( String JavaDoc line ) {
306         if( line == null || line.equals("") ) {
307             return "";
308         }
309         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
310         //HashMap usedReservedWords = new HashMap(); // >= Java2 only (not thread-safe)
311
Hashtable JavaDoc usedReservedWords = new Hashtable JavaDoc(); // < Java2 (thread-safe)
312
int i=0, startAt=0;
313         char ch;
314         StringBuffer JavaDoc temp = new StringBuffer JavaDoc();
315         while( i < line.length() ) {
316             temp.setLength(0);
317             ch = line.charAt(i);
318             startAt = i;
319             // 65-90, uppercase letters
320
// 97-122, lowercase letters
321
while( i<line.length() && ( ( ch >= 65 && ch <= 90 )
322                     || ( ch >= 97 && ch <= 122 ) ) ) {
323                 temp.append(ch);
324                 i++;
325                 if( i < line.length() ) {
326                     ch = line.charAt(i);
327                 }
328             }
329             String JavaDoc tempString = temp.toString();
330             if( reservedWords.containsKey(tempString) && !usedReservedWords.containsKey(tempString)) {
331                 usedReservedWords.put(tempString,tempString);
332                 line = replace( line, tempString, (reservedWordStart+tempString+reservedWordEnd) );
333                 i += (reservedWordStart.length() + reservedWordEnd.length());
334             }
335             else {
336                 i++;
337             }
338         }
339         buf.append(line);
340         return buf.toString();
341     }
342
343     /**
344      * Replaces all instances of oldString with newString in line.
345      */

346     public static final String JavaDoc replace( String JavaDoc line, String JavaDoc oldString, String JavaDoc newString )
347     {
348         int i=0;
349         if ( ( i=line.indexOf( oldString, i ) ) >= 0 ) {
350             char [] line2 = line.toCharArray();
351             char [] newString2 = newString.toCharArray();
352             int oLength = oldString.length();
353             StringBuffer JavaDoc buf = new StringBuffer JavaDoc(line2.length);
354             buf.append(line2, 0, i).append(newString2);
355             i += oLength;
356             int j = i;
357             while( ( i=line.indexOf( oldString, i ) ) > 0 ) {
358                 buf.append(line2, j, i-j).append(newString2);
359                 i += oLength;
360                 j = i;
361             }
362             buf.append(line2, j, line2.length - j);
363             return buf.toString();
364         }
365         return line;
366     }
367
368     /*
369      * Checks to see if some position in a line is between String start and
370      * ending characters. Not yet used in code or fully working :)
371      */

372     private boolean isInsideString(String JavaDoc line, int position) {
373         if (line.indexOf("\"") < 0) {
374             return false;
375         }
376         int index;
377         String JavaDoc left = line.substring(0,position);
378         String JavaDoc right = line.substring(position);
379         int leftCount = 0;
380         int rightCount = 0;
381         while ((index = left.indexOf("\"")) > -1) {
382             leftCount ++;
383             left = left.substring(index+1);
384         }
385         while ((index = right.indexOf("\"")) > -1) {
386             rightCount ++;
387             right = right.substring(index+1);
388         }
389         if (rightCount % 2 != 0 && leftCount % 2 != 0) {
390             return true;
391         }
392         else {
393             return false;
394         }
395     }
396
397     /*
398      * Load Hashtable (or HashMap) with Java reserved words. Improved list
399      * in version 1.1 taken directly from Java language spec.
400      */

401     private static void loadKeywords() {
402         reservedWords.put( "abstract", "abstract" );
403         reservedWords.put( "boolean", "boolean" );
404         reservedWords.put( "break", "break" );
405         reservedWords.put( "byte", "byte" );
406         reservedWords.put( "case", "case" );
407         reservedWords.put( "catch", "catch" );
408         reservedWords.put( "char", "char" );
409         reservedWords.put( "class", "class" );
410         reservedWords.put( "const", "const" );
411         reservedWords.put( "continue", "continue" );
412         reservedWords.put( "default", "default" );
413         reservedWords.put( "do", "do" );
414         reservedWords.put( "double", "double" );
415         reservedWords.put( "else", "else" );
416         reservedWords.put( "extends", "extends" );
417         reservedWords.put( "final", "final" );
418         reservedWords.put( "finally", "finally" );
419         reservedWords.put( "float", "float" );
420         reservedWords.put( "for", "for" );
421         reservedWords.put( "goto", "goto" );
422         reservedWords.put( "if", "if" );
423         reservedWords.put( "implements", "implements" );
424         reservedWords.put( "import", "import" );
425         reservedWords.put( "instanceof", "instanceof" );
426         reservedWords.put( "int", "int" );
427         reservedWords.put( "interface", "interface" );
428         reservedWords.put( "long", "long" );
429         reservedWords.put( "native", "native" );
430         reservedWords.put( "new", "new" );
431         reservedWords.put( "package", "package" );
432         reservedWords.put( "private", "private" );
433         reservedWords.put( "protected", "protected" );
434         reservedWords.put( "public", "public" );
435         reservedWords.put( "return", "return" );
436         reservedWords.put( "short", "short" );
437         reservedWords.put( "static", "static" );
438         reservedWords.put( "strictfp", "strictfp" );
439         reservedWords.put( "super", "super" );
440         reservedWords.put( "switch", "switch" );
441         reservedWords.put( "synchronized", "synchronized" );
442         reservedWords.put( "this", "this" );
443         reservedWords.put( "throw", "throw" );
444         reservedWords.put( "throws", "throws" );
445         reservedWords.put( "transient", "transient" );
446         reservedWords.put( "try", "try" );
447         reservedWords.put( "void", "void" );
448         reservedWords.put( "volatile", "volatile" );
449         reservedWords.put( "while", "while" );
450     }
451 }
452
453
Popular Tags