All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.store.db4o.query.QueryToSODAMapper Maven / Gradle / Ivy

/**********************************************************************
Copyright (c) 2007 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 - support for methods. Javadocs. Extend AbstractExpressionEvaluator
    ...
 **********************************************************************/
package org.datanucleus.store.db4o.query;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

import org.datanucleus.store.db4o.DB4OStoreManager;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.query.QueryUtils;
import org.datanucleus.query.compiler.QueryCompilation;
import org.datanucleus.query.evaluator.AbstractExpressionEvaluator;
import org.datanucleus.query.expression.Expression;
import org.datanucleus.query.expression.InvokeExpression;
import org.datanucleus.query.expression.Literal;
import org.datanucleus.query.expression.OrderExpression;
import org.datanucleus.query.expression.ParameterExpression;
import org.datanucleus.query.expression.PrimaryExpression;
import org.datanucleus.query.symbol.SymbolTable;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.Localiser;

import com.db4o.query.Constraint;
import com.db4o.query.Query;

/**
 * Class which maps a compiled query to a db4o SODA query. Utilises the filter and ordering components
 * of the java query and adds them to the underlying SODA query. 
 * All other components are not handled here and instead processed by in-memory evaluator.
 */
public class QueryToSODAMapper extends AbstractExpressionEvaluator
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER_DB4O = Localiser.getInstance(
        "org.datanucleus.store.db4o.Localisation", DB4OStoreManager.class.getClassLoader());

    String candidateAlias;

    /** Filter expression. */
    Expression filterExpr;

    /** Ordering expression(s). */
    Expression[] orderingExpr;

    /** Input parameters. */
    Map parameters;

    /** Symbol table for the compiled query. */
    SymbolTable symtbl;

    /** SODA Query that we are updating. */
    Query query;

    Stack stack = new Stack();

    /**
     * Constructor.
     * @param query SODA query to be updated with the filter/ordering
     * @param compilation The generic query compilation
     * @param parameters Parameters needed
     */
    public QueryToSODAMapper(Query query, QueryCompilation compilation, Map parameters)
    {
        this.parameters = parameters;
        this.orderingExpr = compilation.getExprOrdering();
        this.query = query;
        this.filterExpr = compilation.getExprFilter();
        this.symtbl = compilation.getSymbolTable();
        this.candidateAlias = compilation.getCandidateAlias();
    }

    public void compile()
    {
        if (filterExpr != null)
        {
            // Define the filter on the SODA query
            filterExpr.evaluate(this);
        }

        if (orderingExpr != null)
        {
            // Define the ordering on the SODA query
            for (int i = 0; i < orderingExpr.length; i++)
            {
                String descendedObject = ((PrimaryExpression)orderingExpr[i].getLeft()).getId();
                Query q = query.descend(descendedObject);

                if (((OrderExpression)orderingExpr[i]).getSortOrder() == null ||
                    ((OrderExpression)orderingExpr[i]).getSortOrder().equals("ascending"))
                {
                    q.orderAscending();
                    if (NucleusLogger.QUERY.isDebugEnabled())
                    {
                        NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                            "query.descend(\"" + descendedObject + "\").orderAscending()"));
                    }
                }
                else
                {
                    q.orderDescending();
                    if (NucleusLogger.QUERY.isDebugEnabled())
                    {
                        NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                            "query.descend(\"" + descendedObject + "\").orderDescending()"));
                    }
                }
            }
        }

        if (this.symtbl.hasSymbol(candidateAlias))
        {
            // Query has a set of candidates defined for it
            if (parameters != null && parameters.get(candidateAlias) != null &&
                parameters.get(candidateAlias) instanceof Collection)
            {
                Collection candidates = (Collection)parameters.get(candidateAlias);
                if (candidates.isEmpty())
                {
                    // TODO If we have candidates supplied but is empty should just return null anyway
                }
                else
                {
                    Iterator it = candidates.iterator();
                    Constraint cons = null;
                    while (it.hasNext())
                    {
                        Object next = it.next();
                        if (cons != null)
                        {
                            cons = cons.or(query.constrain(next).equal());
                            if (NucleusLogger.QUERY.isDebugEnabled())
                            {
                                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                                    "query.constrain(\"" + next + "\").equal()"));
                            }
                        }
                        else
                        {
                            cons = query.constrain(next).equal();
                            if (NucleusLogger.QUERY.isDebugEnabled())
                            {
                                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                                    "query.constrain(\"" + next + "\").equal()"));
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Method to process the supplied OR expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processOrExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;

        if (left instanceof Constraint && right instanceof Constraint)
        {
            boolExpr = ((Constraint)left).or((Constraint)right);
        }
        else
        {
            if (left == Boolean.TRUE || right == Boolean.TRUE)
            {
                stack.push(Boolean.TRUE);
                return Boolean.TRUE;
            }
            else
            {
                stack.push(Boolean.FALSE);
                return Boolean.FALSE;
            }
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied AND expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processAndExpression(Expression expr)
    {
        final Object right = stack.pop();
        final Object left = stack.pop();
        Object boolExpr;

        if (left instanceof Constraint && right instanceof Constraint)
        {
            boolExpr = ((Constraint)left).and((Constraint)right);
        }
        else
        {
            if (left == right && left == Boolean.TRUE)
            {
                stack.push(Boolean.TRUE);
                return Boolean.TRUE;
            }
            else
            {
                stack.push(Boolean.FALSE);
                return Boolean.FALSE;
            }
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied EQ expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processEqExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;
        if (left instanceof Query)
        {
            boolExpr = ((Query)left).constrain(right).equal();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + right + "\").equal()"));
            }
        }
        else if (right instanceof Query)
        {
            boolExpr = ((Query)right).constrain(left).equal();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + left + "\").equal()"));
            }
        }
        else
        {
            boolExpr = left.equals(right) ? Boolean.TRUE : Boolean.FALSE;
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied NOTEQ expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processNoteqExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;
        if (left instanceof Query)
        {
            boolExpr = ((Query)left).constrain(right).equal().not();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + right + "\").equal().not()"));
            }
        }
        else if (right instanceof Query)
        {
            boolExpr = ((Query)right).constrain(left).equal().not();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + left + "\").equal().not()"));
            }
        }
        else
        {
            boolExpr = left.equals(right) ? Boolean.FALSE : Boolean.TRUE;
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied GT expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processGtExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;
        if (left instanceof Query)
        {
            boolExpr = ((Query)left).constrain(right).greater();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + right + "\").greater()"));
            }
        }
        else if (right instanceof Query)
        {
            boolExpr = ((Query)right).constrain(left).greater();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + left + "\").greater()"));
            }
        }
        else
        {
            boolExpr = left.equals(right) ? Boolean.TRUE : Boolean.FALSE;
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied LT expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processLtExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;
        if (left instanceof Query)
        {
            boolExpr = ((Query)left).constrain(right).smaller();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + right + "\").greater()"));
            }
        }
        else if (right instanceof Query)
        {
            boolExpr = ((Query)right).constrain(left).smaller();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + left + "\").greater()"));
            }
        }
        else
        {
            boolExpr = left.equals(right) ? Boolean.TRUE : Boolean.FALSE;
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied GTEQ expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processGteqExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;
        if (left instanceof Query)
        {
            boolExpr = ((Query)left).constrain(right).equal().greater();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + right + "\").equal().greater()"));
            }
        }
        else if (right instanceof Query)
        {
            boolExpr = ((Query)right).constrain(left).equal().greater();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + left + "\").equal().greater()"));
            }
        }
        else
        {
            boolExpr = left.equals(right) ? Boolean.TRUE : Boolean.FALSE;
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied LTEQ expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processLteqExpression(Expression expr)
    {
        Object right = stack.pop();
        Object left = stack.pop();
        Object boolExpr;
        if (left instanceof Query)
        {
            boolExpr = ((Query)left).constrain(right).equal().smaller();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + right + "\").equal().smaller()"));
            }
        }
        else if (right instanceof Query)
        {
            boolExpr = ((Query)right).constrain(left).equal().smaller();
            if (NucleusLogger.QUERY.isDebugEnabled())
            {
                NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                    "query.constrain(\"" + left + "\").equal().smaller()"));
            }
        }
        else
        {
            boolExpr = left.equals(right) ? Boolean.TRUE : Boolean.FALSE;
        }
        stack.push(boolExpr);
        return stack.peek();
    }

    /**
     * Method to process the supplied primary expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processPrimaryExpression(PrimaryExpression expr)
    {
        Object value = (parameters != null ? parameters.get(expr.getId()) : null);
        if (value != null)
        {
            stack.push(value);
            return value;
        }

        String exprPath = getPathForPrimaryExpression(expr);
        Query q = query.descend(exprPath);
        if (NucleusLogger.QUERY.isDebugEnabled())
        {
            NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                "query.descend(\"" + exprPath + "\")"));
        }
        stack.push(q);
        return q;
    }

    /**
     * Method to process the supplied parameter expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processParameterExpression(ParameterExpression expr)
    {
        Object value = QueryUtils.getValueForParameterExpression(parameters, expr);
        stack.push(value);
        return value;
    }

    /**
     * Convenience method that takes a PrimaryExpression and returns the path to apply to the
     * query in terms of a "descend" argument.
     * @param expr The PrimaryExpression
     * @return The path
     */
    private String getPathForPrimaryExpression(PrimaryExpression expr)
    {
        // Find the path to apply. This currently only caters for direct fields, and fields of the candidate
        // TODO Cater for other candidate aliases from JPQL "FROM" clause
        String firstTuple = (String)expr.getTuples().iterator().next();
        String exprPath = expr.getId();
        if (firstTuple.equals(candidateAlias))
        {
            exprPath = exprPath.substring(candidateAlias.length()+1);
        }
        return exprPath;
    }

    /**
     * Method to process the supplied invoke expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processInvokeExpression(InvokeExpression expr)
    {
        Expression invokedExpr = expr.getLeft();
        String method = expr.getOperation();
        if (invokedExpr instanceof PrimaryExpression)
        {
            String exprPath = getPathForPrimaryExpression((PrimaryExpression)invokedExpr);
            Query q = query.descend(exprPath);
            if (method.equals("startsWith"))
            {
                // TODO Check if the field we invoke on is String-based
                Literal param = (Literal)expr.getArguments().get(0);
                String arg = null;
                if (param.getLiteral() instanceof String)
                {
                    arg = (String)param.getLiteral();
                }
                else if (param.getLiteral() instanceof Character)
                {
                    arg = ((Character)param.getLiteral()).toString();
                }
                else if (param.getLiteral() instanceof Number)
                {
                    arg = ((Number)param.getLiteral()).toString();
                }
                Constraint cons = q.constrain(arg).startsWith(true);
                if (NucleusLogger.QUERY.isDebugEnabled())
                {
                    NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                        "query.descend(\"" + exprPath + "\").constrain(" + arg + ").startsWith(true)"));
                }

                stack.push(cons);
                return cons;
            }
            else if (method.equals("endsWith"))
            {
                // TODO Check if the field we invoke on is String-based
                Literal param = (Literal)expr.getArguments().get(0);
                String arg = null; // TODO Move this extraction code to a generic method
                if (param.getLiteral() instanceof String)
                {
                    arg = (String)param.getLiteral();
                }
                else if (param.getLiteral() instanceof Character)
                {
                    arg = ((Character)param.getLiteral()).toString();
                }
                else if (param.getLiteral() instanceof Number)
                {
                    arg = ((Number)param.getLiteral()).toString();
                }
                Constraint cons = q.constrain(arg).endsWith(true);
                if (NucleusLogger.QUERY.isDebugEnabled())
                {
                    NucleusLogger.QUERY.debug(LOCALISER_DB4O.msg("DB4O.SODA.Query", 
                        "query.descend(\"" + exprPath + "\").constrain(" + arg + ").endsWith(true)"));
                }

                stack.push(cons);
                return cons;
            }
            else
            {
                // TODO Support more methods
                throw new NucleusException("Compiled filter contains method \"" + method + "\"" +
                " which is not currently supported by SODA queries");
            }
        }
        else
        {
            throw new NucleusException("Invocation of method \"" + method + "\" is on " +
                expr.getLeft() + " but this is not currently supported by SODA queries");
        }
    }

    /**
     * Method to process the supplied invoke expression.
     * @param expr The expression
     * @return The result
     */
    protected Object processLiteral(Literal expr)
    {
        Object value = expr.getLiteral();
        stack.push(value);
        return value;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy