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 java.lang.reflect.InvocationTargetException;
21  
22  import org.apache.commons.collections.Predicate;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
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  * @version $Id$
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(final String propertyName, final 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(final String propertyName, final Object propertyValue, final 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(final Object object) {
196 
197         boolean evaluation = false;
198 
199         try {
200             evaluation = evaluateValue(propertyValue,
201                     PropertyUtils.getProperty(object, propertyName));
202         } catch (final 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                 final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
209                 if (!BeanUtils.initCause(iae, e)) {
210                     log.error(errorMsg, e);
211                 }
212                 throw iae;
213             }
214         } catch (final IllegalAccessException e) {
215             final String errorMsg = "Unable to access the property provided.";
216             final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
217             if (!BeanUtils.initCause(iae, e)) {
218                 log.error(errorMsg, e);
219             }
220             throw iae;
221         } catch (final InvocationTargetException e) {
222             final String errorMsg = "Exception occurred in property's getter";
223             final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
224             if (!BeanUtils.initCause(iae, e)) {
225                 log.error(errorMsg, e);
226             }
227             throw iae;
228         } catch (final NoSuchMethodException e) {
229             final String errorMsg = "Property not found.";
230             final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
231             if (!BeanUtils.initCause(iae, e)) {
232                 log.error(errorMsg, e);
233             }
234             throw iae;
235         }
236 
237         return evaluation;
238     }
239 
240     /**
241      * Utility method which evaluates whether the actual property value equals the expected property
242      * value.
243      *
244      * @param expected The expected value.
245      * @param actual The actual value.
246      * @return True if they are equal; false otherwise.
247      */
248     protected boolean evaluateValue(final Object expected, final Object actual) {
249         return (expected == actual) || ((expected != null) && expected.equals(actual));
250     }
251 
252     /**
253      * Returns the name of the property which will be evaluated when this <code>Predicate</code> is
254      * executed.
255      *
256      * @return The name of the property which will be evaluated when this <code>Predicate</code> is
257      * executed.
258      */
259     public String getPropertyName() {
260         return propertyName;
261     }
262 
263     /**
264      * Returns the value that the property specified by <code>propertyName</code> will be compared to
265      * when this <code>Predicate</code> executes.
266      *
267      * @return The value that the property specified by <code>propertyName</code> will be compared to
268      * when this <code>Predicate</code> executes.
269      */
270     public Object getPropertyValue() {
271         return propertyValue;
272     }
273 
274     /**
275      * Returns the flag which determines whether <code>null</code> objects in the property path will
276      * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then
277      * if any objects in the property path evaluate to <code>null</code> then the
278      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
279      * not rethrown and <code>false</code> will be returned.  If set to <code>false</code> then if
280      * any objects in the property path evaluate to <code>null</code> then the
281      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
282      * rethrown.
283      *
284      * @return The flag which determines whether <code>null</code> objects in the property path will
285      * genenerate an <code>IllegalArgumentException</code> or not.
286      */
287     public boolean isIgnoreNull() {
288         return ignoreNull;
289     }
290 }