KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > Yasna > codeviewer > CodeViewer


1 /**
2  * CodeViewer.java
3  * CoolServlets.com
4  * July 17, 2000
5  *
6  * Copyright (C) 2000 CoolServlets.com
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  * 1) Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  * 2) Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * 3) Neither the name CoolServlets.com nor the names of its contributors may be
16  * used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY COOLSERVLETS.COM AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

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

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

80     static {
81         loadKeywords();
82     }
83
84     /**
85      * Gets the html for the start of a comment block.
86      */

87     public String JavaDoc getCommentStart() {
88         return commentStart;
89     }
90
91     /**
92      * Sets the html for the start of a comment block.
93      */

94     public void setCommentStart(String JavaDoc commentStart) {
95         this.commentStart = commentStart;
96     }
97
98     /**
99      * Gets the html for the end of a comment block.
100      */

101     public String JavaDoc getCommentEnd() {
102         return commentEnd;
103     }
104
105     /**
106      * Sets the html for the end of a comment block.
107      */

108     public void setCommentEnd(String JavaDoc commentEnd) {
109         this.commentEnd = commentEnd;
110     }
111
112     /**
113      * Gets the html for the start of a String.
114      */

115     public String JavaDoc getStringStart() {
116         return stringStart;
117     }
118
119     /**
120      * Sets the html for the start of a String.
121      */

122     public void setStringStart(String JavaDoc stringStart) {
123         this.stringStart = stringStart;
124     }
125
126     /**
127      * Gets the html for the end of a String.
128      */

129     public String JavaDoc getStringEnd() {
130         return stringEnd;
131     }
132
133     /**
134      * Sets the html for the end of a String.
135      */

136     public void setStringEnd(String JavaDoc stringEnd) {
137         this.stringEnd = stringEnd;
138     }
139
140     /**
141      * Gets the html for the start of a reserved word.
142      */

143     public String JavaDoc getReservedWordStart() {
144         return reservedWordStart;
145     }
146
147     /**
148      * Sets the html for the start of a reserved word.
149      */

150     public void setReservedWordStart(String JavaDoc reservedWordStart) {
151         this.reservedWordStart = reservedWordStart;
152     }
153
154     /**
155      * Gets the html for the end of a reserved word.
156      */

157     public String JavaDoc getReservedWordEnd() {
158         return reservedWordEnd;
159     }
160
161     /**
162      * Sets the html for the end of a reserved word.
163      */

164     public void setReservedWordEnd(String JavaDoc reservedWordEnd) {
165         this.reservedWordEnd = reservedWordEnd;
166     }
167
168     /**
169      * Passes off each line to the first filter.
170      * @param line The line of Java code to be highlighted.
171      */

172     public String JavaDoc syntaxHighlight( String JavaDoc line ) {
173        return htmlFilter(line);
174     }
175
176     /*
177      * Filter html tags that appear in the java source into more benign text
178      * that won't disrupt the output.
179      */

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

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

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

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

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

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

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

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