001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.beanutils;
019
020 import org.apache.commons.collections.Predicate;
021 import org.apache.commons.logging.Log;
022 import org.apache.commons.logging.LogFactory;
023
024 import java.lang.reflect.InvocationTargetException;
025
026
027 /**
028 * <p><code>Predicate</code> that evaluates a property value against a specified value.</p>
029 * <p>
030 * An implementation of <code>org.apache.commons.collections.Predicate</code> that evaluates a
031 * property value on the object provided against a specified value and returns <code>true</code>
032 * if equal; <code>false</code> otherwise.
033 * The <code>BeanPropertyValueEqualsPredicate</code> constructor takes two parameters which
034 * determine what property will be evaluated on the target object and what its expected value should
035 * be.
036 * <dl>
037 * <dt>
038 * <strong><code>
039 * <pre>public BeanPropertyValueEqualsPredicate( String propertyName, Object propertyValue )</pre>
040 * </code></strong>
041 * </dt>
042 * <dd>
043 * Will create a <code>Predicate</code> that will evaluate the target object and return
044 * <code>true</code> if the property specified by <code>propertyName</code> has a value which
045 * is equal to the the value specified by <code>propertyValue</code>. Or return
046 * <code>false</code> otherwise.
047 * </dd>
048 * </dl>
049 * </p>
050 * <p>
051 * <strong>Note:</strong> Property names can be a simple, nested, indexed, or mapped property as defined by
052 * <code>org.apache.commons.beanutils.PropertyUtils</code>. If any object in the property path
053 * specified by <code>propertyName</code> is <code>null</code> then the outcome is based on the
054 * value of the <code>ignoreNull</code> attribute.
055 * </p>
056 * <p>
057 * A typical usage might look like:
058 * <code><pre>
059 * // create the closure
060 * BeanPropertyValueEqualsPredicate predicate =
061 * new BeanPropertyValueEqualsPredicate( "activeEmployee", Boolean.FALSE );
062 *
063 * // filter the Collection
064 * CollectionUtils.filter( peopleCollection, predicate );
065 * </pre></code>
066 * </p>
067 * <p>
068 * This would take a <code>Collection</code> of person objects and filter out any people whose
069 * <code>activeEmployee</code> property is <code>false</code>. Assuming...
070 * <ul>
071 * <li>
072 * The top level object in the <code>peeopleCollection</code> is an object which represents a
073 * person.
074 * </li>
075 * <li>
076 * The person object has a <code>getActiveEmployee()</code> method which returns
077 * the boolean value for the object's <code>activeEmployee</code> property.
078 * </li>
079 * </ul>
080 * </p>
081 * <p>
082 * Another typical usage might look like:
083 * <code><pre>
084 * // create the closure
085 * BeanPropertyValueEqualsPredicate predicate =
086 * new BeanPropertyValueEqualsPredicate( "personId", "456-12-1234" );
087 *
088 * // search the Collection
089 * CollectionUtils.find( peopleCollection, predicate );
090 * </pre><code>
091 * </p>
092 * <p>
093 * This would search a <code>Collection</code> of person objects and return the first object whose
094 * <code>personId</code> property value equals <code>456-12-1234</code>. Assuming...
095 * <ul>
096 * <li>
097 * The top level object in the <code>peeopleCollection</code> is an object which represents a
098 * person.
099 * </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 IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
209 if (!BeanUtils.initCause(iae, e)) {
210 log.error(errorMsg, e);
211 }
212 throw iae;
213 }
214 } catch (IllegalAccessException e) {
215 final String errorMsg = "Unable to access the property provided.";
216 IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
217 if (!BeanUtils.initCause(iae, e)) {
218 log.error(errorMsg, e);
219 }
220 throw iae;
221 } catch (InvocationTargetException e) {
222 final String errorMsg = "Exception occurred in property's getter";
223 IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
224 if (!BeanUtils.initCause(iae, e)) {
225 log.error(errorMsg, e);
226 }
227 throw iae;
228 } catch (NoSuchMethodException e) {
229 final String errorMsg = "Property not found.";
230 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(Object expected, 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 }