0001: /*
0002: * SqlFormatter.java
0003: *
0004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
0005: *
0006: * Copyright 2002-2008, Thomas Kellerer
0007: * No part of this code maybe reused without the permission of the author
0008: *
0009: * To contact the author please send an email to: support@sql-workbench.net
0010: *
0011: */
0012: package workbench.sql.formatter;
0013:
0014: import java.io.Reader;
0015: import java.util.ArrayList;
0016: import java.util.Collections;
0017: import java.util.HashSet;
0018: import java.util.List;
0019: import java.util.Set;
0020: import java.util.TreeSet;
0021: import workbench.util.CaseInsensitiveComparator;
0022: import workbench.resource.Settings;
0023: import workbench.sql.syntax.SqlKeywordHelper;
0024: import workbench.sql.wbcommands.CommandTester;
0025: import workbench.util.CharSequenceReader;
0026: import workbench.util.StringUtil;
0027:
0028: /**
0029: * @author support@sql-workbench.net
0030: */
0031: public class SqlFormatter {
0032: private final Set<String> LINE_BREAK_BEFORE = new HashSet<String>();
0033: {
0034: LINE_BREAK_BEFORE.add("SELECT");
0035: LINE_BREAK_BEFORE.add("SET");
0036: LINE_BREAK_BEFORE.add("FROM");
0037: LINE_BREAK_BEFORE.add("WHERE");
0038: LINE_BREAK_BEFORE.add("ORDER BY");
0039: LINE_BREAK_BEFORE.add("GROUP BY");
0040: LINE_BREAK_BEFORE.add("HAVING");
0041: LINE_BREAK_BEFORE.add("VALUES");
0042: LINE_BREAK_BEFORE.add("UNION");
0043: LINE_BREAK_BEFORE.add("UNION ALL");
0044: LINE_BREAK_BEFORE.add("MINUS");
0045: LINE_BREAK_BEFORE.add("INTERSECT");
0046: LINE_BREAK_BEFORE.add("REFRESH");
0047: LINE_BREAK_BEFORE.add("AS");
0048: LINE_BREAK_BEFORE.add("FOR");
0049: LINE_BREAK_BEFORE.add("INNER JOIN");
0050: LINE_BREAK_BEFORE.add("RIGHT OUTER JOIN");
0051: LINE_BREAK_BEFORE.add("LEFT OUTER JOIN");
0052: LINE_BREAK_BEFORE.add("CROSS JOIN");
0053: LINE_BREAK_BEFORE.add("LEFT JOIN");
0054: LINE_BREAK_BEFORE.add("RIGHT JOIN");
0055: LINE_BREAK_BEFORE.add("START WITH");
0056: LINE_BREAK_BEFORE.add("CONNECT BY");
0057: }
0058:
0059: private final Set<String> LINE_BREAK_AFTER = new HashSet<String>();
0060: {
0061: LINE_BREAK_AFTER.add("UNION");
0062: LINE_BREAK_AFTER.add("UNION ALL");
0063: LINE_BREAK_AFTER.add("MINUS");
0064: LINE_BREAK_AFTER.add("INTERSECT");
0065: LINE_BREAK_AFTER.add("AS");
0066: LINE_BREAK_AFTER.add("FOR");
0067: }
0068:
0069: // keywords terminating a WHERE clause
0070: public static final Set<String> WHERE_TERMINAL = new HashSet<String>();
0071: static {
0072: WHERE_TERMINAL.add("ORDER BY");
0073: WHERE_TERMINAL.add("GROUP BY");
0074: WHERE_TERMINAL.add("HAVING");
0075: WHERE_TERMINAL.add("UNION");
0076: WHERE_TERMINAL.add("UNION ALL");
0077: WHERE_TERMINAL.add("INTERSECT");
0078: WHERE_TERMINAL.add("MINUS");
0079: WHERE_TERMINAL.add(";");
0080: }
0081:
0082: // keywords terminating the FROM part
0083: public static final Set<String> FROM_TERMINAL = new HashSet<String>();
0084: static {
0085: FROM_TERMINAL.addAll(WHERE_TERMINAL);
0086: FROM_TERMINAL.add("WHERE");
0087: FROM_TERMINAL.add("START WITH");
0088: FROM_TERMINAL.add("CONNECT BY");
0089: }
0090:
0091: // keywords terminating an GROUP BY clause
0092: private final Set<String> GROUP_BY_TERMINAL = new HashSet<String>();
0093: {
0094: GROUP_BY_TERMINAL.addAll(WHERE_TERMINAL);
0095: GROUP_BY_TERMINAL.add("SELECT");
0096: GROUP_BY_TERMINAL.add("UPDATE");
0097: GROUP_BY_TERMINAL.add("DELETE");
0098: GROUP_BY_TERMINAL.add("INSERT");
0099: GROUP_BY_TERMINAL.add("CREATE");
0100: GROUP_BY_TERMINAL.add("CREATE OR REPLACE");
0101: }
0102:
0103: private final Set<String> ORDER_BY_TERMINAL = new HashSet<String>();
0104: {
0105: ORDER_BY_TERMINAL.remove("GROUP BY");
0106: ORDER_BY_TERMINAL.add(";");
0107: }
0108:
0109: public static final Set<String> SELECT_TERMINAL = new HashSet<String>(
0110: 1);
0111: static {
0112: SELECT_TERMINAL.add("FROM");
0113: }
0114:
0115: private final Set<String> SET_TERMINAL = new HashSet<String>();
0116: {
0117: SET_TERMINAL.add("FROM");
0118: SET_TERMINAL.add("WHERE");
0119: }
0120:
0121: private static final Set<String> TABLE_CONSTRAINTS_KEYWORDS = new HashSet<String>();
0122: {
0123: TABLE_CONSTRAINTS_KEYWORDS.add("FOREIGN KEY");
0124: TABLE_CONSTRAINTS_KEYWORDS.add("PRIMARY KEY");
0125: TABLE_CONSTRAINTS_KEYWORDS.add("CONSTRAINT");
0126: }
0127:
0128: private CharSequence sql;
0129: private SQLLexer lexer;
0130: private StringBuilder result;
0131: private StringBuilder indent = null;
0132: private StringBuilder leadingWhiteSpace = null;
0133: private int realLength = 0;
0134: private int maxSubselectLength = 60;
0135: private Set<String> dbFunctions = Collections.emptySet();
0136: private Set<String> dataTypes = Collections.emptySet();
0137: private int selectColumnsPerLine = 1;
0138: private static final String NL = "\n";
0139: private boolean lowerCaseFunctions;
0140:
0141: public SqlFormatter(CharSequence aScript) {
0142: this (aScript, 0, Settings.getInstance()
0143: .getFormatterMaxSubselectLength());
0144: }
0145:
0146: public SqlFormatter(CharSequence aScript, int maxSubselectLength) {
0147: this (aScript, 0, maxSubselectLength);
0148: }
0149:
0150: private SqlFormatter(CharSequence aScript, int indentCount,
0151: int maxSubselectLength) {
0152: this .sql = aScript;
0153: Reader in = new CharSequenceReader(this .sql);
0154: this .lexer = new SQLLexer(in);
0155: this .result = new StringBuilder(this .sql.length() + 100);
0156: if (indentCount > 0) {
0157: this .indent = new StringBuilder(indentCount);
0158: for (int i = 0; i < indentCount; i++)
0159: this .indent.append(' ');
0160: }
0161: this .maxSubselectLength = maxSubselectLength;
0162: this .dbFunctions = new TreeSet<String>(
0163: new CaseInsensitiveComparator());
0164: this .lowerCaseFunctions = Settings.getInstance()
0165: .getFormatterLowercaseFunctions();
0166: addStandardFunctions(dbFunctions);
0167: }
0168:
0169: public void setUseLowerCaseFunctions(boolean flag) {
0170: this .lowerCaseFunctions = flag;
0171: }
0172:
0173: public String getLineEnding() {
0174: return NL;
0175: }
0176:
0177: public void setMaxColumnsPerSelect(int cols) {
0178: this .selectColumnsPerLine = cols;
0179: }
0180:
0181: public void setDbDataTypes(Set<String> types) {
0182: this .dataTypes = types;
0183: }
0184:
0185: public void setDBFunctions(Set<String> functionNames) {
0186: this .dbFunctions = new HashSet<String>();
0187: if (functionNames != null) {
0188: this .dbFunctions.addAll(functionNames);
0189: }
0190: addStandardFunctions(dbFunctions);
0191: }
0192:
0193: private void addStandardFunctions(Set<String> functions) {
0194: SqlKeywordHelper keyWords = new SqlKeywordHelper();
0195: functions.addAll(keyWords.getSystemFunctions());
0196: }
0197:
0198: private void saveLeadingWhitespace() {
0199: if (this .sql.length() == 0)
0200: return;
0201: char c = this .sql.charAt(0);
0202: int pos = 0;
0203: if (!Character.isWhitespace(c))
0204: return;
0205:
0206: this .leadingWhiteSpace = new StringBuilder(50);
0207: while (Character.isWhitespace(c)) {
0208: this .leadingWhiteSpace.append(c);
0209: pos++;
0210: if (pos >= sql.length())
0211: break;
0212: c = this .sql.charAt(pos);
0213: }
0214: this .sql = this .sql.toString().trim();
0215: }
0216:
0217: public CharSequence getFormattedSql() throws Exception {
0218: saveLeadingWhitespace();
0219: if (this .sql.length() == 0)
0220: return sql;
0221:
0222: this .formatSql();
0223: StringUtil.trimTrailingWhitespace(result);
0224: if (this .leadingWhiteSpace != null) {
0225: this .result.insert(0, this .leadingWhiteSpace);
0226: }
0227: return this .result.toString();
0228: }
0229:
0230: private int getRealLength() {
0231: return this .realLength;
0232: }
0233:
0234: public void setMaxSubSelectLength(int max) {
0235: this .maxSubselectLength = max;
0236: }
0237:
0238: private int getCurrentLineLength() {
0239: int c = this .result.length() - 1;
0240: int pos = 0;
0241: while (this .result.charAt(c) != '\n' && c > 0) {
0242: pos++;
0243: c--;
0244: }
0245: return pos;
0246: }
0247:
0248: private void appendNewline() {
0249: if (this .result.length() == 0)
0250: return;
0251: this .result.append(SqlFormatter.NL);
0252: if (this .indent != null)
0253: this .result.append(indent);
0254: }
0255:
0256: private boolean lastCharIsWhitespace() {
0257: int len = this .result.length();
0258: if (len == 0)
0259: return false;
0260: char c = this .result.charAt(len - 1);
0261: return Character.isWhitespace(c);
0262: }
0263:
0264: private void appendText(char c) {
0265: this .realLength++;
0266: this .result.append(c);
0267: }
0268:
0269: private void appendTokenText(Token t) {
0270: String text = t.getContents();
0271: if (this .lowerCaseFunctions && this .dbFunctions.contains(text)) {
0272: text = text.toLowerCase();
0273: }
0274: appendText(text);
0275: }
0276:
0277: private void appendComment(String text) {
0278: if (text.startsWith("--")) {
0279: if (!this .isStartOfLine())
0280: this .appendNewline();
0281: } else {
0282: if (!this .lastCharIsWhitespace())
0283: this .appendText(' ');
0284: }
0285: this .appendText(text);
0286: if (text.startsWith("--")) {
0287: this .appendNewline();
0288: } else {
0289: this .appendText(' ');
0290: }
0291: }
0292:
0293: private void appendText(String text) {
0294: this .realLength += text.length();
0295: this .result.append(text);
0296: }
0297:
0298: private void appendText(StringBuilder text) {
0299: if (text.length() == 0)
0300: return;
0301: this .realLength += text.length();
0302: this .result.append(text);
0303: }
0304:
0305: private void indent(String text) {
0306: this .result.append(text);
0307: }
0308:
0309: private void indent(StringBuilder text) {
0310: this .result.append(text);
0311: }
0312:
0313: private boolean needsWhitespace(SQLToken last, SQLToken current) {
0314: return this .needsWhitespace(last, current, false);
0315: }
0316:
0317: private boolean isDbFunction(String key) {
0318: if (dbFunctions == null) {
0319: SqlKeywordHelper keyWords = new SqlKeywordHelper();
0320: dbFunctions = keyWords.getSystemFunctions();
0321: }
0322: return this .dbFunctions.contains(key.toUpperCase());
0323: }
0324:
0325: private boolean isDatatype(String key) {
0326: if (dataTypes == null) {
0327: SqlKeywordHelper keyWords = new SqlKeywordHelper();
0328: dataTypes = keyWords.getDataTypes();
0329: }
0330: return this .dataTypes.contains(key.toUpperCase());
0331: }
0332:
0333: /**
0334: * Return true if a whitespace should be added before the current token.
0335: */
0336: private boolean needsWhitespace(SQLToken last, SQLToken current,
0337: boolean ignoreStartOfline) {
0338: String lastV = last.getContents();
0339: String currentV = current.getContents();
0340: char lastChar = lastV.charAt(0);
0341: char currChar = currentV.charAt(0);
0342: if (last.isWhiteSpace())
0343: return false;
0344: if (!ignoreStartOfline && this .isStartOfLine())
0345: return false;
0346: boolean isCurrentOpenBracket = "(".equals(currentV);
0347: boolean isLastOpenBracket = "(".equals(lastV);
0348: boolean isLastCloseBracket = ")".equals(lastV);
0349:
0350: if (isCurrentOpenBracket && last.isIdentifier())
0351: return false;
0352: if (isCurrentOpenBracket && isDbFunction(lastV))
0353: return false;
0354: if (isCurrentOpenBracket && isDatatype(lastV))
0355: return false;
0356: if (isCurrentOpenBracket && last.isReservedWord())
0357: return true;
0358: if (isLastCloseBracket && currChar == ',')
0359: return false;
0360: if (isLastCloseBracket
0361: && (current.isIdentifier() || current.isReservedWord()))
0362: return true;
0363:
0364: if ((lastChar == '-' || lastChar == '+') && current.isLiteral()
0365: && StringUtil.isNumber(currentV))
0366: return false;
0367:
0368: if (last.isLiteral()
0369: && (current.isIdentifier() || current.isReservedWord() || current
0370: .isOperator()))
0371: return true;
0372:
0373: //if (last.isLiteral() && current.isLiteral()) return false;
0374:
0375: if (currChar == '?')
0376: return true;
0377: if (currentV.equals("="))
0378: return true;
0379: if (lastV.equals("="))
0380: return true;
0381:
0382: if (lastChar == '.' && current.isIdentifier())
0383: return false;
0384: if (isLastOpenBracket && current.isReservedWord())
0385: return false;
0386: if (isLastCloseBracket && !current.isSeparator())
0387: return true;
0388: if ((last.isIdentifier() || last.isLiteral())
0389: && current.isOperator())
0390: return true;
0391: if ((current.isIdentifier() || current.isLiteral())
0392: && last.isOperator())
0393: return true;
0394: if (current.isSeparator() || current.isOperator())
0395: return false;
0396: if (last.isOperator()
0397: && (current.isReservedWord() || current.isIdentifier() || current
0398: .isLiteral()))
0399: return true;
0400: if (last.isSeparator() || last.isOperator())
0401: return false;
0402: return true;
0403: }
0404:
0405: private SQLToken processFrom(SQLToken last) throws Exception {
0406: StringBuilder b = new StringBuilder(" ");
0407: StringBuilder oldIndent = this .indent;
0408: //this.indent = null;
0409: SQLToken t = this .lexer.getNextToken(true, false);
0410: SQLToken lastToken = last;
0411: while (t != null) {
0412: String text = t.getContents();
0413:
0414: if (t.isReservedWord()
0415: && FROM_TERMINAL.contains(text.toUpperCase())) {
0416: this .indent = oldIndent;
0417: return t;
0418: } else if (lastToken.isSeparator()
0419: && lastToken.getContents().equals("(")
0420: && text.equalsIgnoreCase("SELECT")) {
0421: t = this .processSubSelect(true);
0422: continue;
0423: }
0424:
0425: if (t.isComment()) {
0426: this .appendComment(text);
0427: } else if (t.isSeparator() && text.equals("(")) {
0428: if ((!lastToken.isSeparator() || lastToken == t)
0429: && !this .lastCharIsWhitespace())
0430: this .appendText(' ');
0431: this .appendText(text);
0432: } else if (t.isSeparator() && text.equals(")")) {
0433: this .appendText(text);
0434: } else if (t.isSeparator() && text.equals(",")) {
0435: this .appendText(",");
0436: this .appendNewline();
0437: this .indent(b);
0438: } else {
0439: if (this .needsWhitespace(lastToken, t))
0440: this .appendText(' ');
0441: if (LINE_BREAK_BEFORE.contains(text)
0442: && !text.equalsIgnoreCase("AS")) {
0443: this .appendNewline();
0444: this .indent(b);
0445: }
0446: this .appendText(text);
0447: if (LINE_BREAK_AFTER.contains(text)
0448: && !text.equalsIgnoreCase("AS")) {
0449: this .appendNewline();
0450: this .indent(b);
0451: }
0452: }
0453: lastToken = t;
0454: t = this .lexer.getNextToken(true, false);
0455: }
0456: this .indent = oldIndent;
0457: return null;
0458: }
0459:
0460: private SQLToken processList(SQLToken last, int indentCount,
0461: Set<String> terminalKeys) throws Exception {
0462: StringBuilder b = new StringBuilder(indentCount);
0463: for (int i = 0; i < indentCount; i++)
0464: b.append(' ');
0465:
0466: int currentColumnCount = 0;
0467: boolean isSelect = last.getContents().equals("SELECT");
0468: SQLToken t = this .lexer.getNextToken(true, false);
0469: SQLToken lastToken = last;
0470:
0471: while (t != null) {
0472: final String text = t.getContents();
0473: if (t.isComment()) {
0474: this .appendComment(text);
0475: } else if (isSelect && "DECODE".equalsIgnoreCase(text)) {
0476: if (this .needsWhitespace(lastToken, t))
0477: this .appendText(' ');
0478: this .appendText(text);
0479: t = processDecode(indentCount);
0480: continue;
0481: } else if ("CASE".equals(text)) {
0482: if (this .needsWhitespace(lastToken, t))
0483: this .appendText(' ');
0484: this .appendText(text);
0485: int caseIndent = indentCount;
0486: if (!isSelect) {
0487: caseIndent = this .getCurrentLineLength() - 4;
0488: this .appendText(' ');
0489: }
0490: t = processCase(caseIndent);
0491: continue;
0492: } else if (t.isReservedWord()
0493: && terminalKeys.contains(text.toUpperCase())) {
0494: return t;
0495: } else if (t.isSeparator() && text.equals("(")) {
0496: if (this .needsWhitespace(lastToken, t))
0497: this .appendText(' ');
0498: this .appendText("(");
0499: // an equal sign immediately followed by an opening
0500: // bracket cannot be a function call (the function name
0501: // is missing) so it has to be a sub-select
0502: if ("=".equals(lastToken.getContents())) {
0503: t = this .processSubSelect(false);
0504: this .appendTokenText(t);
0505: } else {
0506: t = this .processFunctionCall(t);
0507: if (t == null)
0508: return null;
0509: if (t.isIdentifier()) {
0510: this .appendText(' ');
0511: this .appendTokenText(t);
0512: }
0513: }
0514: } else if (t.isSeparator() && text.equals(",")) {
0515: this .appendText(',');
0516: currentColumnCount++;
0517: if (!isSelect
0518: || currentColumnCount >= selectColumnsPerLine) {
0519: currentColumnCount = 0;
0520: this .appendNewline();
0521: this .indent(b);
0522: } else {
0523: this .appendText(' ');
0524: }
0525: } else if (text.equals("*") && !lastToken.isSeparator()) {
0526: this .appendText(" *");
0527: } else {
0528: if (this .needsWhitespace(lastToken, t))
0529: this .appendText(' ');
0530: this .appendTokenText(t);
0531: }
0532: lastToken = t;
0533: t = this .lexer.getNextToken(true, false);
0534: }
0535: return null;
0536: }
0537:
0538: private SQLToken processSubSelect(boolean addSelectKeyword)
0539: throws Exception {
0540: SQLToken t = this .lexer.getNextToken();
0541: int bracketCount = 1;
0542: StringBuilder subSql = new StringBuilder(250);
0543:
0544: // this method gets called when then "parser" hits an
0545: // IN ( situation. If no SELECT is coming, we assume
0546: // its a list like IN ('x','Y')
0547: if (!"SELECT".equalsIgnoreCase(t.getContents())
0548: && !addSelectKeyword) {
0549: return this .processInList(t);
0550: }
0551:
0552: if (addSelectKeyword) {
0553: subSql.append("SELECT ");
0554: }
0555:
0556: int lastIndent = this .getCurrentLineLength();
0557:
0558: while (t != null) {
0559: String text = t.getContents();
0560: if (text.equals(")")) {
0561: bracketCount--;
0562:
0563: if (bracketCount == 0) {
0564: appendSubSelect(subSql, lastIndent);
0565: return t;
0566: }
0567: } else if (t.isSeparator() && text.equals("(")) {
0568: bracketCount++;
0569: }
0570: subSql.append(text);
0571: t = this .lexer.getNextToken();
0572: }
0573: this .appendText(subSql);
0574: return t;
0575: }
0576:
0577: private void appendSubSelect(StringBuilder subSql, int lastIndent)
0578: throws Exception {
0579: SqlFormatter f = new SqlFormatter(subSql.toString(),
0580: lastIndent, this .maxSubselectLength);
0581: String s = f.getFormattedSql().toString();
0582: if (f.getRealLength() < this .maxSubselectLength) {
0583: s = s.replaceAll(" *" + SqlFormatter.NL + " *", " ");
0584: }
0585: this .appendText(s.trim());
0586: }
0587:
0588: private SQLToken processDecode(int indent) throws Exception {
0589: StringBuilder current = new StringBuilder(indent);
0590:
0591: for (int i = 0; i < indent; i++)
0592: current.append(' ');
0593:
0594: StringBuilder b = new StringBuilder(indent + 2);
0595: for (int i = 0; i < indent; i++)
0596: b.append(' ');
0597: b.append(" ");
0598:
0599: boolean newLinePending = false;
0600:
0601: SQLToken t = this .lexer.getNextToken(true, true);
0602: int commaCount = 0;
0603: int bracketCount = 0;
0604:
0605: boolean inQuotes = false;
0606: while (t != null) {
0607: final String text = t.getContents();
0608: if ("'".equals(text)) {
0609: inQuotes = !inQuotes;
0610: } else if (")".equals(text)) {
0611: bracketCount--;
0612: } else if ("(".equals(text)) {
0613: bracketCount++;
0614: }
0615:
0616: if (",".equals(text) && !inQuotes && bracketCount == 1)
0617: commaCount++;
0618:
0619: if (",".equals(text) && !inQuotes && bracketCount == 1) {
0620: this .appendText(text);
0621: if (commaCount % 2 == 1) {
0622: this .appendNewline();
0623: this .indent(b);
0624: }
0625: } else if (")".equalsIgnoreCase(text) && !inQuotes
0626: && bracketCount == 0) {
0627: this .appendNewline();
0628: this .indent(current);
0629: this .appendText(") ");
0630: t = this .lexer.getNextToken(true, false);
0631: return t;
0632: } else if (text.indexOf("\n") == -1
0633: && text.indexOf("\r") == -1) {
0634: this .appendTokenText(t);
0635: }
0636: t = this .lexer.getNextToken(true, true);
0637: }
0638: return null;
0639: }
0640:
0641: private SQLToken processCase(int indent) throws Exception {
0642: StringBuilder current = new StringBuilder(indent);
0643:
0644: for (int i = 0; i < indent; i++)
0645: current.append(' ');
0646:
0647: StringBuilder b = new StringBuilder(indent + 2);
0648: for (int i = 0; i < indent; i++)
0649: b.append(' ');
0650: b.append(" ");
0651:
0652: SQLToken last = null;
0653: SQLToken t = this .lexer.getNextToken(true, false);
0654: while (t != null) {
0655: final String text = t.getContents();
0656:
0657: if ("SELECT".equals(text) && last.getContents().equals("(")) {
0658: t = this .processSubSelect(true);
0659: if (t == null)
0660: return null;
0661: if (t.getContents().equals(")"))
0662: this .appendText(")");
0663: } else if ("WHEN".equals(text) || "ELSE".equals(text)) {
0664: this .appendNewline();
0665: this .indent(b);
0666: this .appendText(text);
0667: appendText(' ');
0668: } else if ("THEN".equals(text)) {
0669: if (last != null && this .needsWhitespace(last, t))
0670: appendText(' ');
0671: this .appendText(text);
0672: appendText(' ');
0673: } else if ("END".equals(text) || "END CASE".equals(text)) {
0674: this .appendNewline();
0675: this .indent(current);
0676: this .appendText(text);
0677: // Get the next token after the END. If that is the keyword AS,
0678: // the CASE statement ist not yet ended and we have to add the AS keyword
0679: // and the alias that was given before returning to the caller
0680: t = this .lexer.getNextToken(true, false);
0681: if (t != null && t.getContents().equals("AS")) {
0682: this .appendText(' ');
0683: this .appendText(t.getContents());
0684: t = this .lexer.getNextToken(true, false);
0685: if (t != null) {
0686: this .appendText(' ');
0687: this .appendText(t.getContents());
0688: t = this .lexer.getNextToken(true, false);
0689: }
0690: }
0691: this .appendNewline();
0692: return t;
0693: } else if (t.isComment()) {
0694: this .appendComment(text);
0695: } else if (!t.isWhiteSpace()) {
0696: this .appendTokenText(t);
0697: }
0698: last = t;
0699: t = this .lexer.getNextToken(true, false);
0700: }
0701: return null;
0702: }
0703:
0704: private SQLToken processWbCommand(int indent) throws Exception {
0705: StringBuilder b = new StringBuilder(indent);
0706:
0707: for (int i = 0; i < indent; i++)
0708: b.append(' ');
0709: this .appendText(' ');
0710:
0711: SQLToken t = this .lexer.getNextToken(true, false);
0712: boolean first = true;
0713: boolean isParm = false;
0714: boolean inQuotes = false;
0715: while (t != null) {
0716: String text = inQuotes ? t.getText() : t.getContents();
0717: if (text.equals("'") || text.equals("\"")) {
0718: inQuotes = !inQuotes;
0719: }
0720: if (isParm)
0721: text = text.toLowerCase();
0722: if (text.equals("-") && !inQuotes) {
0723: if (!first) {
0724: this .appendNewline();
0725: this .indent(b);
0726: }
0727: isParm = true;
0728: } else {
0729: isParm = false;
0730: }
0731: this .appendText(text);
0732: t = this .lexer.getNextToken(true, inQuotes);
0733: first = false;
0734: }
0735: return null;
0736: }
0737:
0738: private SQLToken processBracketList(int indentCount)
0739: throws Exception {
0740: StringBuilder b = new StringBuilder(indentCount);
0741:
0742: for (int i = 0; i < indentCount; i++)
0743: b.append(' ');
0744:
0745: this .appendText(b);
0746: SQLToken t = this .lexer.getNextToken(true, false);
0747:
0748: while (t != null) {
0749: final String text = t.getContents();
0750: if (text.equals(")")) {
0751: this .appendNewline();
0752: //this.indent(b);
0753: this .appendText(")");
0754: return this .lexer.getNextToken();
0755: } else if (text.equals("(")) {
0756: this .appendText(" (");
0757: t = this .processFunctionCall(t);
0758: continue;
0759: } else if (text.equals(",")) {
0760: this .appendText(",");
0761: this .appendNewline();
0762: this .indent(b);
0763: } else if (!t.isWhiteSpace()) {
0764: this .appendTokenText(t);
0765: if (t.isComment())
0766: this .appendText(' ');
0767: }
0768: t = this .lexer.getNextToken(true, false);
0769: }
0770: return null;
0771: }
0772:
0773: private SQLToken processInList(SQLToken current) throws Exception {
0774: ArrayList<StringBuilder> list = new ArrayList<StringBuilder>(25);
0775: list.add(new StringBuilder(""));
0776: SQLToken t = current;
0777:
0778: int bracketcount = 0;
0779: int elementcounter = 0;
0780:
0781: while (t != null) {
0782: String text = t.getContents();
0783: if (t.isSeparator() && text.equals(")")) {
0784: if (bracketcount == 0) {
0785: this .appendCommaList(list);
0786: return this .lexer.getNextToken();
0787: } else {
0788: StringBuilder b = list.get(elementcounter);
0789: if (b == null) {
0790: b = new StringBuilder(text);
0791: if (elementcounter < list.size())
0792: list.set(elementcounter, b);
0793: } else {
0794: b.append(text);
0795: }
0796: }
0797: } else if (t.isSeparator() && text.equals("(")) {
0798: bracketcount++;
0799: } else if (t.isSeparator() && text.equals(",")) {
0800: if (bracketcount == 0) {
0801: list.add(new StringBuilder(""));
0802: elementcounter = list.size() - 1;
0803: }
0804: } else if (!t.isWhiteSpace()) {
0805: StringBuilder b = list.get(elementcounter);
0806: if (b == null) {
0807: b = new StringBuilder(text);
0808: if (t.isComment())
0809: b.append(' ');
0810: list.set(elementcounter, b);
0811: } else {
0812: b.append(text);
0813: if (t.isComment())
0814: b.append(' ');
0815: }
0816: }
0817: t = this .lexer.getNextToken(true, false);
0818: }
0819: return null;
0820: }
0821:
0822: private void appendCommaList(ArrayList aList) {
0823: int indentCount = this .getCurrentLineLength();
0824: StringBuilder ind = new StringBuilder(indentCount);
0825: for (int i = 0; i < indentCount; i++)
0826: ind.append(' ');
0827: boolean newline = (aList.size() > 10);
0828: int count = aList.size();
0829: for (int i = 0; i < count; i++) {
0830: this .appendText((StringBuilder) aList.get(i));
0831: if (i < count - 1)
0832: this .appendText(", ");
0833: if (newline) {
0834: this .appendNewline();
0835: this .indent(ind);
0836: }
0837: }
0838: this .appendText(")");
0839: }
0840:
0841: private boolean isStartOfLine() {
0842: int len = this .result.length();
0843: if (len == 0)
0844: return true;
0845:
0846: // simulates endsWith() on a StringBuilder
0847: int pos = result.lastIndexOf(SqlFormatter.NL);
0848: if (pos == len - NL.length())
0849: return true;
0850:
0851: // Current text does not end with a newline, but
0852: // if the "current line" consist of the current indent, it
0853: // is considered as a "start of line" as well.
0854: String remain = result.substring(pos + NL.length());
0855: int indentLength = (indent == null ? 0 : indent.length());
0856: if (remain.trim().length() == 0
0857: && remain.length() == indentLength)
0858: return true;
0859: return false;
0860: }
0861:
0862: private void formatSql() throws Exception {
0863: SQLToken t = this .lexer.getNextToken(true, false);
0864: SQLToken lastToken = t;
0865: CommandTester wbTester = new CommandTester();
0866:
0867: while (t != null) {
0868: final String word = t.getContents().toUpperCase();
0869: if (t.isComment()) {
0870: String text = t.getContents();
0871: this .appendComment(text);
0872: } else if (t.isReservedWord()) {
0873: if (lastToken.isComment() && !isStartOfLine())
0874: this .appendNewline();
0875:
0876: if (LINE_BREAK_BEFORE.contains(word)) {
0877: if (!isStartOfLine())
0878: this .appendNewline();
0879:
0880: // For UPDATE statements
0881: if ("SET".equals(word)) {
0882: this .indent(" ");
0883: }
0884: } else {
0885: //if (!lastToken.isSeparator() && lastToken != t && !isStartOfLine()) this.appendText(' ');
0886: if (needsWhitespace(lastToken, t))
0887: this .appendText(' ');
0888: }
0889:
0890: if (wbTester.isWbCommand(word)) {
0891: this .appendText(wbTester.formatVerb(word));
0892: } else {
0893: this .appendTokenText(t);
0894: }
0895:
0896: if (LINE_BREAK_AFTER.contains(word)) {
0897: this .appendNewline();
0898: }
0899:
0900: if (word.equals("SELECT")) {
0901: lastToken = t;
0902: t = this .processList(t, "SELECT".length() + 1,
0903: SELECT_TERMINAL);
0904: if (t == null)
0905: return;
0906: continue;
0907: }
0908:
0909: if (word.equals("SET")) {
0910: lastToken = t;
0911: t = this .processList(t, "SET".length() + 4,
0912: SET_TERMINAL);
0913: if (t == null)
0914: return;
0915: continue;
0916: }
0917:
0918: if (word.equals("CREATE")
0919: || word.equals("CREATE OR REPLACE")) {
0920: lastToken = t;
0921: t = this .processCreate(t);
0922: if (t == null)
0923: return;
0924: continue;
0925: }
0926:
0927: if (word.equals("FROM")) {
0928: lastToken = t;
0929: t = this .processFrom(t);
0930: if (t == null)
0931: return;
0932: continue;
0933: }
0934:
0935: if (word.equals("GROUP BY")) {
0936: lastToken = t;
0937: t = this .processList(lastToken, (word + " ")
0938: .length(), GROUP_BY_TERMINAL);
0939: if (t == null)
0940: return;
0941: continue;
0942: }
0943:
0944: if (word.equals("ORDER BY")) {
0945: lastToken = t;
0946: t = this .processList(lastToken, (word + " ")
0947: .length(), ORDER_BY_TERMINAL);
0948: if (t == null)
0949: return;
0950: }
0951:
0952: if (word.equalsIgnoreCase("WHERE")) {
0953: lastToken = t;
0954: t = this .processWhere(t);
0955: if (t == null)
0956: return;
0957: continue;
0958: }
0959:
0960: if (word.equalsIgnoreCase("INTO")) {
0961: lastToken = t;
0962: t = this .processIntoKeyword();
0963: continue;
0964: }
0965:
0966: if (word.equalsIgnoreCase("VALUES")) {
0967: // the next (non-whitespace token has to be a (
0968: t = this .lexer.getNextToken(false, false);
0969: if (t.isSeparator() && t.getContents().equals("(")) {
0970: this .appendNewline();
0971: this .appendText("(");
0972: this .appendNewline();
0973:
0974: t = this .processBracketList(2);
0975: }
0976: if (t == null)
0977: return;
0978: continue;
0979: }
0980:
0981: if (wbTester.isWbCommand(word)) {
0982: t = this .processWbCommand(word.length() + 1);
0983: }
0984:
0985: } else {
0986: if (LINE_BREAK_BEFORE.contains(word)) {
0987: if (!isStartOfLine())
0988: this .appendNewline();
0989: }
0990:
0991: if (word.equals("(")) {
0992: if (this .needsWhitespace(lastToken, t))
0993: this .appendText(' ');
0994: this .appendText('(');
0995: t = this .processFunctionCall(t);
0996: } else {
0997: if (word.equals(";")) {
0998: this .appendText(word);
0999: this .appendNewline();
1000: this .appendNewline();
1001: } else {
1002: if (this .needsWhitespace(lastToken, t))
1003: this .appendText(' ');
1004: this .appendText(t.getContents());
1005: }
1006: }
1007: }
1008: lastToken = t;
1009: t = this .lexer.getNextToken(true, false);
1010: }
1011: // this.appendNewline();
1012: // this.appendNewline();
1013: }
1014:
1015: private SQLToken processWhere(SQLToken previousToken)
1016: throws Exception {
1017: SQLToken t = this .lexer.getNextToken(true, false);
1018: SQLToken lastToken = previousToken;
1019: int bracketCount = 0;
1020: while (t != null) {
1021: String verb = t.getContents();
1022:
1023: if (t.isReservedWord() && WHERE_TERMINAL.contains(verb)) {
1024: return t;
1025: }
1026:
1027: if (verb.equals(";")) {
1028: return t;
1029: }
1030:
1031: if (verb.equals(")")) {
1032: bracketCount--;
1033: this .appendText(")");
1034: } else if (t.isComment()) {
1035: this .appendComment(verb);
1036: } else if (t.getContents().equals("(")) {
1037: String lastWord = lastToken.getContents();
1038:
1039: if (lastWord != null)
1040: lastWord = lastWord.toUpperCase();
1041: if (!lastToken.isSeparator()
1042: && !this .dbFunctions.contains(lastWord))
1043: this .appendText(' ');
1044: this .appendText(t.getContents());
1045:
1046: SQLToken next = skipComments();
1047: if (next == null)
1048: return null;
1049:
1050: if ("SELECT".equals(next.getContents())) {
1051: t = this .processSubSelect(true);
1052: if (t == null)
1053: return null;
1054: if (t.getContents().equals(")"))
1055: this .appendText(")");
1056: } else {
1057: bracketCount++;
1058:
1059: lastToken = t;
1060: t = next;
1061: continue;
1062: }
1063: } else if (bracketCount == 0 && t.isReservedWord()
1064: && (verb.equals("AND") || verb.equals("OR"))) {
1065: // TODO: this attempt to keep conditions in bracktes together results
1066: // in effectively no formatting when the whole WHERE clause is put
1067: // between brackets (because bracketCount will never be zero until
1068: // the end of the WHERE clause)
1069: if (!this .isStartOfLine())
1070: this .appendNewline();
1071: this .appendText(verb);
1072: this .appendText(" ");
1073: if (verb.equals("OR"))
1074: this .appendText(' ');
1075: } else {
1076: if (this .needsWhitespace(lastToken, t))
1077: this .appendText(' ');
1078: this .appendText(verb);
1079: }
1080:
1081: lastToken = t;
1082: t = this .lexer.getNextToken(true, false);
1083: }
1084: return null;
1085: }
1086:
1087: private SQLToken skipComments() throws Exception {
1088: SQLToken next = lexer.getNextToken(true, false);
1089: if (!next.isComment())
1090: return next;
1091: while (next != null) {
1092: if (!next.isComment())
1093: return next;
1094: this .appendComment(next.getContents());
1095: next = lexer.getNextToken(true, false);
1096: }
1097: return null;
1098: }
1099:
1100: private SQLToken processIntoKeyword() throws Exception {
1101: SQLToken t = this .lexer.getNextToken(false, false);
1102: // we expect an identifier now (the table name)
1103: // but to be able to handle "wrong statements" we'll
1104: // make sure everything's fine
1105:
1106: if (t.isIdentifier()) {
1107: this .appendText(' ');
1108: this .appendText(t.getContents());
1109: t = this .lexer.getNextToken(false, false);
1110: if (t.getContents().equalsIgnoreCase("VALUES")) {
1111: // no column list to format here...
1112: return t;
1113: } else if (t.isSeparator() && t.getContents().equals("(")) {
1114: this .appendNewline();
1115: this .appendText(t.getContents());
1116: this .appendNewline();
1117: return this .processBracketList(2);
1118: }
1119: }
1120: return t;
1121: }
1122:
1123: private SQLToken processFunctionCall(SQLToken last)
1124: throws Exception {
1125: int bracketCount = 1;
1126: SQLToken t = this .lexer.getNextToken(true, false);
1127:
1128: if (t != null && t.getContents().equals("SELECT")) {
1129: t = processSubSelect(true);
1130: if (t == null)
1131: return null;
1132: if (t.getContents().equals(")")) {
1133: this .appendText(')');
1134: SQLToken l = t;
1135: t = this .lexer.getNextToken(true, false);
1136: }
1137: return t;
1138: }
1139:
1140: SQLToken lastToken = last;
1141: while (t != null) {
1142: String text = t.getContents();
1143: if (text.equals(")")) {
1144: bracketCount--;
1145: }
1146: if (text.equals("(")) {
1147: bracketCount++;
1148: }
1149: if (this .needsWhitespace(lastToken, t))
1150: this .appendText(' ');
1151: this .appendText(t.getContents());
1152:
1153: if (bracketCount == 0) {
1154: return t;
1155: }
1156: lastToken = t;
1157: t = this .lexer.getNextToken(true, false);
1158: }
1159: return null;
1160: }
1161:
1162: private SQLToken processCreate(SQLToken previous) throws Exception {
1163: SQLToken t = this .lexer.getNextToken(true, false);
1164: String verb = t.getContents();
1165: int createStart = t.getCharBegin();
1166:
1167: if (verb.equals("TABLE")) {
1168: this .appendText(' ');
1169: this .appendText(t.getContents());
1170: this .appendText(' ');
1171: t = this .processCreateTable(t);
1172: return t;
1173: } else if (verb.equals("VIEW") || verb.equals("SNAPSHOT")) {
1174: this .appendText(' ');
1175: this .appendText(verb);
1176: this .appendText(' ');
1177: return this .processCreateView(t);
1178: } else if (verb.equals("INDEX")) {
1179: this .appendText(' ');
1180: this .appendText(verb);
1181: //this.appendText(' ');
1182: return this .processCreateIndex(t);
1183: } else if (verb.equals("OR")) {
1184: // Check for Oracle's CREATE OR REPLACE
1185: this .appendText(' ');
1186: this .appendText(verb);
1187: t = this .lexer.getNextToken(true, false);
1188: if (t == null)
1189: return t;
1190: verb = t.getContents();
1191: if (verb.equals("REPLACE")) {
1192: this .appendText(' ');
1193: this .appendText(verb);
1194: this .appendText(' ');
1195: return this .processCreateView(t);
1196: }
1197: }
1198:
1199: return t;
1200: }
1201:
1202: private boolean isTableConstraint(String keyword) {
1203: return TABLE_CONSTRAINTS_KEYWORDS.contains(keyword);
1204: }
1205:
1206: private SQLToken processTableDefinition() {
1207: List<StringBuilder> cols = new ArrayList<StringBuilder>();
1208: SQLToken t = lexer.getNextToken(true, false);
1209: StringBuilder line = new StringBuilder(50);
1210: int maxColLength = 0;
1211:
1212: boolean isColname = true;
1213: int bracketCount = 0;
1214:
1215: SQLToken last = null;
1216:
1217: while (t != null) {
1218: String w = t.getContents();
1219:
1220: if (isTableConstraint(w)) {
1221: // end of column definitions reached
1222: break;
1223: }
1224:
1225: if ("(".equals(w))
1226: bracketCount++;
1227: if (")".equals(w))
1228: bracketCount--;
1229:
1230: // Closing bracket reached --> end of
1231: if (bracketCount < 0) {
1232: cols.add(line);
1233: break;
1234: }
1235:
1236: if (!isColname && last != null
1237: && needsWhitespace(last, t, true)) {
1238: line.append(' ');
1239: }
1240: line.append(w);
1241:
1242: if (isColname && t.isIdentifier()) {
1243: if (w.length() > maxColLength)
1244: maxColLength = w.length();
1245: isColname = false;
1246: }
1247:
1248: if (w.equals(",") && bracketCount == 0) {
1249: cols.add(line);
1250: line = new StringBuilder(50);
1251: isColname = true;
1252: }
1253:
1254: last = t;
1255: t = this .lexer.getNextToken(true, false);
1256: }
1257:
1258: // Now process the collected column definitions
1259: for (StringBuilder col : cols) {
1260: int pos = StringUtil.findFirstWhiteSpace(col);
1261: String colname = col.substring(0, pos).trim();
1262: String def = col.substring(pos + 1).trim();
1263: appendText(" ");
1264: appendText(colname);
1265: while (pos < maxColLength) {
1266: this .appendText(' ');
1267: pos++;
1268: }
1269: this .appendText(" ");
1270: appendText(def);
1271: appendNewline();
1272: }
1273:
1274: if (t == null)
1275: return t;
1276:
1277: // now the definitions are added
1278: // check if we need to process more
1279: if (!t.getContents().equals(")")) {
1280: appendText(" ");
1281: bracketCount = 0;
1282: last = null;
1283:
1284: while (t != null) {
1285: String w = t.getContents();
1286: if ("(".equals(w))
1287: bracketCount++;
1288:
1289: if (")".equals(w)) {
1290: if (bracketCount == 0) {
1291: break;
1292: } else {
1293: bracketCount--;
1294: }
1295: }
1296:
1297: if (last != null && needsWhitespace(last, t, true)) {
1298: appendText(' ');
1299: }
1300: appendText(w);
1301:
1302: if (",".equals(w) && bracketCount == 0) {
1303: this .appendNewline();
1304: this .appendText(" ");
1305: }
1306:
1307: last = t;
1308: t = this .lexer.getNextToken(true, false);
1309: }
1310: appendNewline();
1311: }
1312:
1313: appendText(')');
1314: appendNewline();
1315: t = this .lexer.getNextToken(false, false);
1316:
1317: return t;
1318: }
1319:
1320: /**
1321: * Process a CREATE TABLE statement.
1322: * The CREATE TABLE has already been added!
1323: */
1324: private SQLToken processCreateTable(SQLToken previous)
1325: throws Exception {
1326: SQLToken t = this .lexer.getNextToken(false, false);
1327: if (t == null)
1328: return t;
1329:
1330: // the next token has to be the table name, so
1331: // we can simply write it out
1332:
1333: this .appendText(t.getContents());
1334: this .appendText(' ');
1335:
1336: t = this .lexer.getNextToken(false, false);
1337: if (t == null)
1338: return t;
1339:
1340: // this has to be the opening bracket before the table definition
1341: this .appendNewline();
1342: this .appendText('(');
1343: this .appendNewline();
1344:
1345: t = processTableDefinition();
1346:
1347: return t;
1348: }
1349:
1350: /**
1351: * Process the elements in a () combination
1352: * Any bracket inside the brackets are assumed to be "function calls"
1353: * and just treated as further elements.
1354: * It is assumed that the passed SQLToken is the opening bracket
1355: *
1356: * @return the token after the closing bracket
1357: */
1358: private SQLToken processCommaList(SQLToken previous,
1359: int maxElements, int indentCount) throws Exception {
1360: StringBuilder definition = new StringBuilder(200);
1361: SQLToken t = previous;
1362: SQLToken last = previous;
1363: int bracketCount = 0;
1364:
1365: while (t != null) {
1366: if (t.getContents().equals("(")) {
1367: if (bracketCount > 0) {
1368: definition.append('(');
1369: }
1370: bracketCount++;
1371: } else if (t.getContents().equals(")")) {
1372: if (bracketCount == 1) {
1373: List elements = StringUtil.stringToList(definition
1374: .toString(), ",");
1375: this .outputElements(elements, maxElements,
1376: indentCount);
1377: return this .lexer.getNextToken(true, false);
1378: } else {
1379: definition.append(')');
1380: }
1381: bracketCount--;
1382: } else if (bracketCount > 0) {
1383: if (this .needsWhitespace(last, t, true)) {
1384: definition.append(' ');
1385: }
1386: definition.append(t.getContents());
1387: }
1388: last = t;
1389: t = this .lexer.getNextToken(true, false);
1390: }
1391: return t;
1392: }
1393:
1394: /*
1395: * Output the elements of the given List comma separated
1396: * If the list contains more elements, then maxElements
1397: * each element will be put on a single line
1398: * If more then one line is "printed" they will be indented by
1399: * indentCount spaces
1400: */
1401: private void outputElements(List elements, int maxElements,
1402: int indentCount) {
1403: StringBuilder myIndent = new StringBuilder(indentCount);
1404: for (int i = 0; i < indentCount; i++)
1405: myIndent.append(' ');
1406:
1407: int count = elements.size();
1408:
1409: if (count > maxElements) {
1410: this .appendNewline();
1411: this .indent(myIndent);
1412: this .appendText("(");
1413: } else {
1414: this .appendText(" (");
1415: }
1416:
1417: if (count > maxElements) {
1418: this .appendNewline();
1419: this .indent(myIndent);
1420: this .indent(" ");
1421: }
1422:
1423: for (int i = 0; i < count; i++) {
1424: String text = (String) elements.get(i);
1425: this .appendText(text);
1426: if (i < count - 1) {
1427: if (count > maxElements) {
1428: this .appendText(',');
1429: this .appendNewline();
1430: this .indent(myIndent);
1431: this .indent(" ");
1432: } else {
1433: this .appendText(", ");
1434: }
1435: }
1436: }
1437: if (count > maxElements) {
1438: this .appendNewline();
1439: this .indent(myIndent);
1440: }
1441: this .appendText(")");
1442: }
1443:
1444: /**
1445: * Format a CREATE VIEW statement
1446: */
1447: private SQLToken processCreateView(SQLToken previous)
1448: throws Exception {
1449: SQLToken t = this .lexer.getNextToken(false, false);
1450: SQLToken last = previous;
1451: int bracketCount = 0;
1452: StringBuilder definition = new StringBuilder(200);
1453:
1454: while (t != null) {
1455: if (t.getContents().equals("(")) {
1456: if (bracketCount == 0) {
1457: // start of column definitions...
1458: t = this .processCommaList(t, 1, 0);
1459: }
1460: bracketCount++;
1461: }
1462:
1463: if ("SELECT".equals(t.getContents())) {
1464: return t;
1465: } else if ("AS".equals(t.getContents())) {
1466: this .appendNewline();
1467: this .appendText(t.getContents());
1468: this .appendNewline();
1469: } else {
1470: this .appendText(t.getContents());
1471: if (this .needsWhitespace(last, t, true)) {
1472: this .appendText(' ');
1473: }
1474: }
1475: last = t;
1476: t = this .lexer.getNextToken(false, false);
1477: }
1478: return t;
1479: }
1480:
1481: private SQLToken processCreateIndex(SQLToken previous)
1482: throws Exception {
1483: SQLToken t = this .lexer.getNextToken(true, false);
1484: SQLToken last = previous;
1485:
1486: while (t != null) {
1487: String text = t.getContents();
1488: if (t.getContents().equals("(")) {
1489: return this .processCommaList(t, 5, 7);
1490: } else if (t.isReservedWord() && "ON".equals(text)) {
1491: this .appendNewline();
1492: this .indent(" ");
1493: this .appendText(text);
1494: } else {
1495: if (this .needsWhitespace(last, t))
1496: this .appendText(' ');
1497: this .appendText(text);
1498: }
1499: t = this .lexer.getNextToken(true, false);
1500: }
1501: return t;
1502: }
1503:
1504: }
|