Package org.datanucleus.query.compiler

Source Code of org.datanucleus.query.compiler.JavaQueryCompiler

/**********************************************************************
Copyright (c) 2008 Andy Jefferson 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.datanucleus.query.compiler;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.Relation;
import org.datanucleus.query.expression.Expression;
import org.datanucleus.query.expression.ExpressionCompiler;
import org.datanucleus.query.expression.Literal;
import org.datanucleus.query.expression.PrimaryExpressionIsClassLiteralException;
import org.datanucleus.query.expression.PrimaryExpressionIsClassStaticFieldException;
import org.datanucleus.query.expression.PrimaryExpressionIsVariableException;
import org.datanucleus.query.node.Node;
import org.datanucleus.query.symbol.PropertySymbol;
import org.datanucleus.query.symbol.Symbol;
import org.datanucleus.query.symbol.SymbolResolver;
import org.datanucleus.query.symbol.SymbolTable;
import org.datanucleus.store.query.QueryCompilerSyntaxException;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.Imports;
import org.datanucleus.util.NucleusLogger;

/**
* Typical implementation of a compiler for a java-based query language.
* The constructor takes in the components of the query, and the method compile() compiles it
* returning the compiled query, for use elsewhere.
* <p>
* Each "Expression" is effectively a tree of Expressions. You can navigate through each expression based on
* their type. For example, a DyadicExpression has a "left" and "right" and an operator between them.
* The left could also be a DyadicExpression, so you would navigate to its left/right components etc etc.
* </p>
*/
public abstract class JavaQueryCompiler implements SymbolResolver
{
    protected final MetaDataManager metaDataManager;
    protected final ClassLoaderResolver clr;

    /** Primary candidate class (if defined). */
    protected Class candidateClass;

    /** Alias for the primary candidate. Default to "this" (JDOQL) but can be set. */
    protected String candidateAlias = "this";

    protected String from;
    protected Collection candidates;

    protected String update;
    protected String filter;
    protected String ordering;
    protected String parameters;
    protected String variables;
    protected String grouping;
    protected String having;
    protected String result;
    protected Imports imports;

    /** Compiled Symbol Table. */
    protected SymbolTable symtbl;

    /** Parser specific to the type of query being compiled. */
    protected Parser parser;

    public JavaQueryCompiler(MetaDataManager metaDataManager, ClassLoaderResolver clr,
            String from, Class candidateClass, Collection candidates,
            String filter, Imports imports, String ordering, String result, String grouping, String having,
            String params, String variables, String update)
    {
        this.metaDataManager = metaDataManager;
        this.clr = clr;

        this.from = from;
        this.candidateClass = candidateClass;
        this.candidates = candidates;

        this.filter = filter;
        this.result = result;
        this.grouping = grouping;
        this.having = having;
        this.ordering = ordering;
        this.parameters = params;
        this.variables = variables;
        this.update = update;

        this.imports = imports;
        if (imports == null)
        {
            this.imports = new Imports();
            if (candidateClass != null)
            {
                // Add candidate class
                this.imports.importClass(candidateClass.getName());
                this.imports.importPackage(candidateClass.getName());
            }
        }
    }

    /**
     * Method to compile the query.
     * @param parameters The parameter values keyed by name.
     * @param subqueryMap Map of subqueries keyed by the subquery name
     * @return The query compilation
     */
    public abstract QueryCompilation compile(Map parameters, Map subqueryMap);

    /**
     * Compile the candidates, variables and parameters.
     * @param parameters Map of parameter values keyed by their name
     */
    public void compileCandidatesParametersVariables(Map parameters)
    {
        compileCandidates();
        compileVariables();
        compileParameters();
    }

