FileDocCategorySizeDatePackage
Constraint.javaAPI DocGlassfish v2 API12013Fri May 04 22:35:14 BST 2007com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint

Constraint.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * Constraint.java
 *
 * Created on March 3, 2000
 *
 */

package com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint;

import com.sun.jdo.spi.persistence.support.sqlstore.ActionDesc;
import com.sun.jdo.spi.persistence.support.sqlstore.model.LocalFieldDesc;
import com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.QueryPlan;

import java.util.ArrayList;
import java.util.List;


/**
 */
public class Constraint extends Object {
    /**
     * The stack that contains all constraints beside join constraints.
     * Join constraints are handled by a separate stack.
     */
    public List stack;

    /**
     * The stack that contains outer join constraints. All the elements of this stack
     * are instance of {@link ConstraintJoin}. Outer join constraints can be appended
     * to the query w/o changing the semantic.
     */
    private List outerJoinStack;

    /**
     * The stack that contains order by constraints. We would like to
     * separate Order By constraints from the main stack, but they must
     * be merged with all other constraints to preserve the order of
     * constraints from different stacks. E.g.
     * "order by firstname ascending, department.name ascending"
     *
     * @see #isEmptyOrOrderBy
     */
//    private List orderByStack;

    /**
     * Adds a field to the constraint stack.
     *
     * AddField creates a ConstraintField node for the indicated
     * named field, optionally including an operation descriptor
     * and adds it to the constraint stack.
     *
     * @param name
     * 	The name parameter specifies the name of the field to be
     * 	added to the constrant stack.
     *
     * @param desc
     * 	The desc parameter specifies an operation descriptor describing
     * 	what is to be done with the field named by the name parameter.
     */
    public void addField(String name, ActionDesc desc) {
        stack.add(new ConstraintFieldName(name, desc));
    }

    /**
     * Adds a field to the constraint stack.
     *
     * AddField creates a ConstraintFieldDesc node for the indicated
     * field descriptor and adds it to the constraint stack.
     *
     * @param desc
     * 	The Desc parameter is the field descriptor to be
     * 	added to the constrant stack.
     */
    public void addField(LocalFieldDesc desc) {
        stack.add(new ConstraintFieldDesc(desc));
    }

    /**
     * Adds a field to the constraint stack.
     *
     * AddField creates a ConstraintFieldDesc node for the indicated
     * field descriptor and adds it to the constraint stack.
     *
     * @param desc
     *     The Desc parameter is the field descriptor to be
     *     added to the constrant stack.
     *
     * @param plan
     *     The query plan to which this desc belongs
     */
    public void addField(LocalFieldDesc desc, QueryPlan plan) {
        stack.add(new ConstraintFieldDesc(desc, plan));
    }

    public void addField(ConstraintFieldDesc constraintDesc) {
        stack.add(constraintDesc);
    }

    public void addForeignField(String name, ActionDesc desc) {
        stack.add(new ConstraintForeignFieldName(name, desc));
    }

    /**
     * Adds an operation to the constraint stack.
     *
     * AddOperation creates a ConstraintOperation node whose operation is
     * operation and adds it to the constraint stack.
     *
     * @param operation
     * 	The operation parameter specifies the operation to be added to
     * 	the constrant stack.
     */
    public void addOperation(int operation) {
        stack.add(new ConstraintOperation(operation));
    }

    /**
     * Adds a data value to the constraint stack. Creates a ConstraintValue
     * node whose value is value and adds it to the constraint stack.
     * @param value The value to be added to the constrant stack.
     * @param localField The localField to which this value is bound.
     * Please note that localField can be null for values involved in
     * complex expressions in a query.
     */
    public void addValue(Object value, LocalFieldDesc localField) {
        stack.add(new ConstraintValue(value, localField));
    }

    /**
     * Adds a subquery constraint on the stack.
     * @param field The field on which subquery constraint is added.
     * @param rd    Retrieve descriptor corresponding to the subquery.
     */
    public void addConstraintFieldSubQuery(String field, ActionDesc rd)  {
        stack.add(new ConstraintFieldNameSubQuery(field, rd));
    }

    /**
     * Adds the index of a parameter to the stack.
     * @param index the parameter index.
     * @param enumType the type for this parameter.
     * @param localField the localField to which this parameter is bound.
     */
    public void addParamIndex(int index, int enumType,
                                    LocalFieldDesc localField) {
        stack.add(new ConstraintParamIndex(index,enumType, localField));
    }

