001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.lib.jdbc;
020:
021: import java.util.ArrayList;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.StringTokenizer;
025:
026: import org.apache.openjpa.lib.util.J2DoPrivHelper;
027:
028: /*
029: * Lots of this could be abstracted out into a word-wrapping class.
030: */
031:
032: /**
033: * Converts single-line SQL strings into nicely-formatted
034: * multi-line, indented statements.
035: * Example: from PERSON t0, COMPANY t1 WHERE t0.ID = 10 AND \
036: * t0.COMPANY_ID = t1.ID AND t1.NAME = 'OpenJPA'</code> becomes
037: * <code>SELECT * FROM PERSON t0, COMPANY t1
038: * WHERE t0.ID = 10 AND t0.COMPANY_ID = t1.ID AND t1.NAME = 'OpenJPA'\
039: * </code> and
040: * <code>INSERT INTO PERSON VALUES('Patrick', 'Linskey', 'OpenJPA', \
041: * '202 595 2064 x1111')</code> becomes
042: * <code>INSERT INTO PERSON VALUES('Patrick', 'Linskey', 'OpenJPA', '202
043: * 595 2064 x1111')</code> etc.
044: *
045: * @author Patrick Linskey
046: */
047: public class SQLFormatter {
048:
049: private boolean multiLine = false;
050: private boolean doubleSpace = true;
051: private String newline = J2DoPrivHelper.getLineSeparator();
052: private int lineLength = 72;
053: private String wrapIndent = " ";
054: private String clauseIndent = " ";
055:
056: private static final String[] selectSeparators = new String[] {
057: "FROM ", "WHERE ", "ORDER BY ", // ### is this order correct?
058: "GROUP BY ", "HAVING ", };
059:
060: private static final String[] insertSeparators = new String[] { "VALUES ", };
061:
062: private static final String[] updateSeparators = new String[] {
063: "SET ", "WHERE ", };
064:
065: private static final String[] deleteSeparators = new String[] { "WHERE ", };
066:
067: private static final String[] createTableSeparators = new String[] { "( ", };
068:
069: private static final String[] createIndexSeparators = new String[] {
070: "ON ", "( ", };
071:
072: public void setNewline(String val) {
073: newline = val;
074: }
075:
076: public String getNewline() {
077: return newline;
078: }
079:
080: public void setLineLength(int val) {
081: lineLength = val;
082: }
083:
084: public int getLineLength() {
085: return lineLength;
086: }
087:
088: public void setWrapIndent(String val) {
089: wrapIndent = val;
090: }
091:
092: public String getWrapIndent() {
093: return wrapIndent;
094: }
095:
096: public void setClauseIndent(String val) {
097: clauseIndent = val;
098: }
099:
100: public String getClauseIndent() {
101: return clauseIndent;
102: }
103:
104: /**
105: * If true, then try to parse multi-line SQL statements.
106: */
107: public void setMultiLine(boolean multiLine) {
108: this .multiLine = multiLine;
109: }
110:
111: /**
112: * If true, then try to parse multi-line SQL statements.
113: */
114: public boolean getMultiLine() {
115: return this .multiLine;
116: }
117:
118: /**
119: * If true, then output two lines after multi-line statements.
120: */
121: public void setDoubleSpace(boolean doubleSpace) {
122: this .doubleSpace = doubleSpace;
123: }
124:
125: /**
126: * If true, then output two lines after multi-line statements.
127: */
128: public boolean getDoubleSpace() {
129: return this .doubleSpace;
130: }
131:
132: public Object prettyPrint(Object sqlObject) {
133: if (!multiLine) {
134: return prettyPrintLine(sqlObject);
135: } else {
136: StringBuffer sql = new StringBuffer(sqlObject.toString());
137: StringBuffer buf = new StringBuffer(sql.length());
138:
139: while (sql.length() > 0) {
140: String line = null;
141:
142: int index = Math.max(sql.toString().indexOf(";\n"), sql
143: .toString().indexOf(";\r"));
144: if (index == -1)
145: line = sql.toString();
146: else
147: line = sql.substring(0, index + 2);
148:
149: // remove the current line from the sql buffer
150: sql.delete(0, line.length());
151:
152: buf.append(prettyPrintLine(line));
153: for (int i = 0; i < 1 + (getDoubleSpace() ? 1 : 0); i++)
154: buf.append(J2DoPrivHelper.getLineSeparator());
155: }
156:
157: return buf.toString();
158: }
159: }
160:
161: private Object prettyPrintLine(Object sqlObject) {
162: String sql = sqlObject.toString().trim();
163: String lowerCaseSql = sql.toLowerCase();
164:
165: String[] separators;
166: if (lowerCaseSql.startsWith("select"))
167: separators = selectSeparators;
168: else if (lowerCaseSql.startsWith("insert"))
169: separators = insertSeparators;
170: else if (lowerCaseSql.startsWith("update"))
171: separators = updateSeparators;
172: else if (lowerCaseSql.startsWith("delete"))
173: separators = deleteSeparators;
174: else if (lowerCaseSql.startsWith("create table"))
175: separators = createTableSeparators;
176: else if (lowerCaseSql.startsWith("create index"))
177: separators = createIndexSeparators;
178: else
179: separators = new String[0];
180:
181: int start = 0;
182: int end = -1;
183: StringBuffer clause;
184: List clauses = new ArrayList();
185: clauses.add(new StringBuffer());
186: for (int i = 0; i < separators.length; i++) {
187: end = lowerCaseSql.indexOf(" "
188: + separators[i].toLowerCase(), start);
189: if (end == -1)
190: break;
191:
192: clause = (StringBuffer) clauses.get(clauses.size() - 1);
193: clause.append(sql.substring(start, end));
194:
195: clause = new StringBuffer();
196: clauses.add(clause);
197: clause.append(clauseIndent);
198: clause.append(separators[i]);
199:
200: start = end + 1 + separators[i].length();
201: }
202:
203: clause = (StringBuffer) clauses.get(clauses.size() - 1);
204: clause.append(sql.substring(start));
205:
206: StringBuffer pp = new StringBuffer(sql.length());
207: for (Iterator iter = clauses.iterator(); iter.hasNext();) {
208: pp
209: .append(wrapLine(((StringBuffer) iter.next())
210: .toString()));
211: if (iter.hasNext())
212: pp.append(newline);
213: }
214:
215: return pp.toString();
216: }
217:
218: private String wrapLine(String line) {
219: StringBuffer lines = new StringBuffer(line.length());
220:
221: // ensure that any leading whitespace is preserved.
222: for (int i = 0; i < line.length()
223: && (line.charAt(i) == ' ' || line.charAt(i) == '\t'); i++) {
224: lines.append(line.charAt(i));
225: }
226:
227: StringTokenizer tok = new StringTokenizer(line);
228: int length = 0;
229: String elem;
230: while (tok.hasMoreTokens()) {
231: elem = tok.nextToken();
232: length += elem.length();
233:
234: // if we would have exceeded the max, write out a newline
235: // before writing the elem.
236: if (length >= lineLength) {
237: lines.append(newline);
238: lines.append(wrapIndent);
239: lines.append(elem);
240: lines.append(' ');
241: length = wrapIndent.length() + elem.length() + 1;
242: continue;
243: }
244:
245: // if the current length is greater than the max, then the
246: // last word alone was too long, so just write out a
247: // newline and move on.
248: if (elem.length() >= lineLength) {
249: lines.append(elem);
250: if (tok.hasMoreTokens())
251: lines.append(newline);
252: lines.append(wrapIndent);
253: length = wrapIndent.length();
254: continue;
255: }
256:
257: lines.append(elem);
258: lines.append(' ');
259: length++;
260: }
261:
262: return lines.toString();
263: }
264:
265: public static void main(String[] args) {
266: SQLFormatter formatter = new SQLFormatter();
267: for (int i = 0; i < args.length; i++) {
268: System.out.println(formatter.prettyPrint(args[i]));
269: }
270: }
271: }
|