001: //$Id: Formatter.java 9961 2006-05-30 13:17:45Z max.andersen@jboss.com $
002: package org.hibernate.pretty;
003:
004: import java.util.HashSet;
005: import java.util.LinkedList;
006: import java.util.Set;
007: import java.util.StringTokenizer;
008:
009: import org.hibernate.util.StringHelper;
010:
011: public class Formatter {
012:
013: private static final Set BEGIN_CLAUSES = new HashSet();
014: private static final Set END_CLAUSES = new HashSet();
015: private static final Set LOGICAL = new HashSet();
016: private static final Set QUANTIFIERS = new HashSet();
017: private static final Set DML = new HashSet();
018: private static final Set MISC = new HashSet();
019: static {
020:
021: BEGIN_CLAUSES.add("left");
022: BEGIN_CLAUSES.add("right");
023: BEGIN_CLAUSES.add("inner");
024: BEGIN_CLAUSES.add("outer");
025: BEGIN_CLAUSES.add("group");
026: BEGIN_CLAUSES.add("order");
027:
028: END_CLAUSES.add("where");
029: END_CLAUSES.add("set");
030: END_CLAUSES.add("having");
031: END_CLAUSES.add("join");
032: END_CLAUSES.add("from");
033: END_CLAUSES.add("by");
034: END_CLAUSES.add("join");
035: END_CLAUSES.add("into");
036: END_CLAUSES.add("union");
037:
038: LOGICAL.add("and");
039: LOGICAL.add("or");
040: LOGICAL.add("when");
041: LOGICAL.add("else");
042: LOGICAL.add("end");
043:
044: QUANTIFIERS.add("in");
045: QUANTIFIERS.add("all");
046: QUANTIFIERS.add("exists");
047: QUANTIFIERS.add("some");
048: QUANTIFIERS.add("any");
049:
050: DML.add("insert");
051: DML.add("update");
052: DML.add("delete");
053:
054: MISC.add("select");
055: MISC.add("on");
056: //MISC.add("values");
057:
058: }
059:
060: String indentString = " ";
061: String initial = "\n ";
062:
063: boolean beginLine = true;
064: boolean afterBeginBeforeEnd = false;
065: boolean afterByOrSetOrFromOrSelect = false;
066: boolean afterValues = false;
067: boolean afterOn = false;
068: boolean afterBetween = false;
069: boolean afterInsert = false;
070: int inFunction = 0;
071: int parensSinceSelect = 0;
072: private LinkedList parenCounts = new LinkedList();
073: private LinkedList afterByOrFromOrSelects = new LinkedList();
074:
075: int indent = 1;
076:
077: StringBuffer result = new StringBuffer();
078: StringTokenizer tokens;
079: String lastToken;
080: String token;
081: String lcToken;
082:
083: public Formatter(String sql) {
084: tokens = new StringTokenizer(sql, "()+*/-=<>'`\"[],"
085: + StringHelper.WHITESPACE, true);
086: }
087:
088: public Formatter setInitialString(String initial) {
089: this .initial = initial;
090: return this ;
091: }
092:
093: public Formatter setIndentString(String indent) {
094: this .indentString = indent;
095: return this ;
096: }
097:
098: public String format() {
099:
100: result.append(initial);
101:
102: while (tokens.hasMoreTokens()) {
103: token = tokens.nextToken();
104: lcToken = token.toLowerCase();
105:
106: if ("'".equals(token)) {
107: String t;
108: do {
109: t = tokens.nextToken();
110: token += t;
111: } while (!"'".equals(t) && tokens.hasMoreTokens()); // cannot handle single quotes
112: } else if ("\"".equals(token)) {
113: String t;
114: do {
115: t = tokens.nextToken();
116: token += t;
117: } while (!"\"".equals(t));
118: }
119:
120: if (afterByOrSetOrFromOrSelect && ",".equals(token)) {
121: commaAfterByOrFromOrSelect();
122: } else if (afterOn && ",".equals(token)) {
123: commaAfterOn();
124: }
125:
126: else if ("(".equals(token)) {
127: openParen();
128: } else if (")".equals(token)) {
129: closeParen();
130: }
131:
132: else if (BEGIN_CLAUSES.contains(lcToken)) {
133: beginNewClause();
134: }
135:
136: else if (END_CLAUSES.contains(lcToken)) {
137: endNewClause();
138: }
139:
140: else if ("select".equals(lcToken)) {
141: select();
142: }
143:
144: else if (DML.contains(lcToken)) {
145: updateOrInsertOrDelete();
146: }
147:
148: else if ("values".equals(lcToken)) {
149: values();
150: }
151:
152: else if ("on".equals(lcToken)) {
153: on();
154: }
155:
156: else if (afterBetween && lcToken.equals("and")) {
157: misc();
158: afterBetween = false;
159: }
160:
161: else if (LOGICAL.contains(lcToken)) {
162: logical();
163: }
164:
165: else if (isWhitespace(token)) {
166: white();
167: }
168:
169: else {
170: misc();
171: }
172:
173: if (!isWhitespace(token))
174: lastToken = lcToken;
175:
176: }
177: return result.toString();
178: }
179:
180: private void commaAfterOn() {
181: out();
182: indent--;
183: newline();
184: afterOn = false;
185: afterByOrSetOrFromOrSelect = true;
186: }
187:
188: private void commaAfterByOrFromOrSelect() {
189: out();
190: newline();
191: }
192:
193: private void logical() {
194: if ("end".equals(lcToken))
195: indent--;
196: newline();
197: out();
198: beginLine = false;
199: }
200:
201: private void on() {
202: indent++;
203: afterOn = true;
204: newline();
205: out();
206: beginLine = false;
207: }
208:
209: private void misc() {
210: out();
211: if ("between".equals(lcToken)) {
212: afterBetween = true;
213: }
214: if (afterInsert) {
215: newline();
216: afterInsert = false;
217: } else {
218: beginLine = false;
219: if ("case".equals(lcToken)) {
220: indent++;
221: }
222: }
223: }
224:
225: private void white() {
226: if (!beginLine) {
227: result.append(" ");
228: }
229: }
230:
231: private void updateOrInsertOrDelete() {
232: out();
233: indent++;
234: beginLine = false;
235: if ("update".equals(lcToken))
236: newline();
237: if ("insert".equals(lcToken))
238: afterInsert = true;
239: }
240:
241: private void select() {
242: out();
243: indent++;
244: newline();
245: parenCounts.addLast(new Integer(parensSinceSelect));
246: afterByOrFromOrSelects.addLast(new Boolean(
247: afterByOrSetOrFromOrSelect));
248: parensSinceSelect = 0;
249: afterByOrSetOrFromOrSelect = true;
250: }
251:
252: private void out() {
253: result.append(token);
254: }
255:
256: private void endNewClause() {
257: if (!afterBeginBeforeEnd) {
258: indent--;
259: if (afterOn) {
260: indent--;
261: afterOn = false;
262: }
263: newline();
264: }
265: out();
266: if (!"union".equals(lcToken))
267: indent++;
268: newline();
269: afterBeginBeforeEnd = false;
270: afterByOrSetOrFromOrSelect = "by".equals(lcToken)
271: || "set".equals(lcToken) || "from".equals(lcToken);
272: }
273:
274: private void beginNewClause() {
275: if (!afterBeginBeforeEnd) {
276: if (afterOn) {
277: indent--;
278: afterOn = false;
279: }
280: indent--;
281: newline();
282: }
283: out();
284: beginLine = false;
285: afterBeginBeforeEnd = true;
286: }
287:
288: private void values() {
289: indent--;
290: newline();
291: out();
292: indent++;
293: newline();
294: afterValues = true;
295: }
296:
297: private void closeParen() {
298: parensSinceSelect--;
299: if (parensSinceSelect < 0) {
300: indent--;
301: parensSinceSelect = ((Integer) parenCounts.removeLast())
302: .intValue();
303: afterByOrSetOrFromOrSelect = ((Boolean) afterByOrFromOrSelects
304: .removeLast()).booleanValue();
305: }
306: if (inFunction > 0) {
307: inFunction--;
308: out();
309: } else {
310: if (!afterByOrSetOrFromOrSelect) {
311: indent--;
312: newline();
313: }
314: out();
315: }
316: beginLine = false;
317: }
318:
319: private void openParen() {
320: if (isFunctionName(lastToken) || inFunction > 0) {
321: inFunction++;
322: }
323: beginLine = false;
324: if (inFunction > 0) {
325: out();
326: } else {
327: out();
328: if (!afterByOrSetOrFromOrSelect) {
329: indent++;
330: newline();
331: beginLine = true;
332: }
333: }
334: parensSinceSelect++;
335: }
336:
337: private static boolean isFunctionName(String tok) {
338: final char begin = tok.charAt(0);
339: final boolean isIdentifier = Character
340: .isJavaIdentifierStart(begin)
341: || '"' == begin;
342: return isIdentifier && !LOGICAL.contains(tok)
343: && !END_CLAUSES.contains(tok)
344: && !QUANTIFIERS.contains(tok) && !DML.contains(tok)
345: && !MISC.contains(tok);
346: }
347:
348: private static boolean isWhitespace(String token) {
349: return StringHelper.WHITESPACE.indexOf(token) >= 0;
350: }
351:
352: private void newline() {
353: result.append("\n");
354: for (int i = 0; i < indent; i++) {
355: result.append(indentString);
356: }
357: beginLine = true;
358: }
359:
360: public static void main(String[] args) {
361: if (args.length > 0)
362: System.out.println(new Formatter(StringHelper.join(" ",
363: args)).format());
364: }
365:
366: }
|