Pretty is an application that
formats the java source code in order to make it more readable, with the possibility to remove comments from the source code. It is also possible to convert the java source code in colorized HTML.
Download pretty.jar
As an example, here is the output of Pretty applied to itself:
/* Program pretty: used to reformat a java program in order to look better
05-11-2005 Version 0.1: initial release
05-11-2006 Version 0.2: minor changes
10-11-2007 Version 0.3: conversion to html now supported
Usage: java Pretty [-notabs] [-nocomment] [-strings] [-html] [-help] <filename> [<outfile>]
-notabs use blanks instead of tabs
-nocomment remove every comment
-strings print-out strings
-html convert java source to html, colorize java source
-help print this help message
Requires Java 1.5
javalc6
*/
import java.io.*;
import parser.*;
import java.util.ArrayList;
enum State { SOURCE, STRING, COMMENT, SHORT_COMMENT, CHAR }
interface callback {
void print(String s, State state);
void println(String s, State state);
}
public class Pretty {
boolean notabs, nocomment, strings, html;
PrintWriter out = null;
boolean newline;
int column;
final String blanks = new blanks().generate(1000);
String color; // used only for html
final boolean testmode = false; // testmode, normally false
private class blanks {
String generate(int num) {
String result ="";
for (int i = 0; i < num; i++) result += " ";
return (result);
}
};
private class HandleSource implements callback {
public void print(String s, State state) {
StringBuffer str = new StringBuffer();
if (s.length() == 0) return;
if ((state == State.SOURCE) && newline) {
// source in newline
String ss = removeLeadingChars(s);
if ((ss.length() > 0) && (ss.charAt(0) == '}')) column--;
if (notabs) str.append(blanks.substring(0,column*4));
else for (int i =0; i < column; i++)
{ str.append("\t"); }
str.append(ss);
for (int i = 0; i < ss.length(); i++) {
if (ss.charAt(i) == '{') column++;
if ((i > 0)&&(ss.charAt(i) == '}')) column--;
}
}
else {
if (!nocomment) str.append(s);
else if (state != State.COMMENT) // comment
str.append(s);
if (state == State.SOURCE) // source
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '{') column++;
if (s.charAt(i) == '}') column--;
}
}
if (html) printHTML(str, state);
else out.print(str);
newline = false;
}
public void println(String s, State state) {
print(s, state);
out.println();
newline = true;
}
}
Pretty (String [] files, boolean notabs, boolean nocomment, boolean strings, boolean html) {
this.notabs = notabs;
this.nocomment = nocomment;
this.strings = strings;
this.html = html;
java.util.Arrays.sort(Java.keywords);
newline = true;
column = 0;
try {
String result;
if (files.length == 2) result = files[1];
else if (html) result = files[0]+".html";
else result = files[0]+".new";
LineNumberReader in = new LineNumberReader(new FileReader(files[0]));
long zz = System.currentTimeMillis();
out = open(result, files[0]);
syntax(in, new HandleSource());
close(out);
if (testmode) {
System.out.println("Result: "+lazyCompare(new File(files[0]), new File(result))+" Elapsed time (ms): "+(System.currentTimeMillis() - zz));
}
else System.out.println("Elapsed time (ms): "+(System.currentTimeMillis() - zz));
}
catch (IOException ex) {
System.err.println(ex.toString());
if (out != null)
out.close();
}
}
void syntax (LineNumberReader in, callback cb) throws IOException {
String s;
State state = State.SOURCE;
while ((s = in.readLine()) != null) {
int marker = 0;
main: // for each char in string
for (int i = 0; i < s.length(); i++)
{
switch (state)
{
case SOURCE : // source
if (s.startsWith("//", i))
{ cb.print(s.substring(marker, i), state); // source
marker = i;
state = State.SHORT_COMMENT; // short_comment
break main;
}
if (s.startsWith("/*", i))
{ cb.print(s.substring(marker, i), state); // source
marker = i;
state = State.COMMENT; // comment
break;
}
if (s.charAt(i) == '\"')
{ cb.print(s.substring(marker, i), state); // source
marker = i;
state = State.STRING; // string
break;
}
if (s.charAt(i) == '\'')
{ cb.print(s.substring(marker, i), state); // source
marker = i;
state = State.CHAR; // char
break;
}
break;
case STRING: // string
if (s.charAt(i) == '\"')
{ cb.print(s.substring(marker, i), state); // string
if (strings) System.out.println(s.substring(marker, i+1));
marker = i;
state = State.SOURCE; // source
}
if (s.charAt(i) == '\\') i++; // escape, skip
break;
case COMMENT: // comment
if (s.startsWith("*/", i))
{ cb.print(s.substring(marker, i+2), state); // comment
marker = i+2;
state = State.SOURCE; // source
} // else if (s.indexOf("*/", i) == -1) break main;
break;
case SHORT_COMMENT: // short_comment
throw new RuntimeException("Assert failed");
case CHAR: // char
if (s.charAt(i) == '\'')
{ cb.print(s.substring(marker, i), State.STRING); // string ???
marker = i;
state = State.SOURCE; // source
}
if (s.charAt(i) == '\\') i++; // escape, skip
break;
}
}
// all chars in the current line were analysed
switch (state)
{
case SOURCE : // source
cb.println(s.substring(marker), state); // source
break;
case STRING: // string
throw new IOException("Wrong syntax: string not terminated correctly at line "+in.getLineNumber());
case COMMENT: // comment
cb.println(s.substring(marker), state); // comment
break;
case SHORT_COMMENT: // short_comment
cb.println(s.substring(marker), State.COMMENT); // comment (even if it is short...)
state = State.SOURCE; // source
break;
case CHAR: // char
throw new IOException("Wrong syntax: char not terminated correctly at line "+in.getLineNumber());
}
// main while
}
in.close();
}
String removeLeadingChars(String line) {
int i=0;
while ((i < line.length())&&((line.charAt(i) == ' ')||(line.charAt(i) == '\t'))) i++;
return (line.substring(i));
}
PrintWriter open(String filename, String source) throws IOException {
PrintWriter out = new PrintWriter(new FileWriter(new File(filename)));
if (html) {
out.println();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<html>");
out.println("<head>");
out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">");
out.println("<meta name=\"description\" content=\"Programmi Java\">");
out.println("<title>"+encodeHTML(new StringBuffer(source))+"</title>");
out.println("</head>");
out.println("<body><pre><font color=\"#000000\">");
color = "#000000"; // used only for html
}
return out;
}
void close(PrintWriter out) {
if (html) {
out.println("</font></pre></body>");
out.println("</html>");
}
out.close();
}
boolean testAlfa(char ch) {
return ((ch >= 'a')&&(ch <= 'z'))||((ch >= 'A')&&(ch <= 'Z'));
}
boolean testDigit(char ch) {
return ((ch >= '0')&&(ch <= '9'));
}
void printHTML(StringBuffer str, State state) {
String oldcolor = color;
switch (state) {
case SOURCE : color = "#000000";
break;
case COMMENT : color = "#339966";
break;
default: color = "#0033CC";
}
if (!color.equals(oldcolor)) {
out.print("</font><font color=\""+color+"\">");
}
if (state == State.SOURCE) {
int i = 0;
while (i < str.length()) {
// skips blank and tabs
char ch = str.charAt(i);
if (testAlfa(ch)||(ch == '_')||(ch == '$')) {
StringBuffer id = new StringBuffer();
do {
id.append(ch);
i++;
if (i >= str.length()) break;
ch = str.charAt(i);
}
while (testAlfa(ch)||(ch == '_')||(ch == '$') || testDigit(ch));
if (java.util.Arrays.binarySearch(Java.keywords, id.toString()) >= 0)
out.print("<b>"+encodeHTML(id)+"</b>");
else out.print(encodeHTML(id));
}
if (i >= str.length()) break;
out.print(encodeHTML(new StringBuffer(str.substring(i,i+1))));
i++;
}
} else out.print(encodeHTML(str));
}
public StringBuffer encodeHTML(StringBuffer s) {
StringBuffer out = new StringBuffer();
for(int i=0; i<s.length(); i++)
{
char c = s.charAt(i);
if(c > 127 || c=='"' || c=='<' || c=='>') {
out.append("&#"+(int)c+";");
}
else if (c=='"') out.append(""");
else if (c=='<') out.append("<");
else if (c=='>') out.append(">");
else if (c=='&') out.append("&");
else {
out.append(c);
}
}
return out;
}
public static boolean lazyCompare(File file1, File file2) throws IOException {
InputStream bufferedInput1 = new BufferedInputStream(new java.io.FileInputStream(file1));
InputStream bufferedInput2 = new BufferedInputStream(new java.io.FileInputStream(file2));
int ch = bufferedInput1.read();
int ch2 = bufferedInput2.read();
while(true) {
while ((-1 != ch)&&(" \t\n\r".indexOf(ch) != -1)) {
ch = bufferedInput1.read();
}
while ((-1 != ch2)&&(" \t\n\r".indexOf(ch2) != -1)) {
ch2 = bufferedInput2.read();
}
if (ch == -1) return -1 == ch2;
// System.err.println(ch+"<->"+ch2);
if( ch != ch2 ) return false;
ch = bufferedInput1.read();
ch2 = bufferedInput2.read();
}
}
//main
public static void main(String[] args) throws ParserException
{
Parser cli = new Parser();
Option notabs = cli.addOption("-notabs", false, null,"use blanks instead of tabs");
Option nocomment = cli.addOption("-nocomment", false, null,"remove every comment");
Option strings = cli.addOption("-strings", false, null,"print-out strings");
Option html = cli.addOption("-html", false, null,"convert java to html");
cli.addOption("-help", false, null,"print this help message");
String [] result = cli.parse(args);
if ((result.length > 0)&&(result.length < 3)) {
if (new File(result[0]).isDirectory()) System.err.println("Error: "+result[0]+" is a directory!");
else new Pretty(result, cli.hasOption(notabs), cli.hasOption(nocomment), cli.hasOption(strings), cli.hasOption(html));
}
else System.err.println("Usage: java Pretty"+cli.getUsage("<filename> [<outfile>]"));
}
}