    /**
     * Method to compile the "from" clause (if present for the query language).
     */
    protected Expression[] compileFrom()
    {
        if (from == null)
        {
            return null;
        }

        Node[] node = parser.parseFrom(from);
        Expression[] expr = new Expression[node.length];
        for (int i = 0; i < node.length; i++)
        {
            String className = (String)node[i].getNodeValue();
            String classAlias = null;
            Class cls = resolveClass(className);
            List children = node[i].getChildNodes();
            for (int j=0;j<children.size();j++)
            {
                Node child = (Node)children.get(j);
                if (child.getNodeType() == Node.NAME) // Alias - maybe should assume it is the first child
                {
                    classAlias = (String)child.getNodeValue();
                }
            }

            if (classAlias != null && symtbl.getSymbol(classAlias) == null)
            {
                // Add symbol for this candidate under its alias
                symtbl.addSymbol(new PropertySymbol(classAlias, cls));
            }

            if (i == 0)
            {
                // First expression so set up candidateClass/alias
                candidateClass = cls;
                candidateAlias = classAlias;
            }

            Iterator childIter = node[i].getChildNodes().iterator();
            while (childIter.hasNext())
            {
                // Add entries in symbol table for any joined aliases
                Node childNode = (Node)childIter.next();
                if (childNode.getNodeType() == Node.OPERATOR)
                {
                    Node joinedNode = childNode.getFirstChild();
                    Symbol joinedSym = symtbl.getSymbol((String)joinedNode.getNodeValue());
                    if (joinedSym == null)
                    {
                        throw new QueryCompilerSyntaxException("FROM clause has identifier " + joinedNode.getNodeValue() + " but this is unknown");
                    }
                    AbstractClassMetaData joinedCmd = metaDataManager.getMetaDataForClass(joinedSym.getValueType(), clr);
                    Class joinedCls = joinedSym.getValueType();
                    while (joinedNode.getFirstChild() != null)
                    {
                        joinedNode = joinedNode.getFirstChild();
                        String joinedMember = (String)joinedNode.getNodeValue();
                        AbstractMemberMetaData mmd = joinedCmd.getMetaDataForMember(joinedMember);
                        if (mmd == null)
                        {
                            throw new QueryCompilerSyntaxException("FROM clause has reference to " + joinedCmd.getFullClassName() + "." + joinedMember + " but it doesn't exist!");
                        }

                        int relationType = mmd.getRelationType(clr);
                        switch (relationType)
                        {
                            case Relation.ONE_TO_ONE_UNI:
                            case Relation.ONE_TO_ONE_BI:
                            case Relation.MANY_TO_ONE_BI:
                                joinedCls = mmd.getType();
                                joinedCmd = metaDataManager.getMetaDataForClass(joinedCls, clr);
                                break;
                            case Relation.ONE_TO_MANY_UNI:
                            case Relation.ONE_TO_MANY_BI:
                            case Relation.MANY_TO_MANY_BI:
                                if (mmd.hasCollection())
                                {
                                    // TODO Don't currently allow interface field navigation
                                    joinedCmd = mmd.getCollection().getElementClassMetaData(clr);
                                    joinedCls = clr.classForName(joinedCmd.getFullClassName());
                                }
                                else if (mmd.hasArray())
                                {
                                    // TODO Don't currently allow interface field navigation
                                    joinedCmd = mmd.getArray().getElementClassMetaData(clr);
                                    joinedCls = clr.classForName(joinedCmd.getFullClassName());
                                }
                                break;
                            default:
                                break;
                        }
                    }

                    Node aliasNode = childNode.getNextChild();
                    if (aliasNode.getNodeType() == Node.NAME)
                    {
                        symtbl.addSymbol(new PropertySymbol((String)aliasNode.getNodeValue(), joinedCls));
                    }
                }
            }

            ExpressionCompiler comp = new ExpressionCompiler();
            comp.setSymbolTable(symtbl);
            expr[i] = comp.compileFromExpression(node[i]);
            if (expr[i] != null)
            {
                expr[i].bind();
            }
        }
        return expr;
    }

    private void compileCandidates()
    {
        if (symtbl.getSymbol(candidateAlias) == null)
        {
            // Add candidate symbol if not already present (from "compileFrom")
            PropertySymbol symbol = new PropertySymbol(candidateAlias, candidateClass);
            symtbl.addSymbol(symbol);
        }
    }

