Package org.datanucleus.query.expression

Source Code of org.datanucleus.query.expression.ExpressionCompiler

/**********************************************************************
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:
2008 Andy Jefferson - javadocs. Support for DISTINCT. Support for implicit parameters
2008 Andy Jefferson - compileFrom(), VariableExpression, ParameterExpression
2008 Andy Jefferson - support for CastExpression
2009 Andy Jefferson - support for Literal.invoke
2009 Andy Jefferson - support for arrays
2009 Andy Jefferson - support for subqueries
    ...
**********************************************************************/
package org.datanucleus.query.expression;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.query.expression.JoinExpression.JoinType;
import org.datanucleus.query.node.Node;
import org.datanucleus.query.node.ParameterNode;
import org.datanucleus.query.symbol.Symbol;
import org.datanucleus.query.symbol.SymbolTable;
import org.datanucleus.store.query.QueryCompilerSyntaxException;
import org.datanucleus.util.NucleusLogger;

/**
* Compiler for expressions. Responsible for taking a Node tree and creating an Expression tree.
*/
public class ExpressionCompiler
{
    SymbolTable symtbl;

    public void setSymbolTable(SymbolTable symtbl)
    {
        this.symtbl = symtbl;
    }

    /**
     * Primary entry point for compiling a node for the order clause.
     * @param node The node
     * @return Its compiled expression
     */
    public Expression compileOrderExpression(Node node)
    {
        if (isOperator(node, "order"))
        {
            if (node.getChildNodes().size() > 1)
            {
                return new OrderExpression(symtbl, compileExpression(node.getFirstChild()), (String) node.getNextChild().getNodeValue());
            }
            if (node.getChildNodes().size() == 1)
            {
                return new OrderExpression(symtbl, compileExpression(node.getFirstChild()));
            }
        }
        return compileExpression(node.getFirstChild());
    }

    /**
     * Primary entry point for compiling a node for the from clause.
     * @param node The node
     * @return Its compiled expression
     */
    public Expression compileFromExpression(Node node)
    {
        if (node.getNodeType() == Node.CLASS)
        {
            Node aliasNode = node.getFirstChild();
            ClassExpression clsExpr = new ClassExpression(symtbl, (String)aliasNode.getNodeValue());

            // Process any joins, chained down off the ClassExpression
            // So you can do clsExpr.getRight() to get the JoinExpression
            // then joinExpr.getRight() to get the next JoinExpression (if any)
            JoinExpression currentJoinExpr = null;
            Iterator childIter = node.getChildNodes().iterator();
            while (childIter.hasNext())
            {
                Node childNode = (Node)childIter.next();
                if (childNode.getNodeType() == Node.OPERATOR)
                {
                    String joinType = (String)childNode.getNodeValue();
                    JoinType joinTypeId = JoinType.JOIN_INNER;
                    if (joinType.equals("JOIN_INNER_FETCH"))
                    {
                        joinTypeId = JoinType.JOIN_INNER_FETCH;
                    }
                    else if (joinType.equals("JOIN_OUTER_FETCH"))
                    {
                        joinTypeId = JoinType.JOIN_OUTER_FETCH;
                    }
                    else if (joinType.equals("JOIN_OUTER"))
                    {
                        joinTypeId = JoinType.JOIN_OUTER;
                    }
                    Node joinedNode = childNode.getFirstChild();
                    Node joinedAliasNode = childNode.getNextChild();
                    PrimaryExpression primExpr = (PrimaryExpression)compilePrimaryExpression(joinedNode);

                    JoinExpression joinExpr = new JoinExpression(symtbl, primExpr,
                        (String)joinedAliasNode.getNodeValue(), joinTypeId);
                    if (currentJoinExpr != null)
                    {
                        currentJoinExpr.setJoinExpression(joinExpr);
                    }
                    else
                    {
                        clsExpr.setJoinExpression(joinExpr);
                    }
                    currentJoinExpr = joinExpr;
                }
            }
            return clsExpr;
        }
        return null;
    }

