Package org.jpox.store.rdbms.query

Source Code of org.jpox.store.rdbms.query.ResultExpressionsQueryable$ResultLiteralJavaTypeMapping

/**********************************************************************
Copyright (c) 2004 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:
2005 Andy Jefferson - changed to use ResultClassROF
2005 Andy Jefferson - added use of DiscriminatorIteratorStatement
2005 Andy Jefferson - added support for expressions that have no "expressionList"
    ...
**********************************************************************/
package org.jpox.store.rdbms.query;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.jpox.ClassLoaderResolver;
import org.jpox.ObjectManager;
import org.jpox.StateManager;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.IdentityType;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.mapped.StatementExpressionIndex;
import org.jpox.store.mapped.expression.AggregateExpression;
import org.jpox.store.mapped.expression.BooleanExpression;
import org.jpox.store.mapped.expression.Literal;
import org.jpox.store.mapped.expression.LogicSetExpression;
import org.jpox.store.mapped.expression.NewObjectExpression;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.expression.ScalarExpression;
import org.jpox.store.mapped.mapping.AbstractContainerMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.SingleFieldMapping;
import org.jpox.store.mapped.query.Queryable;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.util.Localiser;

/**
* Result expressions to be returned by the query. The results might be fields
* of persistent instances, instances of classes other than the candidate class,
* or aggregates of fields.
* @version $Revision: 1.17 $
*/
public class ResultExpressionsQueryable implements Queryable
{
    protected static final Localiser LOCALISER=Localiser.getInstance("org.jpox.store.rdbms.Localisation",
        RDBMSManager.class.getClassLoader());

    private final MappedStoreManager storeMgr;
    private final ClassLoaderResolver clr;

    /** Candidate class. */
    private final Class candidateClass;

    /** User defined candidate classes (if provided). */
    private final Collection userCandidates;

    /** Whether to include subclasses. */
    private final boolean subclasses;

    private ScalarExpression[] expressions;

    /** Whether we know that there will be aggregate expressions only (override the result expressions). */
    private Boolean hasAggregatedExpressionsOnly = null;

    /**
     * Constructor.
     * @param om Object Manager
     * @param candidateClass The candidate
     * @param subclasses Whether to include subclasses
     */
    public ResultExpressionsQueryable(ObjectManager om, Class candidateClass, boolean subclasses)
    {
        this.candidateClass = candidateClass;
        this.storeMgr = (MappedStoreManager)om.getStoreManager();
        this.clr = om.getClassLoaderResolver();
        this.subclasses = subclasses;
        this.userCandidates = null;
    }

    /**
     * Constructor.
     * @param om Object Manager
     * @param candidateClass The candidate
     * @param userCandidates The users candidates
     * @param subclasses Whether to include subclasses
     */
    public ResultExpressionsQueryable(ObjectManager om, Class candidateClass, Collection userCandidates, boolean subclasses)
    {
        this.userCandidates = userCandidates;
        this.candidateClass = candidateClass;
        this.storeMgr = (MappedStoreManager)om.getStoreManager();
        this.clr = om.getClassLoaderResolver();
        this.subclasses = subclasses;
    }

    /**
     * Create a new query to search for the candidate classes etc.
     * @return The new QueryStatement.
     */
    public QueryExpression newQueryStatement()
    {
        return newQueryStatement(candidateClass, null);
    }