    public Expression[] compileUpdate()
    {
        if (update == null)
        {
            return null;
        }
        Node[] node = parser.parseTupple(update);
        Expression[] expr = new Expression[node.length];
        for (int i = 0; i < node.length; i++)
        {
            ExpressionCompiler comp = new ExpressionCompiler();
            comp.setSymbolTable(symtbl);
            expr[i] = comp.compileExpression(node[i]);
            expr[i].bind();
        }
        return expr;
    }

    /**
     * Compile the filter and return the compiled expression.
     * @return The compiled expression
     */
    public Expression compileFilter()
    {
        if (filter != null)
        {
            // Generate the node tree for the filter
            Node node = parser.parse(filter);

            ExpressionCompiler comp = new ExpressionCompiler();
            comp.setSymbolTable(symtbl);
            Expression expr = comp.compileExpression(node);
            expr.bind();
            return expr;
        }
        return null;
    }

    public Expression[] compileResult()
    {
        if (result == null)
        {
            return null;
        }
        Node[] node = parser.parseTupple(result);
        Expression[] expr = new Expression[node.length];
        for (int i = 0; i < node.length; i++)
        {
            ExpressionCompiler comp = new ExpressionCompiler();
            comp.setSymbolTable(symtbl);
            expr[i] = comp.compileExpression(node[i]);
            try
            {
                expr[i].bind();
            }
            catch (PrimaryExpressionIsClassLiteralException peil)
            {
                // PrimaryExpression should be swapped for a class Literal
                expr[i] = peil.getLiteral();
                expr[i].bind();
            }
            catch (PrimaryExpressionIsClassStaticFieldException peil)
            {
                // PrimaryExpression should be swapped for a static field Literal
                Field fld = peil.getLiteralField();
                try
                {
                    // Get the value of the static field
                    Object value = fld.get(null);
                    expr[i] = new Literal(value);
                    expr[i].bind();
                }
                catch (Exception e)
                {
                    throw new NucleusUserException("Error processing static field " + fld.getName(), e);
                }
            }
            catch (PrimaryExpressionIsVariableException pive)
            {
                // TODO Swap for an implicit variable ?
                throw new QueryCompilerSyntaxException("Illegal result clause : " + pive.getMessage());
            }
        }
        return expr;
    }

    public Expression[] compileGrouping()
    {
        if (grouping == null)
        {
            return null;
        }
        Node[] node = parser.parseTupple(grouping);
        Expression[] expr = new Expression[node.length];
        for (int i = 0; i < node.length; i++)
        {
            ExpressionCompiler comp = new ExpressionCompiler();
            comp.setSymbolTable(symtbl);
            expr[i] = comp.compileExpression(node[i]);
            expr[i].bind();
        }
        return expr;
    }

    public Expression compileHaving()
    {
        if (having == null)
        {
            return null;
        }
        Node node = parser.parse(having);
        ExpressionCompiler comp = new ExpressionCompiler();
        comp.setSymbolTable(symtbl);
        Expression expr = comp.compileExpression(node);
        expr.bind();
        return expr;
    }

    private void compileVariables()
    {
        if (variables == null)
        {
            return;
        }

        Node[][] node = parser.parseVariables(variables);
        for (int i = 0; i < node.length; i++)
        {
            String varName = (String) node[i][1].getNodeValue();
            Symbol varSym = symtbl.getSymbol(varName);
            if (varSym != null)
            {
                NucleusLogger.QUERY.warn(">> compileVariables param=" + varName + " but symbol already exists in table");
                varSym.setValueType(resolveClass(node[i][0].getNodeChildId()));
            }
            else
            {
                PropertySymbol sym = new PropertySymbol(varName, resolveClass(node[i][0].getNodeChildId()));
                sym.setType(Symbol.VARIABLE);
                symtbl.addSymbol(sym);
            }
        }
    }

