package org.hibernate.eclipse.console.utils; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import java.util.StringTokenizer; import org.hibernate.util.xpl.StringHelper; /** * Old formatter from previous Hibernate versions which allowed a bit more control, i.e. we want *less* spaces not more. * In here to simplify migration from 3.2 to 3.3 where this class changed. * */ public class Formatter { private static final Set<String> BEGIN_CLAUSES = new HashSet<String>(); private static final Set<String> END_CLAUSES = new HashSet<String>(); private static final Set<String> LOGICAL = new HashSet<String>(); private static final Set<String> QUANTIFIERS = new HashSet<String>(); private static final Set<String> DML = new HashSet<String>(); private static final Set<String> MISC = new HashSet<String>(); static { BEGIN_CLAUSES.add("left"); //$NON-NLS-1$ BEGIN_CLAUSES.add("right"); //$NON-NLS-1$ BEGIN_CLAUSES.add("inner"); //$NON-NLS-1$ BEGIN_CLAUSES.add("outer"); //$NON-NLS-1$ BEGIN_CLAUSES.add("group"); //$NON-NLS-1$ BEGIN_CLAUSES.add("order"); //$NON-NLS-1$ END_CLAUSES.add("where"); //$NON-NLS-1$ END_CLAUSES.add("set"); //$NON-NLS-1$ END_CLAUSES.add("having"); //$NON-NLS-1$ END_CLAUSES.add("join"); //$NON-NLS-1$ END_CLAUSES.add("from"); //$NON-NLS-1$ END_CLAUSES.add("by"); //$NON-NLS-1$ END_CLAUSES.add("join"); //$NON-NLS-1$ END_CLAUSES.add("into"); //$NON-NLS-1$ END_CLAUSES.add("union"); //$NON-NLS-1$ LOGICAL.add("and"); //$NON-NLS-1$ LOGICAL.add("or"); //$NON-NLS-1$ LOGICAL.add("when"); //$NON-NLS-1$ LOGICAL.add("else"); //$NON-NLS-1$ LOGICAL.add("end"); //$NON-NLS-1$ QUANTIFIERS.add("in"); //$NON-NLS-1$ QUANTIFIERS.add("all"); //$NON-NLS-1$ QUANTIFIERS.add("exists"); //$NON-NLS-1$ QUANTIFIERS.add("some"); //$NON-NLS-1$ QUANTIFIERS.add("any"); //$NON-NLS-1$ DML.add("insert"); //$NON-NLS-1$ DML.add("update"); //$NON-NLS-1$ DML.add("delete"); //$NON-NLS-1$ MISC.add("select"); //$NON-NLS-1$ MISC.add("on"); //$NON-NLS-1$ //MISC.add("values"); } String indentString = " "; //$NON-NLS-1$ String initial = "\n "; //$NON-NLS-1$ boolean beginLine = true; boolean afterBeginBeforeEnd = false; boolean afterByOrSetOrFromOrSelect = false; boolean afterValues = false; boolean afterOn = false; boolean afterBetween = false; boolean afterInsert = false; int inFunction = 0; int parensSinceSelect = 0; private LinkedList<Integer> parenCounts = new LinkedList<Integer>(); private LinkedList<Boolean> afterByOrFromOrSelects = new LinkedList<Boolean>(); int indent = 1; StringBuffer result = new StringBuffer(); StringTokenizer tokens; String lastToken; String token; String lcToken; public Formatter(String sql) { tokens = new StringTokenizer( sql, "()+*/-=<>'`\"[]," + StringHelper.WHITESPACE, //$NON-NLS-1$ true ); } public Formatter setInitialString(String initial) { this.initial = initial; return this; } public Formatter setIndentString(String indent) { this.indentString = indent; return this; } public String format() { result.append(initial); while ( tokens.hasMoreTokens() ) { token = tokens.nextToken(); lcToken = token.toLowerCase(); if ( "'".equals(token) ) { //$NON-NLS-1$ String t; do { t = tokens.nextToken(); token += t; } while ( !"'".equals(t) && tokens.hasMoreTokens() ); // cannot handle single quotes //$NON-NLS-1$ } else if ( "\"".equals(token) ) { //$NON-NLS-1$ String t; do { t = tokens.nextToken(); token += t; } while ( !"\"".equals(t) ); //$NON-NLS-1$ } if ( afterByOrSetOrFromOrSelect && ",".equals(token) ) { //$NON-NLS-1$ commaAfterByOrFromOrSelect(); } else if ( afterOn && ",".equals(token) ) { //$NON-NLS-1$ commaAfterOn(); } else if ( "(".equals(token) ) { //$NON-NLS-1$ openParen(); } else if ( ")".equals(token) ) { //$NON-NLS-1$ closeParen(); } else if ( BEGIN_CLAUSES.contains(lcToken) ) { beginNewClause(); } else if ( END_CLAUSES.contains(lcToken) ) { endNewClause(); } else if ( "select".equals(lcToken) ) { //$NON-NLS-1$ select(); } else if ( DML.contains(lcToken) ) { updateOrInsertOrDelete(); } else if ( "values".equals(lcToken) ) { //$NON-NLS-1$ values(); } else if ( "on".equals(lcToken) ) { //$NON-NLS-1$ on(); } else if ( afterBetween && lcToken.equals("and") ) { //$NON-NLS-1$ misc(); afterBetween = false; } else if ( LOGICAL.contains(lcToken) ) { logical(); } else if ( isWhitespace(token) ) { white(); } else { misc(); } if ( !isWhitespace( token ) ) lastToken = lcToken; } return result.toString(); } private void commaAfterOn() { out(); indent--; newline(); afterOn = false; afterByOrSetOrFromOrSelect = true; } private void commaAfterByOrFromOrSelect() { out(); newline(); } private void logical() { if ( "end".equals(lcToken) ) indent--; //$NON-NLS-1$ newline(); out(); beginLine = false; } private void on() { indent++; afterOn = true; newline(); out(); beginLine = false; } private void misc() { out(); if ( "between".equals(lcToken) ) { //$NON-NLS-1$ afterBetween = true; } if (afterInsert) { newline(); afterInsert = false; } else { beginLine = false; if ( "case".equals(lcToken) ) { //$NON-NLS-1$ indent++; } } } private void white() { if ( !beginLine ) { result.append(" "); //$NON-NLS-1$ } } private void updateOrInsertOrDelete() { out(); indent++; beginLine = false; if ( "update".equals(lcToken) ) newline(); //$NON-NLS-1$ if ( "insert".equals(lcToken) ) afterInsert = true; //$NON-NLS-1$ } private void select() { out(); indent++; newline(); parenCounts.addLast( new Integer(parensSinceSelect) ); afterByOrFromOrSelects.addLast( new Boolean(afterByOrSetOrFromOrSelect) ); parensSinceSelect = 0; afterByOrSetOrFromOrSelect = true; } private void out() { result.append(token); } private void endNewClause() { if (!afterBeginBeforeEnd) { indent--; if (afterOn) { indent--; afterOn=false; } newline(); } out(); if ( !"union".equals(lcToken) ) indent++; //$NON-NLS-1$ newline(); afterBeginBeforeEnd = false; afterByOrSetOrFromOrSelect = "by".equals(lcToken) //$NON-NLS-1$ || "set".equals(lcToken) //$NON-NLS-1$ || "from".equals(lcToken); //$NON-NLS-1$ } private void beginNewClause() { if (!afterBeginBeforeEnd) { if (afterOn) { indent--; afterOn=false; } indent--; newline(); } out(); beginLine = false; afterBeginBeforeEnd = true; } private void values() { indent--; newline(); out(); indent++; newline(); afterValues = true; } private void closeParen() { parensSinceSelect--; if (parensSinceSelect<0) { indent--; parensSinceSelect = ( (Integer) parenCounts.removeLast() ).intValue(); afterByOrSetOrFromOrSelect = ( (Boolean) afterByOrFromOrSelects.removeLast() ).booleanValue(); } if ( inFunction>0 ) { inFunction--; out(); } else { if (!afterByOrSetOrFromOrSelect) { indent--; newline(); } out(); } beginLine = false; } private void openParen() { if ( isFunctionName( lastToken ) || inFunction>0 ) { inFunction++; } beginLine = false; if ( inFunction>0 ) { out(); } else { out(); if (!afterByOrSetOrFromOrSelect) { indent++; newline(); beginLine = true; } } parensSinceSelect++; } private static boolean isFunctionName(String tok) { final char begin = tok.charAt(0); final boolean isIdentifier = Character.isJavaIdentifierStart( begin ) || '"'==begin; return isIdentifier && !LOGICAL.contains(tok) && !END_CLAUSES.contains(tok) && !QUANTIFIERS.contains(tok) && !DML.contains(tok) && !MISC.contains(tok); } private static boolean isWhitespace(String token) { return StringHelper.WHITESPACE.indexOf(token)>=0; } private void newline() { result.append("\n"); //$NON-NLS-1$ for ( int i=0; i<indent; i++ ) { result.append(indentString); } beginLine = true; } public static void main(String[] args) { if ( args.length>0 ) System.out.println( new Formatter( StringHelper.join(" ", args) ).format() //$NON-NLS-1$ ); } }