UnaryPredicateExpr.java :  » Content-Management-System » daisy » org » outerj » daisy » query » model » Java Open Source

Java Open Source » Content Management System » daisy 
daisy » org » outerj » daisy » query » model » UnaryPredicateExpr.java
/*
 * Copyright 2004 Outerthought bvba and Schaubroeck nv
 *
 * 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.
 */
package org.outerj.daisy.query.model;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.ArrayList;

import org.outerj.daisy.query.EvaluationInfo;
import org.outerj.daisy.query.QueryContext;
import org.outerj.daisy.repository.HierarchyPath;
import org.outerj.daisy.repository.query.QueryException;

public abstract class UnaryPredicateExpr extends AbstractPredicateExpr {
    protected final ValueExpr valueExpr1;
    protected final ValueExpr valueExpr2;

    public UnaryPredicateExpr(ValueExpr valueExpr1, ValueExpr valueExpr2) {
        this.valueExpr1 = valueExpr1;
        this.valueExpr2 = valueExpr2;
    }

    public void prepare(QueryContext context) throws QueryException {
        valueExpr1.prepare(context);
        valueExpr2.prepare(context);

        if (valueExpr1.isSymbolicIdentifier() && valueExpr2.isSymbolicIdentifier())
            throw new QueryException("Two symbolic identifiers cannot be compared with each other: \"" + valueExpr1.getExpression() + "\" and \"" + valueExpr2.getExpression() + "\".");

        if ( (valueExpr1.isSymbolicIdentifier() && (valueExpr2.getValueType() == null || valueExpr2.getValueType() == QValueType.STRING))
                || (valueExpr2.isSymbolicIdentifier() && (valueExpr1.getValueType() == null || valueExpr1.getValueType() == QValueType.STRING))) {
            // then it is OK
        } else if (!valueExpr1.getValueType().isCompatible(valueExpr2.getValueType())) {
            throw new QueryException("Cannot use a comparison operator on two different types of data: expression \""  + valueExpr1.getExpression() + "\" is of type " + valueExpr1.getValueType() + " and expression \"" + valueExpr2.getExpression() + "\" is of type " + valueExpr2.getValueType());
        }

        if ( (valueExpr1.getValueType() == QValueType.BOOLEAN || valueExpr2.getValueType() == QValueType.BOOLEAN
              || !ValueExprUtil.isPrimitiveValue(valueExpr1) || !ValueExprUtil.isPrimitiveValue(valueExpr2) )
             && !makesSenseForNonOrderedValues()) {
            throw new QueryException("The operator \"" + getOperatorSqlSymbol() + "\" is used between expressions that have no order-relation: \"" + valueExpr1.getExpression() + "\" and \"" + valueExpr2.getExpression() + "\".");
        }
    }

    protected boolean makesSenseForNonOrderedValues() {
        return false;
    }

    public void generateSql(StringBuilder sql, SqlGenerationContext context) throws QueryException {
        sql.append(" (");

        String valueExpr1PreCond = valueExpr2.isSymbolicIdentifier() ? null : valueExpr1.getSqlPreConditions(context);
        String valueExpr2PreCond = valueExpr1.isSymbolicIdentifier() ? null : valueExpr2.getSqlPreConditions(context);

        if (valueExpr1PreCond != null)
            sql.append(' ').append(valueExpr1PreCond);
        if (valueExpr2PreCond != null) {
            if (valueExpr1PreCond != null)
                sql.append(" and ");
            sql.append(' ').append(valueExpr2PreCond);
        }

        if (valueExpr1PreCond != null || valueExpr2PreCond != null)
            sql.append(" and ");

        if (valueExpr2.isSymbolicIdentifier())
            sql.append(" ? ");
        else
            valueExpr1.generateSqlValueExpr(sql, context);

        sql.append(getOperatorSqlSymbol());

        if (valueExpr1.isSymbolicIdentifier())
            sql.append(" ? ");
        else
            valueExpr2.generateSqlValueExpr(sql, context);

        sql.append(")");
    }

    protected abstract String getOperatorSqlSymbol();

    public int bindSql(PreparedStatement stmt, int bindPos, EvaluationInfo evaluationInfo) throws SQLException, QueryException {
        bindPos = valueExpr2.isSymbolicIdentifier() ? bindPos : valueExpr1.bindPreConditions(stmt, bindPos, evaluationInfo);
        bindPos = valueExpr1.isSymbolicIdentifier() ? bindPos : valueExpr2.bindPreConditions(stmt, bindPos, evaluationInfo);

        QValueType valueType = determineValueType();

        if (valueExpr1.isSymbolicIdentifier() && valueExpr2.isSymbolicIdentifier()) {
            // this should never occur as it is check in the prepare()
            throw new QueryException("Assertion error: cannot compare two symbolic identifiers: \"" + valueExpr1.getExpression() + "\" and \"" + valueExpr2.getExpression() + "\".");
        } else if (valueExpr1.isSymbolicIdentifier()) {
            bindPos = valueExpr1.bindValueExpr(stmt, bindPos, valueType, evaluationInfo);
            bindPos = Literal.bindLiteral(stmt, bindPos, valueType, valueExpr1.translateSymbolic(valueExpr2, evaluationInfo), evaluationInfo.getQueryContext());
        } else if (valueExpr2.isSymbolicIdentifier()) {
            bindPos = Literal.bindLiteral(stmt, bindPos, valueType, valueExpr2.translateSymbolic(valueExpr1, evaluationInfo), evaluationInfo.getQueryContext());
            bindPos = valueExpr2.bindValueExpr(stmt, bindPos, valueType, evaluationInfo);
        } else {
            bindPos = valueExpr1.bindValueExpr(stmt, bindPos, valueType, evaluationInfo);
            bindPos = valueExpr2.bindValueExpr(stmt, bindPos, valueType, evaluationInfo);
        }

        return bindPos;
    }

