Package org.jpox.store.mapped.expression

Source Code of org.jpox.store.mapped.expression.CollectionExpression

/**********************************************************************
Copyright (c) 2002 Mike Martin (TJDO) 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:
2003 Andy Jefferson - coding standards
2003 Andy Jefferson - renamed from CandidateSetExpression
    ...
**********************************************************************/
package org.jpox.store.mapped.expression;

import org.jpox.ClassLoaderResolver;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.IdentifierFactory;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.query.StatementText;
import org.jpox.store.mapped.scostore.CollectionStoreQueryable;
import org.jpox.store.scostore.CollectionStore;

/**
* An expression that represents some collection field in a query candidate class,
* or a collection field in an object linked from the candidate class by navigation.
* <p>
* When navigated through using contains(expr), the elements of the collection
* are relationally joined onto the query statement.
* </p>
* <p>
* As this is a Collection Expression it works equally for Sets and Lists, and we use CollectionStore
* as the backing store interface, so that ListStore and SetStore are equally applicable.
*
* @version $Revision: 1.33 $
*/
public class CollectionExpression extends ScalarExpression
{
    private final CollectionStore collStore;
    private final String fieldName;

    /**
     * Constructor.
     * @param qs The Query Statement
     * @param ownerMapping The mapping to the owner of this collection
     * @param te The Table Expression
     * @param collStore the backing store.
     * @param fieldName Name of the field for the collection.
     **/
    public CollectionExpression(QueryExpression qs,
                                JavaTypeMapping ownerMapping,
                                LogicSetExpression te,
                                CollectionStore collStore,
                                String fieldName)
    {
        super(qs);

        this.mapping = ownerMapping;
        this.collStore = collStore;
        this.fieldName = fieldName;
        this.te = te;
    }

    /**
     * Executed when the size() method is found in a query filter.
     * @return  The NumericExpression resulting from the size() method.
     */
    public NumericExpression sizeMethod()
    {
        IdentifierFactory idFactory = qs.getStoreManager().getIdentifierFactory();
        String ctIdentifier = idFactory.newIdentifier(te.getAlias(), fieldName).getIdentifier();
        DatastoreIdentifier ctRangeVar = idFactory.newIdentifier(IdentifierFactory.TABLE, ctIdentifier);

        return new ContainerSizeExpression(qs, getBackingStoreQueryable().getSizeSubquery(qs, mapping, te, ctRangeVar));
    }

