/**Copyright 2010 Research Studios Austria Forschungsgesellschaft mBH
*
* This file is part of easyrec.
*
* easyrec is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* easyrec 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with easyrec. If not, see <http://www.gnu.org/licenses/>.
*/
package org.easyrec.utils.io;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility for parsing an sql file. The statements from the sql file are
* returned as a <code>List</code> of <code>String</code>. The utility is
* meant for use with batch sql functions.
* <p>
* The class that can be used to execute an sql comfortably is
* {@link at.researchstudio.sat.utio.store.service.sqlscript.impl.SqlScriptServiceImpl}. It relies
* on this class for sql file parsing.
* </p>
* <p/>
* Note: <br />
* This parser will not be able to handle cases where
* <code>;</code>, <code>//</code>, <code>#</code>, <code>--</code> occur in
* string literals, i.e inside <code>' ... '</code>
* <p/>
* <p><b>Company: </b>
* SAT, Research Studios Austria</p>
* <p/>
* <p><b>Copyright: </b>
* (c) 2006</p>
* <p/>
* <p><b>last modified:</b><br/>
* $Author: pmarschik $<br/>
* $Date: 2011-02-11 11:04:49 +0100 (Fr, 11 Feb 2011) $<br/>
* $Revision: 17656 $</p>
*
* @author Florian Kleedorfer
*/
public class SqlFileParser {
private BufferedReader reader;
private final Log logger = LogFactory.getLog(getClass());
public SqlFileParser(BufferedReader reader) {
this.reader = reader;
}
public SqlFileParser(Reader reader) {
this.reader = new BufferedReader(reader);
}
/**
* creates a new parser object for the given file
*
* @param filename
* @throws IllegalArgumentException if the file does not exist
*/
public SqlFileParser(File sqlScript) {
try {
this.reader = new BufferedReader(new FileReader(sqlScript));
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("file not found " + sqlScript);
}
}
/**
* creates a new parser object for the given filename
*
* @param filename
* @throws IllegalArgumentException if the file does not exist
*/
public SqlFileParser(String filename) {
try {
this.reader = new BufferedReader(new FileReader(filename));
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("file not found " + filename);
}
}
/**
* creates a new parser object for the given input steam
*
* @param stream
*/
public SqlFileParser(InputStream stream) {
this.reader = new BufferedReader(new InputStreamReader(stream));
}
public List<String> parse() {
// read the file line per line:
//strip comments
//split on ';'
//concatenate multiple-line statements
String line = null;
int lineCounter = 0;
StringBuilder statement = new StringBuilder();
List<String> statements = new LinkedList<String>();
Pattern commentPattern = Pattern.compile("(//|#|--)+.*$");
try {
while ((line = reader.readLine()) != null) {
lineCounter++;
//strip comment up to the first non-comment
Matcher m = commentPattern.matcher(line);
if (m.find()) {
line = line.substring(0, m.start());
}
//remove leading and trailing whitespace
statement.append(" ");
line = statement.append(line).toString();
line = line.replaceAll("\\s+", " ");
line = line.trim();
//split by ;
//Note: possible problems with ; in ''
String[] tokens = line.split(";");
//trim the tokens (no leading or trailing whitespace
for (int i = 0; i < tokens.length; i++) {
tokens[i] = tokens[i].trim();
}
boolean containsSemicolon = line.contains(";");
boolean endsWithSemicolon = line.endsWith(";");
if (!containsSemicolon) {
//statement is still open, do nothing
continue;
}
if (tokens.length == 1 && endsWithSemicolon) {
//statement is complete, semicolon at the end.
statements.add(tokens[0]);
statement = new StringBuilder();
continue;
}
// other cases must have more than 1 token
//iterate over tokens (but the last one)
for (int i = 0; i < tokens.length - 1; i++) {
statements.add(tokens[0]);
statement = new StringBuilder();
}
//last statement may remain open:
if (endsWithSemicolon) {
statements.add(tokens[0]);
statement = new StringBuilder();
} else {
statement = new StringBuilder();
statement.append(tokens[tokens.length - 1]);
}
}
if (statement != null && statement.toString().trim().length() > 0)
throw new UnclosedStatementException("Statement is not closed until the end of the file.");
} catch (IOException e) {
logger.warn(e);
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return statements;
}
}