package com.venky.swf.sql.parser;
import java.io.InputStream;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.venky.core.string.StringUtil;
import com.venky.parse.Rule;
import com.venky.parse.Rule.Element;
import com.venky.parse.character.CharRange;
import com.venky.parse.character.Exclude;
import com.venky.parse.character.Include;
import com.venky.parse.composite.Any;
import com.venky.parse.composite.CharSequence;
import com.venky.parse.composite.Multiple;
import com.venky.parse.composite.OneOrMore;
import com.venky.parse.composite.Sequence;
import com.venky.parse.composite.ZeroOrMore;
import com.venky.swf.db.Database;
import com.venky.swf.db.JdbcTypeHelper.TypeRef;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.table.BindVariable;
import com.venky.swf.db.table.Table;
import com.venky.swf.db.table.Table.ColumnDescriptor;
import com.venky.swf.sql.Conjunction;
import com.venky.swf.sql.Expression;
import com.venky.swf.sql.Operator;
public class SQLExpressionParser {
Table<? extends Model> table = null;
public SQLExpressionParser(Class<? extends Model> modelClass){
table = Database.getTable(modelClass);
}
public Expression parse(InputStream is){
return parse(StringUtil.read(is));
}
public Expression parse(String expression){
ExpressionRule rule = new ExpressionRule();
Expression ret = null;
if (rule.match(expression) && expression.length() == rule.getMatch().length()){
Element e = rule.getMatch();
ret = new Expression(table.getReflector().getPool(),Conjunction.AND);
parse(e,ret);
}
return ret;
}
private void parse(Element e, Expression parentExpression){
if (e.getRule().getClass() == EQ.class){
parentExpression.add(createExpression(e, Operator.EQ));
}else if (e.getRule().getClass() == LT.class){
parentExpression.add(createExpression(e, Operator.LT));
}else if (e.getRule().getClass() == GT.class){
parentExpression.add(createExpression(e, Operator.GT));
}else if (e.getRule().getClass() == LE.class){
parentExpression.add(createExpression(e, Operator.LE));
}else if (e.getRule().getClass() == GE.class){
parentExpression.add(createExpression(e, Operator.GE));
}else if (e.getRule().getClass() == NE.class){
parentExpression.add(createExpression(e, Operator.NE));
}else if (e.getRule().getClass() == LIKE.class){
parentExpression.add(createExpression(e, Operator.LK));
}else if (e.getRule().getClass() == IN.class){
parentExpression.add(createExpression(e, Operator.IN));
}else if (e.getRule().getClass() == ISNULL.class){
parentExpression.add(createExpression(e, Operator.EQ));
}else if (e.getRule().getClass() == IS_NOTNULL.class){
parentExpression.add(createExpression(e, Operator.NE));
}else {
Expression ex = null;
if (e.getRule().getClass() == And.class){
ex = new Expression(table.getReflector().getPool(),Conjunction.AND);
}else if (e.getRule().getClass() == Or.class){
ex = new Expression(table.getReflector().getPool(),Conjunction.OR);
}
if (ex != null){
parentExpression.add(ex);
parentExpression = ex;
}
}
for (Element child: e.getChildren()){
parse(child,parentExpression);
}
}
private Expression createExpression(Element e, Operator op){
String columnName = columnName(e);
ColumnDescriptor cd = table.getReflector().getColumnDescriptor(columnName);
if (cd == null){
throw new RuntimeException(table.getRealTableName() + " does not have column " + columnName);
}
if (e.getRule().getClass() == ISNULL.class || e.getRule().getClass() == IS_NOTNULL.class){
return new Expression(table.getReflector().getPool(),columnName,op);
}else {
List<BindVariable> columnValues = columnValues(e,cd);
return new Expression(table.getReflector().getPool(),columnName,op,columnValues.toArray());
}
}
private String columnName(Element e){
List<Element> columnName = hunt(ColumnName.class,e);
assert (columnName.size() == 1);
return columnName.get(0).getText().trim();
}
private List<BindVariable> columnValues(Element e, ColumnDescriptor cd){
Collection<Element> columnValues = hunt(ColumnValue.class,e);
List<BindVariable> values = new ArrayList<BindVariable>();
for (Element columnValue:columnValues){
String sValue = columnValue.getText().trim();
if (sValue.startsWith("'")){
List<Element> quotedValues = hunt(QuotedValue.class,columnValue);
assert quotedValues.size() == 1 ;
sValue = quotedValues.get(0).getText();
}
Object value = sValue;
if (cd != null){
int jdbcType = cd.getJDBCType();
if (jdbcType != Types.VARCHAR){
List<TypeRef<?>> refs = Database.getJdbcTypeHelper(table.getReflector().getPool()).getTypeRefs(jdbcType);
TypeRef<?> ref = null;
if (refs.size() == 1){
ref = refs.get(0);
}else {
String field = this.table.getReflector().getFieldName(cd.getName());
ref = Database.getJdbcTypeHelper(table.getReflector().getPool()).getTypeRef(this.table.getReflector().getFieldGetter(field).getReturnType());
}
value = ref.getTypeConverter().valueOf(sValue);
}
}
values.add(new BindVariable(table.getReflector().getPool(),value));
}
return values;
}
private List<Element> hunt(Class<? extends Rule> elementForRule , Element within){
List<Element> ret = new ArrayList<Element>();
if (within.getRule().getClass() == elementForRule){
ret.add(within);
}else {
for (Element child : within.getChildren()){
Collection<Element> hunted = hunt(elementForRule,child);
if (hunted != null){
ret.addAll(hunted);
}
}
}
return ret;
}
public static class ExpressionRule extends PossiblyEnclosed {
public ExpressionRule(){
super(new Compound(new Operation()));
}
}
public static class Operation extends PossiblyEnclosed {
public Operation(){
super(new Compound(new PossiblyEnclosed(new Compound(new SimpleOperation()))));
}
}
public static class PossiblyEnclosed extends Rule {
private Rule ruleTemplate = null;
private int maxNumBrackets = UNKNOWN;
private static final int UNKNOWN = -1;
public PossiblyEnclosed(Rule rule){
this(rule,UNKNOWN);
}
public PossiblyEnclosed(Rule rule,int maxnumBrackets){
this.ruleTemplate = rule;
this.maxNumBrackets = maxnumBrackets;
}
@Override
public boolean match(String input, int offset) {
if (maxNumBrackets == UNKNOWN){
Rule bracket = zeroOrMore(openParen());
boolean bracketsPresent = bracket.match(input, offset);
if (bracketsPresent){
this.maxNumBrackets = bracket.getMatch().length();
}
}
for (int i = 0 ; i <= maxNumBrackets ; i ++){
Rule r = createRule(i);
if (r.match(input,offset)){
Element match = new Element(this);
match.add(r.getMatch());
setMatch(match);
return true;
}
}
return false;
}
private Rule createRule(int numBrackets){
return new Sequence(new Multiple(openParen(),numBrackets,numBrackets),
ruleTemplate.createClone() , new Multiple(closeParen(),numBrackets,numBrackets));
}
}
public static class Compound extends Any{
public Compound(Rule rule){
super (new And(rule.createClone()),new Or(rule.createClone()),rule.createClone());
}
}
public static class And extends Sequence {
public And(Rule rule){
super(rule.createClone(), oneOrMore(sequence(new CharSequence("AND"), spaces(), rule.createClone())));
}
}
public static class Or extends Sequence {
public Or(Rule rule){
super(rule.createClone(), oneOrMore(sequence(new CharSequence("OR"), spaces(), rule.createClone())));
}
}
public static class ColumnName extends Sequence{
public ColumnName(){
super(letter(),new ZeroOrMore(new Any(letter(),underscore(),digit())),spaces());
}
}
public static class ColumnValue extends Any {
public ColumnValue(){
super(new Sequence(new Include('\''),new QuotedValue(), new Include('\''),spaces()),
new Sequence(new OneOrMore(new Exclude(' ','\t','\r','\n','\f','(', ')', '\'' , '>' , '<' , '=' , '!')),spaces()));
}
}
public static class QuotedValue extends OneOrMore {
public QuotedValue(){
super(new Exclude('\''));
}
}
public static class SimpleOperation extends Any{
public SimpleOperation(){
super(new EQ(),new LT(), new GT(), new LE() ,new GE(), new NE() , new LIKE() , new IN() , new ISNULL() , new IS_NOTNULL());
}
}
public static class ISNULL extends Sequence {
public ISNULL(){
super(columnName(), new CharSequence("IS"),spaces(), new CharSequence("NULL"), spaces() );
}
}
public static class IS_NOTNULL extends Sequence {
public IS_NOTNULL(){
super(columnName(), new CharSequence("IS"),spaces(), new CharSequence("NOT"),spaces(), new CharSequence("NULL"), spaces());
}
}
public static class EQ extends Sequence{
public EQ(){
super(columnName(), new Include('='),spaces() , columnValue());
}
}
public static class LT extends Sequence{
public LT(){
super(columnName(), new Include('<'),spaces() , columnValue());
}
}
public static class GT extends Sequence{
public GT(){
super(columnName(), new Include('>'),spaces() , columnValue());
}
}
public static class LE extends Sequence{
public LE(){
super(columnName(), new Include('<'), new Include('='), spaces() , columnValue());
}
}
public static class GE extends Sequence{
public GE(){
super(columnName(), new Include('>'), new Include('='), spaces() , columnValue());
}
}
public static class NE extends Any{
public NE(){
super(
new Sequence(columnName(), new Include('!'), new Include('='), spaces() , columnValue()),
new Sequence(columnName(), new Include('<'), new Include('>'), spaces() , columnValue())
);
}
}
public static class LIKE extends Sequence{
public LIKE(){
super(columnName(), new CharSequence("LIKE"), spaces() , columnValue());
}
}
public static class IN extends Sequence {
public IN(){
super(columnName(), new CharSequence("IN"),spaces() , columnValues());
}
}
public static Rule columnName(){
return new ColumnName();
}
public static Rule columnValue(){
return new ColumnValue();
}
public static Rule columnValues(){
return new Sequence( openParen(),
columnValue() , new ZeroOrMore(new Sequence(new Include(',') , columnValue())) ,
closeParen() );
}
public static Rule underscore(){
return new Include('_');
}
public static Rule openParen(){
return new Sequence(new Include('('),spaces());
}
public static Rule closeParen(){
return new Sequence(new Include(')'),spaces());
}
public static Rule letter(){
return new Any(new CharRange('A', 'Z'),new CharRange('a', 'z'));
}
public static Rule digit(){
return new CharRange('0','9');
}
public static Rule whiteSpace(){
return new Include(' ','\t','\r','\n','\f');
}
public static Rule spaces() {
return new ZeroOrMore(whiteSpace());
}
public static Rule sequence(Rule... rules){
return new Sequence(rules);
}
public static Rule any(Rule... rules){
return new Any(rules);
}
public static Rule oneOrMore(Rule rule){
return new OneOrMore(rule);
}
public static Rule zeroOrMore(Rule rule){
return new ZeroOrMore(rule);
}
}