Java tutorial
/* * 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 com.safi.workshop.sqlexplorer.parsers; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.Preferences; import org.eclipse.emf.common.util.EList; import org.eclipse.jface.dialogs.MessageDialog; import com.safi.db.DbFactory; import com.safi.workshop.part.SafiWorkshopEditorUtil; import com.safi.workshop.sqlexplorer.IConstants; import com.safi.workshop.sqlexplorer.parsers.Tokenizer.Token; import com.safi.workshop.sqlexplorer.parsers.scp.StructuredCommentException; import com.safi.workshop.sqlexplorer.parsers.scp.StructuredCommentParser; import com.safi.workshop.sqlexplorer.plugin.views.SqlexplorerViewConstants; /** * 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. * * (Note: the previous version was the SE3.0.0 parser, this is a rewrite for SE3.5RC6 * * @modified John Spackman */ public class BasicQueryParser extends AbstractQueryParser { // Quote marks that wrap a string private static final String QUOTE_CHARS = "'\""; // Current line number into sql private int lineNo; // Current position into sql for the parser private int charIndex; // Whether quotes inside strings are escaped - and if so, the quote character private char quoteEscapes; // The start of a single-line comment private String slComment; // The start of a multi-line comment private String mlCommentStart; // The end of a multi-line comment private String mlCommentEnd; // Command separator private String cmdSeparator; // Alternative separator (must occur on a line on its own) private String altSeparator; // Whether structured comments are enabled private boolean enableStructuredComments; // The SQL private CharSequence sql; // Parsed queries private LinkedList<Query> queries; /** * Constructor * * @param sql */ public BasicQueryParser(CharSequence sql) { super(); if (sql == null) this.sql = ""; else this.sql = sql; lineNo = 1; cmdSeparator = ";"; altSeparator = "GO"; slComment = "--"; mlCommentStart = "/*"; mlCommentEnd = "*/"; } /** * Constructor * * @param sql */ public BasicQueryParser(CharSequence sql, Preferences prefs, int pLineNo) { this(sql); setCmdSeparator(prefs.getString(IConstants.SQL_QRY_DELIMITER)); setAltSeparator(prefs.getString(IConstants.SQL_ALT_QRY_DELIMITER)); setSlComment(prefs.getString(IConstants.SQL_SL_COMMENT)); setMlCommentStart(prefs.getString(IConstants.SQL_ML_COMMENT_START)); setMlCommentEnd(prefs.getString(IConstants.SQL_ML_COMMENT_END)); setQuoteEscapes(prefs.getString(IConstants.SQL_QUOTE_ESCAPE_CHAR)); enableStructuredComments = prefs.getBoolean(IConstants.ENABLE_STRUCTURED_COMMENTS); String str = getPref(prefs, IConstants.SQL_QUOTE_ESCAPE_CHAR); if (str != null) { str = str.trim(); if (str.length() > 0) quoteEscapes = str.charAt(0); } lineNo = pLineNo; } /* * (non-JavaDoc) * * @see com.safi.workshop.sqlexplorer.parsers.QueryParser#parse() */ public void parse() throws ParserException { if (sql == null) return; if (enableStructuredComments) { 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; } charIndex = 0; // BasicQuery query; queries = new LinkedList<Query>(); for (BasicQuery query = getNextQuery(); query != null; query = getNextQuery()) { queries.add(query); } } /* * (non-JavaDoc) * * @see com.safi.workshop.sqlexplorer.parsers.QueryParser#addLineNoOffset(int, int) */ public void addLineNoOffset(int originalLineNo, int numLines) { // Nothing - not supported } /* * (non-JavaDoc) * * @see com.safi.workshop.sqlexplorer.parsers.QueryParser#adjustLineNo(int) */ public int adjustLineNo(int pLineNo) { return pLineNo; // Not implemented } /* * (non-JavaDoc) * * @see com.safi.workshop.sqlexplorer.parsers.QueryParser#iterator() */ public Iterator<Query> iterator() { return queries.iterator(); } /** * Gets the next query, or returns null if there are no more * * @return */ private BasicQuery getNextQuery() { if (charIndex >= sql.length()) return null; int start = charIndex; int startOfLine = -1; char cQuote = 0; boolean inSLComment = false; boolean inMLComment = false; int startLineNo = -1; for (; charIndex < sql.length(); charIndex++) { char c = sql.charAt(charIndex); char nextC = 0; if (charIndex < sql.length() - 1) nextC = sql.charAt(charIndex + 1); // Skip comments if (inSLComment) { if (c == '\n') { inSLComment = false; } else { continue; } } if (inMLComment) { if (c == '\n') { startOfLine = -1; lineNo++; } inMLComment = !nextIs(mlCommentEnd); continue; } // If we're quoting if (cQuote != 0) { if (c == quoteEscapes && QUOTE_CHARS.indexOf(nextC) > -1) charIndex++; else if (cQuote == c) cQuote = 0; continue; } // Skip leading whitespace if (start == charIndex && Character.isWhitespace(c)) { start++; if (c == '\n') { startOfLine = -1; lineNo++; } continue; } // Calculate the start of line (gets reset to -1 on every \n) if (startOfLine < 0 && !Character.isWhitespace(c)) startOfLine = charIndex; if (cmdSeparator != null && nextIs(cmdSeparator)) { int oldCharIndex = charIndex; charIndex += cmdSeparator.length(); if (startLineNo < 0 || start + cmdSeparator.length() >= oldCharIndex) { start = charIndex; startLineNo = -1; startOfLine = -1; continue; } CharSequence qry = sql.subSequence(start, oldCharIndex); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters((LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; // return new BasicQuery(sql.subSequence(start, oldCharIndex), startLineNo); } // Starting a quote? if (QUOTE_CHARS.indexOf(c) > -1) { cQuote = c; continue; } // Newlines - count line numbers, and look for "GO" (or equivelant) if (c == '\n') { if (rangeIs(startOfLine, charIndex, altSeparator)) { if (startLineNo < 0 || start + altSeparator.length() >= startOfLine) { start = charIndex + 1; startLineNo = -1; startOfLine = -1; lineNo++; continue; } CharSequence qry = sql.subSequence(start, startOfLine); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters( (LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; } if (startOfLine >= 0) { String lineStart = sql.subSequence(startOfLine, Math.min(charIndex, startOfLine + 15)) .toString().toLowerCase(); if (lineStart.startsWith("delimiter")) { String delimiter = lineStart.substring(9).trim(); if (delimiter.length() == 1) { setCmdSeparator(delimiter); setAltSeparator(null); } else { setCmdSeparator(new String(new byte[] { 0 })); setAltSeparator(delimiter); } if (startLineNo < 0 || start + lineStart.length() >= startOfLine) { start = charIndex + 1; startLineNo = -1; startOfLine = -1; lineNo++; continue; } CharSequence qry = sql.subSequence(start, startOfLine); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters( (LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; // return new BasicQuery(sql.subSequence(start, startOfLine), startLineNo); } } startOfLine = -1; lineNo++; if (inSLComment) { inSLComment = false; continue; } } // Starting a single-line comment if (nextIs(slComment)) { if (rangeIs(startOfLine, charIndex, altSeparator)) { if (startLineNo < 0 || start + altSeparator.length() >= startOfLine) { start = charIndex; startLineNo = -1; startOfLine = -1; continue; } CharSequence qry = sql.subSequence(start, startOfLine); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters( (LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; // return new BasicQuery(sql.subSequence(start, startOfLine), startLineNo); } inSLComment = true; continue; } // Starting a multi-line comment if (nextIs(mlCommentStart)) { if (rangeIs(startOfLine, charIndex, altSeparator)) { if (startLineNo < 0 || start + altSeparator.length() >= startOfLine) { start = charIndex; startLineNo = -1; startOfLine = -1; continue; } CharSequence qry = sql.subSequence(start, startOfLine); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters( (LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; // return new BasicQuery(sql.subSequence(start, startOfLine), startLineNo); } inMLComment = true; continue; } // Only update the startLineNo when we know when code starts if (startLineNo < 0 && !Character.isWhitespace(c)) startLineNo = lineNo; } // Returns something if there is something to return if (start < charIndex) { if (rangeIs(startOfLine, charIndex, altSeparator)) { charIndex = sql.length(); if (startLineNo < 0 || start + altSeparator.length() >= startOfLine) return null; CharSequence qry = sql.subSequence(start, startOfLine); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters((LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; // return new BasicQuery(sql.subSequence(start, startOfLine), startLineNo); } CharSequence qry = sql.subSequence(start, charIndex); Set<com.safi.workshop.sqlexplorer.parsers.QueryParameter> parms = null; try { parms = parseArgs(qry.toString()); } catch (ParserException e) { e.printStackTrace(); MessageDialog.openError(SafiWorkshopEditorUtil.getActiveShell(), "Parse Param Error", "Error caught while parsing parameters: " + e.getLocalizedMessage()); return null; } BasicQuery bq = new BasicQuery(qry, startLineNo); bq.setQueryParameters((LinkedHashSet<com.safi.workshop.sqlexplorer.parsers.QueryParameter>) parms); return bq; // return new BasicQuery(sql.subSequence(start, charIndex), startLineNo); } return null; } /** * Determines whether the next part of the SQL is a given string * * @param str * @return */ private boolean nextIs(String str) { if (str == null) return false; if (str.length() > sql.length() - charIndex) return false; CharSequence sub = sql.subSequence(charIndex, charIndex + str.length()); return str.equals(sub.toString()); } /** * Determines whether a string is exactly in a given range * * @param start * @param end * @param str * @return */ private boolean rangeIs(int start, int end, String str) { if (str == null || end - start != str.length()) return false; String sub = sql.subSequence(start, end).toString(); return str.equalsIgnoreCase(sub); } /** * Gets a string from preferences, or null if the string is empty * * @param prefs * @param id * @return */ private String getPref(Preferences prefs, String id) { String str = prefs.getString(id); if (str == null) return null; str = str.trim(); if (str.length() == 0) return null; return str; } private String getValue(String value) { if (value == null) return null; value = value.trim(); if (value.length() == 0) return null; return value; } public String getAltSeparator() { return altSeparator; } public void setAltSeparator(String altSeparator) { this.altSeparator = getValue(altSeparator); } public String getCmdSeparator() { return cmdSeparator; } public void setCmdSeparator(String cmdSeparator) { this.cmdSeparator = getValue(cmdSeparator); } public String getMlCommentEnd() { return mlCommentEnd; } public void setMlCommentEnd(String mlCommentEnd) { this.mlCommentEnd = getValue(mlCommentEnd); } public String getMlCommentStart() { return mlCommentStart; } public void setMlCommentStart(String mlCommentStart) { this.mlCommentStart = getValue(mlCommentStart); } public char getQuoteEscapes() { return quoteEscapes; } public void setQuoteEscapes(char quoteEscapes) { this.quoteEscapes = quoteEscapes; } public void setQuoteEscapes(String quoteEscapes) { quoteEscapes = getValue(quoteEscapes); this.quoteEscapes = quoteEscapes == null ? 0 : quoteEscapes.charAt(0); } public String getSlComment() { return slComment; } public void setSlComment(String slComment) { this.slComment = getValue(slComment); } public static void updateParameters(com.safi.db.Query query) { java.util.regex.Pattern p = Pattern.compile(SqlexplorerViewConstants.PATT_PARAM); java.util.regex.Matcher m = p.matcher(query.getQuerySql()); String param = null; // query.getParameters().clear(); int i = 0; EList<com.safi.db.QueryParameter> params = query.getParameters(); while (m.find()) { param = m.group(); if (params.size() > i) { com.safi.db.QueryParameter qm = params.get(i); if (!StringUtils.equals(qm.getName(), param)) { com.safi.db.QueryParameter pm = DbFactory.eINSTANCE.createQueryParameter(); pm.setName(param); params.set(i, pm); } } else { com.safi.db.QueryParameter pm = DbFactory.eINSTANCE.createQueryParameter(); pm.setName(param); params.add(pm); } i++; } for (; i < params.size();) params.remove(i); } }