Formatting a Java program for web

Tuesday, October 09, 2012

As a programmer we should always make sure our program is easily readable and understandable by other programming fellows. A program can be easily followed only when it is formatted properly i.e.; with proper indentation, with different coloring for keywords, string literals etc.

I've implemented a Java program to format the code which can be shared on the web. i.e; my program will apply a HTML element with class name (for eg, keywords will be applied a style with <span class="keyword">) for each type of programming element.

All the programs that are posted on my blog uses this program. Please note that indentation hasn't been implemented yet. I shall update again when I implement that. This program output will be generated as an HTML file with all the styles applied.

Every IDE will execute a program something similar to this when you request IDE to reformat your program.

FormatProgramV2.java
import java.io.FileReader;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.util.Stack;

/**
 * This class formats a given Java program.
 * @author SANTHOSH REDDY MANDADI
 * @version 1.0
 * @since 03-October-2012
 */
public class FormatProgramV2
{
  public static void main(String args[]) throws Exception
  {
    String fileName = "";
    //Checking for the file name through command line argument
    if(args!=null && args.length>0)
    {
      fileName = args[0];
    }
    else
    {
      System.out.println("Please input a Java program file name as below:");
      System.out.println("java FormatProgramV2 Example.java"); 
      return;
    }

    //Exiting program execution if the file is not of Java
    if(!fileName.endsWith(".java"))
    {
      System.out.println("Sorry, this program can format only Java programs.");
      return;
    }

    if(!(new File(fileName).exists()))
    {
      System.out.println("File not found - "+fileName);
      return;
    }

    //Opening file input stream
    FileReader fileReader = new FileReader(fileName);
    BufferedReader reader = new BufferedReader(fileReader);

    //Creating a new file .html to generate output
    FileWriter fileWriter = new FileWriter(fileName.replaceAll("\\.java", ".html"));
    BufferedWriter writer = new BufferedWriter(fileWriter);

    //Reserved words list to apply a keyword class
    String reservedWords[] = {"class", "abstract", "assert", "boolean", "break", 
                              "byte", "case", "catch", "char", "const", "continue", 
                              "default", "do", "double", "else", "enum",  "extends",
                              "final", "finally", "float", "for", "goto", "if", 
                              "implements", "import", "instanceof", "int", "interface",
                              "long", "native", "new", "package", "private", 
                              "protected", "public", "return", "short", "static", 
                              "strictfp", "super", "switch", "synchronized", "this",
                              "throw", "throws", "transient", "try", "void", 
                              "volatile", "while"
                             };
    //String to hold the entire program
    String program = "";
    //String to hold the generate HTML source
    StringBuffer html;
    //Temporary variable to read content from file
    String str = "";

    //Flag to hold whether doubleQuote started
    boolean doubleQuote = false;
    //Flag to hold whether singleQuote started
    boolean singleQuote = false;
    //Flag to hold whether multiLineComment started
    boolean multiLineComment = false;
    //Flag to hold whether keyword started
    boolean word = false;
    //Flag to hold whether singleLineComment started
    boolean singleLineComment = false;
    //Flag represent any of the above tokens started
    boolean flag = false;

    int commentPosition = -1;
    while(str!=null)
    {
      str = reader.readLine();
      if(str!=null)
      {
        program += str+"\n";
      }
    }
    program = program.replaceAll("<", "<");
    program = program.replaceAll(">", ">");
    html = new StringBuffer(program);
    char prevChar = ' ';
    char secondPrevChar = ' ';
    int programPosition=0;
    int htmlPosition = 0;
    String keyword = "";
 
    try
    {
      for(char ch: program.toCharArray())
      {
        if(Character.isLetterOrDigit(ch))
        {
   keyword += ch;
          word = true;
        }
        else
        {
   if(ch=='"' && ((secondPrevChar=='\\' && prevChar=='\\') || prevChar!='\\' ) &&!singleQuote)
          {
            //Ending double quote
            if(doubleQuote)
            {
              html.insert(htmlPosition+1, "</span>");
              htmlPosition += "</span>".length();
              doubleQuote = false;
            }
            else
            {
              //Inserting double quote
              html.insert(htmlPosition, "<span class=\"str\">");
              htmlPosition += "<span class=\"str\">".length();
              doubleQuote = true;
            }    
          }
          else if(ch=='\'' && ((secondPrevChar=='\\' && prevChar=='\\') || prevChar!='\\' ) &&!doubleQuote)
          {
            if(singleQuote)
            {
              //Ending single quote
              html.insert(htmlPosition+1, "</span>");
              htmlPosition += "</span>".length();
              singleQuote = false;
            }
            else
            {
              //Inserting singel quote
              html.insert(htmlPosition, "<span class=\"char\">");
              htmlPosition += "<span class=\"char\">".length();
              singleQuote = true;
            }
          }
          else if(ch=='/' && html.charAt(htmlPosition+1)=='*' && !multiLineComment && !doubleQuote) /*/*******/
          {
            //Inserting a comment
            html.insert(htmlPosition, "<span class=\"comment\">");
            htmlPosition += "<span class=\"comment\">".length();
            multiLineComment = true;
            commentPosition = programPosition;
          }
          else if(ch=='/' && prevChar == '*' && programPosition>commentPosition+2)
          {
            //Ending a comment
            html.insert(htmlPosition+2, "</span>");
            htmlPosition += "</span>".length();
            multiLineComment = false;
            commentPosition = -1;
          }
          else if(ch=='/' && html.charAt(htmlPosition+1)=='/' && !singleLineComment && !doubleQuote)
          {
            //Inserting a comment
            html.insert(htmlPosition, "<span class=\"comment\">");
            htmlPosition += "<span class=\"comment\">".length();
            singleLineComment = true;
          }
          else if((ch=='\r' || ch=='\n') && singleLineComment) ////Comments....
          {
            //Ending a comment
            html.insert(htmlPosition+2, "</span>");
            htmlPosition += "</span>".length();
            singleLineComment = false;
          }
          
          if(!doubleQuote && !singleQuote && !singleLineComment && !multiLineComment)
   {
     word = false;
     if(!keyword.equals(""))
            {
              for(String s:reservedWords)
              {
         if(s.equals(keyword))
         {
                  //Inserting keyword
    html.insert(htmlPosition-keyword.length(), "<span class=\"word\">");
    htmlPosition += ("<span class=\"word\">".length());
                  //Ending keyword
    html.insert(htmlPosition, "</span>");
    htmlPosition += ("</span>".length());
         }
              }
            }
            keyword = "";
          }
        }
        secondPrevChar = prevChar;
        prevChar = ch;
        programPosition++;
        htmlPosition++;
      }
    }
    finally
    {
      html.insert(0,"<style type=\"text/css\">\n"+
    "  .program {\n"+
    "  font: 400 13px Roboto,sans-serif;\n"+
    "  border-radius: 10px;\n"+
    "  line-height: 1.5em;\n"+
    "  overflow: auto;\n"+
    "  background-color: #f0f0f0;\n"+
    "  color: black;\n"+
    "  margin: 3px 0;\n"+
    "  padding: 10px 0 5px 10px;\n"+
    "  }\n"+
    "  .word{\n"+
    "    color: #008;\n"+
    "  }\n"+
    "  .str {\n"+
    "    color: #080;\n"+
    "  }\n"+
    "  .char {\n"+
    "    color: #080;\n"+
    "  }\n"+
    "  .comment{\n"+
    "    color: #333;\n"+
    "  }\n"+
    "</style><pre class=\"program\">");
 writer.write(html.toString()+"</pre>");
        writer.close();
        fileWriter.close();
        reader.close();
        fileReader.close();
        System.out.println("Generated a formatted source file - "+fileName.replaceAll("\\.java", ".html"));
    }
  }
}
Explanation

Java program file should be passed as a command line argument. I've handled below scenarios to detect the input

  • The file should definitely be a Java program
  • File should exist on the disk
A HTML file will be generated with <<file name>>.html by formatting the program.

Output
santhosh> java FormatProgramV2
Please input a Java program file name as below:
java FormatProgramV2 Example.java

santhosh> java FormatProgramV2 Example.java
File not found - Example.java

santhosh> java FormatProgramV2 Notepad.java
Generated a formatted source file - Notepad.html

If you've sometime, why don't you checkout my other Java programs

1 comment:

  1. Really your publish is actually excellent and that i be thankful. You are writing perfectly that is amazing. I truly astounded by your publish regards.

    ReplyDelete