alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * Copyright (c) 1998 Tal Davidson. All rights reserved.
 *
 * JSFormatter 0.4.0
 * by Tal Davidson (davidsont@bigfoot.com)
 *
 * JSFormatter 0.4.0 is distributed under the "Artistic Licence" detailed below:
 *
 *
 *                           The ``Artistic License''
 *
 * Preamble
 * The intent of this document is to state the conditions under which a Package may
 * be copied, such that the Copyright Holder maintains some semblance of artistic
 * control over the development of the package, while giving the users of the
 * package the right to use and distribute the Package in a more-or-less customary
 * fashion, plus the right to make reasonable modifications.
 *
 * Definitions
 *     ``Package'' refers to the collection of files distributed by the Copyright
 *     Holder, and derivatives of that collection of files created through textual
 *     modification.
 *     ``Standard Version'' refers to such a Package if it has not been modified,
 *     or has been modified in accordance with the wishes of the Copyright Holder
 *     as specified below.
 *     ``Copyright Holder'' is whoever is named in the copyright or copyrights for
 *     the package.
 *     ``You'' is you, if you're thinking about copying or distributing this
 *     Package.
 *     ``Reasonable copying fee'' is whatever you can justify on the basis of media
 *     cost, duplication charges, time of people involved, and so on. (You will not
 *     be required to justify it to the Copyright Holder, but only to the computing
 *     community at large as a market that must bear the fee.)
 *     ``Freely Available'' means that no fee is charged for the item itself,
 *     though there may be fees involved in handling the item. It also means that
 *     recipients of the item may redistribute it under the same conditions they
 *     received it.
 *
 *     1. You may make and give away verbatim copies of the source form of the
 *        Standard Version of this Package without restriction, provided that you
 *        duplicate all of the original copyright notices and associated disclaimers.
 *     2. You may apply bug fixes, portability fixes and other modifications derived
 *        from the Public Domain or from the Copyright Holder. A Package modified in
 *        such a way shall still be considered the Standard Version.
 *     3. You may otherwise modify your copy of this Package in any way, provided that
 *        you insert a prominent notice in each changed file stating how and when you
 *        changed that file, and provided that you do at least ONE of the following:
 *         a. place your modifications in the Public Domain or otherwise make them
 *            Freely Available, such as by posting said modifications to Usenet or an
 *            equivalent medium, or placing the modifications on a major archive site
 *            such as uunet.uu.net, or by allowing the Copyright Holder to include
 *            your modifications in the Standard Version of the Package.
 *         b. use the modified Package only within your corporation or organization.
 *         c. rename any non-standard executables so the names do not conflict with
 *            standard executables, which must also be provided, and provide a
 *            separate manual page for each non-standard executable that clearly
 *            documents how it differs from the Standard Version.
 *         d. make other distribution arrangements with the Copyright Holder.
 *     4. You may distribute the programs of this Package in object code or executable
 *        form, provided that you do at least ONE of the following:
 *         a. distribute a Standard Version of the executables and library files,
 *            together with instructions (in the manual page or equivalent) on where
 *            to get the Standard Version.
 *         b. accompany the distribution with the machine-readable source of the
 *            Package with your modifications.
 *         c. give non-standard executables non-standard names, and clearly document
 *            the differences in manual pages (or equivalent), together with
 *            instructions on where to get the Standard Version.
 *         d. make other distribution arrangements with the Copyright Holder.
 *     5. You may charge a reasonable copying fee for any distribution of this
 *        Package. You may charge any fee you choose for support of this Package. You
 *        may not charge a fee for this Package itself. However, you may distribute
 *        this Package in aggregate with other (possibly commercial) programs as part
 *        of a larger (possibly commercial) software distribution provided that you do
 *        not advertise this Package as a product of your own. You may embed this
 *        Package's interpreter within an executable of yours (by linking); this shall
 *        be construed as a mere form of aggregation, provided that the complete
 *        Standard Version of the interpreter is so embedded.
 *     6. The scripts and library files supplied as input to or produced as output
 *        from the programs of this Package do not automatically fall under the
 *        copyright of this Package, but belong to whomever generated them, and may be
 *        sold commercially, and may be aggregated with this Package. If such scripts
 *        or library files are aggregated with this Package via the so-called "undump"
 *        or "unexec" methods of producing a binary executable image, then
 *        distribution of such an image shall neither be construed as a distribution
 *        of this Package nor shall it fall under the restrictions of Paragraphs 3 and
 *        4, provided that you do not represent such an executable image as a Standard
 *        Version of this Package.
 *     7. C subroutines (or comparably compiled subroutines in other languages)
 *        supplied by you and linked into this Package in order to emulate subroutines
 *        and variables of the language defined by this Package shall not be
 *        considered part of this Package, but are the equivalent of input as in
 *        Paragraph 6, provided these subroutines do not change the language in any
 *        way that would cause it to fail the regression tests for the language.
 *     8. Aggregation of this Package with a commercial distribution is always
 *        permitted provided that the use of this Package is embedded; that is, when
 *        no overt attempt is made to make this Package's interfaces visible to the
 *        end user of the commercial distribution. Such use shall not be construed as
 *        a distribution of this Package.
 *     9. The name of the Copyright Holder may not be used to endorse or promote
 *        products derived from this software without specific prior written
 *        permission.
 *        THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 *        WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 *        MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * The End
 */