    /**
     * Create a query to search for the candidateClass and subclasses if true
     * @param candidateClass The candidate
     * @param candidateAlias Alias for the candidate
     * @return The new QueryStatement
     */
    public QueryExpression newQueryStatement(Class candidateClass, DatastoreIdentifier candidateAlias)
    {
        final DatastoreClass datastoreClass = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
        final JavaTypeMapping m = datastoreClass.getIDMapping();
        final Class type = candidateClass;
        final boolean subclasses = this.subclasses;

        QueryExpression stmt = null;
        if (!queryUsingDiscriminator(datastoreClass) && !subclasses && hasAggregatedExpressionsOnly())
        {
            // TODO This is incorrect. Here we want to return a single row so cant use UNION statements
            // and our classes are not all in one table so shouldn't use the discriminator. We should have
            // a single SELECT like
            // SELECT ... FROM BASE
            // LEFT OUTER JOIN SUB1 ON ...
            // LEFT OUTER JOIN SUB2 ON ...
            // WHERE SUB1.ID = NULL AND SUB2.ID = NULL
            stmt = new DiscriminatorIteratorStatement(clr,
                new Class[] {type}, subclasses, storeMgr, false).getQueryStatement(candidateAlias);
        }
        else if (queryUsingDiscriminator(datastoreClass) || hasAggregatedExpressionsOnly())
        {
            // Create a statement using a single SELECT, and using the discriminator to distinguish classes
            // Don't select the discriminator column since we don't need it for aggregates
            stmt = new DiscriminatorIteratorStatement(clr,
                new Class[] {type}, subclasses, storeMgr, false).getQueryStatement(candidateAlias);
        }
        else
        {
            // Create a statement using UNIONs between the various possible types and tables
            stmt = new UnionIteratorStatement(clr, candidateClass, subclasses, storeMgr,
                type, m, datastoreClass, false, Boolean.FALSE, true, false).getQueryStatement(candidateAlias);
        }

        if (userCandidates != null)
        {
            /*
             * creates a query like WHERE ... AND (CANDIDATE_ID = ?1 OR CANDIDATE_ID =
             * ?2) or for classes with composite primary keys WHERE ... AND (
             * (CANDIDATE_IDa = ?1a AND CANDIDATE_IDb = ?1b) OR (CANDIDATE_IDa = ?2a
             * AND CANDIDATE_IDb = ?2b) )
             */
            BooleanExpression elementsExpr = null;
            for (Iterator it = userCandidates.iterator(); it.hasNext();)
            {
                Object candidateValue = it.next();
                ScalarExpression expr = m.newScalarExpression(stmt, stmt.getMainTableExpression());
                BooleanExpression keyExpr = expr.eq(m.newLiteral(stmt, candidateValue));
                if (elementsExpr == null)
                {
                    elementsExpr = keyExpr;
                }
                else
                {
                    elementsExpr = elementsExpr.ior(keyExpr);
                }
            }
            if (elementsExpr != null)
            {
                stmt.andCondition(elementsExpr, true);
            }
        }
        return stmt;
    }

