View Javadoc

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