001: /*
002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.outerj.daisy.query.model;
017:
018: import java.sql.PreparedStatement;
019: import java.sql.SQLException;
020: import java.util.List;
021: import java.util.ArrayList;
022:
023: import org.outerj.daisy.query.EvaluationInfo;
024: import org.outerj.daisy.query.QueryContext;
025: import org.outerj.daisy.repository.HierarchyPath;
026: import org.outerj.daisy.repository.query.QueryException;
027:
028: public abstract class UnaryPredicateExpr extends AbstractPredicateExpr {
029: protected final ValueExpr valueExpr1;
030: protected final ValueExpr valueExpr2;
031:
032: public UnaryPredicateExpr(ValueExpr valueExpr1, ValueExpr valueExpr2) {
033: this .valueExpr1 = valueExpr1;
034: this .valueExpr2 = valueExpr2;
035: }
036:
037: public void prepare(QueryContext context) throws QueryException {
038: valueExpr1.prepare(context);
039: valueExpr2.prepare(context);
040:
041: if (valueExpr1.isSymbolicIdentifier()
042: && valueExpr2.isSymbolicIdentifier())
043: throw new QueryException(
044: "Two symbolic identifiers cannot be compared with each other: \""
045: + valueExpr1.getExpression() + "\" and \""
046: + valueExpr2.getExpression() + "\".");
047:
048: if ((valueExpr1.isSymbolicIdentifier() && (valueExpr2
049: .getValueType() == null || valueExpr2.getValueType() == QValueType.STRING))
050: || (valueExpr2.isSymbolicIdentifier() && (valueExpr1
051: .getValueType() == null || valueExpr1
052: .getValueType() == QValueType.STRING))) {
053: // then it is OK
054: } else if (!valueExpr1.getValueType().isCompatible(
055: valueExpr2.getValueType())) {
056: throw new QueryException(
057: "Cannot use a comparison operator on two different types of data: expression \""
058: + valueExpr1.getExpression()
059: + "\" is of type "
060: + valueExpr1.getValueType()
061: + " and expression \""
062: + valueExpr2.getExpression()
063: + "\" is of type "
064: + valueExpr2.getValueType());
065: }
066:
067: if ((valueExpr1.getValueType() == QValueType.BOOLEAN
068: || valueExpr2.getValueType() == QValueType.BOOLEAN
069: || !ValueExprUtil.isPrimitiveValue(valueExpr1) || !ValueExprUtil
070: .isPrimitiveValue(valueExpr2))
071: && !makesSenseForNonOrderedValues()) {
072: throw new QueryException(
073: "The operator \""
074: + getOperatorSqlSymbol()
075: + "\" is used between expressions that have no order-relation: \""
076: + valueExpr1.getExpression() + "\" and \""
077: + valueExpr2.getExpression() + "\".");
078: }
079: }
080:
081: protected boolean makesSenseForNonOrderedValues() {
082: return false;
083: }
084:
085: public void generateSql(StringBuilder sql,
086: SqlGenerationContext context) throws QueryException {
087: sql.append(" (");
088:
089: String valueExpr1PreCond = valueExpr2.isSymbolicIdentifier() ? null
090: : valueExpr1.getSqlPreConditions(context);
091: String valueExpr2PreCond = valueExpr1.isSymbolicIdentifier() ? null
092: : valueExpr2.getSqlPreConditions(context);
093:
094: if (valueExpr1PreCond != null)
095: sql.append(' ').append(valueExpr1PreCond);
096: if (valueExpr2PreCond != null) {
097: if (valueExpr1PreCond != null)
098: sql.append(" and ");
099: sql.append(' ').append(valueExpr2PreCond);
100: }
101:
102: if (valueExpr1PreCond != null || valueExpr2PreCond != null)
103: sql.append(" and ");
104:
105: if (valueExpr2.isSymbolicIdentifier())
106: sql.append(" ? ");
107: else
108: valueExpr1.generateSqlValueExpr(sql, context);
109:
110: sql.append(getOperatorSqlSymbol());
111:
112: if (valueExpr1.isSymbolicIdentifier())
113: sql.append(" ? ");
114: else
115: valueExpr2.generateSqlValueExpr(sql, context);
116:
117: sql.append(")");
118: }
119:
120: protected abstract String getOperatorSqlSymbol();
121:
122: public int bindSql(PreparedStatement stmt, int bindPos,
123: EvaluationInfo evaluationInfo) throws SQLException,
124: QueryException {
125: bindPos = valueExpr2.isSymbolicIdentifier() ? bindPos
126: : valueExpr1.bindPreConditions(stmt, bindPos,
127: evaluationInfo);
128: bindPos = valueExpr1.isSymbolicIdentifier() ? bindPos
129: : valueExpr2.bindPreConditions(stmt, bindPos,
130: evaluationInfo);
131:
132: QValueType valueType = determineValueType();
133:
134: if (valueExpr1.isSymbolicIdentifier()
135: && valueExpr2.isSymbolicIdentifier()) {
136: // this should never occur as it is check in the prepare()
137: throw new QueryException(
138: "Assertion error: cannot compare two symbolic identifiers: \""
139: + valueExpr1.getExpression() + "\" and \""
140: + valueExpr2.getExpression() + "\".");
141: } else if (valueExpr1.isSymbolicIdentifier()) {
142: bindPos = valueExpr1.bindValueExpr(stmt, bindPos,
143: valueType, evaluationInfo);
144: bindPos = Literal.bindLiteral(stmt, bindPos, valueType,
145: valueExpr1.translateSymbolic(valueExpr2,
146: evaluationInfo), evaluationInfo
147: .getQueryContext());
148: } else if (valueExpr2.isSymbolicIdentifier()) {
149: bindPos = Literal.bindLiteral(stmt, bindPos, valueType,
150: valueExpr2.translateSymbolic(valueExpr1,
151: evaluationInfo), evaluationInfo
152: .getQueryContext());
153: bindPos = valueExpr2.bindValueExpr(stmt, bindPos,
154: valueType, evaluationInfo);
155: } else {
156: bindPos = valueExpr1.bindValueExpr(stmt, bindPos,
157: valueType, evaluationInfo);
158: bindPos = valueExpr2.bindValueExpr(stmt, bindPos,
159: valueType, evaluationInfo);
160: }
161:
162: return bindPos;
163: }
164:
165: private QValueType determineValueType() throws QueryException {
166: QValueType valueType;
167: if (valueExpr1.getValueType() == QValueType.UNDEFINED
168: && valueExpr2.getValueType() == QValueType.UNDEFINED) {
169: // this is normally only the cases when comparing literals, which is little meaningful
170: // We could throw an exception, or let it just be handled as strings.
171: valueType = QValueType.STRING;
172: } else if (valueExpr1.getValueType() == QValueType.UNDEFINED
173: || valueExpr2.isSymbolicIdentifier()) {
174: valueType = valueExpr2.getValueType();
175: } else if (valueExpr2.getValueType() == QValueType.UNDEFINED
176: || valueExpr1.isSymbolicIdentifier()) {
177: valueType = valueExpr1.getValueType();
178: } else {
179: // value types should be compatible here, since checked in prepare()
180: if (!valueExpr1.getValueType().isCompatible(
181: valueExpr2.getValueType()))
182: throw new QueryException(
183: "Assertion error: value types not compatible.");
184: valueType = valueExpr1.getValueType();
185: }
186: return valueType;
187: }
188:
189: public AclConditionViolation isAclAllowed() {
190: AclConditionViolation violation = valueExpr1.isAclAllowed();
191: if (violation != null)
192: return violation;
193: else
194: return valueExpr2.isAclAllowed();
195: }
196:
197: private boolean evaluateInt(Object value1, Object value2) {
198: // behaviour for multivalue and/or hierarchical fields is: if there's one value that satisfies the condition, the result is true
199: List values1 = expandMultiValue(value1);
200: List values2 = expandMultiValue(value2);
201: for (Object aValues1 : values1) {
202: for (Object aValues2 : values2) {
203: if (evaluate(aValues1, aValues2))
204: return true;
205: }
206: }
207: return false;
208: }
209:
210: private List<Object> expandMultiValue(Object value) {
211: // optimalisation for primitive values
212: if (!(value instanceof Object[] || value instanceof HierarchyPath)) {
213: List<Object> values = new ArrayList<Object>(1);
214: values.add(value);
215: return values;
216: }
217:
218: List<Object> values = new ArrayList<Object>();
219: if (value instanceof Object[]) {
220: for (Object aValue : (Object[]) value)
221: expandHierarchyValue(aValue, values);
222: } else {
223: expandHierarchyValue(value, values);
224: }
225: return values;
226: }
227:
228: private void expandHierarchyValue(Object value, List<Object> values) {
229: if (value instanceof HierarchyPath) {
230: Object[] elements = ((HierarchyPath) value).getElements();
231: for (Object element : elements)
232: values.add(element);
233: } else {
234: values.add(value);
235: }
236: }
237:
238: protected abstract boolean evaluate(Object value1, Object value2);
239:
240: public boolean evaluate(ExprDocData data,
241: EvaluationInfo evaluationInfo) throws QueryException {
242: QValueType valueType = determineValueType();
243: Object value1, value2;
244: if (valueExpr1.isSymbolicIdentifier()
245: && valueExpr2.isSymbolicIdentifier()) {
246: // this should never occur as it is check in the prepare()
247: throw new QueryException(
248: "Assertion error: cannot compare two symbolic identifiers: \""
249: + valueExpr1.getExpression() + "\" and \""
250: + valueExpr2.getExpression() + "\".");
251: } else if (valueExpr1.isSymbolicIdentifier()) {
252: value1 = valueExpr1.evaluate(valueType, data,
253: evaluationInfo);
254: value2 = valueExpr1.translateSymbolic(valueExpr2,
255: evaluationInfo);
256: } else if (valueExpr2.isSymbolicIdentifier()) {
257: value1 = valueExpr2.translateSymbolic(valueExpr1,
258: evaluationInfo);
259: value2 = valueExpr2.evaluate(valueType, data,
260: evaluationInfo);
261: } else {
262: value1 = valueExpr1.evaluate(valueType, data,
263: evaluationInfo);
264: value2 = valueExpr2.evaluate(valueType, data,
265: evaluationInfo);
266: }
267:
268: if (value1 == null || value2 == null)
269: return false;
270:
271: return evaluateInt(value1, value2);
272: }
273:
274: public Tristate appliesTo(ExprDocData data,
275: EvaluationInfo evaluationInfo) throws QueryException {
276: if (valueExpr1.canTestAppliesTo()
277: && valueExpr2.canTestAppliesTo()) {
278: return evaluate(data, evaluationInfo) ? Tristate.YES
279: : Tristate.NO;
280: } else {
281: return Tristate.MAYBE;
282: }
283: }
284:
285: public void collectAccessRestrictions(
286: AccessRestrictions restrictions) {
287: valueExpr1.collectAccessRestrictions(restrictions);
288: valueExpr2.collectAccessRestrictions(restrictions);
289: }
290: }
|