/*
 * Cobertura - http://cobertura.sourceforge.net/
 *
 * Copyright (C) 2005 Mark Doliner
 *
 * Cobertura is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 *
 * Cobertura is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cobertura; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

package net.sourceforge.cobertura.reporting.html;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

public class JavaToHtml {

	// Could use a J2SE 5.0 enum instead of this.
	public abstract static class State {
		public final static int COMMENT_JAVADOC = 0;
		public final static int COMMENT_MULTI = 1;
		public final static int COMMENT_SINGLE = 2;
		public final static int DEFAULT = 3;
		public final static int KEYWORD = 4;
		public final static int IMPORT_NAME = 5;
		public final static int PACKAGE_NAME = 6;
		public final static int QUOTE_DOUBLE = 8;
		public final static int QUOTE_SINGLE = 9;
	}

	// TODO: Set a style for JavaDoc tags
	//private static final Collection javaJavaDocTags;
	private static final Collection javaKeywords;
	private static final Collection javaPrimitiveLiterals;
	private static final Collection javaPrimitiveTypes;

	static {
		// TODO: Probably need to add anything new in J2SE 5.0
		//final String javaJavaDocTagsArray[] = { "see", "author", "version", "param", "return", "exception",
		//		"deprecated", "throws", "link", "since", "serial", "serialField", "serialData", "beaninfo" };
		final String[] javaKeywordsArray = {"abstract", "assert", "break",
				"case", "catch", "class", "const", "continue", "default", "do",
				"else", "extends", "final", "finally", "for", "goto", "if",
				"interface", "implements", "import", "instanceof", "native",
				"new", "package", "private", "protected", "public", "return",
				"static", "strictfp", "super", "switch", "synchronized",
				"this", "throw", "throws", "transient", "try", "volatile",
				"while"};
		final String javaPrimitiveTypesArray[] = {"boolean", "byte", "char",
				"double", "float", "int", "long", "short", "void"};
		final String javaPrimitiveLiteralsArray[] = {"false", "null", "true"};

		//javaJavaDocTags = new HashSet(Arrays.asList(javaJavaDocTagsArray));
		javaKeywords = new HashSet(Arrays.asList(javaKeywordsArray));
		javaPrimitiveTypes = new HashSet(Arrays.asList(javaPrimitiveTypesArray));
		javaPrimitiveLiterals = new HashSet(Arrays
				.asList(javaPrimitiveLiteralsArray));
	}

	private int state = State.DEFAULT;

	private static String escapeEntity(final char character) {
		if (character == '&')
			return "&";
		else if (character == '<')
			return "&lt;";
		else if (character == '>')
			return "&gt;";
		else if (character == '\t')
			return "        ";
		else
			return new Character(character).toString();
	}

	
Add HTML colorization to a block of Java code.
Params:
  • text – The block of Java code.
Returns:The same block of Java code with added span tags. Newlines are preserved.
/** * Add HTML colorization to a block of Java code. * * @param text The block of Java code. * * @return The same block of Java code with added span tags. * Newlines are preserved. */
public String process(final String text) { if (text == null) throw new IllegalArgumentException("\"text\" can not be null."); StringBuffer ret = new StringBuffer(); // This look is really complicated because it preserves all // combinations of \r, \n, \r\n, and \n\r int begin, end, nextCR; begin = 0; end = text.indexOf('\n', begin); nextCR = text.indexOf('\r', begin); if ((nextCR != -1) && ((end == -1) || (nextCR < end))) end = nextCR; while (end != -1) { ret.append(processLine(text.substring(begin, end)) + "<br/>"); if ((end + 1 < text.length()) && ((text.charAt(end + 1) == '\n') || (text.charAt(end + 1) == '\r'))) { ret.append(text.substring(end, end + 1)); begin = end + 2; } else { ret.append(text.charAt(end)); begin = end + 1; } end = text.indexOf('\n', begin); nextCR = text.indexOf('\r', begin); if ((nextCR != -1) && ((end == -1) || (nextCR < end))) end = nextCR; } ret.append(processLine(text.substring(begin))); return ret.toString(); }
Add HTML colorization to a single line of Java code.
Params:
  • line – One line of Java code.
Returns:The same line of Java code with added span tags.
/** * Add HTML colorization to a single line of Java code. * * @param line One line of Java code. * * @return The same line of Java code with added span tags. */
private String processLine(final String line) { if (line == null) throw new IllegalArgumentException("\"line\" can not be null."); if ((line.indexOf('\n') != -1) || (line.indexOf('\r') != -1)) throw new IllegalArgumentException( "\"line\" can not contain newline or carriage return characters."); StringBuffer ret = new StringBuffer(); int currentIndex = 0; while (currentIndex != line.length()) { if (state == State.DEFAULT) { if ((currentIndex + 2 < line.length()) && line.substring(currentIndex, currentIndex + 3) .equals("/**")) { state = State.COMMENT_JAVADOC; } else if ((currentIndex + 1 < line.length()) && line.substring(currentIndex, currentIndex + 2) .equals("/*")) { state = State.COMMENT_MULTI; } else if ((currentIndex + 1 < line.length()) && (line.substring(currentIndex, currentIndex + 2) .equals("//"))) { state = State.COMMENT_SINGLE; } else if (Character.isJavaIdentifierStart(line .charAt(currentIndex))) { state = State.KEYWORD; } else if (line.charAt(currentIndex) == '\'') { state = State.QUOTE_SINGLE; } else if (line.charAt(currentIndex) == '"') { state = State.QUOTE_DOUBLE; } else { // Default: No highlighting. ret.append(escapeEntity(line.charAt(currentIndex++))); } } // End of State.DEFAULT else if ((state == State.COMMENT_MULTI) || (state == State.COMMENT_JAVADOC)) { // Print everything from the current character until the // closing */ No exceptions. ret.append("<span class=\"comment\">"); while ((currentIndex != line.length()) && !((currentIndex + 1 < line.length()) && (line .substring(currentIndex, currentIndex + 2) .equals("*/")))) { ret.append(escapeEntity(line.charAt(currentIndex++))); } if (currentIndex == line.length()) { ret.append("</span>"); } else { ret.append("*/</span>"); state = State.DEFAULT; currentIndex += 2; } } // End of State.COMMENT_MULTI else if (state == State.COMMENT_SINGLE) { // Print everything from the current character until the // end of the line ret.append("<span class=\"comment\">"); while (currentIndex != line.length()) { ret.append(escapeEntity(line.charAt(currentIndex++))); } ret.append("</span>"); state = State.DEFAULT; } // End of State.COMMENT_SINGLE else if (state == State.KEYWORD) { StringBuffer tmp = new StringBuffer(); do { tmp.append(line.charAt(currentIndex++)); } while ((currentIndex != line.length()) && (Character.isJavaIdentifierPart(line .charAt(currentIndex)))); if (javaKeywords.contains(tmp.toString())) ret.append("<span class=\"keyword\">" + tmp + "</span>"); else if (javaPrimitiveLiterals.contains(tmp.toString())) ret.append("<span class=\"keyword\">" + tmp + "</span>"); else if (javaPrimitiveTypes.contains(tmp.toString())) ret.append("<span class=\"keyword\">" + tmp + "</span>"); else ret.append(tmp); if (tmp.toString().equals("import")) state = State.IMPORT_NAME; else if (tmp.toString().equals("package")) state = State.PACKAGE_NAME; else state = State.DEFAULT; } // End of State.KEYWORD else if (state == State.IMPORT_NAME) { ret.append(escapeEntity(line.charAt(currentIndex++))); state = State.DEFAULT; } // End of State.IMPORT_NAME else if (state == State.PACKAGE_NAME) { ret.append(escapeEntity(line.charAt(currentIndex++))); state = State.DEFAULT; } // End of State.PACKAGE_NAME else if (state == State.QUOTE_DOUBLE) { // Print everything from the current character until the // closing ", checking for \" ret.append("<span class=\"string\">"); do { ret.append(escapeEntity(line.charAt(currentIndex++))); } while ((currentIndex != line.length()) && (!(line.charAt(currentIndex) == '"') || ((line .charAt(currentIndex - 1) == '\\') && (line .charAt(currentIndex - 2) != '\\')))); if (currentIndex == line.length()) { ret.append("</span>"); } else { ret.append("\"</span>"); state = State.DEFAULT; currentIndex++; } } // End of State.QUOTE_DOUBLE else if (state == State.QUOTE_SINGLE) { // Print everything from the current character until the // closing ', checking for \' ret.append("<span class=\"string\">"); do { ret.append(escapeEntity(line.charAt(currentIndex++))); } while ((currentIndex != line.length()) && (!(line.charAt(currentIndex) == '\'') || ((line .charAt(currentIndex - 1) == '\\') && (line .charAt(currentIndex - 2) != '\\')))); if (currentIndex == line.length()) { ret.append("</span>"); } else { ret.append("\'</span>"); state = State.DEFAULT; currentIndex++; } } // End of State.QUOTE_SINGLE else { // Default: No highlighting. ret.append(escapeEntity(line.charAt(currentIndex++))); } // End of unknown state } return ret.toString(); }
Reset the state of this Java parser. Call this if you have been parsing one Java file and you want to begin parsing another Java file.
/** * Reset the state of this Java parser. Call this if you have * been parsing one Java file and you want to begin parsing * another Java file. */
public void reset() { state = State.DEFAULT; } }