001: package net.sourceforge.squirrel_sql.fw.sql;
002:
003: /*
004: * Copyright (C) 2001-2003 Johan Compagner
005: * jcompagner@j-com.nl
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.io.BufferedReader;
022: import java.io.File;
023: import java.io.FileReader;
024: import java.util.ArrayList;
025: import java.util.Iterator;
026: import java.util.List;
027:
028: import net.sourceforge.squirrel_sql.fw.preferences.IQueryTokenizerPreferenceBean;
029: import net.sourceforge.squirrel_sql.fw.util.StringUtilities;
030: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
031: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
032:
033: public class QueryTokenizer implements IQueryTokenizer {
034: protected ArrayList<String> _queries = new ArrayList<String>();
035:
036: protected Iterator<String> _queryIterator;
037:
038: protected String _querySep = null;
039:
040: protected String _lineCommentBegin = null;
041:
042: protected boolean _removeMultiLineComment = true;
043:
044: protected ITokenizerFactory _tokenizerFactory = null;
045:
046: /** Logger for this class. */
047: private final static ILogger s_log = LoggerController
048: .createLogger(QueryTokenizer.class);
049:
050: public QueryTokenizer() {
051: }
052:
053: public QueryTokenizer(String querySep, String lineCommentBegin,
054: boolean removeMultiLineComment) {
055: _querySep = querySep;
056: _lineCommentBegin = lineCommentBegin;
057: _removeMultiLineComment = removeMultiLineComment;
058: setFactory();
059: }
060:
061: public QueryTokenizer(IQueryTokenizerPreferenceBean prefs) {
062: this (prefs.getStatementSeparator(), prefs.getLineComment(),
063: prefs.isRemoveMultiLineComments());
064: }
065:
066: /**
067: * Sets the ITokenizerFactory which is used to create additional instances
068: * of the IQueryTokenizer - this is used for handling file includes
069: * recursively.
070: */
071: protected void setFactory() {
072: _tokenizerFactory = new ITokenizerFactory() {
073: public IQueryTokenizer getTokenizer() {
074: return new QueryTokenizer();
075: }
076: };
077: }
078:
079: private int getLenOfQuerySepIfAtLastCharOfQuerySep(String sql,
080: int i, String querySep, boolean inLiteral) {
081: if (inLiteral) {
082: return -1;
083: }
084:
085: char c = sql.charAt(i);
086:
087: if (1 == querySep.length() && c == querySep.charAt(0)) {
088: return 1;
089: } else {
090: int fromIndex = i - querySep.length();
091: if (0 > fromIndex) {
092: return -1;
093: }
094:
095: int querySepIndex = sql.indexOf(querySep, fromIndex);
096:
097: if (0 > querySepIndex) {
098: return -1;
099: }
100:
101: if (Character.isWhitespace(c)) {
102: if (querySepIndex + querySep.length() == i) {
103: if (0 == querySepIndex) {
104: return querySep.length() + 1;
105: } else if (Character.isWhitespace(sql
106: .charAt(querySepIndex - 1))) {
107: return querySep.length() + 2;
108: }
109: }
110: } else if (sql.length() - 1 == i) {
111: if (querySepIndex + querySep.length() - 1 == i) {
112: if (0 == querySepIndex) {
113: return querySep.length();
114: } else if (Character.isWhitespace(sql
115: .charAt(querySepIndex - 1))) {
116: return querySep.length() + 1;
117: }
118: }
119: }
120:
121: return -1;
122: }
123: }
124:
125: public boolean hasQuery() {
126: return _queryIterator.hasNext();
127: }
128:
129: public String nextQuery() {
130: return _queryIterator.next();
131: }
132:
133: public void setScriptToTokenize(String script) {
134: _queries.clear();
135:
136: String MULTI_LINE_COMMENT_END = "*/";
137: String MULTI_LINE_COMMENT_BEGIN = "/*";
138:
139: script = script.replace('\r', ' ');
140:
141: StringBuffer curQuery = new StringBuffer();
142:
143: boolean isInLiteral = false;
144: boolean isInMultiLineComment = false;
145: boolean isInLineComment = false;
146: int literalSepCount = 0;
147:
148: for (int i = 0; i < script.length(); ++i) {
149: char c = script.charAt(i);
150:
151: if (false == isInLiteral) {
152: ///////////////////////////////////////////////////////////
153: // Handling of comments
154:
155: // We look backwards
156: if (isInLineComment
157: && script.startsWith("\n", i - "\n".length())) {
158: isInLineComment = false;
159: }
160:
161: // We look backwards
162: if (isInMultiLineComment
163: && script.startsWith(MULTI_LINE_COMMENT_END, i
164: - MULTI_LINE_COMMENT_END.length())) {
165: isInMultiLineComment = false;
166: }
167:
168: if (false == isInLineComment
169: && false == isInMultiLineComment) {
170: // We look forward
171: isInMultiLineComment = script.startsWith(
172: MULTI_LINE_COMMENT_BEGIN, i);
173: isInLineComment = script.startsWith(
174: _lineCommentBegin, i);
175:
176: if (isInMultiLineComment && _removeMultiLineComment) {
177: // skip ahead so the cursor is now immediately after the begin comment string
178: i += MULTI_LINE_COMMENT_BEGIN.length() + 1;
179: }
180: }
181:
182: if ((isInMultiLineComment && _removeMultiLineComment)
183: || isInLineComment) {
184: // This is responsible that comments are not in curQuery
185: continue;
186: }
187: //
188: ////////////////////////////////////////////////////////////
189: }
190:
191: curQuery.append(c);
192:
193: if ('\'' == c) {
194: if (false == isInLiteral) {
195: isInLiteral = true;
196: } else {
197: ++literalSepCount;
198: }
199: } else {
200: if (0 != literalSepCount % 2) {
201: isInLiteral = false;
202: }
203: literalSepCount = 0;
204: }
205:
206: int querySepLen = getLenOfQuerySepIfAtLastCharOfQuerySep(
207: script, i, _querySep, isInLiteral);
208:
209: if (-1 < querySepLen) {
210: int newLength = curQuery.length() - querySepLen;
211: if (-1 < newLength && curQuery.length() > newLength) {
212: curQuery.setLength(newLength);
213:
214: String newQuery = curQuery.toString().trim();
215: if (0 < newQuery.length()) {
216: _queries.add(curQuery.toString().trim());
217: }
218: }
219: curQuery.setLength(0);
220: }
221: }
222:
223: String lastQuery = curQuery.toString().trim();
224: if (0 < lastQuery.length()) {
225: _queries.add(lastQuery.toString().trim());
226: }
227:
228: _queryIterator = _queries.iterator();
229: }
230:
231: /**
232: * Returns the number of queries that the tokenizer found in the script
233: * given in the last call to setScriptToTokenize, or 0 if
234: * setScriptToTokenize has not yet been called.
235: */
236: public int getQueryCount() {
237: if (_queries == null) {
238: return 0;
239: }
240: return _queries.size();
241: }
242:
243: public static void main(String[] args) {
244: //String sql = "A'''' sss ; GO ;; GO'";
245: //String sql = "A\n--x\n--y\n/*\nB";
246: //String sql = "GO GO";
247: String sql = "@c:\\tools\\sql\\file.sql";
248:
249: QueryTokenizer qt = new QueryTokenizer("GO", "--", true);
250:
251: qt.setScriptToTokenize(sql);
252:
253: while (qt.hasQuery()) {
254: System.out.println(">" + qt.nextQuery() + "<");
255: }
256: }
257:
258: /**
259: * @return the query statement separator
260: */
261: public String getQuerySep() {
262: return _querySep;
263: }
264:
265: /**
266: * @param sep the value to use for the query statement separator
267: */
268: public void setQuerySep(String sep) {
269: _querySep = sep;
270: }
271:
272: /**
273: * @return the _lineCommentBegin
274: */
275: public String getLineCommentBegin() {
276: return _lineCommentBegin;
277: }
278:
279: /**
280: * @param commentBegin the _lineCommentBegin to set
281: */
282: public void setLineCommentBegin(String commentBegin) {
283: _lineCommentBegin = commentBegin;
284: }
285:
286: /**
287: * @return the _removeMultiLineComment
288: */
289: public boolean isRemoveMultiLineComment() {
290: return _removeMultiLineComment;
291: }
292:
293: /**
294: * @param multiLineComment the _removeMultiLineComment to set
295: */
296: public void setRemoveMultiLineComment(boolean multiLineComment) {
297: _removeMultiLineComment = multiLineComment;
298: }
299:
300: /**
301: * This uses statements that begin with scriptIncludePrefix to indicate
302: * that the following text is a filename containing SQL statements that
303: * should be loaded.
304: *
305: * @param scriptIncludePrefix the
306: * @param lineCommentBegin
307: * @param removeMultiLineComment
308: */
309: protected void expandFileIncludes(String scriptIncludePrefix) {
310: if (scriptIncludePrefix == null) {
311: s_log.error("scriptIncludePrefix cannot be null ");
312: return;
313: }
314: ArrayList<String> tmp = new ArrayList<String>();
315: for (Iterator<String> iter = _queries.iterator(); iter
316: .hasNext();) {
317: String sql = iter.next();
318: if (sql.startsWith(scriptIncludePrefix)) {
319: try {
320: String filename = sql.substring(scriptIncludePrefix
321: .length());
322: List<String> fileSQL = getStatementsFromIncludeFile(filename);
323: tmp.addAll(fileSQL);
324: } catch (Exception e) {
325: s_log.error(
326: "Unexpected error while attempting to include file "
327: + "from " + sql, e);
328: }
329:
330: } else {
331: tmp.add(sql);
332: }
333: }
334: _queries = tmp;
335: }
336:
337: protected List<String> getStatementsFromIncludeFile(String filename)
338: throws Exception {
339: if (filename.startsWith("'")) {
340: filename = filename.substring(1);
341: }
342: if (filename.endsWith("'")) {
343: filename = StringUtilities.chop(filename);
344: }
345: if (filename.endsWith("\n")) {
346: filename = StringUtilities.chop(filename);
347: }
348: ArrayList<String> result = new ArrayList<String>();
349: if (s_log.isDebugEnabled()) {
350: s_log.debug("Attemping to open file '" + filename + "'");
351: }
352: File f = new File(filename);
353: /*
354: if (f.canRead()) {
355: */
356: StringBuffer fileLines = new StringBuffer();
357: try {
358: BufferedReader reader = new BufferedReader(
359: new FileReader(f));
360: String next = reader.readLine();
361: while (next != null) {
362: fileLines.append(next);
363: fileLines.append("\n");
364: next = reader.readLine();
365: }
366: } catch (Exception e) {
367: s_log.error(
368: "Unexpected exception while reading lines from file "
369: + "(" + filename + ")", e);
370: }
371: if (fileLines.toString().length() > 0) {
372: IQueryTokenizer qt = null;
373: if (_tokenizerFactory != null) {
374: qt = _tokenizerFactory.getTokenizer();
375: } else {
376: qt = new QueryTokenizer(_querySep, _lineCommentBegin,
377: _removeMultiLineComment);
378: }
379: qt.setScriptToTokenize(fileLines.toString());
380: while (qt.hasQuery()) {
381: String sql = qt.nextQuery();
382: result.add(sql);
383: }
384: }
385: /*
386: } else {
387: s_log.error("Unable to open file: "+filename+" for reading");
388: }
389: */
390: return result;
391: }
392:
393: /* (non-Javadoc)
394: * @see net.sourceforge.squirrel_sql.fw.sql.IQueryTokenizer#getSQLStatementSeparator()
395: */
396: public String getSQLStatementSeparator() {
397: return _querySep;
398: }
399:
400: }
|