1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 }