    /**
     * Executed when the contains() method is found in a query filter.
     * @param expr The ScalarExpression passed as a parameter to contains().
     * @return The BooleanExpression resulting from the contains() method.
     */
    public BooleanExpression containsMethod(ScalarExpression expr)
    {
        IdentifierFactory idFactory = qs.getStoreManager().getIdentifierFactory();
        ClassLoaderResolver clr = qs.getClassLoaderResolver();
        if (expr instanceof NullLiteral)
        {
            // JPOX doesn't currently support querying for nulls in Collections so just return "1 = 0"
            // TODO Add support for querying for nulls in collections
            return new BooleanLiteral(qs, mapping, false).eq(new BooleanLiteral(qs, mapping, true));
        }
        else if (expr instanceof UnboundVariable)
        {
            UnboundVariable var = (UnboundVariable)expr;
            if (var.getVariableType() == null)
            {
                // Set the variable type to be the element type for this collection
                // implicit variable type. We now set the type to the collection type
                var.setVariableType(clr.classForName(collStore.getElementType()));
            }

            // Get the exists query of the collection table
            String existsTableId = idFactory.newIdentifier(
                idFactory.newIdentifier(te.getAlias(), fieldName), var.getVariableName()).getIdentifier();
            DatastoreIdentifier existsTableAlias = idFactory.newIdentifier(IdentifierFactory.TABLE, existsTableId);
            QueryExpression qexpr = getBackingStoreQueryable().getExistsSubquery(qs, mapping, te, existsTableAlias);

            // Join from the collection table to the element table
            DatastoreIdentifier elementTableAlias = null;
            if (expr.te == null)
            {
                String elementTableId = "UNBOUND" + '.' + var.getVariableName();
                elementTableAlias = idFactory.newIdentifier(IdentifierFactory.TABLE, elementTableId);
            }
            else
            {
                elementTableAlias = expr.te.getAlias();
            }
            ScalarExpression joinExpr = getBackingStoreQueryable().joinElementsTo(qexpr, qs, mapping, te,
                existsTableAlias, var.getVariableType(), expr, elementTableAlias, true);

            var.bindTo(joinExpr);

            //START see JDOQLContainerTest.testContainsResultVariable
            LogicSetExpression elementTblExpr = qs.getTableExpression(elementTableAlias);
            if (qs.hasCrossJoin(elementTblExpr))
            {
                // Perhaps some description about what this is supposed to be doing and WHY ????
                qexpr.andCondition(joinExpr.eq(expr.mapping.newScalarExpression(qs, elementTblExpr)));
            }
            //END see JDOQLContainerTest.

            return new ExistsExpression(qs, qexpr, true);
        }
        else
        {
            // "contains(Literal)", "contains(Expression)"
            String existsTableId = idFactory.newIdentifier(te.getAlias(), fieldName).getIdentifier();
            DatastoreIdentifier existsTableAlias = idFactory.newIdentifier(IdentifierFactory.TABLE, existsTableId);

            DatastoreIdentifier elementTableAlias;
            if (expr.te == null) // literals
            {
                int n = 0;
                do
                {
                    String elementTableId = existsTableId + '.' + (++n);
                    elementTableAlias = idFactory.newIdentifier(IdentifierFactory.TABLE, elementTableId);
                } while (qs.getTableExpression(elementTableAlias) != null);
            }
            else // expressions
            {
                elementTableAlias = expr.te.getAlias();
            }

            if (expr instanceof Literal)
            {
                // EXISTS (SELECT 1 FROM COLLECTION_TBL WHERE ...)
                QueryExpression qexpr = getBackingStoreQueryable().getExistsSubquery(qs, mapping, te, existsTableAlias);
                ScalarExpression joinExpr = getBackingStoreQueryable().joinElementsTo(qexpr, qs, mapping, te,
                    existsTableAlias, clr.classForName(expr.getMapping().getType()), expr, elementTableAlias, true);

                // TODO This sometimes adds TBL1.COL1 = TBL1.COL1 - check that the equals() removes such things
                if (!expr.equals(joinExpr))
                {
                    // Join to literal value (?)
                    qexpr.andCondition(expr.eq(joinExpr));
                }

                return new ExistsExpression(qs, qexpr, true);
            }
            else
            {
                boolean existsAlways = false;
                Object ext = expr.qs.getValueForExtension("org.jpox.rdbms.query.containsUsesExistsAlways");
                if (ext != null && ((String)ext).equals("true"))
                {
                    existsAlways = true;
                }

                if (existsAlways)
                {
                    // EXISTS (SELECT 1 FROM COLLECTION_TBL WHERE ...)
                    QueryExpression qexpr = getBackingStoreQueryable().getExistsSubquery(qs, mapping, te, existsTableAlias);
                    ScalarExpression joinExpr = getBackingStoreQueryable().joinElementsTo(qexpr, qs, mapping, te,
                        existsTableAlias, clr.classForName(expr.getMapping().getType()), expr, elementTableAlias, true);

                    // TODO This sometimes adds TBL1.COL1 = TBL1.COL1 - check that the equals() removes such things
                    if (!expr.equals(joinExpr))
                    {
                        // Join to literal value (?)
                        qexpr.andCondition(expr.eq(joinExpr));
                    }

                    return new ExistsExpression(qs, qexpr, true);
                   
                }
                else
                {
                    // Join to element
                    // TODO This is WRONG - see RDBMS-94. All contains() should do "EXISTS (SELECT ... FROM ...)"
                    // The problem is that when using UnboundVariables that are referenced in other legs of SQL
                    // we need to do cross joins up at the parent query, and no mechanism is readily available yet
                    ScalarExpression joinExpr = getBackingStoreQueryable().joinElementsTo(
                        expr.getQueryExpression(), qs, mapping, te,
                        existsTableAlias, clr.classForName(collStore.getElementType()), expr, elementTableAlias, false);

                    return joinExpr.eq(expr);
                }
            }
        }
    }

    /**
     * Return the BooleanExpression for a query filter in the form "collection.isEmpty()".
     * @return The BooleanExpression for a query filter in the form "collection.isEmpty()".
     */
    public BooleanExpression isEmptyMethod()
    {
        IdentifierFactory idFactory = qs.getStoreManager().getIdentifierFactory();
        String existsTableId = idFactory.newIdentifier(te.getAlias(), fieldName).getIdentifier();
        DatastoreIdentifier existsTableAlias = idFactory.newIdentifier(IdentifierFactory.TABLE, existsTableId);
        QueryExpression qexpr = getBackingStoreQueryable().getExistsSubquery(qs, mapping, te, existsTableAlias);

        return new ExistsExpression(qs, qexpr, false);
    }

    /**
     * Method to return the expression for comparing a collection with a value.
     * JPOX only supports comparisons with null currently.
     * @param expr The value to compare with.
     * @return The expression of equality
     */
    public BooleanExpression eq(ScalarExpression expr)
    {
        if (expr instanceof NullLiteral)
        {
            return isEmptyMethod();
        }
        else
        {
            throw new JPOXUserException(LOCALISER.msg("037004"));
        }
    }

    /**
     * Method to return the statement text.
     * @param mode (0=PROJECTION;1=FILTER)
     * @return The statement
     * @throws JPOXUserException since this object is inaccessible directly.
     **/
    public StatementText toStatementText(int mode)
    {
        throw new JPOXUserException("Cannot reference collection object directly: field name = " + fieldName);
    }

    private CollectionStoreQueryable getBackingStoreQueryable()
    {
        return (CollectionStoreQueryable)collStore;
    }
}
TOP

Related Classes of org.jpox.store.mapped.expression.CollectionExpression

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.