1 package org.apache.torque.criteria;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.Serializable;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26
27 import org.apache.commons.lang.builder.EqualsBuilder;
28 import org.apache.commons.lang.builder.HashCodeBuilder;
29 import org.apache.torque.Column;
30
31 /**
32 * Describes one or more where clause parts in the Criteria.
33 * Either the parts list is not null and represents this criterion
34 * or the column, value, comparison and ignoreStringCase columns
35 * are not null and represent this criterion.
36 */
37 public class Criterion implements Serializable
38 {
39 /** Serial version. */
40 private static final long serialVersionUID = 7157097965404611710L;
41
42 /** Constant for the operator " AND ". */
43 public static final String AND = " AND ";
44
45 /** Constant for the operator " OR ". */
46 public static final String OR = " OR ";
47
48 /**
49 * Left-hand-side value of the comparison, may be null.
50 * If this object implements the Column interface, it is interpreted as
51 * a value computed by the database, otherwise as verbatim value.
52 */
53 private Object lValue;
54
55 /** Comparison operator. Can only be null if sql is not null. */
56 private SqlEnum comparison;
57
58 /**
59 * Right-hand-side value of the comparison, may be null.
60 * If this object implements the Column interface, it is interpreted as
61 * a value computed by the database, otherwise as verbatim value.
62 */
63 private Object rValue;
64
65 /** A verbatim SQL for this Criterion. */
66 private String sql;
67
68 /**
69 * Replacements for the placeholders in the verbatim SQL.
70 * Is only used if sql is not null.
71 */
72 private Object[] preparedStatementReplacements;
73
74 /** Flag to ignore case in comparison */
75 private boolean ignoreCase = false;
76
77 /**
78 * The criterion objects which form a composite criterion.
79 * Either this list is not null and represents this criterion
80 * or the column, value, comparison and ignoreStringCase columns
81 * are not null and represent this criterion.
82 */
83 private List<Criterion> parts;
84
85 /**
86 * The operator (AND, OR...) how the composite criterions
87 * are connected.
88 */
89 private String conjunction;
90
91
92 /**
93 * Create a new instance.
94 * Either this Criterion represents a comparison without verbatim SQL;
95 * in this case the parameters lValue and comparison must be not null,
96 * rValue may be not null and sql and preparedStatementReplacements must
97 * be null; or it represents a verbatim sql condition, in which case
98 * the parameter comparison must be null and the sql must be not null
99 * (preparedStatementReplacements may be set to contain sql placeholder
100 * replacement values, and lValue and rValue can be set to add columns
101 * to the automatically computed from clause of the query).
102 *
103 * @param lValue the left hand side value of the comparison.
104 * If this value should be a value from the database,
105 * the object must implement the
106 * <code>org.apache.torque.Column</code> interface.
107 * @param rValue the right hand side value of the comparison.
108 * If this value should be a value from the database,
109 * the object must implement the
110 * <code>org.apache.torque.Column</code> interface.
111 * @param comparison The comparison operator. Either this parameter or
112 * sql must be not null.
113 * @param sql a verbatim sql condition. Either this parameter or
114 * comparison must be not null.
115 * @param preparedStatementReplacements Values for the placeholders
116 * in the verbatim sql condition.
117 *
118 * @throws NullPointerException if column is null.
119 */
120 public Criterion(
121 Object lValue,
122 Object rValue,
123 SqlEnum comparison,
124 String sql,
125 Object[] preparedStatementReplacements)
126 {
127 if (comparison != null
128 && (sql != null || preparedStatementReplacements != null))
129 {
130 throw new IllegalArgumentException("Either comparison or "
131 + "some of (sql, preparedStatementReplacements) "
132 + "can be not null, not both");
133 }
134 if ((lValue == null || comparison == null)
135 && (sql == null))
136 {
137 throw new IllegalArgumentException("Either the values"
138 + "(lValue, comparison) or "
139 + "sql must be not null");
140 }
141 this.lValue = lValue;
142 this.comparison = comparison;
143 this.rValue = rValue;
144 this.sql = sql;
145 this.preparedStatementReplacements = preparedStatementReplacements;
146 }
147
148 /**
149 * Create a new instance without verbatim sql, using equals as
150 * comparison operator.
151 *
152 * @param lValue the left hand side value of the comparison, not null.
153 * If this value should be a value from the database,
154 * the object must implement the
155 * <code>org.apache.torque.Column</code> interface.
156 * @param rValue the right hand side value of the comparison.
157 * If this value should be a value from the database,
158 * the object must implement the
159 * <code>org.apache.torque.Column</code> interface.
160 */
161 public Criterion(Object lValue, Object rValue)
162 {
163 this(lValue, rValue, Criteria.EQUAL, null, null);
164 }
165
166 /**
167 * Create a new instance without verbatim sql.
168 *
169 * @param lValue the left hand side value of the comparison, not null.
170 * If this value should be a value from the database,
171 * the object must implement the
172 * <code>org.apache.torque.Column</code> interface.
173 * @param rValue the right hand side value of the comparison.
174 * If this value should be a value from the database,
175 * the object must implement the
176 * <code>org.apache.torque.Column</code> interface.
177 * @param comparison The comparison operator, not null.
178 */
179 public Criterion(Object lValue, Object rValue, SqlEnum comparison)
180 {
181 this(lValue, rValue, comparison, null, null);
182 }
183
184 /**
185 * Creates a shallow copy of the given Criterion.
186 *
187 * @param toCopy the Criterion to copy from, not null.
188 */
189 public Criterion(Criterion toCopy)
190 {
191 this.lValue = toCopy.lValue;
192 this.comparison = toCopy.comparison;
193 this.rValue = toCopy.rValue;
194 this.sql = toCopy.sql;
195 this.preparedStatementReplacements
196 = toCopy.preparedStatementReplacements;
197 this.ignoreCase = toCopy.ignoreCase;
198 this.parts = toCopy.parts;
199 this.conjunction = toCopy.conjunction;
200 }
201
202 /**
203 * Get the left hand side value of the comparison.
204 *
205 * @return the left hand side value of the comparison.
206 * If this value is a value computed by the database,
207 * the object implements the
208 * <code>org.apache.torque.Column</code> interface.
209 */
210 public Object getLValue()
211 {
212 return this.lValue;
213 }
214
215 /**
216 * Set the left hand side value of the comparison.
217 *
218 * @param lValue the left hand side value of the comparison.
219 * If this value is a value computed by the database,
220 * the object must implement the
221 * <code>org.apache.torque.Column</code> interface.
222 */
223 public void setLValue(Object lValue)
224 {
225 this.lValue = lValue;
226 }
227
228 /**
229 * Get the comparison.
230 *
231 * @return A String with the comparison, or null if this
232 * Criterion represents a verbatim sql condition.
233 */
234 public SqlEnum getComparison()
235 {
236 return this.comparison;
237 }
238
239 /**
240 * Get the right hand side value of the comparison.
241 *
242 * @return the right hand side value of the comparison.
243 * If this value is a value computed by the database,
244 * the object implements the
245 * <code>org.apache.torque.Column</code> interface.
246 */
247 public Object getRValue()
248 {
249 return this.rValue;
250 }
251
252 /**
253 * Set the right hand side value of the comparison.
254 *
255 * @param rValue the right hand side value of the comparison.
256 * If this value is a value computed by the database,
257 * the object must implement the
258 * <code>org.apache.torque.Column</code> interface.
259 */
260 public void setRValue(Object rValue)
261 {
262 this.rValue = rValue;
263 }
264
265 /**
266 * Returns the verbatim sql for this condition.
267 *
268 * @return the verbatim sql for this condition, or null if this
269 * Criterion does not represent a verbatim sql condition.
270 */
271 public String getSql()
272 {
273 return sql;
274 }
275
276 /**
277 * Returns the prepared statement replacements for a verbatim sql condition.
278 *
279 * @return the replacement values, or null.
280 */
281 public Object[] getPreparedStatementReplacements()
282 {
283 return preparedStatementReplacements;
284 }
285
286 /**
287 * Returns whether this Criterion represents a verbatim sql condition.
288 *
289 * @return true if this Criterion represents a verbatim sql condition,
290 * false if the sql is computed from lValue, comparison and rValue.
291 */
292 public boolean isVerbatimSqlCondition()
293 {
294 return (sql != null);
295 }
296
297 /**
298 * Sets ignore case. ignoreCase is ignored for a verbatim sql statement.
299 *
300 * @param b True if case should be ignored.
301 * @return A modified Criterion object.
302 */
303 public Criterion setIgnoreCase(boolean b)
304 {
305 ignoreCase = b;
306 return this;
307 }
308
309 /**
310 * Is ignore case on or off?
311 *
312 * @return True if case is ignored.
313 */
314 public boolean isIgnoreCase()
315 {
316 return ignoreCase;
317 }
318
319 /**
320 * Returns the parts of which this criterion consists.
321 *
322 * @return an unmodifiable list of the clauses,
323 * or null if this criterion is not a composite criterion.
324 */
325 public List<Criterion> getParts()
326 {
327 if (parts == null)
328 {
329 return null;
330 }
331 return Collections.unmodifiableList(parts);
332 }
333
334 /**
335 * Returns the conjunction for the parts of this criterion
336 *
337 * @return the conjunction, or null if this criterion is not a
338 * composite criterion.
339 */
340 public String getConjunction()
341 {
342 return conjunction;
343 }
344
345 /**
346 * Returns whether this criterion is a composite criterion.
347 *
348 * @return true if this criterion is a composite criterion,
349 * false if it represents a single condition.
350 */
351 public boolean isComposite()
352 {
353 return parts != null;
354 }
355
356 /**
357 * Replaces this criterion's condition with
358 * (this criterion's condition AND criterion).
359 *
360 * @param criterion the criterion to and with this criterion,
361 * not null.
362 */
363 public Criterion and(Criterion criterion)
364 {
365 addCompositeCriterion(criterion, AND);
366 return this;
367 }
368
369 /**
370 * Replaces this criterion's condition with
371 * (this criterion's condition OR criterion).
372 *
373 * @param criterion the criterion to and with this criterion,
374 * not null.
375 */
376 public Criterion or(Criterion criterion)
377 {
378 addCompositeCriterion(criterion, OR);
379 return this;
380 }
381
382 /**
383 * Add a composite criterion to this criterion.
384 *
385 * @param criterion the criterion to add, not null.
386 * @param conjunction the conjunction by which to add the criterion,
387 * not null.
388 *
389 * @throws NullPointerException if criterion is null.
390 */
391 private void addCompositeCriterion(
392 Criterion criterion,
393 String conjunction)
394 {
395 if (criterion == null)
396 {
397 throw new NullPointerException("criterion must not be null");
398 }
399 if (isComposite() && this.conjunction.equals(conjunction))
400 {
401 parts.add(criterion);
402 }
403 else
404 {
405 Criterion copy = new Criterion(this);
406 parts = new ArrayList<Criterion>();
407 parts.add(copy);
408 parts.add(criterion);
409 this.conjunction = conjunction;
410 this.rValue = null;
411 this.comparison = null;
412 this.lValue = null;
413 this.sql = null;
414 this.preparedStatementReplacements = null;
415 this.ignoreCase = false;
416 }
417 }
418
419 /**
420 * Appends a debug String representation of the Criterion
421 * onto the String builder.
422 */
423 @SuppressWarnings("deprecation")
424 public void appendTo(StringBuilder sb)
425 {
426 if (isComposite())
427 {
428 boolean first = true;
429 for (Criterion part : parts)
430 {
431 if (!first)
432 {
433 sb.append(conjunction);
434 }
435 if (part.isComposite())
436 {
437 sb.append('(');
438 }
439 part.appendTo(sb);
440 if (part.isComposite())
441 {
442 sb.append(')');
443 }
444 first = false;
445 }
446 }
447 else
448 {
449 if (SqlEnum.CUSTOM == comparison)
450 {
451 if (rValue != null && !"".equals(rValue))
452 {
453 sb.append((String) rValue);
454 }
455 }
456 else if (isVerbatimSqlCondition())
457 {
458 sb.append(sql);
459 }
460 else
461 {
462 String lValueDisplay;
463 if (lValue instanceof Column)
464 {
465 lValueDisplay = ((Column) lValue).getSqlExpression();
466 }
467 else if (lValue != null)
468 {
469 lValueDisplay = lValue.toString();
470 }
471 else
472 {
473 lValueDisplay = "";
474 }
475 String rValueDisplay;
476 if (rValue instanceof Column)
477 {
478 rValueDisplay = ((Column) rValue).getSqlExpression();
479 }
480 else if (rValue != null)
481 {
482 rValueDisplay = rValue.toString();
483 }
484 else
485 {
486 rValueDisplay = "";
487 }
488 sb.append(lValueDisplay)
489 .append(comparison)
490 .append(rValueDisplay);
491 }
492 }
493 }
494
495 /**
496 * Build a string representation of the Criterion for debug purposes.
497 *
498 * @return A String with the representation of the Criterion.
499 */
500 @Override
501 public String toString()
502 {
503 StringBuilder builder = new StringBuilder();
504 appendTo(builder);
505 return builder.toString();
506 }
507
508 /**
509 * This method checks another Criteria.Criterion to see if they contain
510 * the same attributes.
511 */
512 @Override
513 public boolean equals(Object obj)
514 {
515 if (this == obj)
516 {
517 return true;
518 }
519 if (obj == null)
520 {
521 return false;
522 }
523 if (obj.getClass() != this.getClass())
524 {
525 return false;
526 }
527
528 Criterion criterion = (Criterion) obj;
529 EqualsBuilder equalsBuilder = new EqualsBuilder();
530 equalsBuilder.append(criterion.lValue, this.lValue)
531 .append(criterion.comparison, this.comparison)
532 .append(criterion.rValue, this.rValue)
533 .append(criterion.sql, this.sql)
534 .append(criterion.preparedStatementReplacements,
535 this.preparedStatementReplacements)
536 .append(criterion.ignoreCase, this.ignoreCase)
537 .append(criterion.parts, this.parts)
538 .append(criterion.conjunction, this.conjunction);
539 return equalsBuilder.isEquals();
540 }
541
542 /**
543 * Returns a hash code value for the object.
544 */
545 @Override
546 public int hashCode()
547 {
548 HashCodeBuilder hashCodeBuilder = new HashCodeBuilder();
549 hashCodeBuilder.append(this.lValue)
550 .append(this.comparison)
551 .append(this.rValue)
552 .append(this.sql)
553 .append(this.preparedStatementReplacements)
554 .append(this.ignoreCase)
555 .append(this.parts)
556 .append(this.conjunction);
557 return hashCodeBuilder.toHashCode();
558 }
559 }
560