    /**
     * Convenience method to set if we know there are only aggregate expressions and so can ignore any
     * checks on the result expressions. This is typically called where we haven't done a full compile
     * of the result just scanning for aggregate keywords, allowing us to generate the QueryStatement correctly.
     * @param flag Whether we just have aggregates
     */
    public void setHasAggregatedExpressionsOnly(boolean flag)
    {
        this.hasAggregatedExpressionsOnly = (flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * @param expressions The expressions to set.
     */
    public void setResultExpressions(ScalarExpression[] expressions)
    {
        this.expressions = expressions;
    }

    /**
     * @return The expressions to project.
     */
    public ScalarExpression[] getResultExpressions()
    {
        return this.expressions;
    }

    /**
     * Whether only aggregated expressions are returned
     * @return Whether it has only aggregate expressions
     */
    public boolean hasAggregatedExpressionsOnly()
    {
        if (hasAggregatedExpressionsOnly != null)
        {
            // Caller has defined if there are only aggregates
            return hasAggregatedExpressionsOnly.booleanValue();
        }

        boolean aggregatedOnly = true;
        if (expressions == null)
        {
            // No expressions set so default to false
            return false;
        }

        // Return based on the expressions we have
        for (int i=0;expressions != null && i<expressions.length;i++)
        {       
            if (!(expressions[i] instanceof AggregateExpression))
            {
                aggregatedOnly = false;
            }
        }
        return aggregatedOnly;
    }
   
    /**
     * Utility to return whether we should use a discriminator query.
     * Checks if the table has a discriminator, and whether we need to query
     * multiple tables.
     * @param table The (base) table
     * @return Whether to use a discriminator query
     */
    private boolean queryUsingDiscriminator(DatastoreClass table)
    {
        if (table.getDiscriminatorMetaData() == null || table.getDiscriminatorMapping(false) == null)
        {
            // No discriminator metadata available
            return false;
        }

        // Check if our iterator will require multiple candidate tables
        if (subclasses)
        {
            Iterator iterator = storeMgr.getSubClassesForClass(candidateClass.getName(), true, clr).iterator();
            while (iterator.hasNext())
            {
                String subCandidateName = (String)iterator.next();
                if (!table.managesClass(subCandidateName))
                {
                    // Multiple tables so cant use discriminator
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Add the ScalarExpression <code>expr1</code> to the QueryExpression <code>stmt</code> and provide
     * the index to the result into <code>stmtExprIndexList</code> 
     * @param stmt the QueryExpression
     * @param stmtExprIndexList the List of StatementExpressionIndex
     * @param expr1 the ScalarExpression
     */
    private void selectNewObjectExpression(QueryExpression stmt,List stmtExprIndexList, ScalarExpression expr1)
    {
        // NewObjectExpression, so provide one for each of its arguments.
        List argExprs = ((NewObjectExpression)expr1).getArgumentExpressions();
        for (int j=0;j<argExprs.size();j++)
        {
            ScalarExpression argExpr = (ScalarExpression)argExprs.get(j);
            selectScalarExpression(stmt, stmtExprIndexList, argExpr);
        }
    }

    /**
     * Add the ScalarExpression <code>expr1</code> to the QueryExpression <code>stmt</code> and provide
     * the index to the result into <code>stmtExprIndexList</code> 
     * @param stmt the QueryExpression
     * @param stmtExprIndexList the List of StatementExpressionIndex
     * @param expr1 the ScalarExpression
     */
    private void selectLiteralExpression(QueryExpression stmt, List stmtExprIndexList, ScalarExpression expr1)
    {
        StatementExpressionIndex stmtExprIndex = new StatementExpressionIndex();
        //when we have literal values, we don't put the literal in the SQL, but we put the identity columns, so it returns the correct
        //we IGNORE the literal values in the query, and use the literal mapping when the result set is returned
        //here, we just make sure that some columns are added to the SELECT by adding the ID mapping
        AbstractClassMetaData cmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(candidateClass, stmt.getClassLoaderResolver());
        if (cmd.getIdentityType() == IdentityType.DATASTORE)
        {
            stmt.select(storeMgr.getDatastoreClass(candidateClass.getName(), clr).getDataStoreObjectIdMapping(), true);
            stmtExprIndex.setMapping(expr1.getMapping());
        }
        else if (cmd.getIdentityType() == IdentityType.APPLICATION)
        {
            int[] prefetchFieldNumbers = new int[cmd.getPKMemberPositions().length];
            for (int j = 0; j < prefetchFieldNumbers.length; ++j)
            {
                prefetchFieldNumbers[j] = cmd.getPKMemberPositions()[j];
                JavaTypeMapping m = storeMgr.getDatastoreClass(candidateClass.getName(), clr).getFieldMapping(
                    cmd.getMetaDataForManagedMemberAtAbsolutePosition(prefetchFieldNumbers[j]));
                if (m != null) // field is not stored in the table, e.g List, Set, etc or is transactional
                {
                    if (m.includeInFetchStatement() && !(m instanceof AbstractContainerMapping))
                    {
                        stmt.select(m, true);
                    }
                }
            }
            stmtExprIndex.setMapping(expr1.getMapping());
        }
        if (expr1.getMapping() instanceof SingleFieldMapping)
        {
            if (expr1 instanceof Literal)
            {
                stmtExprIndex.setMapping(new ResultLiteralJavaTypeMapping(expr1.getMapping(), ((Literal) expr1).getValue()));
            }
        }
        stmtExprIndexList.add(stmtExprIndex);
    }

    /**
     * Add the ScalarExpression <code>expr1</code> to the QueryExpression <code>stmt</code> and provide
     * the index to the result into <code>stmtExprIndexList</code> 
     * @param stmt the QueryExpression
     * @param stmtExprIndexList the List of StatementExpressionIndex
     * @param expr1 the ScalarExpression
     */
    private void selectScalarExpression(QueryExpression stmt, List stmtExprIndexList, ScalarExpression expr1)
    {
        if (expr1 instanceof Literal)
        {
            selectLiteralExpression(stmt, stmtExprIndexList, expr1);
        }
        else if (expr1 instanceof NewObjectExpression)
        {
            selectNewObjectExpression(stmt, stmtExprIndexList, expr1);
        }
        else
        {
            StatementExpressionIndex stmtExprIndex = new StatementExpressionIndex();
            int[] exprIndex = null;

            ScalarExpression[] expr = expr1.getExpressionList().toArray();
            if (expr.length > 0)
            {
                // Expression made up of sub-expressions
                exprIndex = new int[expr.length];
                for (int j=0; j<expr.length; j++)
                {
                    exprIndex[j] = stmt.selectScalarExpression(expr[j], true);
                }
            }
            else
            {
                exprIndex = new int[1];
                exprIndex[0] = stmt.selectScalarExpression(expr1, true);
            }
            stmtExprIndex.setExpressionIndex(exprIndex);
            stmtExprIndex.setMapping(expr1.getMapping());
            if (expr1.getAlias() != null)
            {
                stmtExprIndex.setColumnName(expr1.getAlias());
            }
            stmtExprIndexList.add(stmtExprIndex);
        }
    }
   
    /* (non-Javadoc)
     * @see org.jpox.store.query.Queryable#newResultObjectFactory(org.jpox.store.QueryStatement, boolean)
     */
    public ResultObjectFactory newResultObjectFactory(QueryExpression stmt, boolean ignoreCache, Class resultClass, boolean useFetchPlan)
    {
        List stmtExprIndex = new ArrayList();
        for (int i=0; i<expressions.length; i++)
        {
            if (!(expressions[i] instanceof NewObjectExpression) && expressions[i].getMapping() == null)
            {
                throw new JPOXUserException(LOCALISER.msg("021074", expressions[i]));
            }
            selectScalarExpression(stmt, stmtExprIndex, expressions[i]);
        }

        if (stmt.getNumberOfScalarExpressions()<1)
        {
            //force using at least SELECT 1 FROM XXXX
            JavaTypeMapping mapping = storeMgr.getDatastoreAdapter().getMapping(BigInteger.class, storeMgr);
            stmt.selectScalarExpression(mapping.newLiteral(stmt, BigInteger.ONE));

            // Catch the situation where we have no selected columns, and hence the SELECT has no content
            //throw new JDOUserException("The query has resulted in SQL that has no columns in the SELECT clause. This may be due to a feature not yet supported by JPOX.");
        }
        StatementExpressionIndex[] statementExpressionIndex = (StatementExpressionIndex[]) stmtExprIndex.toArray(new StatementExpressionIndex[stmtExprIndex.size()]);
        // Provide a ResultObjectFactory to convert to the required type
        if (resultClass != null)
        {
            return new ResultClassROF(resultClass, statementExpressionIndex, expressions);
        }
        else if (expressions.length == 1)
        {
            return new ResultClassROF(Object.class, statementExpressionIndex, expressions);
        }
        else
        {
            return new ResultClassROF(Object[].class, statementExpressionIndex, expressions);
        }
    }

    /**
     * Returns <tt>true</tt> if this collection contains no elements.<p>
     * @return <tt>true</tt> if this collection contains no elements.
     */
    public boolean isEmpty()
    {
        if (userCandidates != null)
        {
            return userCandidates.isEmpty();           
        }
        return false;
    }
   
    /**
     * Literals defined in setResult are not going to the query. When we retrieve
     * the query results we already have the literal value in this mapping, which is invoked when
     * the user requests the value for the literal.
     *
     * This allows putting any literal value in setResult
     */
    class ResultLiteralJavaTypeMapping extends JavaTypeMapping
    {
        final JavaTypeMapping mapping;
       
        final Object literal;

        public ResultLiteralJavaTypeMapping(JavaTypeMapping mapping, Object literal)
        {
            super(null, mapping.getType(), mapping.getFieldMetaData(), null);
            this.mapping = mapping;
            this.literal = literal;
        }
       
        public DatastoreContainerObject getDatastoreContainer()
        {
            return mapping.getDatastoreContainer();
        }

        public Class getJavaType()
        {
            return mapping.getJavaType();
        }

        public Object getSampleValue(ClassLoaderResolver clr)
        {
            return mapping.getSampleValue(clr);
        }

        public boolean includeInFetchStatement()
        {
            return mapping.includeInFetchStatement();
        }

        public boolean includeInUpdateStatement()
        {
            return mapping.includeInUpdateStatement();
        }

        public boolean includeInInsertStatement()
        {
            return mapping.includeInInsertStatement();
        }

        public ScalarExpression newLiteral(QueryExpression qs, Object value)
        {
            return mapping.newLiteral(qs, value);
        }

        public ScalarExpression newScalarExpression(QueryExpression qs, LogicSetExpression te)
        {
            return mapping.newScalarExpression(qs, te);
        }

        public boolean getBoolean(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Boolean)literal).booleanValue();
        }
       
        public byte getByte(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Byte)literal).byteValue();
        }
       
        public char getChar(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Character)literal).charValue();
        }
       
        public double getDouble(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Number)literal).doubleValue();
        }
       
        public float getFloat(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Number)literal).floatValue();
        }
       
        public int getInt(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Number)literal).intValue();
        }
       
        public long getLong(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Number)literal).longValue();
        }
       
        public Object getObject(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return literal;
        }
       
        public Object getObject(ObjectManager om, Object resultSet, int[] exprIndex, StateManager ownerSM, int ownerFieldNumber)
        {
            return literal;
        }
       
        public short getShort(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return ((Number)literal).shortValue();
        }
       
        public String getString(ObjectManager om, Object resultSet, int[] exprIndex)
        {
            return (String) literal;
        }
    }
}
TOP

Related Classes of org.jpox.store.rdbms.query.ResultExpressionsQueryable$ResultLiteralJavaTypeMapping

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.