import java.util.*;
import java.io.*;

public class JSFormatter {
  // headers[] - an array of headers that require indentation
  private static String headers[] = {"if", "else", "for", "while", "do",
  "try", "catch", "synchronized", "switch", "static"};

  // parenHeaders[] - an array of the headers that require parenthesies after them, i.e. while (...)
  private static String parenHeaders[] = {"if", "for", "while", "catch",
  "synchronized", "switch"};

  private static String nonParenHeaders[] = {"else", "do", "try", "static"};

  private static String statementHeaders[] = {"class", "interface", "throws"};

  private static String longOperators[] = {"==", "!=", ">=", "<=", "+=",
  "-=", "*=", "/=", "%=", "^=", "|=", "&=", "++", "--", "&&", "||", ".*" };

  private static Hashtable closingHeaders;
  static {
    closingHeaders = new Hashtable();
    closingHeaders.put("if", "else");
    closingHeaders.put("do", "while");
    closingHeaders.put("try", "catch");
  }

  public JSBeautifier beautifier;
  private JSLineBreaker lineBreaker;
  private StringBuffer outBuffer; // current parsed-line buffer
  private String tempLine = ""; // parts of previous line that haven't been parsed yet
  private Stack openingStack;
  private Stack parenDepthsStack;

  private Stack bracketBlockStateStack;
  private char quoteChar;
  private int parenDepth;
  private int leadingWhiteSpaces;
  private String currentHeader;
  private boolean isInHeader;

  private boolean isSpecialChar; // true if a char of type '\X' (i.e. '\n'...)
  private boolean isInQuote; // true if in quote
  private boolean isInComment; // true if in /**/ comment
  private boolean isBlockNeeded; // ---not currently used.
  private boolean isSpecialBlock; // ---not currently used.
  private boolean isCloseSpecialBlock; // ---not currently used.
  private boolean isParenNeeded; // true if a parenthesis statement is expected (i.e. right after a 'while' header...)
  private boolean isNewLineNeeded; // true if current formatted line has reached its end.
  private boolean checkBlockOpen; // hint for  checking if a '{' has been reached
  private boolean checkBlockClose; // hint for  checking if a '}' has been reached
  private boolean checkIf;
  private boolean checkClosingHeader;
  private boolean foundOrigLineBreak;
  private boolean isInQuestion;
  private boolean isSummarized;
  private boolean isInBracketOpen;
  private boolean isInBracketClose;
  private boolean isInClassStatement;
  private boolean bracketBreak = false;

  private char prevNonSpaceCh;
  private char currentNonSpaceCh;


