Coverage Report - org.kuali.rice.core.api.criteria.PredicateFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
PredicateFactory
79%
31/39
66%
12/18
1.864
PredicateFactory$DynPredicateException
0%
0/4
N/A
1.864
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.core.api.criteria;
 17  
 
 18  
 import org.apache.commons.collections.CollectionUtils;
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 
 21  
 import java.lang.reflect.InvocationTargetException;
 22  
 import java.lang.reflect.Method;
 23  
 import java.util.HashSet;
 24  
 import java.util.Set;
 25  
 
 26  
 /**
 27  
  * This is a factory class to construct {@link Predicate Predicates}.
 28  
  *
 29  
  * <p>
 30  
  *    For more readable predicate construction it is recommended that this class
 31  
  *    is statically imported.
 32  
  * <code>
 33  
  *    import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
 34  
  * </code>
 35  
  * </p>
 36  
  *
 37  
  * to create a simple predicate where the property
 38  
  * foo.bar equals "baz" do the following:
 39  
  * <code>
 40  
  *     Predicate simple = equals("foo.bar", "baz");
 41  
  * </code>
 42  
  *
 43  
  * to create a compound predicate where the property
 44  
  * foo.bar equals "baz" and foo.id equals 1 do the following:
 45  
  * <code>
 46  
  *     Predicate compound = and(equals("foo.bar", "baz"), equals("foo.id", 1))
 47  
  * </code>
 48  
  *
 49  
  * to create a deeply nested predicate where lots of
 50  
  * properties are evaluated do the following:
 51  
  *
 52  
  * Predicate apiAuthors =
 53  
  *  and(
 54  
  *      like("display", "*Eric*"),
 55  
  *                greaterThan("birthDate", gtBirthDate),
 56  
  *                 lessThan("birthDate", ltBirthDate),
 57  
  *      or(
 58  
  *         equal("name.first", "Travis"),
 59  
  *         equal("name.last", "Schneeberger")))
 60  
  *
 61  
  * <p>
 62  
  *     <strong>WARNING:</strong> this class does automatic reductions
 63  
  *     such that you cannot assume the concrete {@link Predicate} type
 64  
  *     returned from this factory.
 65  
  * </p>
 66  
  *
 67  
  * @see QueryByCriteria
 68  
  */
 69  
 public final class PredicateFactory {
 70  0
     private PredicateFactory() {
 71  0
         throw new IllegalArgumentException("do not call");
 72  
     }
 73  
 
 74  
     /**
 75  
          * Creates an predicate representing equals comparison.  Defines that the property
 76  
      * represented by the given path should be equal to the specified value.
 77  
          *
 78  
          * <p>Supports the following types of values:
 79  
          *
 80  
          * <ul>
 81  
          *   <li>character data</li>
 82  
          *   <li>decimals</li>
 83  
          *   <li>integers</li>
 84  
          *   <li>date-time</li>
 85  
          * </ul>
 86  
          *
 87  
          * @param propertyPath the path to the property which should be evaluated
 88  
          * @param value the value to compare with the property value located at the
 89  
          * propertyPath
 90  
          *
 91  
          * @return a predicate
 92  
          *
 93  
          * @throws IllegalArgumentException if the propertyPath is null
 94  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 95  
          */
 96  
         public static Predicate equal(String propertyPath, Object value) {
 97  22
                 return new EqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 98  
         }
 99  
 
 100  
         /**
 101  
          * Creates a predicate representing not equals comparison.  Defines that the property
 102  
      * represented by the given path should <strong>not</strong> be
 103  
          * equal to the specified value.
 104  
          *
 105  
          * <p>Supports the following types of values:
 106  
          *
 107  
          * <ul>
 108  
          *   <li>character data</li>
 109  
          *   <li>decimals</li>
 110  
          *   <li>integers</li>
 111  
          *   <li>date-time</li>
 112  
          * </ul>
 113  
          *
 114  
          * @param propertyPath the path to the property which should be evaluated
 115  
          * @param value the value to compare with the property value located at the
 116  
          * propertyPath
 117  
          *
 118  
          * @return a predicate
 119  
          *
 120  
          * @throws IllegalArgumentException if the propertyPath is null
 121  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 122  
          */
 123  
         public static Predicate notEqual(String propertyPath, Object value) {
 124  9
                 return new NotEqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 125  
         }
 126  
 
 127  
     /**
 128  
          * Creates an equals ignore case predicate.  Defines that the property
 129  
      * represented by the given path should be equal to the specified value ignoring
 130  
      * the case of the value.
 131  
          *
 132  
          * <p>Supports the following types of values:
 133  
          *
 134  
          * <ul>
 135  
          *   <li>character data</li>
 136  
          * </ul>
 137  
          *
 138  
          * @param propertyPath the path to the property which should be evaluated
 139  
          * @param value the value to compare with the property value located at the
 140  
          * propertyPath
 141  
          *
 142  
          * @return a predicate
 143  
          *
 144  
          * @throws IllegalArgumentException if the propertyPath is null
 145  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 146  
          */
 147  
         public static Predicate equalIgnoreCase(String propertyPath, CharSequence value) {
 148  2
                 return new EqualIgnoreCasePredicate(propertyPath, new CriteriaStringValue(value));
 149  
         }
 150  
 
 151  
         /**
 152  
          * Creates a not equals ignore case predicate.  Defines that the property
 153  
      * represented by the given path should <strong>not</strong> be
 154  
          * equal to the specified value ignoring the case of the value.
 155  
          *
 156  
          * <p>Supports the following types of values:
 157  
          *
 158  
          * <ul>
 159  
          *   <li>character data</li>
 160  
          * </ul>
 161  
          *
 162  
          * @param propertyPath the path to the property which should be evaluated
 163  
          * @param value the value to compare with the property value located at the
 164  
          * propertyPath
 165  
          *
 166  
          * @return a predicate
 167  
          *
 168  
          * @throws IllegalArgumentException if the propertyPath is null
 169  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 170  
          */
 171  
         public static Predicate notEqualIgnoreCase(String propertyPath, CharSequence value) {
 172  2
                 return new NotEqualIgnoreCasePredicate(propertyPath, new CriteriaStringValue(value));
 173  
         }
 174  
 
 175  
         /**
 176  
          * Creates a like predicate.  Defines that the property
 177  
      * represented by the given path should match the specified value,
 178  
          * but supports the use of wildcards in the given value.
 179  
          *
 180  
          * <p>The supported wildcards include:
 181  
          *
 182  
          * <ul>
 183  
          *   <li><strong>?</strong> - matches on any single character</li>
 184  
          *   <li><strong>*</strong> - matches any string of any length (including zero length)</li>
 185  
          * </ul>
 186  
          *
 187  
          * <p>Because of this, the like predicate only supports character data
 188  
          * for the passed-in value.
 189  
          *
 190  
          * @param propertyPath the path to the property which should be evaluated
 191  
          * @param value the value to compare with the property value located at the
 192  
          * propertyPath
 193  
          *
 194  
          * @return a predicate
 195  
          *
 196  
          * @throws IllegalArgumentException if the propertyPath is null
 197  
          * @throws IllegalArgumentException if the value is null
 198  
          */
 199  
         public static Predicate like(String propertyPath, CharSequence value) {
 200  8
                 return new LikePredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 201  
         }
 202  
 
 203  
     /**
 204  
          * Creates a not like predicate.  Defines that the property
 205  
      * represented by the given path should <strong>not</strong> match the specified value,
 206  
          * but supports the use of wildcards in the given value.
 207  
          *
 208  
          * <p>The supported wildcards include:
 209  
          *
 210  
          * <ul>
 211  
          *   <li><strong>?</strong> - matches on any single character</li>
 212  
          *   <li><strong>*</strong> - matches any string of any length (including zero length)</li>
 213  
          * </ul>
 214  
          *
 215  
          * <p>Because of this, the like predicate only supports character data
 216  
          * for the passed-in value.
 217  
          *
 218  
          * @param propertyPath the path to the property which should be evaluated
 219  
          * @param value the value to compare with the property value located at the
 220  
          * propertyPath
 221  
          *
 222  
          * @return a predicate
 223  
          *
 224  
          * @throws IllegalArgumentException if the propertyPath is null
 225  
          * @throws IllegalArgumentException if the value is null
 226  
          */
 227  
         public static Predicate notLike(String propertyPath, CharSequence value) {
 228  5
                 return new NotLikePredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 229  
         }
 230  
 
 231  
         /**
 232  
          * Create an in predicate.  Defines that the property
 233  
      * represented by the given path should be contained within the
 234  
          * specified list of values.
 235  
          *
 236  
          * <p>Supports any of the valid types of value in the value list, with the
 237  
          * restriction that all items in the list of values must be of the same type.
 238  
          *
 239  
          * @param propertyPath the path to the property which should be evaluated
 240  
          * @param values the values to compare with the property value located at the
 241  
          * propertyPath
 242  
          *
 243  
          * @return a predicate
 244  
          *
 245  
          * @throws IllegalArgumentException if the propertyPath is null
 246  
          * @throws IllegalArgumentException if the values list is null, empty,
 247  
          * contains object of different types, or includes objects of an invalid type
 248  
          */
 249  
         public static <T> Predicate in(String propertyPath, T... values) {
 250  10
                 return new InPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValueList(values));
 251  
         }
 252  
 
 253  
         /**
 254  
          * Create a not in predicate.  Defines that the property
 255  
      * represented by the given path should <strong>not</strong> be
 256  
          * contained within the specified list of values.
 257  
          *
 258  
          * <p>Supports any of the valid types of value in the value list, with the
 259  
          * restriction that all items in the list of values must be of the same type.
 260  
          *
 261  
          * @param propertyPath the path to the property which should be evaluated
 262  
          * @param values the values to compare with the property value located at the
 263  
          * propertyPath
 264  
          *
 265  
          * @return a predicate
 266  
          *
 267  
          * @throws IllegalArgumentException if the propertyPath is null
 268  
          * @throws IllegalArgumentException if the values list is null, empty,
 269  
          * contains object of different types, or includes objects of an invalid type
 270  
          */
 271  
         public static <T> Predicate notIn(String propertyPath, T... values) {
 272  7
                 return new NotInPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValueList(values));
 273  
         }
 274  
 
 275  
     /**
 276  
          * Create an in ignore case predicate.  Defines that the property
 277  
      * represented by the given path should be contained within the
 278  
          * specified list of values ignoring the case of the values.
 279  
          *
 280  
          * <p>Supports any of CharSequence value in the value list, with the
 281  
          * restriction that all items in the list of values must be of the same type.
 282  
          *
 283  
          * @param propertyPath the path to the property which should be evaluated
 284  
          * @param values the values to compare with the property value located at the
 285  
          * propertyPath
 286  
          *
 287  
          * @return a predicate
 288  
          *
 289  
          * @throws IllegalArgumentException if the propertyPath is null
 290  
          * @throws IllegalArgumentException if the values list is null, empty,
 291  
          * contains object of different types, or includes objects of an invalid type
 292  
          */
 293  
         public static <T extends CharSequence> Predicate inIgnoreCase(String propertyPath, T... values) {
 294  2
                 return new InIgnoreCasePredicate(propertyPath, CriteriaSupportUtils.createCriteriaStringValueList(values));
 295  
         }
 296  
 
 297  
         /**
 298  
          * Create a not in ignore case.  Defines that the property
 299  
      * represented by the given path should <strong>not</strong> be
 300  
          * contained within the specified list of values ignoring the case of the values.
 301  
          *
 302  
          * <p>Supports any CharSequence value in the value list, with the
 303  
          * restriction that all items in the list of values must be of the same type.
 304  
          *
 305  
          * @param propertyPath the path to the property which should be evaluated
 306  
          * @param values the values to compare with the property value located at the
 307  
          * propertyPath
 308  
          *
 309  
          * @return a predicate
 310  
          *
 311  
          * @throws IllegalArgumentException if the propertyPath is null
 312  
          * @throws IllegalArgumentException if the values list is null, empty,
 313  
          * contains object of different types, or includes objects of an invalid type
 314  
          */
 315  
         public static <T extends CharSequence> Predicate notInIgnoreCase(String propertyPath, T... values) {
 316  2
                 return new NotInIgnoreCasePredicate(propertyPath, CriteriaSupportUtils.createCriteriaStringValueList(values));
 317  
         }
 318  
 
 319  
         /**
 320  
          * Creates a greater than predicate.  Defines that the property
 321  
      * represented by the given path should be greater than the specified value.
 322  
          *
 323  
          * <p>Supports the following types of values:
 324  
          *
 325  
          * <ul>
 326  
          *   <li>decimals</li>
 327  
          *   <li>integers</li>
 328  
          *   <li>date-time</li>
 329  
          * </ul>
 330  
          *
 331  
          * @param propertyPath the path to the property which should be evaluated
 332  
          * @param value the value to compare with the property value located at the
 333  
          * propertyPath
 334  
          *
 335  
          * @return a predicate
 336  
          *
 337  
          * @throws IllegalArgumentException if the propertyPath is null
 338  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 339  
          */
 340  
         public static Predicate greaterThan(String propertyPath, Object value) {
 341  10
                 return new GreaterThanPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 342  
         }
 343  
 
 344  
         /**
 345  
          * Creates a greater than or equal predicate.  Defines that the
 346  
      * property represented by the given path should be greater than
 347  
          * or equal to the specified value.
 348  
          *
 349  
          * <p>Supports the following types of values:
 350  
          *
 351  
          * <ul>
 352  
          *   <li>decimals</li>
 353  
          *   <li>integers</li>
 354  
          *   <li>date-time</li>
 355  
          * </ul>
 356  
          *
 357  
          * @param propertyPath the path to the property which should be evaluated
 358  
          * @param value the value to compare with the property value located at the
 359  
          * propertyPath
 360  
          *
 361  
          * @return a predicate
 362  
          *
 363  
          * @throws IllegalArgumentException if the propertyPath is null
 364  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 365  
          */
 366  
         public static Predicate greaterThanOrEqual(String propertyPath, Object value) {
 367  5
                 return new GreaterThanOrEqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 368  
         }
 369  
 
 370  
         /**
 371  
          * Creates a less than predicate.  Defines that the property
 372  
      * represented by the given path should be less than the specified value.
 373  
          *
 374  
          * <p>Supports the following types of values:
 375  
          *
 376  
          * <ul>
 377  
          *   <li>decimals</li>
 378  
          *   <li>integers</li>
 379  
          *   <li>date-time</li>
 380  
          * </ul>
 381  
          *
 382  
          * @param propertyPath the path to the property which should be evaluated
 383  
          * @param value the value to compare with the property value located at the
 384  
          * propertyPath
 385  
          *
 386  
          * @return a predicate
 387  
          *
 388  
          * @throws IllegalArgumentException if the propertyPath is null
 389  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 390  
          */
 391  
         public static Predicate lessThan(String propertyPath, Object value) {
 392  7
                 return new LessThanPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 393  
         }
 394  
 
 395  
         /**
 396  
          * Creates a less than or equal predicate.  Defines that the
 397  
      * property represented by the given path should be less than
 398  
          * or equal to the specified value.
 399  
          *
 400  
          * <p>Supports the following types of values:
 401  
          *
 402  
          * <ul>
 403  
          *   <li>decimals</li>
 404  
          *   <li>integers</li>
 405  
          *   <li>date-time</li>
 406  
          * </ul>
 407  
          *
 408  
          * @param propertyPath the path to the property which should be evaluated
 409  
          * @param value the value to compare with the property value located at the
 410  
          * propertyPath
 411  
          *
 412  
          * @return a predicate
 413  
          *
 414  
          * @throws IllegalArgumentException if the propertyPath is null
 415  
          * @throws IllegalArgumentException if the value is null or of an invalid type
 416  
          */
 417  
         public static Predicate lessThanOrEqual(String propertyPath, Object value) {
 418  5
                 return new LessThanOrEqualPredicate(propertyPath, CriteriaSupportUtils.determineCriteriaValue(value));
 419  
         }
 420  
 
 421  
         /**
 422  
          * Creates an is null predicate.  Defines that the
 423  
          * property represented by the given path should be null.
 424  
          *
 425  
          * @param propertyPath the path to the property which should be evaluated
 426  
          *
 427  
          * @return a predicate
 428  
          *
 429  
          * @throws IllegalArgumentException if the propertyPath is null
 430  
          */
 431  
         public static Predicate isNull(String propertyPath) {
 432  6
                 return new NullPredicate(propertyPath);
 433  
         }
 434  
 
 435  
         /**
 436  
          * Creates an is not null predicate.  Defines that the property
 437  
      * represented by the given path should <strong>not</strong> be null.
 438  
          *
 439  
          * @param propertyPath the path to the property which should be evaluated
 440  
          *
 441  
          * @return a predicate
 442  
          *
 443  
          * @throws IllegalArgumentException if the propertyPath is null
 444  
          */
 445  
         public static Predicate isNotNull(String propertyPath) {
 446  6
                 return new NotNullPredicate(propertyPath);
 447  
         }
 448  
 
 449  
         /**
 450  
          * Creates an and predicate that is used to "and" predicates together.
 451  
          *
 452  
          * <p>An "and" predicate will evaluate the truth value of of it's
 453  
          * internal predicates and, if all of them evaluate to true, then
 454  
          * the and predicate itself should evaluate to true.  The implementation
 455  
      * of an and predicate may short-circuit.
 456  
      *
 457  
      * <p>
 458  
      *     This factory method does automatic reductions.
 459  
      * </p>
 460  
          *
 461  
      * @param predicates to "and" together
 462  
      *
 463  
          * @return a predicate
 464  
          */
 465  
         public static Predicate and(Predicate... predicates) {
 466  
         //reduce single item compound
 467  18
         if (predicates != null && predicates.length == 1 && predicates[0] != null) {
 468  9
             return predicates[0];
 469  
         }
 470  9
         final Set<Predicate> predicateSet = new HashSet<Predicate>();
 471  9
         CollectionUtils.addAll(predicateSet, predicates);
 472  9
         return new AndPredicate(predicateSet);
 473  
         }
 474  
 
 475  
         /**
 476  
          * Creates an  or predicate that is used to "or" predicate together.
 477  
          *
 478  
          * <p>An "or" predicate will evaluate the truth value of of it's
 479  
          * internal predicates and, if any one of them evaluate to true, then
 480  
          * the or predicate itself should evaluate to true.  If all predicates
 481  
          * contained within the "or" evaluate to false, then the or iself will
 482  
          * evaluate to false.   The implementation of an or predicate may
 483  
      * short-circuit.
 484  
          *
 485  
      * <p>
 486  
      *     This factory method does automatic reductions.
 487  
      * </p>
 488  
      * @param predicates to "or" together
 489  
      *
 490  
          * @return a predicate
 491  
          */
 492  
         public static Predicate or(Predicate... predicates) {
 493  
         //reduce single item compound
 494  8
         if (predicates != null && predicates.length == 1 && predicates[0] != null) {
 495  2
             return predicates[0];
 496  
         }
 497  
 
 498  6
         final Set<Predicate> predicateSet = new HashSet<Predicate>();
 499  6
         CollectionUtils.addAll(predicateSet, predicates);
 500  6
                 return new OrPredicate(predicateSet);
 501  
         }
 502  
 
 503  
     /**
 504  
      * This method will construct a predicate based on the predicate name.
 505  
      *
 506  
      * ex: "or", Or, "OrPredicate" passing the arguments to the construction method.
 507  
      *
 508  
      * @param name the name of the predicate to create.
 509  
      * @param args the args required to construct the predicate
 510  
      * @return the Predicate
 511  
      */
 512  
     public static Predicate dynConstruct(String name, Object... args) {
 513  38
         if (StringUtils.isBlank(name)) {
 514  0
             throw new IllegalArgumentException("name is blank");
 515  
         }
 516  
 
 517  38
         final String correctedName = CriteriaSupportUtils.findDynName(name);
 518  377
         for (Method m : PredicateFactory.class.getMethods()) {
 519  
 
 520  
             //currently this class does NOT overload therefore this method doesn't have to worry about
 521  
             //overload resolution - just get the Method handle based on the passed in name
 522  377
             if (m.getName().equals(correctedName)) {
 523  
                 try {
 524  38
                     return (Predicate) m.invoke(null, args);
 525  0
                 } catch (InvocationTargetException e) {
 526  0
                     throw new DynPredicateException(e);
 527  0
                 } catch (IllegalAccessException e) {
 528  0
                     throw new DynPredicateException(e);
 529  
                 }
 530  
             }
 531  
         }
 532  
 
 533  0
         throw new DynPredicateException("predicate: " + name + " doesn't exist");
 534  
     }
 535  
 
 536  
     //this is really a fatal error (programming error)
 537  
     //and therefore is just a private exception not really meant to be caught
 538  
     private static class DynPredicateException extends RuntimeException {
 539  
         DynPredicateException(Throwable t) {
 540  0
             super(t);
 541  0
         }
 542  
 
 543  
         DynPredicateException(String s) {
 544  0
             super(s);
 545  0
         }
 546  
     }
 547  
 }