    /**
     * Primary entry point for compiling a node for the filter, grouping, having, result clauses.
     * @param node The node
     * @return Its compiled expression
     */
    public Expression compileExpression(Node node)
    {
        return compileOrAndExpression(node);
    }

    /**
     * This method deals with the OR/AND conditions.
     * A condition specifies a combination of one or more expressions and
     * logical (Boolean) operators and returns a value of TRUE, FALSE, or unknown
     * @param node The Node to process
     */
    private Expression compileOrAndExpression(Node node)
    {
        if (isOperator(node, "||"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_OR,right);
        }
        else if (isOperator(node, "&&"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_AND,right);
        }
        else if (isOperator(node, "|"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_OR,right);
        }
        else if (isOperator(node, "^"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_OR,right);
        }
        else if (isOperator(node, "&"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_AND,right);
        }
        return compileRelationalExpression(node);
    }

    private Expression compileRelationalExpression(Node node)
    {
        if (isOperator(node, "=="))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left, Expression.OP_EQ, right);
        }
        else if (isOperator(node, "!="))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left, Expression.OP_NOTEQ, right);
        }
        else if (isOperator(node, "LIKE"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left, Expression.OP_LIKE, right);
        }
        else if (isOperator(node, "<="))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_LTEQ,right);
        }
        else if (isOperator(node, ">="))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_GTEQ,right);
        }
        else if (isOperator(node, "<"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_LT,right);
        }
        else if (isOperator(node, ">"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_GT,right);
        }
        else if (isOperator(node, "instanceof"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_IS,right);
        }
        return compileAdditiveMultiplicativeExpression(node);
    }

    private Expression compileAdditiveMultiplicativeExpression(Node node)
    {
        if (isOperator(node, "+"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_ADD,right);
        }
        else if (isOperator(node, "-") && node.getChildNodes().size() > 1) // Subtract so multiple children
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_SUB,right);
        }
        else if (isOperator(node, "*"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_MUL,right);
        }
        else if (isOperator(node, "/"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_DIV,right);
        }
        else if (isOperator(node, "%"))
        {
            Expression left = compileExpression(node.getFirstChild());
            Expression right = compileExpression(node.getNextChild());
            return new DyadicExpression(left,Expression.OP_MOD,right);
        }
        return compileUnaryExpression(node);
    }

    private Expression compileUnaryExpression(Node node)
    {
        if (isOperator(node, "-") && node.getChildNodes().size() == 1) // Negate, not subtract so one child
        {
            Expression left = compileExpression(node.getFirstChild());
            if (left instanceof Literal)
            {
                // Swap sign of the literal rather than leaving a negate operator in
                ((Literal)left).negate();
                return left;
            }
            return new DyadicExpression(Expression.OP_NEG, left);
        }
        else if (isOperator(node, "~"))
        {
            Expression left = compileExpression(node.getFirstChild());
            return new DyadicExpression(Expression.OP_COM, left);
        }
        else if (isOperator(node, "!"))
        {
            Expression left = compileExpression(node.getFirstChild());
            return new DyadicExpression(Expression.OP_NOT, left);
        }
        else if (isOperator(node, "DISTINCT"))
        {
            Expression left = compileExpression(node.getFirstChild());
            return new DyadicExpression(Expression.OP_DISTINCT, left);
        }
        return compilePrimaryExpression(node);
    }   

    private Expression compilePrimaryExpression(Node node)
    {
        if (node.getNodeType() == Node.IDENTIFIER)
        {
            Node currentNode = node;
            List tupple = new ArrayList();
            Expression currentExpr = null;
            while (currentNode != null)
            {
                tupple.add(currentNode.getNodeValue());

                if (currentNode.getNodeType() == Node.INVOKE)
                {
                    if (currentExpr == null && tupple.size() > 1)
                    {
                        // Check for starting with parameter/variable
                        String first = (String)tupple.get(0);
                        Symbol firstSym = symtbl.getSymbol(first);
                        if (firstSym != null)
                        {
                            if (firstSym.getType() == Symbol.PARAMETER)
                            {
                                currentExpr = new ParameterExpression(symtbl, first, -1);
                                if (tupple.size() > 2)
                                {
                                    currentExpr = new PrimaryExpression(symtbl, currentExpr, tupple.subList(1, tupple.size()-1));
                                }
                            }
                            else if (firstSym.getType() == Symbol.VARIABLE)
                            {
                                currentExpr = new VariableExpression(symtbl, first);
                                if (tupple.size() > 2)
                                {
                                    currentExpr = new PrimaryExpression(symtbl, currentExpr, tupple.subList(1, tupple.size()-1));
                                }
                            }
                        }
                        if (currentExpr == null)
                        {
                            currentExpr = new PrimaryExpression(symtbl, tupple.subList(0, tupple.size()-1));
                        }
                    }

                    String methodName = (String)tupple.get(tupple.size()-1);
                    if (currentExpr instanceof PrimaryExpression)
                    {
                        // TODO Add plugin point to define aliases for methods for the query language
                        // ExpressionCompiler would need to know the query language name
                        String id = ((PrimaryExpression)currentExpr).getId();
                        if (id.equals("Math") || id.equals("java.lang.Math"))
                        {
                            methodName = "Math." + methodName;
                            currentExpr = null;
                        }
                        else if (id.equals("JDOHelper") || id.equals("javax.jdo.JDOHelper"))
                        {
                            methodName = "JDOHelper." + methodName;
                            currentExpr = null;
                        }
                    }
                    List parameterExprs = getExpressionsForPropertiesOfNode(currentNode);
                    currentExpr = new InvokeExpression(symtbl, currentExpr, methodName, parameterExprs);

                    currentNode = currentNode.getFirstChild();
                    tupple = new ArrayList();
                }
                else if (currentNode.getNodeType() == Node.CAST)
                {
                    if (currentExpr == null && tupple.size() > 1)
                    {
                        // Start from PrimaryExpression and invoke on that
                        currentExpr = new PrimaryExpression(symtbl, tupple.subList(0, tupple.size()-1));
                        PrimaryExpression primExpr = (PrimaryExpression)currentExpr;
                        if (primExpr.tuples.size() == 1)
                        {
                            Symbol sym = symtbl.getSymbol(primExpr.getId());
                            if (sym != null)
                            {
                                if (sym.getType() == Symbol.PARAMETER)
                                {
                                    // Parameter symbol registered for this identifier so use ParameterExpression
                                    currentExpr = new ParameterExpression(symtbl, primExpr.getId(), -1);
                                }
                                else if (sym.getType() == Symbol.VARIABLE)
                                {
                                    // Variable symbol registered for this identifier so use VariableExpression
                                    currentExpr = new VariableExpression(symtbl, primExpr.getId());
                                }
                            }
                        }
                    }

                    String className = (String)tupple.get(tupple.size()-1);
                    currentExpr = new CastExpression(symtbl, currentExpr, className);

                    currentNode = currentNode.getFirstChild();
                    tupple = new ArrayList();
                }
                else
                {
                    // Part of identifier chain
                    currentNode = currentNode.getFirstChild();
                }
            }

            if (currentExpr != null && tupple.size() > 0)
            {
                // We have a trailing identifier expression
                // e.g "((B)a).c" where we have a CastExpression and trailing "c"
                currentExpr = new PrimaryExpression(symtbl, currentExpr, tupple);
            }

            if (currentExpr == null)
            {
                // Find type of first of tupples
                String first = (String)tupple.get(0);
                Symbol firstSym = symtbl.getSymbol(first);
                if (firstSym != null)
                {
                    if (firstSym.getType() == Symbol.PARAMETER)
                    {
                        ParameterExpression paramExpr = new ParameterExpression(symtbl, first, -1);
                        if (tupple.size() > 1)
                        {
                            currentExpr = new PrimaryExpression(symtbl, paramExpr, tupple.subList(1, tupple.size()));
                        }
                        else
                        {
                            currentExpr = paramExpr;
                        }
                    }
                    else if (firstSym.getType() == Symbol.VARIABLE)
                    {
                        VariableExpression varExpr = new VariableExpression(symtbl, first);
                        if (tupple.size() > 1)
                        {
                            currentExpr = new PrimaryExpression(symtbl, varExpr, tupple.subList(1, tupple.size()));
                        }
                        else
                        {
                            currentExpr = varExpr;
                        }
                    }
                    else
                    {
                        currentExpr = new PrimaryExpression(symtbl, tupple);
                    }
                }
                else
                {
                    currentExpr = new PrimaryExpression(symtbl, tupple);
                }
            }

            return currentExpr;
        }
        else if (node.getNodeType() == Node.PARAMETER)
        {
            // "{paramExpr}", "{paramExpr}.invoke(...)", "{paramExpr}.invoke(...).invoke(...)"
            Object val = node.getNodeValue();
            Expression currentExpr = null;
            if (val instanceof Integer)
            {
                // Positional parameter TODO Store as Integer to avoid confusion
                currentExpr = new ParameterExpression(symtbl, "" + node.getNodeValue(),
                    ((ParameterNode)node).getPosition());
            }
            else
            {
                // Named parameter
                currentExpr = new ParameterExpression(symtbl, (String)node.getNodeValue(),
                    ((ParameterNode)node).getPosition());
            }

            Node childNode = node.getFirstChild();
            while (childNode != null)
            {
                if (childNode.getNodeType() == Node.INVOKE)
                {
                    String methodName = (String)childNode.getNodeValue();
                    List parameterExprs = getExpressionsForPropertiesOfNode(childNode);
                    currentExpr = new InvokeExpression(symtbl, currentExpr, methodName, parameterExprs);
                }
                else
                {
                    throw new QueryCompilerSyntaxException("Dont support compilation of " + node);
                }
                childNode = childNode.getFirstChild();
            }
            return currentExpr;
        }
        else if (node.getNodeType() == Node.INVOKE)
        {
            Node currentNode = node;
            List tupple = new ArrayList();
            Expression currentExpr = null;
            while (currentNode != null)
            {
                tupple.add(currentNode.getNodeValue());

                if (currentNode.getNodeType() == Node.INVOKE)
                {
                    String methodName = (String)tupple.get(tupple.size()-1);
                    List parameterExprs = getExpressionsForPropertiesOfNode(currentNode);
                    currentExpr = new InvokeExpression(symtbl, currentExpr, methodName, parameterExprs);

                    currentNode = currentNode.getFirstChild();
                    if (currentNode != null)
                    {
                        // Continue on along the chain
                        tupple = new ArrayList();
                        tupple.add(currentExpr); // Start from this expression
                    }
                }
                else
                {
                    // TODO What node type is this that comes after an INVOKE?
                    currentNode = currentNode.getFirstChild();
                }
            }
            return currentExpr;
        }
        else if (node.getNodeType() == Node.CREATOR)
        {
            Node currentNode = node.getFirstChild();
            List tupple = new ArrayList();
            boolean method = false;
            while (currentNode != null)
            {
                tupple.add(currentNode.getNodeValue());
                if (currentNode.getNodeType() == Node.INVOKE)
                {
                    method = true;
                    break;
                }
                currentNode = currentNode.getFirstChild();
            }

            List parameterExprs = null;
            if (method)
            {
                parameterExprs = getExpressionsForPropertiesOfNode(currentNode);
            }
            else
            {
                parameterExprs = new ArrayList();
            }
            return new CreatorExpression(symtbl, tupple, parameterExprs);
        }
        else if (node.getNodeType() == Node.LITERAL)
        {
            Node currentNode = node;
            List tupple = new ArrayList();
            Expression currentExpr = null;
            while (currentNode != null)
            {
                tupple.add(currentNode.getNodeValue());

                if (currentNode.getNodeType() == Node.INVOKE)
                {
                    if (currentExpr == null && tupple.size() > 1)
                    {
                        // Start from Literal and invoke on that
                        currentExpr = new Literal(node.getNodeValue());
                    }

                    String methodName = (String)tupple.get(tupple.size()-1);
                    List parameterExprs = getExpressionsForPropertiesOfNode(currentNode);
                    currentExpr = new InvokeExpression(symtbl, currentExpr, methodName, parameterExprs);

                    currentNode = currentNode.getFirstChild();
                    tupple = new ArrayList();
                }
                else
                {
                    currentNode = currentNode.getFirstChild();
                }
            }

            if (currentExpr == null)
            {
                currentExpr = new Literal(node.getNodeValue());
            }
            return currentExpr;
        }
        else if (node.getNodeType() == Node.ARRAY)
        {
            Node currentNode = node;
            List<Node> arrayElements = (List<Node>)node.getNodeValue();

            // Check if this is an array literal
            boolean literal = true;
            Class type = null;
            Iterator<Node> iter = arrayElements.iterator();
            while (iter.hasNext())
            {
                Node element = iter.next();
                if (type == null)
                {
                    type = element.getNodeValue().getClass();
                }
                else if (type != element.getNodeValue().getClass())
                {
                    // TODO Allow for mixed-mode arrays e.g "(1,3,4,this.longField)"
                    throw new QueryCompilerSyntaxException("Array elements are of inconsistent types in " + node);
                }
                if (element.getNodeType() == Node.IDENTIFIER)
                {
                    literal = false;
                    break;
                }
            }

            Expression currentExpr = null;
            if (literal)
            {
                Object array = Array.newInstance(type, arrayElements.size());
                iter = arrayElements.iterator();
                int index = 0;
                while (iter.hasNext())
                {
                    Node element = iter.next();
                    Array.set(array, index++, element.getNodeValue());
                }
                currentExpr = new Literal(array);
            }
            else
            {
                // TODO Handle ArrayExpression
                throw new NucleusUserException("Compilation of an array of expressions is not yet supported");
            }
            currentNode = currentNode.getFirstChild();

            List tupple = new ArrayList();
            while (currentNode != null)
            {
                tupple.add(currentNode.getNodeValue());

                if (currentNode.getNodeType() == Node.INVOKE)
                {
                    if (currentExpr == null && tupple.size() > 1)
                    {
                        // Start from Literal and invoke on that
                        currentExpr = new Literal(node.getNodeValue());
                    }

                    String methodName = (String)tupple.get(tupple.size()-1);
                    List parameterExprs = getExpressionsForPropertiesOfNode(currentNode);
                    currentExpr = new InvokeExpression(symtbl, currentExpr, methodName, parameterExprs);

                    currentNode = currentNode.getFirstChild();
                    tupple = new ArrayList();
                }
                else
                {
                    currentNode = currentNode.getFirstChild();
                }
            }

            return currentExpr;
        }
        else if (node.getNodeType() == Node.SUBQUERY)
        {
            List children = node.getChildNodes();
            if (children.size() != 1)
            {
                throw new QueryCompilerSyntaxException("Invalid number of children for SUBQUERY node : " + node);
            }
            Node varNode = (Node)children.get(0);
            VariableExpression subqueryExpr = new VariableExpression(symtbl, varNode.getNodeId());
            Expression currentExpr = new SubqueryExpression((String)node.getNodeValue(), subqueryExpr);
            return currentExpr;
        }
        else
        {
            NucleusLogger.QUERY.warn("ExpressionCompiler.compilePrimary " + node + " ignored by ExpressionCompiler");
        }
        return null;
    }

    /**
     * Convenience method to extract properties for this node and return the associated list of expressions.
     * @param node The node
     * @return The list of expressions for the properties (or null if no properties)
     */
    private List<Expression> getExpressionsForPropertiesOfNode(Node node)
    {
        if (node.hasProperties())
        {
            List<Expression> parameterExprs = new ArrayList();
            List propNodes = node.getProperties();
            for (int i=0;i<propNodes.size();i++)
            {
                parameterExprs.add(compileExpression((Node)propNodes.get(i)));
            }
            return parameterExprs;
        }
        return Collections.EMPTY_LIST;
    }

    private boolean isOperator(Node node, String operator)
    {
        return node.getNodeType() == Node.OPERATOR && node.getNodeValue().equals(operator);
    }
}
TOP

Related Classes of org.datanucleus.query.expression.ExpressionCompiler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.