  public static void main(String args[]) {
    JSFormatter formatter = new JSFormatter();
    Vector fileNameVector = new Vector();
    BufferedReader inReader = null;
    PrintWriter outWriter = null;
    boolean isHelpShown = false;


    for (int i = 0; i < args.length; i++) {
      String arg = args[i];
      if (arg.equals("-b"))
        formatter.setBracketBreak(true);
      else if ("-ib".equals(arg)) {
        formatter.setBracketBreak(true);
        formatter.setBracketIndent(true);
      } else if ("-fs".equals(arg))
        formatter.setSwitchIndent(false);
      else if (arg.startsWith("-ll")) {
        int length = 70;
        try {
          length = Integer.valueOf(arg.substring(3)).intValue();
        } catch (NumberFormatException e) {};
        formatter.setPreferredLineLength(length);
      } else if (arg.startsWith("-ld")) {
        int dev = 5;
        try {
          dev = Integer.valueOf(arg.substring(3)).intValue();
        } catch (NumberFormatException e) {};
        formatter.setLineLengthDeviation(dev);
      } else if (arg.equals("-nn")) {
        formatter.setNestedConnection(false);
      } else if (arg.startsWith("-") && !isHelpShown) {
        isHelpShown = true;

        System.err.println("");
        System.err.println("Usage  : java jstyle.JSFormatter [options] < Original.java > Formatted.java");
        System.err.println("         java jstyle.JSFormatter [options] Foo.java Bar.java  [...]");
        System.err.println("");
        System.err.println("When given a specific file, JSFormatter will create an output file with a");
        System.err.println("suffix of \".js\" added to the original filename, i.e: Foo.java --> Foo.java.js");
        System.err.println("");
        System.err.println("Options: -ll#  Set preferred line length to #");
        System.err.println("         -ld#  Set preferred upper line length deviation to #");
        System.err.println("         -b    Break lines BEFORE '{' brackets (ala C++ style)");
        System.err.println("         -ib   Same as '-b', but add extra indentation to brackets");
        System.err.println("         -fs   flush (i.e. don't indent) 'switch' blocks");
        System.err.println("         -h    Print this help message");
        System.err.println();
        System.exit(0);
      } else
        fileNameVector.addElement(arg);
    }

    if (fileNameVector.isEmpty()) {
      inReader = new BufferedReader(new InputStreamReader(System.in));
      outWriter = new PrintWriter(System.out);

      try {
        formatter.format(inReader, outWriter);
      } catch (IOException e) {
        e.printStackTrace();
      }

      try {
        inReader.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
      outWriter.close();
    } else {
      for (int i = 0; i < fileNameVector.size(); i++) {
        try {
          String fileName = (String) fileNameVector.elementAt(i);
          inReader = new BufferedReader(new FileReader(fileName));
          outWriter =
                  new PrintWriter(new FileWriter(fileName + ".js"), true);

          formatter.format(inReader, outWriter);
        } catch (IOException e) {
          e.printStackTrace();
        }

        outWriter.close();
        try {
          inReader.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

  }

  /**
   * Constructor for JSFormatter
   */
  public JSFormatter() {
    beautifier = new JSBeautifier();
    lineBreaker = new JSLineBreaker();
    init();
  }

  /**
    * Initialize the formatter so that it is ready for the formation
    * of a new file of source code.
    */
  public void init() {
    beautifier.init();
    lineBreaker.init();

    outBuffer = new StringBuffer();
    openingStack = new Stack();
    parenDepthsStack = new Stack();
    bracketBlockStateStack = new Stack();
    bracketBlockStateStack.push(new Boolean(true));
    tempLine = "";
    parenDepth = 0;
    isSpecialChar = false;
    isInQuote = false;
    isInComment = false;
    isBlockNeeded = false;
    isParenNeeded = false;
    isSpecialBlock = false;
    isCloseSpecialBlock = false;
    isNewLineNeeded = false;
    checkIf = false;
    checkBlockOpen = false;
    checkClosingHeader = false;
    checkBlockClose = false;
    foundOrigLineBreak = false;
    isInQuestion = false;
    isSummarized = false;
    isInBracketOpen = false;
    isInBracketClose = false;
    leadingWhiteSpaces = 0;
    isInHeader = false;
    isInClassStatement = false;

    prevNonSpaceCh = '{';
    currentNonSpaceCh = '{';


  }

  /**
    * Format source code that is read from inReader, and print the
    * formatted result to outWriter.
    *
    * @param      inReader     a BufferedReader from which to input original source code
    * @param      outWriter    a PrintWriter to output beutified source code to
    *
    * @exception  IOException
    */
  public void format(BufferedReader inReader,
          PrintWriter outWriter) throws IOException {
    String line = null;

    init();

    try {
      while (true) {
        while (!hasMoreFormattedLines()) {
          line = inReader.readLine();
          if (line == null)
            throw new NullPointerException();
          formatLine(line);
        }

        while (hasMoreFormattedLines())
          outWriter.println(nextFormattedLine());
      }
    } catch (NullPointerException e) {}

    summarize();

    while (hasMoreFormattedLines())
      outWriter.println(nextFormattedLine());
  }

  /**
    * Check if the formatter has more formatted lines to return.
    *
    * As long as there are more formatted lines to return, the
    * caller should NOT call the method formatLine() with a new
    * line of source code, but rather collect the current available
    * formatted lines with the method nextFormattedLine(), i.e:
    *
    * while (formatter.hasMoreFormattedLines())
    *     System.out.println(formatter.nextFormattedLine()
    */
  public boolean hasMoreFormattedLines() {
    if (lineBreaker.hasMoreBrokenLines())
      return true;
    else
      while ((!isSummarized && !isNewLineRequested()) ||
              (isSummarized && hasMoreSummarizedLines())) {
        String formatResult = format(null);
        if (formatResult != null) {
          lineBreaker.breakLine(formatResult);
          return true;
        }
      }
    return false;
  }

  /**
    * format a line of source code. formatLine should NOT be called
    * if there are still formatted lines ready to be collected.
    * This can be checked with the method hasMoreFormattedLines()
    *
    * @param      line       a line of source code to be formatted.
    */
  public void formatLine(String line) {
    String formatResult = format(line);
    if (formatResult != null)
      lineBreaker.breakLine(formatResult);
  }

  /**
    * Get the next formatted line. This should be called ONLY after
    * checking with the method hasMoreFormattedLines() that there
    * actualy is a formatted line ready to be collected.
    */
  public String nextFormattedLine() {
    return lineBreaker.nextBrokenLine();
  }

  /**
    * summarize() is to be called when there are no more lines
    * of unformatted source code to be passed to the formatter.
    */
  public void summarize() {
    formatLine("");
    isSummarized = true;
  }

  public void setBracketBreak(boolean br) {
    bracketBreak = br;
  }

  public void setBracketIndent(boolean state) {
    beautifier.setBracketIndent(state);
  }

  public void setSwitchIndent(boolean state) {
    beautifier.setSwitchIndent(state);
  }

  public void setPreferredLineLength(int length) {
    lineBreaker.setPreferredLineLength(length);
  }

  public void setLineLengthDeviation(int dev) {
    lineBreaker.setLineLengthDeviation(dev);
  }

  public void setNestedConnection(boolean nest) {
    lineBreaker.setNestedConnection(nest);
  }

  /*
    * Does the formatter request a new line?
    * This should be checked only if we have a new line to
    * actually give the formatter via the format(line) method.
    */
  private boolean isNewLineRequested() {
    return (tempLine.indexOf("//") == -1 &&
            tempLine.indexOf("/*") == -1 && tempLine.indexOf("*/") == -1);
  }

  /*
    * Does formatter have more formatted lines in its belly?
    * This should be called only after there are no more original
    * lines to send the formatter.
    * Until false, the new formatted lines can be retreived by
    * calling the format method with an empty string, i.e. format("");
    */
  private boolean hasMoreSummarizedLines() {
    return !((tempLine.length() == 0) ||
            (tempLine.length() == 2 && tempLine.charAt(0) == '\r' &&
            tempLine.charAt(1) == '\n'));
  }

  /*
    * Format the original line sent.
    * Actually, the returned String is the next parsed line that is ready,
    * and may be a part of a formerly sent original line
    */
  public String format(String line) {
    boolean isLineComment = false; // true when the current character is in a // comment (such as this line ...)
    char ch = ' '; // the current character
    char prevCh = ' ';
    String outString = null;
    int i;
    boolean shouldPublish = false;
    boolean isBreakCalled = false;

    currentHeader = null;

    // connect new unparsed line to former unparsed line.
    if (line == null)
      line = "";
    else {
      // remove the white-space around the current line
      if (!isInComment) {
        leadingWhiteSpaces = 0;
        while (leadingWhiteSpaces < line.length() &&
                (line.charAt(leadingWhiteSpaces) == ' ' ||
                line.charAt(leadingWhiteSpaces) == '\t'))
          leadingWhiteSpaces++;

        line = line.trim();
      } else {
        int trimSize;
        for (trimSize = 0; trimSize < line.length() &&
                trimSize < leadingWhiteSpaces &&
                (line.charAt(trimSize) == ' ' ||
                line.charAt(trimSize) == '\t'); trimSize++)
          ;

        line = line.substring(trimSize);
      }

      //line = line.trim();
      if ("".equals(line))
        line = "\n";
    }

    line = tempLine + " \r" + line;

    // parse characters in the current line.
    for (i = 0; i < line.length(); i++) {
      prevCh = ch;

      ch = line.charAt(i);


      //shouldPublish = false;

      if (!isInComment && !isLineComment && ch == '\t')
        ch = ' ';

      // '\n' exists when an empty line has been sent
      if (ch == '\n') {
        /*if (checkClosingHeader)
         {
             isDoubleBreak = true;
             //checkClosingHeader = false;
         }
         */
        //if (foundOrigLineBreak)
        //    foundOrigLineBreak = false;
        isBreakCalled = true;
        break;
      }

      // '\r' exists at the connection points between original lines
      if (ch == '\r') {
        //System.out.println("found \\r at:" + i +" out of " + line.length());
        ch = ' ';
        if (isBreakCalled)
          break;
        else if (checkBlockClose) {
          checkBlockClose = false;
          isBreakCalled = true;
          break;
        } else {
          foundOrigLineBreak = true;
          continue;
        }
      }

      if (ch != ' ' && ch != '\t' && !isInComment && !isLineComment &&
              !isInQuote && !line.regionMatches(false, i, "//", 0, 2) &&
              !line.regionMatches(false, i, "/*", 0, 2)) {
        prevNonSpaceCh = currentNonSpaceCh;
        currentNonSpaceCh = ch;
      }

      // minimize white-space
      // and remove spaces that come right after parenthesies...
      if (!isInComment && !isLineComment && !isInQuote && ch == ' ') {
        if (currentNonSpaceCh != '(' && currentNonSpaceCh != ')' &&
                currentNonSpaceCh != '[' && currentNonSpaceCh != ']')
          appendSpace(outBuffer);
        continue;
      }
      //if (!isInComment && !isInQuote && (ch == ' ' || ch == '\t'))
      //{
      //    if (prevCh != ' ' && prevCh != '\t')
      //        outBuffer.append(ch);
      //    continue;
      //}


      shouldPublish = false; // called specifically AFTER white space is treated.


      if (checkBlockClose) {
        checkBlockClose = false;
        if (ch != '}')
          isBreakCalled = true;
      }



      // handle comments
      if (!isInQuote && !(isInComment || isLineComment) &&
              line.regionMatches(false, i, "//", 0, 2)) {
        if (foundOrigLineBreak) {
          foundOrigLineBreak = false;
          if (checkClosingHeader) {
            checkClosingHeader = false;
            i--;
            isBreakCalled = true;
            break;
          }
        }

        isLineComment = true;
        checkClosingHeader = false;
        outBuffer.append("//");
        i++;
        continue;
      } else if (!isInQuote && !(isInComment || isLineComment) &&
              line.regionMatches(false, i, "/*", 0, 2)) {
        if (foundOrigLineBreak) {
          foundOrigLineBreak = false;
          if (checkClosingHeader) {
            checkClosingHeader = false;
            i--;
            isBreakCalled = true;
            break;
          }
        }

        isInComment = true;
        outBuffer.append("/*");
        i++;
        continue;
      } else if (!isInQuote && (isInComment || isLineComment) &&
              line.regionMatches(false, i, "*/", 0, 2)) {
        isInComment = false;
        outBuffer.append("*/");
        shouldPublish = true;
        i++;
        continue;
      }
      if (isInComment || isLineComment) {
        outBuffer.append(ch);
		if (outBuffer.toString().regionMatches(false, 0, "/*", 0, 2))
			outBuffer.insert(0, ' ');
        continue;
      }

      // if we have reached here, then we are NOT in a comment

      if (isInHeader) {
        isInHeader = false;
        currentHeader = (String) openingStack.peek();
      } else
        currentHeader = null;

      foundOrigLineBreak = false;

      if (isBreakCalled) {
        i--;
        break;
      }

      /**/
      if (checkClosingHeader) {
        checkClosingHeader = false;

        if (bracketBreak)
          if (ch != ';') {
            i--;
            isBreakCalled = true;
            break;
          } else {
            i--;
            continue;
          }

        while (!"{".equals(openingStack.pop()))
          ;
        if (!openingStack.isEmpty()) {
          String openingHeader = (String) openingStack.peek();
          String closingHeader = (String) closingHeaders.get(openingHeader);
          i--;
          if (closingHeader == null ||
                  !line.regionMatches(false, i + 1, closingHeader, 0,
                  closingHeader.length())) {
            if (ch != ';') {
              outString = outBuffer.toString();
              outBuffer.setLength(0);
              break;
            } else
              i++;
          } else {
            int lastBufCharPlace = outBuffer.length() - 1;
            if ((lastBufCharPlace >= 0) &&
                    (outBuffer.charAt(lastBufCharPlace) != ' '))
              appendSpace(outBuffer);//outBuffer.append(' ');

            ch = ' ';
            openingStack.pop(); // pop the opening header
            continue;
          }
        }
      }
      /**/


      if (checkIf) {
        checkIf = false;
        if (line.regionMatches(false, i, "if", 0, 2))
          isNewLineNeeded = false;
      }

      if (!isParenNeeded && checkBlockOpen) {
        checkBlockOpen = false;
        if (ch == '{' || "static".equals(currentHeader))
          isNewLineNeeded = false;
      }

      if (isNewLineNeeded && !isParenNeeded) {
        isNewLineNeeded = false;
        //if (!isParenNeeded)
        {
          i--;
          isBreakCalled = true;
          continue;
        }
      }



      // handle special characters (i.e. backslash+character such as \n, \t, ...)
      if (isSpecialChar) {
        outBuffer.append(ch);
        isSpecialChar = false;
        continue;
      }
      if (!(isInComment || isLineComment) &&
              line.regionMatches(false, i, "\\\\", 0, 2)) {
        outBuffer.append("\\\\");
        i++;
        continue;
      }
      if (!(isInComment || isLineComment) && ch == '\\') {
        isSpecialChar = true;
        outBuffer.append(ch);
        continue;
      }


      // handle quotes (such as 'x' and "Hello Dolly")
      if (ch == '"' || ch == '\'')
        if (!isInQuote) {
          quoteChar = ch;
          isInQuote = true;
        } else if (quoteChar == ch) {
          isInQuote = false;
          outBuffer.append(ch);
          continue;
        }
      if (isInQuote) {
        outBuffer.append(ch);
        continue;
      }


      // handle parenthesies
      if (ch == '(' || ch == '[' || ch == ')' || ch == ']') {
        if (ch == '(' || ch == '[')
          parenDepth++;
        else if (ch == ')' || ch == ']')
          parenDepth--;

        if (parenDepth == 0 && isParenNeeded) {
          isParenNeeded = false;
          checkBlockOpen = true;
        }

        //outBuffer.append(ch);
        //continue;
      }

      //don't do special parsing as long as parenthesies are open...
      /*
      if (parenDepth != 0)
    {
          outBuffer.append(ch);
          continue;
    }
      */
      /*
                  if (isNewLineNeeded && !isParenNeeded)
                  {
                      isNewLineNeeded = false;
                      i--;
                      isBreakCalled = true;
                      continue;
                  }
      */
      if (prevCh == ' ') {
        boolean foundHeader = false;
        for (int h = 0; h < headers.length; h++) {
          if (line.regionMatches(false, i, headers[h], 0,
                  headers[h].length())) {
            int lineLength = line.length();
            int headerEnd = i + headers[h].length();
            char endCh = 0;

            if (headerEnd < lineLength)
              endCh = line.charAt(headerEnd);
            if ((headerEnd > lineLength) ||
                    (endCh >= 'a' && endCh <= 'z') ||
                    (endCh >= 'A' && endCh <= 'Z') ||
                    (endCh >= '0' && endCh <= '9'))
              break;

            foundHeader = true;
            outBuffer.append(headers[h]);
            i += headers[h].length() - 1;
            if ("else".equals(headers[h]))
              checkIf = true;
            checkBlockOpen = true;
            isNewLineNeeded = true;
            isBlockNeeded = false;
            openingStack.push(headers[h]);

            appendSpace(outBuffer);
            ; //outBuffer.append(' ');
            ch = ' ';

            int p;
            for (p = 0; p < parenHeaders.length; p++)
              if (headers[h].equals(parenHeaders[p])) {
                isParenNeeded = true;
                break;
              }
            break;
          }
        }
        if (foundHeader) {
          isInHeader = true;
          continue;
        }
      }

      if (ch == '?')
        isInQuestion = true;

      if (ch == ':')//isInCase)
      {
        if (isInQuestion)
          isInQuestion = false;
        else {
          //isInCase = false;
          outBuffer.append(ch);
          isBreakCalled = true;
          continue;
        }

      }

      if (ch == ';' && parenDepth == 0) {
        outBuffer.append(ch);
        isBreakCalled = true;
        continue;
      }

      if (ch == '{') {
        if (!(bracketBreak && isInBracketOpen)) {
          boolean isBlockOpener = false;

          // first, check if '{' is a block-opener or an static-array opener
          isBlockOpener |= (prevNonSpaceCh == '{' &&
                  ((Boolean) bracketBlockStateStack.peek()).
                  booleanValue());

          isBlockOpener |= (prevNonSpaceCh == ')' || prevNonSpaceCh == ';');

          isBlockOpener |= isInClassStatement;

          isBlockOpener |= (prevNonSpaceCh == ':' && !isInQuestion);

          isInClassStatement = false;

          if (!isBlockOpener && currentHeader != null) {
            for (int n = 0; n < nonParenHeaders.length; n++)
              if (currentHeader.equals(nonParenHeaders[n])) {
                isBlockOpener = true;
                break;
              }
          }

          bracketBlockStateStack.push(new Boolean(isBlockOpener));
          if (!isBlockOpener) {
            outBuffer.append('{');
            continue;
          }
        }

        // if I have reached here, then I am in a block...

        if (bracketBreak) {
          if (isInBracketOpen) {
            isInBracketOpen = false;
          } else {
            isInBracketOpen = true;
            isBreakCalled = true;
            i--;
            break;
          }
        }

        checkBlockClose = true;

        int lastBufCharPlace = outBuffer.length() - 1;
        if ((lastBufCharPlace >= 0) &&
                (outBuffer.charAt(lastBufCharPlace) != ' '))
          appendSpace(outBuffer); //outBuffer.append(' ');
        outBuffer.append('{');
        openingStack.push("{");
        //checkBlockClose = true;
        parenDepthsStack.push(new Integer(parenDepth));
        parenDepth = 0;
        continue;
      }

      if (ch == '}') {
        // first check if this '}' closes a previous block, or a static array...
        if (!((Boolean) bracketBlockStateStack.pop()).booleanValue()) {
          outBuffer.append(ch);
          continue;
        }

        if (!parenDepthsStack.isEmpty())
          parenDepth = ((Integer) parenDepthsStack.pop()).intValue();
        outBuffer.append(ch);
        checkClosingHeader = true;
        continue;
      }

      if (prevCh == ' ') {
        boolean foundHeader = false;
        for (int h = 0; h < statementHeaders.length; h++) {
          if (line.regionMatches(false, i, statementHeaders[h], 0,
                  statementHeaders[h].length())) {
            int lineLength = line.length();
            int headerEnd = i + statementHeaders[h].length();
            char endCh = 0;

            if (headerEnd < lineLength)
              endCh = line.charAt(headerEnd);
            if ((headerEnd > lineLength) ||
                    (endCh >= 'a' && endCh <= 'z') ||
                    (endCh >= 'A' && endCh <= 'Z') ||
                    (endCh >= '0' && endCh <= '9'))
              break;

            isInClassStatement = true;
            break;
          }
        }
      }

      if (prevCh == ' ' && line.regionMatches(false, i, "return", 0, 6)) {
        int lineLength = line.length();
        int headerEnd = i + 6;
        char endCh = 0;

        if (headerEnd < lineLength)
          endCh = line.charAt(headerEnd);
        if (!((headerEnd > lineLength) || (endCh >= 'a' && endCh <= 'z') ||
                (endCh >= 'A' && endCh <= 'Z') ||
                (endCh >= '0' && endCh <= '9'))) {
          outBuffer.append("return");
          i += 5;
          currentNonSpaceCh = '-'; // treat 'return' a an operator.
          continue;
        }
      }


      // add space when a non-operator follows a closing parenthesis
      if ((prevNonSpaceCh == ')' || prevNonSpaceCh == ']') &&
              Character.isLetterOrDigit(ch) && ch != '.' &&
              ch != '_' && ch != '$' && ch != '(' && ch != '[' &&
              ch != ')' && ch != ']')
        appendSpace(outBuffer);

      if ((!Character.isLetterOrDigit(ch) && ch != '.' && ch != '_' &&
              ch != '$' && ch != '(' && ch != '[' && ch != ')' &&
              ch != ']') && (Character.isLetterOrDigit(prevNonSpaceCh) ||
              prevNonSpaceCh == '.' || prevNonSpaceCh == '_' ||
              prevNonSpaceCh == '$' || prevNonSpaceCh == ')' ||
              prevNonSpaceCh == ']')) {
        boolean isLongOperator = false;
        String longOperator = null;

        for (int l = 0; l < longOperators.length; l++) {
          if (line.regionMatches(false, i, longOperators[l], 0,
                  longOperators[l].length())) {
            isLongOperator = true;
            longOperator = longOperators[l];
            break;
          }
        }

        if (isLongOperator) {
          if (!"--".equals(longOperator) && !"++".equals(longOperator) &&
                  !".*".equals(longOperator)) {
            appendSpace(outBuffer);
            outBuffer.append(longOperator);
            appendSpace(outBuffer);
            ch = ' ';
          } else {
            outBuffer.append(longOperator);
            currentNonSpaceCh = '0'; // hide the operator
          }

          i += 1; // since all long operators are 2 chars long...
        } else if (!(ch == '*' && prevNonSpaceCh == '.'))// not '.*'
        {
          if (ch != ',' && ch != ';')// && ch != ')' && ch != ']')

            appendSpace(outBuffer);
          outBuffer.append(ch);
          appendSpace(outBuffer);
          ch = ' ';
        } else
          outBuffer.append(ch);

        continue;

      }

      if (ch == ')' || ch == ']')
        clearPaddingSpace(outBuffer);


      // default
      outBuffer.append(ch);
    }


    try {
      tempLine = line.substring(i + (i < line.length() ? 1 : 0));
    } catch (Exception e) { /*is this exception really needed??? - check if the above ?: solves the problem...*/
      tempLine = "";
    }

    if (isBreakCalled || isInComment || isLineComment || shouldPublish) {
      outString = outBuffer.toString();
      outBuffer.setLength(0);
    }

    if (outString != null && !"".equals(outString))
      outString = beautifier.beautify(outString);
    else if (ch != '[' && ch != ']' && ch != '(' && ch != ')' &&
            ch != '.' && ch != '_' && ch != '$')
      appendSpace(outBuffer); //outBuffer.append(" ");

/*	if (outString != null && !"".equals(outString)) {
		if (isInComment && !isLineComment) {
			System.err.println("In Block Comment: " + outString);
			StringBuffer s = new StringBuffer(outString);
			if ("\t".equals(beautifier.getIndentString())) {
				int p = outString.indexOf("\t  *");
				if (p > -1) {
					s.delete(p, p + 4);
					s.insert(p, "\t *");
				}
			}
			outString = s.toString();
			System.err.println("After replace:    " + outString);
		}
	}*/
    return outString;
  }


  private void appendSpace(StringBuffer buf) {
    if (buf.length() == 0 || buf.charAt(buf.length() - 1) != ' ')
      buf.append(' ');
  }

  private void clearPaddingSpace(StringBuffer buf) {
    int bufLength = buf.length();
    if (bufLength != 0 && buf.charAt(bufLength - 1) == ' ')
      buf.setLength(bufLength - 1);
  }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.