    /**
     * Adds specified join constraint. Equi joins are added to the
     * constraint stack, outer joins are added to a separate stack, the
     * <code>outerJoinStack</code>.
     *
     * @param join The join constraint to be added.
     */
    public void addJoinConstraint(ConstraintJoin join) {
        if (join.operation == ActionDesc.OP_EQUIJOIN) {
            stack.add(join);
        } else {
            // The current logic is written with the assumption that a join
            // that is not an equi join is always a left join
            assert join.operation == ActionDesc.OP_LEFTJOIN;
            outerJoinStack.add(join);
        }
    }

    /**
     * Merges the stack with the specified foreign constraint stack.
     * @param foreignConstraint The constraint to be merged.
     * @param joinOp Join operation as defined in {@link ActionDesc}.
     * @return True, if we need to add an additional "AND" constraint.
     */
    public boolean mergeConstraint(Constraint foreignConstraint, int joinOp) {

        stack.addAll(foreignConstraint.stack);
        outerJoinStack.addAll(foreignConstraint.outerJoinStack);

        return addAnd(foreignConstraint, joinOp);
    }

    /**
     * Decides, if we need to add an additional "AND" constraint to
     * the stack <em>after</em> merging the foreign stack.
     *
     * @param foreignConstraint Constraint to be joined.
     * @param joinOp Join operator.
     * @return True, if we need to add an additional "AND" constraint.
     */
    private boolean addAnd(Constraint foreignConstraint, int joinOp) {
        // Add "AND" constraints for equi-joins only.
        // * Don't add an "AND" constraint for outer joins. Outer joins
        // don't contribute to the "where"-clause in ANSI-case. In the
        // non-ANSI case, outer joins can be appended to the query w/o
        // changing the semantic.
        // * Don't add an "AND" constaint for non-relationship joins,
        // as no additional join constraint is added in this case.
        return (joinOp == ActionDesc.OP_EQUIJOIN)

        // Never add an "AND" constraint, if the foreign stack is
        // empty or contains "ORDER_BY" constraints only, as "ORDER_BY"
        // constraints don't contribute to the "where"-clause.
        && !foreignConstraint.isEmptyOrOrderBy();
    }

    /**
     * Checks, if the constraint stack is empty or contains "ORDER_BY"
     * constraints only. "ORDER_BY" constraints are recognized as an
     * ORDER_BY operation followed by a field name constraint, and
     * an optional Value constraint giving the position of the Order
     * By constraint.
     *  <em>NOTE:</em> The value constraints giving the position for
     * the order by constraints are currently not generated by the
     * query compiler. Order by constraints stay in the correct order
     * because of two reasons
     * <ul>
     * <li>the way, constraints are processed by the query compiler</li>
     * <li>the way constraint stacks are joined in
     * {@link com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.SelectQueryPlan#processForeignConstraints}.
     * </li>
     * </ul>
     */
    private boolean isEmptyOrOrderBy() {
        boolean rc = true;
        // Abort the loop at first possible opportunity.
        for (int i = stack.size() - 1; i >= 0 && rc; ) {
            ConstraintNode node = (ConstraintNode) stack.get(i);

            if ((node instanceof ConstraintOperation)
                    && ((((ConstraintOperation) node).operation == ActionDesc.OP_ORDERBY) ||
                    (((ConstraintOperation) node).operation == ActionDesc.OP_ORDERBY_DESC))) {
                if ((i > 0) && (
                        stack.get(i - 1) instanceof ConstraintFieldName ||
                        stack.get(i - 1) instanceof ConstraintFieldDesc)) {
                    // Order By constraint.
                    i--;
                    if ((i > 0) && (stack.get(i - 1) instanceof ConstraintValue)) {
                        // Optional Value constraint.
                        i--;
                    }
                } else {
                    rc = false;
                }
            } else {
                rc = false;
            }

            // Check the next constraint if any.
            i--;
        }
        return rc;
    }

    /**
      * Gets the where clause constraints for this Constraint
      * @return The where clause constraints for this Constraint
      */
     public List getConstraints() {
         return stack;
     }

    /**
      * Gets the outer join constraints for this Constraint
      * @return The outer join constraints for this Constraint
      */
     public List getOuterJoinConstraints() {
         return outerJoinStack;
     }

    public Constraint() {
        stack = new ArrayList();
        outerJoinStack = new ArrayList();
    }

}