    private void compileParameters()
    {
        if (parameters == null)
        {
            return;
        }

        Node[][] node = parser.parseParameters(parameters);
        for (int i = 0; i < node.length; i++)
        {
            String paramName = (String) node[i][1].getNodeValue();
            if (symtbl.getSymbol(paramName) != null)
            {
                NucleusLogger.QUERY.warn(">> compileParameters param=" + paramName + " but symbol already exists in table");
            }

            PropertySymbol sym = new PropertySymbol(paramName, resolveClass(node[i][0].getNodeChildId()));
            sym.setType(Symbol.PARAMETER);
            symtbl.addSymbol(sym);
        }
    }

    public Expression[] compileOrdering()
    {
        if (ordering == null)
        {
            return null;
        }
        Node[] node = parser.parseOrder(ordering);
        Expression[] expr = new Expression[node.length];
        for (int i = 0; i < node.length; i++)
        {
            ExpressionCompiler comp = new ExpressionCompiler();
            comp.setSymbolTable(symtbl);
            expr[i] = comp.compileOrderExpression(node[i]);
            expr[i].bind();
        }
        return expr;
    }

    public Class getPrimaryClass()
    {
        return candidateClass;
    }

    /**
     * Method to perform a lookup of the class name from the input name.
     * Makes use of the query "imports" and the lookup to "entity name".
     * @param className Name of the class
     * @return The class corresponding to this name
     * @throws ClassNotResolvedException thrown if not resolvable using imports or entity name
     */
    public Class resolveClass(String className)
    {
        if (imports != null)
        {
            // Try using the imports
            try
            {
                Class cls = imports.resolveClassDeclaration(className, clr, null);
                if (cls != null)
                {
                    return cls;
                }
            }
            catch (NucleusException ne)
            {
                // Ignore
            }
        }

        // Try via "entity name"
        AbstractClassMetaData acmd = metaDataManager.getMetaDataForEntityName(className);
        if (acmd != null)
        {
            String fullClassName = acmd.getFullClassName();
            if (fullClassName != null)
            {
                return clr.classForName(fullClassName);
            }
        }

        throw new ClassNotResolvedException("Class " + className + " for query has not been resolved. Check the query and any imports specification");
    }

    public Class getType(List tuples)
    {
        Class type = null;
        Symbol symbol = null;
        String firstTuple = (String)tuples.get(0);
        if (caseSensitiveSymbolNames())
        {
            symbol = symtbl.getSymbol(firstTuple);
        }
        else
        {
            symbol = symtbl.getSymbol(firstTuple);
            if (symbol == null)
            {
                symbol = symtbl.getSymbol(firstTuple.toUpperCase());
            }
            if (symbol == null)
            {
                symbol = symtbl.getSymbol(firstTuple.toLowerCase());
            }
        }
        if (symbol != null)
        {
            type = symbol.getValueType();
            if (type == null)
            {
                // Implicit variables don't have their type defined
                throw new NucleusUserException("Cannot find type of " + tuples.get(0) +
                    " since symbol has no type; implicit variable?");
            }

            for (int i=1; i<tuples.size(); i++)
            {
                type = getType(type, (String)tuples.get(i));
            }
        }
        else
        {
            symbol = symtbl.getSymbol(candidateAlias);
            type = symbol.getValueType();
            for (int i=0; i<tuples.size(); i++)
            {
                type = getType(type, (String)tuples.get(i));
            }
        }
        return type;
    }

    Class getType(Class cls, String fieldName)
    {
        AbstractClassMetaData acmd = metaDataManager.getMetaDataForClass(cls, clr);
        if (acmd != null)
        {
            AbstractMemberMetaData fmd = acmd.getMetaDataForMember(fieldName);
            if (fmd == null)
            {
                throw new NucleusUserException("Cannot access field "+fieldName+" on type "+cls.getName());
            }
            return fmd.getType();
        }
        else
        {
            Field field = ClassUtils.getFieldForClass(cls, fieldName);
            if (field == null)
            {
                throw new NucleusUserException("Cannot access field "+fieldName+" on type "+cls.getName());
            }
            return field.getType();
        }
    }
}
TOP

Related Classes of org.datanucleus.query.compiler.JavaQueryCompiler

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.