/*
* Copyright (C) 2007 SQL Explorer Development Team
* http://sourceforge.net/projects/eclipsesql
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package net.sourceforge.sqlexplorer.parsers;
import java.util.Iterator;
import java.util.StringTokenizer;
import net.sourceforge.sqlexplorer.IConstants;
import net.sourceforge.sqlexplorer.parsers.Tokenizer.Token;
import net.sourceforge.sqlexplorer.parsers.scp.StructuredCommentException;
import net.sourceforge.sqlexplorer.parsers.scp.StructuredCommentParser;
import net.sourceforge.sqlexplorer.plugin.SQLExplorerPlugin;
import org.eclipse.core.runtime.Preferences;
/**
* This is the original QueryTokenizer implementation from SQLExplorer v3.0; it's included
* for completness and for backward compatibility but be warned this code has a few bugs
* (eg regarding comments).
*
* This parser is based on scanning the SQL text looking for separators (eg ";", "go", or
* "/") that show where to split the text into separate queries; conversely, the new
* AbstractSyntaxQueryParser derived style is based on scanning the SQL text for language grammar
* tokens so that it can split the SQL by natural syntax.
*
* @modified John Spackman
*/
public class BasicQueryParser extends AbstractQueryParser {
private static String _alternateQuerySeparator;
private static String _querySeparator;
private String _sQuerys;
private String _sNextQuery;
private int lineNo = 1;
private CharSequence sql;
/**
* These characters at the beginning of an SQL statement indicate that it is
* a comment.
*/
private String _solComment;
public BasicQueryParser(CharSequence sql) {
super();
Preferences prefs = SQLExplorerPlugin.getDefault().getPluginPreferences();
String querySeparator = prefs.getString(IConstants.SQL_QRY_DELIMITER);
String alternateSeparator = prefs.getString(IConstants.SQL_ALT_QRY_DELIMITER);
String solComment = prefs.getString(IConstants.SQL_COMMENT_DELIMITER);
if (querySeparator != null && querySeparator.trim().length() > 0) {
_querySeparator = querySeparator.substring(0, 1);
} else {
// failsave..
_querySeparator = ";";
}
if (alternateSeparator != null && alternateSeparator.trim().length() > 0) {
_alternateQuerySeparator = alternateSeparator;
} else {
_alternateQuerySeparator = null;
}
if (solComment != null && solComment.trim().length() > 0) {
_solComment = solComment;
} else {
_solComment = null;
}
if (sql == null)
sql = "";
this.sql = sql;
}
/* (non-JavaDoc)
* @see net.sourceforge.sqlexplorer.parsers.QueryParser#parse()
*/
public void parse() throws ParserException {
if (sql == null)
return;
Preferences prefs = SQLExplorerPlugin.getDefault().getPluginPreferences();
if (prefs.getBoolean(IConstants.ENABLE_STRUCTURED_COMMENTS)) {
StringBuffer buffer = new StringBuffer(sql.toString());
Tokenizer tokenizer = new Tokenizer(buffer);
StructuredCommentParser structuredComments = new StructuredCommentParser(this, buffer);
// Otherwise just use a standard tokenizer
try {
Token token;
while ((token = tokenizer.nextToken()) != null) {
if (token.getTokenType() == Tokenizer.TokenType.EOL_COMMENT ||
token.getTokenType() == Tokenizer.TokenType.ML_COMMENT) {
structuredComments.addComment(token);
}
}
}catch(StructuredCommentException e) {
}
// Do the structured comments and then reset the tokenizer
structuredComments.process();
tokenizer.reset();
tokenizer = null;
sql = buffer;
}
_sQuerys = prepareSQL(sql.toString());
_sNextQuery = doParse();
sql = null;
}
/* (non-JavaDoc)
* @see net.sourceforge.sqlexplorer.parsers.QueryParser#addLineNoOffset(int, int)
*/
public void addLineNoOffset(int originalLineNo, int numLines) {
// Nothing - not supported
}
/* (non-JavaDoc)
* @see net.sourceforge.sqlexplorer.parsers.QueryParser#adjustLineNo(int)
*/
public int adjustLineNo(int lineNo) {
return lineNo; // Not implemented
}
/* (non-JavaDoc)
* @see net.sourceforge.sqlexplorer.parsers.QueryParser#iterator()
*/
public Iterator<Query> iterator() {
return new Iterator<Query>() {
/* (non-JavaDoc)
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return _sNextQuery != null;
}
/* (non-JavaDoc)
* @see java.util.Iterator#next()
*/
public Query next() {
return nextQuery();
}
/* (non-JavaDoc)
* @see java.util.Iterator#remove()
*/
public void remove() {
throw new IllegalAccessError();
}
};
}
/**
* Retrieves the next Query in the sequence
* @return
*/
public Query nextQuery() {
if (_sNextQuery == null)
return null;
String sReturnQuery = _sNextQuery;
int thisLineNo = lineNo;
_sNextQuery = doParse();
if (sReturnQuery == null)
return null;
if (sReturnQuery.startsWith("--"))
return nextQuery();
return new BasicQuery(sReturnQuery, thisLineNo);
}
private int findFirstSeparator() {
String separator = _querySeparator;
int separatorLength = _querySeparator.length();
int iQuoteCount = 1;
int iIndex1 = 0 - separatorLength;
while (iQuoteCount % 2 != 0) {
iQuoteCount = 0;
iIndex1 = _sQuerys.indexOf(separator, iIndex1 + separatorLength);
if (iIndex1 > -1) {
int iIndex2 = _sQuerys.lastIndexOf('\'', iIndex1 + separatorLength - 1);
while (iIndex2 != -1) {
if (_sQuerys.charAt(iIndex2 - 1) != '\\') {
iQuoteCount++;
}
iIndex2 = _sQuerys.lastIndexOf('\'', iIndex2 - 1);
}
} else {
return -1;
}
}
return iIndex1;
}
private int findFirstAlternateSeparator() {
if (_alternateQuerySeparator == null) {
return -1;
}
String separator = _alternateQuerySeparator;
int separatorLength = _alternateQuerySeparator.length();
int iQuoteCount = 1;
int iIndex1 = 0 - separatorLength;
while (iQuoteCount % 2 != 0) {
iQuoteCount = 0;
iIndex1 = _sQuerys.indexOf(separator, iIndex1 + separatorLength);
if (iIndex1 > -1) {
int iIndex2 = _sQuerys.lastIndexOf('\'', iIndex1 + separatorLength - 1);
while (iIndex2 != -1) {
if (_sQuerys.charAt(iIndex2 - 1) != '\\') {
iQuoteCount++;
}
iIndex2 = _sQuerys.lastIndexOf('\'', iIndex2 - 1);
}
} else {
return -1;
}
}
return iIndex1;
}
public String doParse() {
if (_sQuerys.length() == 0) {
return null;
}
// MOD sizhaoliu 2012-04-13 TDQ-5042 avoid empty result because of the script description
if (_sQuerys.startsWith("--")) {
int nlIndex = _sQuerys.indexOf("\n");
if (nlIndex > 0) {
String sNextQuery = _sQuerys.substring(0, nlIndex);
_sQuerys = _sQuerys.substring(nlIndex + 1).trim();
return sNextQuery;
}
}
String separator = _querySeparator;
int indexSep = findFirstSeparator();
int indexAltSep = findFirstAlternateSeparator();
if (indexAltSep > -1) {
if (indexSep < 0 || indexAltSep < indexSep) {
// use alternate separator
separator = _alternateQuerySeparator;
}
}
int separatorLength = separator.length();
int iQuoteCount = 1;
int iIndex1 = 0 - separatorLength;
while (iQuoteCount % 2 != 0) {
iQuoteCount = 0;
iIndex1 = _sQuerys.indexOf(separator, iIndex1 + separatorLength);
if (iIndex1 > -1) {
int iIndex2 = _sQuerys.lastIndexOf('\'', iIndex1 + separatorLength - 1);
while (iIndex2 != -1) {
if (_sQuerys.charAt(iIndex2 - 1) != '\\') {
iQuoteCount++;
}
iIndex2 = _sQuerys.lastIndexOf('\'', iIndex2 - 1);
}
} else {
String sNextQuery = _sQuerys;
_sQuerys = "";
if (_solComment != null && sNextQuery.startsWith(_solComment)) {
return doParse();
}
return replaceLineFeeds(sNextQuery);
}
}
String sNextQuery = _sQuerys.substring(0, iIndex1);
_sQuerys = _sQuerys.substring(iIndex1 + separatorLength).trim();
if (_solComment != null && sNextQuery.startsWith(_solComment)) {
return doParse();
}
return replaceLineFeeds(sNextQuery);
}
private String prepareSQL(String sql) {
StringBuffer results = new StringBuffer(1024);
for (StringTokenizer tok = new StringTokenizer(sql.trim(), "\n", false); tok.hasMoreTokens();) {
String line = tok.nextToken();
if (!line.startsWith(_solComment)) {
results.append(line).append('\n');
}
}
// JHS 2007-07-16
// PL/SQL does not like having a forced CR (presumably only a Windows issue)
for (int i = 0; i < results.length(); ) {
if (results.charAt(i) == '\r')
results.deleteCharAt(i);
else
i++;
}
return results.toString();
}
private String replaceLineFeeds(String sql) {
StringBuffer sbReturn = new StringBuffer();
int iPrev = 0;
int linefeed = sql.indexOf('\n');
int iQuote = -1;
while (linefeed != -1) {
iQuote = sql.indexOf('\'', iQuote + 1);
if (iQuote != -1 && iQuote < linefeed) {
int iNextQute = sql.indexOf('\'', iQuote + 1);
if (iNextQute > linefeed) {
sbReturn.append(sql.substring(iPrev, linefeed));
sbReturn.append('\n');
iPrev = linefeed + 1;
linefeed = sql.indexOf('\n', iPrev);
}
} else {
linefeed = sql.indexOf('\n', linefeed + 1);
}
}
sbReturn.append(sql.substring(iPrev));
// Count up the line numbers. This is a dirty and inefficient hack but as the parser
// is to be completely rewritten soon anyway... >:)
for (int i = 0; i < sbReturn.length(); i++)
if (sbReturn.charAt(i) == '\n')
lineNo++;
return sbReturn.toString();
}
}