package com.alvazan.orm.parser.antlr;
public class OptimizeGltLtConversion {
private String aliasAndColumnName;
private ParsedNode firstMatch;
private ParsedNode secondMatch;
private ParsedNode rootNode;
public OptimizeGltLtConversion(String aliasAndColumnName) {
this.aliasAndColumnName = aliasAndColumnName;
}
public void setFirstMatch(ParsedNode node) {
firstMatch = node;
}
public ParsedNode getFirstMatch() {
return firstMatch;
}
public void setSecondMatch(ParsedNode node) {
this.secondMatch = node;
}
public ParsedNode getSecondMatch() {
return secondMatch;
}
public ParsedNode walkAndFixTree(ParsedNode node, String query, MetaFacade facade) {
this.rootNode = node;
walkTree(rootNode, query, facade);
return rootNode;
}
public void walkTree(ParsedNode node, String query, MetaFacade facade) {
if(node.getType() != NoSqlLexer.AND && node.getType() != NoSqlLexer.OR)
return; //We are not interested in other nodes, only AND and OR nodes so we can look at their children ourselves
findProcessMatch(node, ChildSide.RIGHT, facade);
findProcessMatch(node, ChildSide.LEFT, facade);
ParsedNode right = node.getChild(ChildSide.RIGHT);
ParsedNode left = node.getChild(ChildSide.LEFT);
walkTree(right, query, facade);
walkTree(left, query, facade);
}
private void findProcessMatch(ParsedNode node, ChildSide side, MetaFacade facade) {
if(node.getType() != NoSqlLexer.AND)
return; // nothing to do
ParsedNode match = findSidesMatch(node, side);
if(match == null)
return; //nothing to do
if(getFirstMatch() == null)
processFirstMatch(node, match, side);
else if(getSecondMatch() == null)
processSecondMatch(node, match, facade);
else
throw new IllegalArgumentException("Your query uses the column="+aliasAndColumnName
+" 3 times in the query with AND every time. This is not allowed as it only needs to be used twice in the 'and' clauses");
}
private ParsedNode findSidesMatch(ParsedNode node, ChildSide side) {
ParsedNode childNode = node.getChild(side);
//attribute is always on the left side since we rewrite the tree
ParsedNode attributeNode = childNode.getChild(ChildSide.LEFT);
//The parent must be an AND so we can try to find another reference to same
//variable that is ANDED with this guy
if(attributeNode.getType() == NoSqlLexer.ATTR_NAME) {
String aliasAndCol = attributeNode.getAliasAndColumn();
if(aliasAndColumnName.equals(aliasAndCol))
return childNode;
}
return null;
}
private void processSecondMatch(ParsedNode node, ParsedNode match, MetaFacade facade) {
setSecondMatch(match);
ParsedNode betweenExpr = facade.createExpression(NoSqlLexer.BETWEEN);
ParsedNode first = getFirstMatch();
match.getParent().replace(match, betweenExpr);
delete(first);
addExpression(betweenExpr, first, match);
}
private void delete(ParsedNode first) {
ParsedNode parent = first.getParent();
ParsedNode nodeToMove = parent.getOppositeChild(first);
if(parent == rootNode) {
//If we are the root node, the tree is now collapsing and removing root node
rootNode = nodeToMove;
} else {
parent.replace(first, nodeToMove);
}
}
private void processFirstMatch(ParsedNode node, ParsedNode match, ChildSide side) {
//let's cache for tree organization later
setFirstMatch(match);
}
public void addExpression(ParsedNode betweenExpr, ParsedNode firstMatch, ParsedNode secondMatch) {
ParsedNode leftAttributeSide = firstMatch.getChild(ChildSide.LEFT);
String aliasAndColumn = leftAttributeSide.getAliasAndColumn();
if(firstMatch.getType() == NoSqlLexer.EQ || secondMatch.getType() == NoSqlLexer.EQ) {
throw new IllegalArgumentException("uhhhmmmm, you are using column="+aliasAndColumn
+" twice with 'AND' statement yet one has an = so change to 'OR' or get rid of one. ");
} else if(firstMatch.getType() == NoSqlLexer.GE || firstMatch.getType() == NoSqlLexer.GT) {
betweenExpr.setChild(ChildSide.LEFT, firstMatch);
betweenExpr.setChild(ChildSide.RIGHT, secondMatch);
if(secondMatch.getType() == NoSqlLexer.GE || secondMatch.getType() == NoSqlLexer.GT)
throw new IllegalArgumentException("You are using > or >= twice on the same column so delete one of those in your query so we don't have to");
} else if(firstMatch.getType() == NoSqlLexer.LE || firstMatch.getType() == NoSqlLexer.LT) {
betweenExpr.setChild(ChildSide.LEFT, secondMatch);
betweenExpr.setChild(ChildSide.RIGHT, firstMatch);
if(secondMatch.getType() == NoSqlLexer.LE || secondMatch.getType() == NoSqlLexer.LT)
throw new IllegalArgumentException("uhmmmm, you are using column="+aliasAndColumn+" twice(which is fine) but both of them are using less than, delete one of them. ");
} else
throw new RuntimeException("bug, we should never get here but this should be easy to fix. ");
}
}