/**********************************************************************
Copyright (c) 2008 Erik Bengtson and others. All rights reserved.
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.
Contributors:
...
**********************************************************************/
package org.jpox.query.compiler;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.query.node.Node;
import org.jpox.store.query.QueryCompilerSyntaxException;
public class JDOQLParser
{
private Lexer p;
private Stack stack = new Stack();
public Node compileSingleString(String expression)
{
p = new Lexer(expression);
boolean found = p.parseString("SELECT");
if (!found)
{
found = p.parseString("select");
}
if (!found)
{
throw new QueryCompilerSyntaxException("Expected keyword SELECT", p.getIndex(), p.getInput(), new String[]{"SELECT"});
}
Node select = new Node(Node.OPERATOR, "select");
boolean unique = p.parseString("UNIQUE");
if (!unique)
{
unique = p.parseString("unique");
}
if (unique)
{
select.appendChildNode(new Node(Node.OPERATOR, "unique"));
}
Node[] result = compileTupple(p);
if (result.length > 0)
{
select.appendChildNode(result);
}
boolean into = p.parseString("INTO");
if (!into)
{
into = p.parseString("into");
}
if (into)
{
Node node = new Node(Node.OPERATOR, "into");
select.appendChildNode(node);
Node intoExpr = compileExpression();
node.appendChildNode(intoExpr);
}
boolean from = p.parseString("FROM");
if (!from)
{
from = p.parseString("from");
}
if (from)
{
Node fromExpr = compileExpression();
Node node = new Node(Node.OPERATOR, "from");
select.appendChildNode(node);
node.appendChildNode(fromExpr);
}
boolean where = p.parseString("WHERE");
if (!where)
{
where = p.parseString("where");
}
if (where)
{
Node whereExpr = compileExpression();
Node node = new Node(Node.OPERATOR, "where");
select.appendChildNode(node);
node.appendChildNode(whereExpr);
}
boolean variables = p.parseString("VARIABLES");
if (!variables)
{
variables = p.parseString("variables");
}
if (variables)
{
Node[][] variablesExpr = compileVariables(p);
Node node = new Node(Node.OPERATOR, "variables");
select.appendChildNode(node);
node.appendChildNode(variablesExpr);
}
boolean parameters = p.parseString("PARAMETERS");
if (!parameters)
{
parameters = p.parseString("parameters");
}
if (parameters)
{
Node[][] parametersExpr = compileParameters(p);
Node node = new Node(Node.OPERATOR, "parameters");
select.appendChildNode(node);
node.appendChildNode(parametersExpr);
}
List imports = new ArrayList();
while (p.parseString("import"))
{
compilePrimary();
if (stack.isEmpty())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
imports.add(stack.pop());
if (!p.parseString(";"))
{
break;
}
}
boolean group = p.parseString("GROUP");
if (!group)
{
group = p.parseString("group");
}
if (group)
{
boolean by = p.parseString("BY");
if (!by)
{
by = p.parseString("by");
}
Node[] groupBy = compileTupple(p);
}
boolean order = p.parseString("ORDER");
if (!order)
{
order = p.parseString("order");
}
if (order)
{
boolean by = p.parseString("BY");
if (!by)
{
by = p.parseString("by");
}
Node[] orderBy = compileOrder(p);
}
boolean range = p.parseString("RANGE");
if (!range)
{
range = p.parseString("range");
}
if (range)
{
Node[] rangeExpr = compileTupple(p);
}
return select;
}
public Node compile(String expression)
{
p = new Lexer(expression);
stack = new Stack();
return compileExpression();
}
public Node compileVariable(String expression)
{
p = new Lexer(expression);
stack = new Stack();
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
Node nodeVariable = (Node) stack.pop();
Node nodeType = (Node) stack.pop();
nodeType.appendChildNode(nodeVariable);
return nodeType;
}
public Node[] compileOrder(String expression)
{
p = new Lexer(expression);
return compileOrder(p);
}
public Node[] compileOrder(Lexer p)
{
stack = new Stack();
return compileOrderExpression();
}
public Node[] compileTupple(String expression)
{
p = new Lexer(expression);
return compileTupple(p);
}
public Node[] compileTupple(Lexer p)
{
stack = new Stack();
List nodes = new ArrayList();
do
{
compileExpression();
Node expr = (Node) stack.pop();
nodes.add(expr);
}
while (p.parseString(","));
return (Node[])nodes.toArray(new Node[nodes.size()]);
}
public Node[][] compileVariables(Lexer p)
{
List nodes = new ArrayList();
do
{
compilePrimary();
if (stack.isEmpty())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
Node nodeVariable = (Node) stack.pop();
Node nodeType = (Node) stack.pop();
nodes.add(new Node[]{nodeType, nodeVariable});
}
while (p.parseString(";"));
return (Node[][]) nodes.toArray(new Node[nodes.size()][2]);
}
public Node[][] compileVariables(String expression)
{
p = new Lexer(expression);
stack = new Stack();
List nodes = new ArrayList();
do
{
compilePrimary();
if (stack.isEmpty())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
Node nodeVariable = (Node) stack.pop();
Node nodeType = (Node) stack.pop();
nodes.add(new Node[]{nodeType, nodeVariable});
}
while (p.parseString(";"));
return (Node[][]) nodes.toArray(new Node[nodes.size()][2]);
}
public Node[][] compileParameters(Lexer p)
{
List nodes = new ArrayList();
do
{
compilePrimary();
if (stack.isEmpty())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
}
Node nodeVariable = (Node) stack.pop();
Node nodeType = (Node) stack.pop();
nodes.add(new Node[]{nodeType, nodeVariable});
}
while (p.parseString(","));
return (Node[][]) nodes.toArray(new Node[nodes.size()][2]);
}
public Node[][] compileParameters(String expression)
{
p = new Lexer(expression);
return compileParameters(p);
}
private Node[] compileOrderExpression()
{
List nodes = new ArrayList();
do
{
compileExpression();
if (p.parseString("asc") || p.parseString("ascending"))
{
Node expr = new Node(Node.OPERATOR, "ascending");
stack.push(expr);
}
else if (p.parseString("desc") || p.parseString("descending"))
{
Node expr = new Node(Node.OPERATOR, "descending");
stack.push(expr);
}
Node expr = new Node(Node.OPERATOR, "order");
expr.insertChildNode((Node) stack.pop());
if (!stack.empty())
{
expr.insertChildNode((Node) stack.pop());
}
nodes.add(expr);
}
while (p.parseString(","));
return (Node[]) nodes.toArray(new Node[nodes.size()]);
}
private Node compileExpression()
{
compileConditionalOrExpression();
return (Node) stack.peek();
}
/**
* This method deals with the OR condition
* A condition specifies a combination of one or more expressions and logical (Boolean) operators and
* returns a value of TRUE, FALSE, or unknown
*/
private void compileConditionalOrExpression()
{
compileConditionalAndExpression();
while (p.parseString("||"))
{
compileConditionalAndExpression();
Node expr = new Node(Node.OPERATOR, "||");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
}
/**
* This method deals with the AND condition
* A condition specifies a combination of one or more expressions and
* logical (Boolean) operators and returns a value of TRUE, FALSE, or
* unknown
*/
private void compileConditionalAndExpression()
{
compileInclusiveOrExpression();
while (p.parseString("&&"))
{
compileInclusiveOrExpression();
Node expr = new Node(Node.OPERATOR, "&&");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
}
private void compileInclusiveOrExpression()
{
compileExclusiveOrExpression();
while (p.parseChar('|', '|'))
{
compileExclusiveOrExpression();
Node expr = new Node(Node.OPERATOR, "|");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
}
private void compileExclusiveOrExpression()
{
compileAndExpression();
while (p.parseChar('^'))
{
compileAndExpression();
Node expr = new Node(Node.OPERATOR, "^");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
}
private void compileAndExpression()
{
compileEqualityExpression();
while (p.parseChar('&', '&'))
{
compileEqualityExpression();
Node expr = new Node(Node.OPERATOR, "&");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
}
private void compileEqualityExpression()
{
compileRelationalExpression();
for (;;)
{
if (p.parseString("=="))
{
compileRelationalExpression();
Node expr = new Node(Node.OPERATOR, "==");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseString("!="))
{
compileRelationalExpression();
Node expr = new Node(Node.OPERATOR, "!=");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseString("="))
{
// Assignment operator is invalid (user probably meant to specify "==")
throw new RuntimeException("invalid operator");
}
else
{
break;
}
}
}
private void compileRelationalExpression()
{
compileAdditiveExpression();
for (;;)
{
if (p.parseString("<="))
{
compileAdditiveExpression();
Node expr = new Node(Node.OPERATOR, "<=");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseString(">="))
{
compileAdditiveExpression();
Node expr = new Node(Node.OPERATOR, ">=");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('<'))
{
compileAdditiveExpression();
Node expr = new Node(Node.OPERATOR, "<");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('>'))
{
compileAdditiveExpression();
Node expr = new Node(Node.OPERATOR, ">");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseString("instanceof"))
{
compileAdditiveExpression();
Node expr = new Node(Node.OPERATOR, "instanceof");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else
{
break;
}
}
}
protected void compileAdditiveExpression()
{
compileMultiplicativeExpression();
for (;;)
{
if (p.parseChar('+'))
{
compileMultiplicativeExpression();
Node expr = new Node(Node.OPERATOR, "+");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('-'))
{
compileMultiplicativeExpression();
Node expr = new Node(Node.OPERATOR, "-");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else
{
break;
}
}
}
protected void compileMultiplicativeExpression()
{
compileUnaryExpression();
for (;;)
{
if (p.parseChar('*'))
{
compileMultiplicativeExpression();
Node expr = new Node(Node.OPERATOR, "*");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('/'))
{
compileMultiplicativeExpression();
Node expr = new Node(Node.OPERATOR, "/");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('%'))
{
compileMultiplicativeExpression();
Node expr = new Node(Node.OPERATOR, "%");
expr.insertChildNode((Node) stack.pop());
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else
{
break;
}
}
}
protected void compileUnaryExpression()
{
if (p.parseString("++"))
{
throw new JPOXUserException("Unsupported operator '++'");
}
else if (p.parseString("--"))
{
throw new JPOXUserException("Unsupported operator '--'");
}
if (p.parseChar('+'))
{
compileUnaryExpression();
Node expr = new Node(Node.OPERATOR, "+");
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('-'))
{
compileUnaryExpression();
Node expr = new Node(Node.OPERATOR, "-");
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else
{
compileUnaryExpressionNotPlusMinus();
}
}
protected void compileUnaryExpressionNotPlusMinus()
{
if (p.parseChar('~'))
{
compileUnaryExpression();
Node expr = new Node(Node.OPERATOR, "~");
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else if (p.parseChar('!'))
{
compileUnaryExpression();
Node expr = new Node(Node.OPERATOR, "!");
expr.insertChildNode((Node) stack.pop());
stack.push(expr);
}
else
{
compilePrimary();
}
}
/**
* this compiles a primary. First look for a literal (e.g. "text"), then
* an identifier(e.g. variable) In the next step, call a function, if
* executing a function, on the literal or the identifier found.
*
* @return Scalar Expression for the primary compiled expression
*/
protected void compilePrimary()
{
if (compileCreator())
{
return;
}
if (compileLiteral())
{
return;
}
if (compileMethod())
{
return;
}
if (p.parseChar('('))
{
compileExpression();
if (!p.parseChar(')'))
{
throw new QueryCompilerSyntaxException("expected ')'", p.getIndex(), p.getInput());
}
return;
}
// if primary == null, literal not found...
// We will have an identifier (variable, parameter, or field of candidate class)
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("Identifier expected", p.getIndex(), p.getInput());
}
int size = stack.size();
/*
* run function on literals or identifiers
* e.g. "primary.runMethod(arg)"
*/
while (p.parseChar('.'))
{
if (compileMethod())
{
//((ExpressionNode) stack.pop()).addChildNode((ExpressionNode) stack.pop());
}
else
{
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("Identifier expected", p.getIndex(), p.getInput());
}
}
}
while (stack.size() > size)
{
Node top = (Node) stack.pop();
Node peek = ((Node) stack.peek());
peek.insertChildNode(top);
}
}
private boolean compileCreator()
{
if (p.parseString("new"))
{
int size = stack.size();
if (!compileMethod())
{
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("Identifier expected", p.getIndex(), p.getInput());
}
/*
* run function on literals or identifiers
* e.g. "primary.runMethod(arg)"
*/
while (p.parseChar('.'))
{
if (compileMethod())
{
//((ExpressionNode) stack.pop()).addChildNode((ExpressionNode) stack.pop());
}
else
{
if (!compileIdentifier())
{
throw new QueryCompilerSyntaxException("Identifier expected", p.getIndex(), p.getInput());
}
}
}
}
while (stack.size() - 1 > size)
{
Node top = (Node) stack.pop();
Node peek = ((Node) stack.peek());
peek.insertChildNode(top);
}
Node expr = (Node) stack.pop();
Node newExpr = new Node(Node.CREATOR);
newExpr.insertChildNode(expr);
stack.push(newExpr);
return true;
}
return false;
}
private boolean compileMethod()
{
String method = p.parseMethod();
if (method != null)
{
p.skipWS();
p.parseChar('(');
// Found syntax for a method, so invoke the method
Node expr = new Node(Node.INVOKE, method);
if (!p.parseChar(')'))
{
do
{
compileExpression();
expr.appendChildNode((Node) stack.pop());
}
while (p.parseChar(','));
if (!p.parseChar(')'))
{
throw new QueryCompilerSyntaxException("')' expected", p.getIndex(), p.getInput());
}
}
stack.push(expr);
return true;
}
return false;
}
/**
* A literal is one value of any type.
* Supported literals are of types String, Floating Point, Integer,
* Character, Boolean and null e.g. 'J', "String", 1, 1.8, true, false, null.
* @return The compiled literal
*/
protected boolean compileLiteral()
{
Class litType = null;
Object litValue = null;
String sLiteral;
BigDecimal fLiteral;
BigInteger iLiteral;
Boolean bLiteral;
boolean single_quote_next = p.nextIsSingleQuote();
if ((sLiteral = p.parseStringLiteral()) != null)
{
// Both String and Character are allowed to use single-quotes
// so we need to check if it was single-quoted and
// use CharacterLiteral if length is 1.
if (sLiteral.length() == 1 && single_quote_next)
{
litType = Character.class;
litValue = new Character(sLiteral.charAt(0));
}
else
{
litType = String.class;
litValue = sLiteral;
}
}
else if ((fLiteral = p.parseFloatingPointLiteral()) != null)
{
litType = BigDecimal.class;
litValue = fLiteral;
}
else if ((iLiteral = p.parseIntegerLiteral()) != null)
{
litType = Long.class;
litValue = new Long(iLiteral.longValue());
}
else if ((bLiteral = p.parseBooleanLiteral()) != null)
{
litType = Boolean.class;
litValue = bLiteral;
}
else if (p.parseNullLiteral())
{
litValue = null;
}
else
{
return false;
}
stack.push(new Node(Node.LITERAL, litValue));
return true;
}
/**
* An identifier always designates a reference to a single value.
* A single value can be one collection, one field.
* @return The compiled identifier
*/
private boolean compileIdentifier()
{
String id = p.parseIdentifier();
if (id == null)
{
return false;
}
Node expr = new Node(Node.IDENTIFIER, id);
stack.push(expr);
return true;
}
}