    private QValueType determineValueType() throws QueryException {
        QValueType valueType;
        if (valueExpr1.getValueType() == QValueType.UNDEFINED && valueExpr2.getValueType() == QValueType.UNDEFINED) {
            // this is normally only the cases when comparing literals, which is little meaningful
            // We could throw an exception, or let it just be handled as strings.
            valueType = QValueType.STRING;
        } else if (valueExpr1.getValueType() == QValueType.UNDEFINED || valueExpr2.isSymbolicIdentifier()) {
            valueType = valueExpr2.getValueType();
        } else if (valueExpr2.getValueType() == QValueType.UNDEFINED || valueExpr1.isSymbolicIdentifier()) {
            valueType = valueExpr1.getValueType();
        } else {
            // value types should be compatible here, since checked in prepare()
            if (!valueExpr1.getValueType().isCompatible(valueExpr2.getValueType()))
                throw new QueryException("Assertion error: value types not compatible.");
            valueType = valueExpr1.getValueType();
        }
        return valueType;
    }

    public AclConditionViolation isAclAllowed() {
        AclConditionViolation violation = valueExpr1.isAclAllowed();
        if (violation != null)
            return violation;
        else
            return valueExpr2.isAclAllowed();
    }

    private boolean evaluateInt(Object value1, Object value2) {
        // behaviour for multivalue and/or hierarchical fields is: if there's one value that satisfies the condition, the result is true
        List values1 = expandMultiValue(value1);
        List values2 = expandMultiValue(value2);
        for (Object aValues1 : values1) {
            for (Object aValues2 : values2) {
                if (evaluate(aValues1, aValues2))
                    return true;
            }
        }
        return false;
    }

    private List<Object> expandMultiValue(Object value) {
        // optimalisation for primitive values
        if (!(value instanceof Object[] || value instanceof HierarchyPath)) {
            List<Object> values = new ArrayList<Object>(1);
            values.add(value);
            return values;
        }

        List<Object> values = new ArrayList<Object>();
        if (value instanceof Object[]) {
            for (Object aValue : (Object[])value)
                expandHierarchyValue(aValue, values);
        } else {
            expandHierarchyValue(value, values);
        }
        return values;
    }

    private void expandHierarchyValue(Object value, List<Object> values) {
        if (value instanceof HierarchyPath) {
            Object[] elements = ((HierarchyPath)value).getElements();
            for (Object element : elements)
                values.add(element);
        } else {
            values.add(value);
        }
    }

    protected abstract boolean evaluate(Object value1, Object value2);

    public boolean evaluate(ExprDocData data, EvaluationInfo evaluationInfo) throws QueryException {
        QValueType valueType = determineValueType();
        Object value1, value2;
        if (valueExpr1.isSymbolicIdentifier() && valueExpr2.isSymbolicIdentifier()) {
            // this should never occur as it is check in the prepare()
            throw new QueryException("Assertion error: cannot compare two symbolic identifiers: \"" + valueExpr1.getExpression() + "\" and \"" + valueExpr2.getExpression() + "\".");
        } else if (valueExpr1.isSymbolicIdentifier()) {
            value1 = valueExpr1.evaluate(valueType, data, evaluationInfo);
            value2 = valueExpr1.translateSymbolic(valueExpr2, evaluationInfo);
        } else if (valueExpr2.isSymbolicIdentifier()) {
            value1 = valueExpr2.translateSymbolic(valueExpr1, evaluationInfo);
            value2 = valueExpr2.evaluate(valueType, data, evaluationInfo);
        } else {
            value1 = valueExpr1.evaluate(valueType, data, evaluationInfo);
            value2 = valueExpr2.evaluate(valueType, data, evaluationInfo);
        }

        if (value1 == null || value2 == null)
            return false;

        return evaluateInt(value1, value2);
    }

    public Tristate appliesTo(ExprDocData data, EvaluationInfo evaluationInfo) throws QueryException {
        if (valueExpr1.canTestAppliesTo() && valueExpr2.canTestAppliesTo()) {
            return evaluate(data, evaluationInfo) ? Tristate.YES : Tristate.NO;
        } else {
            return Tristate.MAYBE;
        }
    }

    public void collectAccessRestrictions(AccessRestrictions restrictions) {
        valueExpr1.collectAccessRestrictions(restrictions);
        valueExpr2.collectAccessRestrictions(restrictions);
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.