CodeViewer.java
上传用户:yuyunping
上传日期:2013-03-21
资源大小:1844k
文件大小:15k
源码类别:

Java书籍

开发平台:

Java

  1. /**
  2.  * $RCSfile: CodeViewer.java,v $
  3.  * $Revision: 1.7 $
  4.  * $Date: 2001/07/31 05:39:19 $
  5.  *
  6.  * Copyright (C) 1999-2001 CoolServlets, Inc. All rights reserved.
  7.  *
  8.  * This software is the proprietary information of CoolServlets, Inc.
  9.  * Use is subject to license terms.
  10.  */
  11.  
  12. package net.acai.codeviewer;
  13. import java.util.*;
  14. /**
  15.  * A class that syntax highlights Java code into html.
  16.  * <p>
  17.  * A CodeViewer object is created and then keeps state as
  18.  * lines are passed in. Each line passed in as java text, is returned as syntax
  19.  * highlighted html text.
  20.  * <p>
  21.  * Users of the class can set how the java code will be highlighted with
  22.  * setter methods.
  23.  * <p>
  24.  * Only valid java lines should be passed in since the object maintains
  25.  * state and may not handle illegal code gracefully.
  26.  * <p>
  27.  * The actual system is implemented as a series of filters that deal with
  28.  * specific portions of the java code. The filters are as follows:
  29.  * <p>
  30.  * <pre>
  31.  *  htmlFilter
  32.  *     |__
  33.  *        multiLineCommentFilter
  34.  *           |___
  35.  *                inlineCommentFilter
  36.  *                   |___
  37.  *                        stringFilter
  38.  *                           |__
  39.  *                               keywordFilter
  40.  * </pre>
  41.  *
  42.  */
  43. public class CodeViewer {
  44.     private static HashMap reservedWords = new HashMap(150); // >= Java2 only (also, not thread-safe)
  45.     //private static Hashtable reservedWords = new Hashtable(150); // < Java2 (thread-safe)
  46.     private boolean inMultiLineComment = false;
  47.     private String backgroundColor = "#ffffff";
  48.     private String commentStart = "<font color="#aa0000"><i>";
  49.     private String commentEnd = "</font></i>";
  50.     private String stringStart = "<font color="#000099">";
  51.     private String stringEnd = "</font>";
  52.     private String reservedWordStart = "<b>";
  53.     private String reservedWordEnd = "</b>";
  54.     /**
  55.      * Load all keywords at class loading time.
  56.      */
  57.     static {
  58.         loadKeywords();
  59.     }
  60.     /**
  61.      * Gets the html for the start of a comment block.
  62.      */
  63.     public String getCommentStart() {
  64.         return commentStart;
  65.     }
  66.     /**
  67.      * Sets the html for the start of a comment block.
  68.      */
  69.     public void setCommentStart(String commentStart) {
  70.         this.commentStart = commentStart;
  71.     }
  72.     /**
  73.      * Gets the html for the end of a comment block.
  74.      */
  75.     public String getCommentEnd() {
  76.         return commentEnd;
  77.     }
  78.     /**
  79.      * Sets the html for the end of a comment block.
  80.      */
  81.     public void setCommentEnd(String commentEnd) {
  82.         this.commentEnd = commentEnd;
  83.     }
  84.     /**
  85.      * Gets the html for the start of a String.
  86.      */
  87.     public String getStringStart() {
  88.         return stringStart;
  89.     }
  90.     /**
  91.      * Sets the html for the start of a String.
  92.      */
  93.     public void setStringStart(String stringStart) {
  94.         this.stringStart = stringStart;
  95.     }
  96.     /**
  97.      * Gets the html for the end of a String.
  98.      */
  99.     public String getStringEnd() {
  100.         return stringEnd;
  101.     }
  102.     /**
  103.      * Sets the html for the end of a String.
  104.      */
  105.     public void setStringEnd(String stringEnd) {
  106.         this.stringEnd = stringEnd;
  107.     }
  108.     /**
  109.      * Gets the html for the start of a reserved word.
  110.      */
  111.     public String getReservedWordStart() {
  112.         return reservedWordStart;
  113.     }
  114.     /**
  115.      * Sets the html for the start of a reserved word.
  116.      */
  117.     public void setReservedWordStart(String reservedWordStart) {
  118.         this.reservedWordStart = reservedWordStart;
  119.     }
  120.     /**
  121.      * Gets the html for the end of a reserved word.
  122.      */
  123.     public String getReservedWordEnd() {
  124.         return reservedWordEnd;
  125.     }
  126.     /**
  127.      * Sets the html for the end of a reserved word.
  128.      */
  129.     public void setReservedWordEnd(String reservedWordEnd) {
  130.         this.reservedWordEnd = reservedWordEnd;
  131.     }
  132.     /**
  133.      * Passes off each line to the first filter.
  134.      * @param   line    The line of Java code to be highlighted.
  135.      */
  136.     public String syntaxHighlight( String line ) {
  137.        return htmlFilter(line);
  138.     }
  139.     /*
  140.      * Filter html tags that appear in the java source into more benign text
  141.      * that won't disrupt the output.
  142.      */
  143.     private String htmlFilter( String line ) {
  144.         if( line == null || line.equals("") ) {
  145.             return "";
  146.         }
  147.         // replace ampersands with HTML escape sequence for ampersand;
  148.         line = replace(line, "&", "&#38;");
  149.         // replace " sequences with HTML escape sequences;
  150.         line = replace(line, "\"", "&#92;&#34");
  151.         // replace the \ with HTML escape sequences. fixes a problem when
  152.         // backslashes preceed quotes.
  153.         line = replace(line, "\\", "&#92;&#92;" );
  154.         // replace less-than signs which might be confused
  155.         // by HTML as tag angle-brackets;
  156.         line = replace(line, "<", "&#60;");
  157.         // replace greater-than signs which might be confused
  158.         // by HTML as tag angle-brackets;
  159.         line = replace(line, ">", "&#62;");
  160.         return multiLineCommentFilter(line);
  161.     }
  162.     /*
  163.      * Filter out multiLine comments. State is kept with a private boolean
  164.      * variable.
  165.      */
  166.     private String multiLineCommentFilter(String line) {
  167.         if (line == null || line.equals("")) {
  168.             return "";
  169.         }
  170.         StringBuffer buf = new StringBuffer();
  171.         int index;
  172.         //First, check for the end of a multi-line comment.
  173.         if (inMultiLineComment && (index = line.indexOf("*/")) > -1 && !isInsideString(line,index)) {
  174.             inMultiLineComment = false;
  175.             buf.append(line.substring(0,index));
  176.             buf.append("*/").append(commentEnd);
  177.             if (line.length() > index+2) {
  178.                 buf.append(inlineCommentFilter(line.substring(index+2)));
  179.             }
  180.             return buf.toString();
  181.         }
  182.         //If there was no end detected and we're currently in a multi-line
  183.         //comment, we don't want to do anymore work, so return line.
  184.         else if (inMultiLineComment) {
  185.             return line;
  186.         }
  187.         //We're not currently in a comment, so check to see if the start
  188.         //of a multi-line comment is in this line.
  189.         else if ((index = line.indexOf("/*")) > -1 && !isInsideString(line,index)) {
  190.             inMultiLineComment = true;
  191.             //Return result of other filters + everything after the start
  192.             //of the multiline comment. We need to pass the through the
  193.             //to the multiLineComment filter again in case the comment ends
  194.             //on the same line.
  195.             buf.append(inlineCommentFilter(line.substring(0,index)));
  196.             buf.append(commentStart).append("/*");
  197.             buf.append(multiLineCommentFilter(line.substring(index+2)));
  198.             return buf.toString();
  199.         }
  200.         //Otherwise, no useful multi-line comment information was found so
  201.         //pass the line down to the next filter for processesing.
  202.         else {
  203.             return inlineCommentFilter(line);
  204.         }
  205.     }
  206.     /*
  207.      * Filter inline comments from a line and formats them properly.
  208.      */
  209.     private String inlineCommentFilter(String line) {
  210.         if (line == null || line.equals("")) {
  211.             return "";
  212.         }
  213.         StringBuffer buf = new StringBuffer();
  214.         int index;
  215.         if ((index = line.indexOf("//")) > -1 && !isInsideString(line,index)) {
  216.             buf.append(stringFilter(line.substring(0,index)));
  217.             buf.append(commentStart);
  218.             buf.append(line.substring(index));
  219.             buf.append(commentEnd);
  220.         }
  221.         else {
  222.             buf.append(stringFilter(line));
  223.         }
  224.         return buf.toString();
  225.     }
  226.     /*
  227.      * Filters strings from a line of text and formats them properly.
  228.      */
  229.     private String stringFilter(String line) {
  230.         if (line == null || line.equals("")) {
  231.             return "";
  232.         }
  233.         StringBuffer buf = new StringBuffer();
  234.         if (line.indexOf(""") <= -1) {
  235.             return keywordFilter(line);
  236.         }
  237.         int start = 0;
  238.         int startStringIndex = -1;
  239.         int endStringIndex = -1;
  240.         int tempIndex;
  241.         //Keep moving through String characters until we want to stop...
  242.         while ((tempIndex = line.indexOf(""")) > -1) {
  243.             //We found the beginning of a string
  244.             if (startStringIndex == -1) {
  245.                 startStringIndex = 0;
  246.                 buf.append( stringFilter(line.substring(start,tempIndex)) );
  247.                 buf.append(stringStart).append(""");
  248.                 line = line.substring(tempIndex+1);
  249.             }
  250.             //Must be at the end
  251.             else {
  252.                 startStringIndex = -1;
  253.                 endStringIndex = tempIndex;
  254.                 buf.append(line.substring(0,endStringIndex+1));
  255.                 buf.append(stringEnd);
  256.                 line = line.substring(endStringIndex+1);
  257.             }
  258.         }
  259.         buf.append( keywordFilter(line) );
  260.         return buf.toString();
  261.     }
  262.     /*
  263.      * Filters keywords from a line of text and formats them properly.
  264.      */
  265.     private String keywordFilter( String line ) {
  266.         if( line == null || line.equals("") ) {
  267.             return "";
  268.         }
  269.         StringBuffer buf = new StringBuffer();
  270.         //HashMap usedReservedWords = new HashMap(); // >= Java2 only (not thread-safe)
  271.         Hashtable usedReservedWords = new Hashtable(); // < Java2 (thread-safe)
  272.         int i=0, startAt=0;
  273.         char ch;
  274.         StringBuffer temp = new StringBuffer();
  275.         while( i < line.length() ) {
  276.             temp.setLength(0);
  277.             ch = line.charAt(i);
  278.             startAt = i;
  279.             // 65-90, uppercase letters
  280.             // 97-122, lowercase letters
  281.             while( i<line.length() && ( ( ch >= 65 && ch <= 90 )
  282.                     || ( ch >= 97 && ch <= 122 ) ) ) {
  283.                 temp.append(ch);
  284.                 i++;
  285.                 if( i < line.length() ) {
  286.                     ch = line.charAt(i);
  287.                 }
  288.             }
  289.             String tempString = temp.toString();
  290.             if( reservedWords.containsKey(tempString) && !usedReservedWords.containsKey(tempString)) {
  291.                 usedReservedWords.put(tempString,tempString);
  292.                 line = replace( line, tempString, (reservedWordStart+tempString+reservedWordEnd) );
  293.                 i += (reservedWordStart.length() + reservedWordEnd.length());
  294.             }
  295.             else {
  296.                 i++;
  297.             }
  298.         }
  299.         buf.append(line);
  300.         return buf.toString();
  301.     }
  302.     /**
  303.      * Replaces all instances of oldString with newString in line.
  304.      */
  305.     private static final String replace( String line, String oldString, String newString )
  306.     {
  307.         int i=0;
  308.         if ((i=line.indexOf(oldString, i)) >= 0 ) {
  309.             char [] line2 = line.toCharArray();
  310.             char [] newString2 = newString.toCharArray();
  311.             int oLength = oldString.length();
  312.             StringBuffer buf = new StringBuffer(line2.length);
  313.             buf.append(line2, 0, i).append(newString2);
  314.             i += oLength;
  315.             int j = i;
  316.             while((i=line.indexOf(oldString, i)) > 0 ) {
  317.                 buf.append(line2, j, i-j).append(newString2);
  318.                 i += oLength;
  319.                 j = i;
  320.             }
  321.             buf.append(line2, j, line2.length - j);
  322.             return buf.toString();
  323.         }
  324.         return line;
  325.     }
  326.     /*
  327.      * Checks to see if some position in a line is between String start and
  328.      * ending characters. Not yet used in code or fully working :)
  329.      */
  330.     private boolean isInsideString(String line, int position) {
  331.         if (line.indexOf(""") < 0) {
  332.             return false;
  333.         }
  334.         int index;
  335.         String left = line.substring(0,position);
  336.         String right = line.substring(position);
  337.         int leftCount = 0;
  338.         int rightCount = 0;
  339.         while ((index = left.indexOf(""")) > -1) {
  340.             leftCount ++;
  341.             left = left.substring(index+1);
  342.         }
  343.         while ((index = right.indexOf(""")) > -1) {
  344.             rightCount ++;
  345.             right = right.substring(index+1);
  346.         }
  347.         if (rightCount % 2 != 0 && leftCount % 2 != 0) {
  348.             return true;
  349.         }
  350.         else {
  351.             return false;
  352.         }
  353.     }
  354.     /*
  355.      * Load Hashtable (or HashMap) with Java reserved words. Improved list
  356.      * in version 1.1 taken directly from Java language spec.
  357.      */
  358.     private static void loadKeywords() {
  359.         reservedWords.put("abstract", "abstract");
  360.         reservedWords.put("boolean", "boolean");
  361.         reservedWords.put("break", "break");
  362.         reservedWords.put("byte", "byte");
  363.         reservedWords.put("case", "case");
  364.         reservedWords.put("catch", "catch");
  365.         reservedWords.put("char", "char");
  366.         reservedWords.put("class", "class");
  367.         reservedWords.put("const", "const");
  368.         reservedWords.put("continue", "continue");
  369.         reservedWords.put("default", "default");
  370.         reservedWords.put("do", "do");
  371.         reservedWords.put("double", "double");
  372.         reservedWords.put("else", "else");
  373.         reservedWords.put("extends", "extends");
  374.         reservedWords.put("false", "false");
  375.         reservedWords.put("final", "final");
  376.         reservedWords.put("finally", "finally");
  377.         reservedWords.put("float", "float");
  378.         reservedWords.put("for", "for");
  379.         reservedWords.put("goto", "goto");
  380.         reservedWords.put("if", "if");
  381.         reservedWords.put("implements", "implements");
  382.         reservedWords.put("import", "import");
  383.         reservedWords.put("instanceof", "instanceof");
  384.         reservedWords.put("int", "int");
  385.         reservedWords.put("interface", "interface");
  386.         reservedWords.put("long", "long");
  387.         reservedWords.put("native", "native");
  388.         reservedWords.put("new", "new");
  389.         reservedWords.put("package", "package");
  390.         reservedWords.put("private", "private");
  391.         reservedWords.put("protected", "protected");
  392.         reservedWords.put("public", "public");
  393.         reservedWords.put("return", "return");
  394.         reservedWords.put("short", "short");
  395.         reservedWords.put("static", "static");
  396.         reservedWords.put("strictfp", "strictfp");
  397.         reservedWords.put("super", "super");
  398.         reservedWords.put("switch", "switch");
  399.         reservedWords.put("synchronized", "synchronized");
  400.         reservedWords.put("this", "this");
  401.         reservedWords.put("throw", "throw");
  402.         reservedWords.put("throws", "throws");
  403.         reservedWords.put("transient", "transient");
  404.         reservedWords.put("true", "true");
  405.         reservedWords.put("try", "try");
  406.         reservedWords.put("void", "void");
  407.         reservedWords.put("volatile", "volatile");
  408.         reservedWords.put("while", "while");
  409.     }
  410. }