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