import java.util.Stack; import java.util.Vector; import com.ibm.jdt.compiler.api.*; import com.ibm.jdt.formatter.api.*; /** *

This formatter calls the default IBM formatter and then * takes its output and reprocesses it to make each open brace * line up column-wise with its matching end brace. This is to * undo the new "feature" in the VAJ 3.5.3 code formatter that * moved the open brace in by an extra indentation factor. * *

DISCLAIMER

* *

This Java code is provided to you on an 'as-is' basis * without warranty or condition of any kind, either express or * implied, including, but not limited to, warranty or condition of * merchantable quality or fitness for a particular purpose.

* *

Copyright (c) 2001, IBM and Kenton Lynne * All Rights Reserved

* * @author Kenton Lynne * @version 1.0.2 - fixes for parsing code correctly */ public class MyFixForIBMCodeFormatter { static public Vector matchedBraces = new Vector(); /** * Adds a Braces object to the matchedBraces Vector passed such that * the Vector is ordered by open brace position * Creation date: (06/06/01 3:48:01 PM) */ static public void addBraces(int openPos, int closePos) { // create a two-element array to hold the values int[] braces = new int[2]; braces[0] = openPos; braces[1] = closePos; // insert this into the matchedBraces Vector ordered by openPos int i; for (i = 0; i < matchedBraces.size(); i++) { try { if (((int[]) matchedBraces.elementAt(i))[0] > openPos) { break; // done! insert at position i } } catch (ArrayIndexOutOfBoundsException e) // in case Vector is empty { break; //done! insert at position i (should be 0) } } // i should now contain the position to insert this Braces element in the Vector matchedBraces.insertElementAt(braces, i); } /** * Count the number of whitespace characters before the character pointed at by * index within the input string * Creation date: (06/07/01 9:03:31 AM) * @return int */ static public int countSpaces(String stg, int index) { int count = 0; while (index >= 0 && (stg.charAt(index - 1) == ' ' || stg.charAt(index - 1) == '\t')) { count++; index--; } return count; } /** * Searches the input string for open and closed braces * and builds in matchedBraces Vector a sorted list of * Braces objects that contain pointers to the matched * braces within that code segment. * Braces that appear within comments or quoted strings are ignored * Creation date: (06/06/01 3:30:11 PM) * @return void * @param param java.lang.String */ static public void findBraces(String s) { Stack openBraces = new Stack(); int state = 0; // state machine: // 0: not in a comment // 1: possible start of comment ('/' found) // 2: within a line comment ('//' found) // 3: within a block comment ('/*' found) // 4: possible end of block comment ('*' found) // 5: within a single-quoted string // 7: within a double-quoted string // 6: within a single-quoted string ('\' found) // 8: within a double-quoted string ('\' found) // run a state machine to find all the braces that aren't within in comments for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (state) { //////////////////////////////// case 0 : // not within a comment if (ch == '/') // start of comment? { state = 1; } else if (ch == '\'') // single quoted string { state = 5; } else if (ch == '\"') // double-quoted string { state = 7; } else if (ch == '{') // stack the open brace position { openBraces.push(new Integer(i)); } else if (ch == '}') // match with open brace on top of stack { addBraces(((Integer) openBraces.pop()).intValue(), i); } break; ///////////////////////////////// case 1 : // possible start of comment if (ch == '/') // line comment { state = 2; } else if (ch == '*') // block comment { state = 3; } else // not a comment { state = 0; } break; ///////////////////////////////// case 2 : // within a line comment if (ch == '\n') // end of comment { state = 0; } break; //////////////////////////////// case 3 : // within a block comment if (ch == '*') // possible end of comment { state = 4; } else // not end of comment { state = 2; } break; //////////////////////////////// case 4 : // possible end of block comment if (ch == '/') // end of block comment { state = 0; } else if (ch == '*') // still possible end of comment { // stay in same state } else // back inside of block comment { state = 3; } break; /////////////////////////////// case 5 : // within a single-quoted string - previous char was NOT an escape char if (ch == '\'') // end of string { state = 0; } else if (ch == '\\') // escape char within quoted string { state = 6; } break; /////////////////////////////// case 6 : // within a single-quoted string - previous char was an escape char // doesn't matter what character follows the escape character - still within quoted string state = 5; break; /////////////////////////////// case 7 : // within a double-quoted string - previous char was NOT an escape char if (ch == '\"') // end of string { state = 0; } else if (ch == '\\') // escape char within quoted string { state = 8; } break; /////////////////////////////// case 8 : // within a double-quoted string - previous char was an escape char // doesn't matter what character follows the escape character - still within quoted string state = 7; break; /////////////////////////////// } // end of switch } // end of for loop } /**

This method performs the formatting. It's called for * all formatting inside VisualAge when control-w (or * whatever key you assign) is pressed in the editor. This applies * for class definitions or method definitions. There is no way * to tell the difference between a call to format a class definition * or a method definition.

* *

Depending on the VA Assist/J settings, this may also be called * when importing, exporting, or viewing the entire class source.

* *

This sample version of the format method calls the default * VisualAge formatter and adds a comment at the top of the code. * The comment contains a list of all options passed to the * formatter from VisualAge. If you want to use the IBM * formatting options, this is an example of how to get them.

* * @param code (java.lang.String)
* The source code to be formatted
* * @param n (int)
* The function of this parameter is unknown, * but it's always 0...
* * @param options (com.ibm.jdt.compiler.api.ConfigurableOption[])
* An array of option objects representing the settings * in VisualAge's Window->Options dialog.
* * @return (java.lang.String)
* The formatted code. You should use the appropriate line * separation character for your platform in place of * newlines. You can obtain this by calling *
System.getProperty("line.separator")
*/ public static String format(String code, int n, ConfigurableOption[] vajOptions) { matchedBraces.clear(); // Ask VisualAge's default formatter to format the code String formatted; boolean debug = false; if (vajOptions != null) { formatted = CodeFormatter.format(code, 0, vajOptions); } else // called by our own main method { formatted = code; debug = true; } try { // Find all the matched sorted braces in the string findBraces(formatted); StringBuffer s = new StringBuffer(formatted.length()); // add the substrings of the original string while adjusting the // amount of whitespace for the opening braces to match their // closing brace int startIndex = 0; // keeps track of where we are in the string for (int i = 0; i < matchedBraces.size(); i++) { // get the next pair of matching braces int[] braces = (int[]) matchedBraces.elementAt(i); if (debug) System.out.println("Braces at: " + braces[0] + " and " + braces[1]); // get the amount of spaces in front of each brace int openSpaces = countSpaces(formatted, braces[0]); int closedSpaces = countSpaces(formatted, braces[1]); // append from the previous index to this brace (minus preceeding blanks) s.append(formatted.substring(startIndex, braces[0] - openSpaces)); // now append the correct amount of spaces using the closed brace spaces s.append(formatted.substring(braces[1] - closedSpaces, braces[1])); // now update the starting index to point to the open brace startIndex = braces[0]; } // finish up by appending the last chunk left s.append(formatted.substring(startIndex)); return new String(s); } catch (Throwable t) { return formatted; } } /** * Insert the method's description here. * Creation date: (06/07/01 3:36:18 PM) * @param args java.lang.String[] */ public static void main(String[] args) { String test = "// {{}{" + '\n' + " {" + '\n' + " if (a=='{')" + '\n' + " {" + '\n' + " hey(\"{{}\");" + '\n' + " } /* } } {{} */" + '\n' + " else if (a=='\\t')" + '\n' + " {" + '\n' + " b = \"abc\\def\\\"hij\";" + '\n' + " }" + '\n' + "}"; String result = format(test, 0, null); System.out.println("Before:\n" + test); System.out.println("\nAfter: \n" + result); System.exit(0); } }