/*****************************************************************************
* Copyright 2012 celum Slovakia s r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*****************************************************************************/
package com.celum.dbtool.sql;
import com.celum.dbtool.installer.IOStreamException;
import org.antlr.v4.runtime.*;
import org.apache.commons.io.input.ReaderInputStream;
import java.io.*;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.List;
/**
* The loader goes through SQL Script and do parsing of each query which
* is then processed by current {@link SqlQueryStrategy strategy} impl.
*
* Because loader has DSL based API, here are examples how to use it.
*
* example of load queries into list:
* <code>
* InputStream is = ..;
* List<String> queries =
* Sql.asStream(is)
* .run(new ListStrategy())
* .andReturnListOfQueries();
* </code>
*
* example of your own strategy impl:
* <code>
* InputStream is = ..;
* Sql.asStream(is)
* .run( new SqlQueryStrategy() {
* public void query(String sql) {
* System.out.println("sql:" + sql);
* }
* });
* </code>
*
* You could also modify each SQL command before comes into strategy via interceptors in
* interceptor chain. You might have various interceptors like login interceptor which
* log each command before strategy, velocity interceptor which replace placeholders in SQL
* by values etc.
*
* example of interceptor usage:
* <code>
* InputStream is = ..;
* Sql.asStream(is)
* .interceptingWith(new CustomInterceptor(), new LogInterceptor())
* .run(new ListStrategy());
* </code>
*
* @see SqlQueryStrategy
*
* @author Zdenko Vrabel (zdenko.vrabel@celum.com)
*/
public class Sql {
/** carries the input stream of step */
private final CharStream script;
/** chain of interceptors which could affect the SQL command before it's executed */
private final List<SqlInterceptor> interceptorChain = new LinkedList<SqlInterceptor>();
/**
* Hidden constructor
* @param script
*/
private Sql(CharStream script)
{
this.script = script;
}
/**
* Creates loader for step stream.
*/
public static Sql fromStream(InputStream is)
{
try {
return new Sql(new ANTLRInputStream(is));
} catch (IOException e) {
throw new IOStreamException(e);
}
}
/**
* Creates loader for step reader.
*/
public static Sql fromReader(Reader reader)
{
return fromStream(new ReaderInputStream(reader));
}
/**
* Loads step from file source.
*/
public static Sql fromFile(File f)
{
try {
return new Sql(new ANTLRFileStream(f.getAbsolutePath()));
} catch (IOException e) {
throw new IOStreamException(e);
}
}
/**
* Loads step as text.
*
* @param scriptText
* @return
*/
public static Sql asString(String scriptText)
{
return fromReader(new StringReader(scriptText));
}
/**
* add interceptor into interceptor-chain
*/
public Sql interceptingWith(SqlInterceptor... interceptor)
{
for (int i = 0; i < interceptor.length; ++i) {
if (interceptor[i] != null) {
this.interceptorChain.add(interceptor[i]);
}
}
return this;
}
/**
* Method load and parse SQL step into sql queries and call
* {@link SqlQueryStrategy strategy} 'query()' method for each query.
*
* @param strategy determines how the Query will be processed
*
* @return method return the same strategy you fill in because DSL reasons
* and strategy could contain the results of loading.
*/
public <R extends SqlQueryStrategy> R run(R strategy)
{
//create and prepare lexer, parser and grammar-listener which store the result
SqlGrammarLexer lexer = new SqlGrammarLexer(script);
SqlGrammarParser parser = new SqlGrammarParser(new CommonTokenStream(lexer));
ParserGrammarListener pgl = new ParserGrammarListener(strategy, interceptorChain);
parser.addParseListener(pgl);
//parsing the SQL step
parser.sql();
return strategy;
}
/**
* Method parse long SQL scripts and return list of commands.
*/
public ListStrategy parse()
{
return run(new ListStrategy());
}
/**
* run the SQL command typically SELECTs and process the
* result-set.
*/
public SqlResultSetStrategy run(Connection jdbcConnection)
{
return run(new SqlResultSetStrategy(jdbcConnection));
}
/**
* Method execute the SQL commands typically DDLs as
* JDBC operations.
*
*/
public void execute(Connection jdbcConnection)
{
run(new SqlExecutionStrategy(jdbcConnection));
}
}