View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.commons.beanutils;
    19
    20  import org.apache.commons.collections.Predicate;
    21  import org.apache.commons.logging.Log;
    22  import org.apache.commons.logging.LogFactory;
    23
    24  import java.lang.reflect.InvocationTargetException;
    25
    26
    27  /***
    28   * <p><code>Predicate</code> that evaluates a property value against a specified value.</p>
    29   * <p>
    30   * An implementation of <code>org.apache.commons.collections.Predicate</code> that evaluates a
    31   * property value on the object provided against a specified value and returns <code>true</code>
    32   * if equal; <code>false</code> otherwise.
    33   * The <code>BeanPropertyValueEqualsPredicate</code> constructor takes two parameters which
    34   * determine what property will be evaluated on the target object and what its expected value should
    35   * be.
    36   * <dl>
    37   *    <dt>
    38   *       <strong><code>
    39   *           <pre>public BeanPropertyValueEqualsPredicate( String propertyName, Object propertyValue )</pre>
    40   *       </code></strong>
    41   *    </dt>
    42   *    <dd>
    43   *       Will create a <code>Predicate</code> that will evaluate the target object and return
    44   *       <code>true</code> if the property specified by <code>propertyName</code> has a value which
    45   *       is equal to the the value specified by <code>propertyValue</code>. Or return
    46   *       <code>false</code> otherwise.
    47   *    </dd>
    48   * </dl>
    49   * </p>
    50   * <p>
    51   * <strong>Note:</strong> Property names can be a simple, nested, indexed, or mapped property as defined by
    52   * <code>org.apache.commons.beanutils.PropertyUtils</code>.  If any object in the property path
    53   * specified by <code>propertyName</code> is <code>null</code> then the outcome is based on the
    54   * value of the <code>ignoreNull</code> attribute.
    55   * </p>
    56   * <p>
    57   * A typical usage might look like:
    58   * <code><pre>
    59   * // create the closure
    60   * BeanPropertyValueEqualsPredicate predicate =
    61   *    new BeanPropertyValueEqualsPredicate( "activeEmployee", Boolean.FALSE );
    62   *
    63   * // filter the Collection
    64   * CollectionUtils.filter( peopleCollection, predicate );
    65   * </pre></code>
    66   * </p>
    67   * <p>
    68   * This would take a <code>Collection</code> of person objects and filter out any people whose
    69   * <code>activeEmployee</code> property is <code>false</code>. Assuming...
    70   * <ul>
    71   *    <li>
    72   *       The top level object in the <code>peeopleCollection</code> is an object which represents a
    73   *       person.
    74   *    </li>
    75   *    <li>
    76   *       The person object has a <code>getActiveEmployee()</code> method which returns
    77   *       the boolean value for the object's <code>activeEmployee</code> property.
    78   *    </li>
    79   * </ul>
    80   * </p>
    81   * <p>
    82   * Another typical usage might look like:
    83   * <code><pre>
    84   * // create the closure
    85   * BeanPropertyValueEqualsPredicate predicate =
    86   *    new BeanPropertyValueEqualsPredicate( "personId", "456-12-1234" );
    87   *
    88   * // search the Collection
    89   * CollectionUtils.find( peopleCollection, predicate );
    90   * </pre><code>
    91   * </p>
    92   * <p>
    93   * This would search a <code>Collection</code> of person objects and return the first object whose
    94   * <code>personId</code> property value equals <code>456-12-1234</code>. Assuming...
    95   * <ul>
    96   *    <li>
    97   *       The top level object in the <code>peeopleCollection</code> is an object which represents a
    98   *       person.
    99   *    </li>
    100  *    <li>
    101  *       The person object has a <code>getPersonId()</code> method which returns
    102  *       the value for the object's <code>personId</code> property.
    103  *    </li>
    104  * </ul>
    105  * </p>
    106  *
    107  * @author Norm Deane
    108  * @see org.apache.commons.beanutils.PropertyUtils
    109  * @see org.apache.commons.collections.Predicate
    110  */
    111 public class BeanPropertyValueEqualsPredicate implements Predicate {
    112
    113     /*** For logging. */
    114     private final Log log = LogFactory.getLog(this.getClass());
    115
    116     /***
    117      * The name of the property which will be evaluated when this <code>Predicate</code> is executed.
    118      */
    119     private String propertyName;
    120
    121     /***
    122      * The value that the property specified by <code>propertyName</code>
    123      * will be compared to when this <code>Predicate</code> executes.
    124      */
    125     private Object propertyValue;
    126
    127     /***
    128      * <p>Should <code>null</code> objects in the property path be ignored?</p>
    129      * <p>
    130      * Determines whether <code>null</code> objects in the property path will genenerate an
    131      * <code>IllegalArgumentException</code> or not. If set to <code>true</code> then if any objects
    132      * in the property path evaluate to <code>null</code> then the
    133      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
    134      * not rethrown and <code>false</code> will be returned.  If set to <code>false</code> then if
    135      * any objects in the property path evaluate to <code>null</code> then the
    136      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
    137      * rethrown.
    138      * </p>
    139      */
    140     private boolean ignoreNull;
    141
    142     /***
    143      * Constructor which takes the name of the property, its expected value to be used in evaluation,
    144      * and assumes <code>ignoreNull</code> to be <code>false</code>.
    145      *
    146      * @param propertyName The name of the property that will be evaluated against the expected value.
    147      * @param propertyValue The value to use in object evaluation.
    148      * @throws IllegalArgumentException If the property name provided is null or empty.
    149      */
    150     public BeanPropertyValueEqualsPredicate(String propertyName, Object propertyValue) {
    151         this(propertyName, propertyValue, false);
    152     }
    153
    154     /***
    155      * Constructor which takes the name of the property, its expected value
    156      * to be used in evaluation, and a boolean which determines whether <code>null</code> objects in
    157      * the property path will genenerate an <code>IllegalArgumentException</code> or not.
    158      *
    159      * @param propertyName The name of the property that will be evaluated against the expected value.
    160      * @param propertyValue The value to use in object evaluation.
    161      * @param ignoreNull Determines whether <code>null</code> objects in the property path will
    162      * genenerate an <code>IllegalArgumentException</code> or not.
    163      * @throws IllegalArgumentException If the property name provided is null or empty.
    164      */
    165     public BeanPropertyValueEqualsPredicate(String propertyName, Object propertyValue, boolean ignoreNull) {
    166         super();
    167
    168         if ((propertyName != null) && (propertyName.length() > 0)) {
    169             this.propertyName = propertyName;
    170             this.propertyValue = propertyValue;
    171             this.ignoreNull = ignoreNull;
    172         } else {
    173             throw new IllegalArgumentException("propertyName cannot be null or empty");
    174         }
    175     }
    176
    177     /***
    178      * Evaulates the object provided against the criteria specified when this
    179      * <code>BeanPropertyValueEqualsPredicate</code> was constructed.  Equality is based on
    180      * either reference or logical equality as defined by the property object's equals method. If
    181      * any object in the property path leading up to the target property is <code>null</code> then
    182      * the outcome will be based on the value of the <code>ignoreNull</code> attribute. By default,
    183      * <code>ignoreNull</code> is <code>false</code> and would result in an
    184      * <code>IllegalArgumentException</code> if an object in the property path leading up to the
    185      * target property is <code>null</code>.
    186      *
    187      * @param object The object to be evaluated.
    188      * @return True if the object provided meets all the criteria for this <code>Predicate</code>;
    189      * false otherwise.
    190      * @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or
    191      * NoSuchMethodException is thrown when trying to access the property specified on the object
    192      * provided. Or if an object in the property path provided is <code>null</code> and
    193      * <code>ignoreNull</code> is set to <code>false</code>.
    194      */
    195     public boolean evaluate(Object object) {
    196
    197         boolean evaluation = false;
    198
    199         try {
    200             evaluation = evaluateValue(propertyValue,
    201                     PropertyUtils.getProperty(object, propertyName));
    202         } catch (IllegalArgumentException e) {
    203             final String errorMsg = "Problem during evaluation. Null value encountered in property path...";
    204
    205             if (ignoreNull) {
    206                 log.warn("WARNING: " + errorMsg, e);
    207             } else {
    208                 log.error("ERROR: " + errorMsg, e);
    209                 throw e;
    210             }
    211         } catch (IllegalAccessException e) {
    212             final String errorMsg = "Unable to access the property provided.";
    213             log.error(errorMsg, e);
    214             throw new IllegalArgumentException(errorMsg);
    215         } catch (InvocationTargetException e) {
    216             final String errorMsg = "Exception occurred in property's getter";
    217             log.error(errorMsg, e);
    218             throw new IllegalArgumentException(errorMsg);
    219         } catch (NoSuchMethodException e) {
    220             final String errorMsg = "Property not found.";
    221             log.error(errorMsg, e);
    222             throw new IllegalArgumentException(errorMsg);
    223         }
    224
    225         return evaluation;
    226     }
    227
    228     /***
    229      * Utility method which evaluates whether the actual property value equals the expected property
    230      * value.
    231      *
    232      * @param expected The expected value.
    233      * @param actual The actual value.
    234      * @return True if they are equal; false otherwise.
    235      */
    236     private boolean evaluateValue(Object expected, Object actual) {
    237         return (expected == actual) || ((expected != null) && expected.equals(actual));
    238     }
    239
    240     /***
    241      * Returns the name of the property which will be evaluated when this <code>Predicate</code> is
    242      * executed.
    243      *
    244      * @return The name of the property which will be evaluated when this <code>Predicate</code> is
    245      * executed.
    246      */
    247     public String getPropertyName() {
    248         return propertyName;
    249     }
    250
    251     /***
    252      * Returns the value that the property specified by <code>propertyName</code> will be compared to
    253      * when this <code>Predicate</code> executes.
    254      *
    255      * @return The value that the property specified by <code>propertyName</code> will be compared to
    256      * when this <code>Predicate</code> executes.
    257      */
    258     public Object getPropertyValue() {
    259         return propertyValue;
    260     }
    261
    262     /***
    263      * Returns the flag which determines whether <code>null</code> objects in the property path will
    264      * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then
    265      * if any objects in the property path evaluate to <code>null</code> then the
    266      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
    267      * not rethrown and <code>false</code> will be returned.  If set to <code>false</code> then if
    268      * any objects in the property path evaluate to <code>null</code> then the
    269      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
    270      * rethrown.
    271      *
    272      * @return The flag which determines whether <code>null</code> objects in the property path will
    273      * genenerate an <code>IllegalArgumentException</code> or not.
    274      */
    275     public boolean isIgnoreNull() {
    276         return ignoreNull;
    277     }
    278 }