View Javadoc

1   package org.apache.maven.jcoveragereport;
2   
3   /**
4    * CodeViewer.java
5    * 
6    * Bill Lynch & Matt Tucker
7    * CoolServlets.com, October 1999
8    * 
9    * Please visit CoolServlets.com for high quality, open source Java servlets. 
10   *
11   * Copyright (C) 1999  CoolServlets.com
12   * 
13   * Any errors or suggested improvements to this class can be reported
14   * as instructed on Coolservlets.com. We hope you enjoy
15   * this program... your comments will encourage further development!
16   * 
17   * This software is distributed under the terms of The BSD License.
18   * 
19   * Redistribution and use in source and binary forms, with or without
20   * modification, are permitted provided that the following conditions are met:
21   * * Redistributions of source code must retain the above copyright notice,
22   *   this list of conditions and the following disclaimer.
23   * * Redistributions in binary form must reproduce the above copyright notice,
24   *   this list of conditions and the following disclaimer in the documentation
25   *   and/or other materials provided with the distribution.
26   * Neither name of CoolServlets.com nor the names of its contributors may be
27   * used to endorse or promote products derived from this software without
28   * specific prior written permission.
29   * 
30   * THIS SOFTWARE IS PROVIDED BY COOLSERVLETS.COM AND CONTRIBUTORS ``AS IS'' AND
31   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33   * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR CONTRIBUTORS BE LIABLE FOR
34   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
37   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
38   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40   */
41  
42  import java.util.HashMap;
43  
44  public class JavaToHtml
45  {
46      private static HashMap reservedWords = new HashMap();
47      private static boolean inMultiLineComment = false;
48      private static String commentStart = "<span class=\"comment\">";
49      private static String commentEnd = "</span>";
50      private static String stringStart = "<span class=\"string\">";
51      private static String stringEnd = "</span>";
52      private static String reservedWordStart = "<span class=\"keyword\">";
53      private static String reservedWordEnd = "</span>";
54  
55      static
56      {
57          loadHash();
58      }
59  
60      /**
61       * Passes off each line to the first filter.
62       * @param line The line of Java code to be highlighted.
63       * @return Highlighted line.
64       */
65      public static String syntaxHighlight( String line )
66      {
67         return htmlFilter(line);
68      }
69  
70      /*
71       * Filter html tags into more benign text.
72       */
73      private static String htmlFilter(String line)
74      {
75          if (line == null || line.equals(""))
76          {
77              return "";
78          }
79  
80          // replace ampersands with HTML escape sequence for ampersand;
81          line = replace(line, "&", "&#38;");
82  
83          // replace the \\ with HTML escape sequences. fixes a problem when
84          // backslashes preceed quotes.
85          line = replace(line, "\\\\", "&#92;&#92;" );
86  
87          // replace \" sequences with HTML escape sequences;
88          line = replace(line, "" + (char) 92 + (char) 34, "&#92;&#34");
89  
90          // replace less-than signs which might be confused
91          // by HTML as tag angle-brackets;
92          line = replace(line, "<", "&#60;");
93          // replace greater-than signs which might be confused
94          // by HTML as tag angle-brackets;
95          line = replace(line, ">", "&#62;");
96  
97          return multiLineCommentFilter(line);
98      }
99  
100     /*
101      * Filter out multiLine comments. State is kept with a private boolean
102      * variable.
103      */
104     private static String multiLineCommentFilter(String line)
105     {
106         if (line == null || line.equals(""))
107         {
108             return "";
109         }
110         StringBuffer buf = new StringBuffer();
111         int index;
112         //First, check for the end of a multi-line comment.
113         if (inMultiLineComment && (index = line.indexOf("*/")) > -1 && !isInsideString(line, index))
114         {
115             inMultiLineComment = false;
116             buf.append(commentStart);
117             buf.append(line.substring(0, index));
118             buf.append("*/").append(commentEnd);
119             if (line.length() > index + 2)
120             {
121                 buf.append(inlineCommentFilter(line.substring(index + 2)));
122             }
123             return buf.toString();
124         }
125         //If there was no end detected and we're currently in a multi-line
126         //comment, we don't want to do anymore work, so return line.
127         else if (inMultiLineComment)
128         {
129             buf.append(commentStart);
130             buf.append(line);
131             buf.append(commentEnd);
132             return buf.toString();
133         }
134         //We're not currently in a comment, so check to see if the start
135         //of a multi-line comment is in this line.
136         else if ((index = line.indexOf("/*")) > -1 && !isInsideString(line, index))
137         {
138             inMultiLineComment = true;
139             //Return result of other filters + everything after the start
140             //of the multiline comment. We need to pass the through the
141             //to the multiLineComment filter again in case the comment ends
142             //on the same line.
143             buf.append(inlineCommentFilter(line.substring(0, index)));
144             buf.append(commentStart).append("/*");
145             buf.append(multiLineCommentFilter(line.substring(index + 2)));
146             buf.append(commentEnd);
147             return buf.toString();
148         }
149         //Otherwise, no useful multi-line comment information was found so
150         //pass the line down to the next filter for processesing.
151         else
152         {
153             return inlineCommentFilter(line);
154         }
155     }
156 
157     /*
158      * Filter inline comments from a line and formats them properly.
159      */
160     private static String inlineCommentFilter(String line)
161     {
162         if (line == null || line.equals(""))
163         {
164             return "";
165         }
166         StringBuffer buf = new StringBuffer();
167         int index;
168         if ((index = line.indexOf("//")) > -1 && !isInsideString(line, index))
169         {
170             buf.append(stringFilter(line.substring(0, index)));
171             buf.append(commentStart);
172             buf.append(line.substring(index));
173             buf.append(commentEnd);
174         }
175         else
176         {
177             buf.append(stringFilter(line));
178         }
179         return buf.toString();
180     }
181 
182     /*
183      * Filters strings from a line of text and formats them properly.
184      */
185     private static String stringFilter(String line)
186     {
187         if (line == null || line.equals(""))
188         {
189             return "";
190         }
191         StringBuffer buf = new StringBuffer();
192         if (line.indexOf("\"") <= -1)
193         {
194             return keywordFilter(line);
195         }
196         int start = 0;
197         int startStringIndex = -1;
198         int endStringIndex = -1;
199         int tempIndex;
200         //Keep moving through String characters until we want to stop...
201         while ((tempIndex = line.indexOf("\"")) > -1)
202         {
203             //We found the beginning of a string
204             if (startStringIndex == -1)
205             {
206                 startStringIndex = 0;
207                 buf.append(stringFilter(line.substring(start, tempIndex)));
208                 buf.append(stringStart).append("\"");
209                 line = line.substring(tempIndex + 1);
210             }
211             //Must be at the end
212             else
213             {
214                 startStringIndex = -1;
215                 endStringIndex = tempIndex;
216                 buf.append(line.substring(0, endStringIndex + 1));
217                 buf.append(stringEnd);
218                 line = line.substring(endStringIndex + 1);
219             }
220         }
221 
222         buf.append(keywordFilter(line));
223 
224         return buf.toString();
225     }
226 
227     /*
228      * Filters keywords from a line of text and formats them properly.
229      */
230     private static String keywordFilter( String line )
231     {
232         if (line == null || line.equals(""))
233         {
234             return "";
235         }
236         StringBuffer buf = new StringBuffer();
237         HashMap usedReservedWords = new HashMap(); // >= Java2 only (not thread-safe)
238         //Hashtable usedReservedWords = new Hashtable(); // < Java2 (thread-safe)
239         int i = 0, startAt = 0;
240         char ch;
241         StringBuffer temp = new StringBuffer();
242         while (i < line.length())
243         {
244             temp.setLength(0);
245             ch = line.charAt(i);
246             startAt = i;
247             // 65-90, uppercase letters
248             // 97-122, lowercase letters
249             while (i < line.length() && ((ch >= 65 && ch <= 90 )
250                    || (ch >= 97 && ch <= 122)))
251             {
252                 temp.append(ch);
253                 i++;
254                 if (i < line.length())
255                 {
256                     ch = line.charAt(i);
257                 }
258             }
259             String tempString = temp.toString();
260             if (reservedWords.containsKey(tempString) && !usedReservedWords.containsKey(tempString))
261             {
262                 usedReservedWords.put(tempString, tempString);
263                 line = replace(line, tempString, (reservedWordStart + tempString + reservedWordEnd), startAt);
264                 i += (reservedWordStart.length() + reservedWordEnd.length());
265             }
266             else
267             {
268                 i++;
269             }
270         }
271         buf.append(line);
272         return buf.toString();
273     }
274 
275     /*
276      * All important replace method. Replaces all occurences of oldString in
277      * line with newString.
278      */
279     private static String replace( String line, String oldString, String newString )
280     {
281         return replace(line, oldString, newString, 0);
282     }
283 
284     /*
285      * All important replace method. Replaces all occurences of oldString in
286      * line with newString.
287      */
288     private static String replace( String line, String oldString, String newString, int startAt )
289     {
290         int i = startAt;
291         while ((i = line.indexOf(oldString, i)) >= 0)
292         {
293             line = (new StringBuffer().append(line.substring(0, i))
294                                       .append(newString)
295                                       .append(line.substring(i + oldString.length()))).toString();
296             i += newString.length();
297         }
298         return line;
299     }
300 
301     /*
302      * Checks to see if some position in a line is between String start and
303      * ending characters. Not yet used in code or fully working :)
304      */
305     private static boolean isInsideString(String line, int position)
306     {
307         if (line.indexOf("\"") < 0)
308         {
309             return false;
310         }
311         int index;
312         String left = line.substring(0, position);
313         String right = line.substring(position);
314         int leftCount = 0;
315         int rightCount = 0;
316         while ((index = left.indexOf("\"")) > -1)
317         {
318             leftCount++;
319             left = left.substring(index + 1);
320         }
321         while ((index = right.indexOf("\"")) > -1)
322         {
323             rightCount++;
324             right = right.substring(index + 1);
325         }
326         if (rightCount % 2 != 0 && leftCount % 2 != 0)
327         {
328             return true;
329         }
330         else
331         {
332             return false;
333         }
334     }
335 
336     /*
337      * Load Hashtable (or HashMap) with Java reserved words.
338      */
339     private static void loadHash()
340     {
341         reservedWords.put( "abstract", "abstract" );
342         reservedWords.put( "do", "do" );
343         reservedWords.put( "inner", "inner" );
344         reservedWords.put( "public", "public" );
345         reservedWords.put( "var", "var" );
346         reservedWords.put( "boolean", "boolean" );
347         reservedWords.put( "continue", "continue" );
348         reservedWords.put( "int", "int" );
349         reservedWords.put( "return", "return" );
350         reservedWords.put( "void", "void" );
351         reservedWords.put( "break", "break" );
352         reservedWords.put( "else", "else" );
353         reservedWords.put( "interface", "interface" );
354         reservedWords.put( "short", "short" );
355         reservedWords.put( "volatile", "volatile" );
356         reservedWords.put( "byvalue", "byvalue" );
357         reservedWords.put( "extends", "extends" );
358         reservedWords.put( "long", "long" );
359         reservedWords.put( "static", "static" );
360         reservedWords.put( "while", "while" );
361         reservedWords.put( "case", "case" );
362         reservedWords.put( "final", "final" );
363         reservedWords.put( "native", "native" );
364         reservedWords.put( "super", "super" );
365         reservedWords.put( "transient", "transient" );
366         reservedWords.put( "cast", "cast" );
367         reservedWords.put( "float", "float" );
368         reservedWords.put( "new", "new" );
369         reservedWords.put( "rest", "rest" );
370         reservedWords.put( "catch", "catch" );
371         reservedWords.put( "for", "for" );
372         reservedWords.put( "null", "null" );
373         reservedWords.put( "synchronized", "synchronized" );
374         reservedWords.put( "char", "char" );
375         reservedWords.put( "finally", "finally" );
376         reservedWords.put( "operator", "operator" );
377         reservedWords.put( "this", "this" );
378         reservedWords.put( "class", "class" );
379         reservedWords.put( "generic", "generic" );
380         reservedWords.put( "outer", "outer" );
381         reservedWords.put( "switch", "switch" );
382         reservedWords.put( "const", "const" );
383         reservedWords.put( "goto", "goto" );
384         reservedWords.put( "package", "package" );
385         reservedWords.put( "throw", "throw" );
386         reservedWords.put( "double", "double" );
387         reservedWords.put( "if", "if" );
388         reservedWords.put( "private", "private" );
389         reservedWords.put( "true", "true" );
390         reservedWords.put( "default", "default" );
391         reservedWords.put( "import", "import" );
392         reservedWords.put( "protected", "protected" );
393         reservedWords.put( "try", "try" );
394     }
395 }