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: * <p><code>Predicate</code> that evaluates a property value against a specified value.</p>
028: * <p>
029: * An implementation of <code>org.apache.commons.collections.Predicate</code> that evaluates a
030: * property value on the object provided against a specified value and returns <code>true</code>
031: * if equal; <code>false</code> otherwise.
032: * The <code>BeanPropertyValueEqualsPredicate</code> constructor takes two parameters which
033: * determine what property will be evaluated on the target object and what its expected value should
034: * be.
035: * <dl>
036: * <dt>
037: * <strong><code>
038: * <pre>public BeanPropertyValueEqualsPredicate( String propertyName, Object propertyValue )</pre>
039: * </code></strong>
040: * </dt>
041: * <dd>
042: * Will create a <code>Predicate</code> that will evaluate the target object and return
043: * <code>true</code> if the property specified by <code>propertyName</code> has a value which
044: * is equal to the the value specified by <code>propertyValue</code>. Or return
045: * <code>false</code> otherwise.
046: * </dd>
047: * </dl>
048: * </p>
049: * <p>
050: * <strong>Note:</strong> Property names can be a simple, nested, indexed, or mapped property as defined by
051: * <code>org.apache.commons.beanutils.PropertyUtils</code>. If any object in the property path
052: * specified by <code>propertyName</code> is <code>null</code> then the outcome is based on the
053: * value of the <code>ignoreNull</code> attribute.
054: * </p>
055: * <p>
056: * A typical usage might look like:
057: * <code><pre>
058: * // create the closure
059: * BeanPropertyValueEqualsPredicate predicate =
060: * new BeanPropertyValueEqualsPredicate( "activeEmployee", Boolean.FALSE );
061: *
062: * // filter the Collection
063: * CollectionUtils.filter( peopleCollection, predicate );
064: * </pre></code>
065: * </p>
066: * <p>
067: * This would take a <code>Collection</code> of person objects and filter out any people whose
068: * <code>activeEmployee</code> property is <code>false</code>. Assuming...
069: * <ul>
070: * <li>
071: * The top level object in the <code>peeopleCollection</code> is an object which represents a
072: * person.
073: * </li>
074: * <li>
075: * The person object has a <code>getActiveEmployee()</code> method which returns
076: * the boolean value for the object's <code>activeEmployee</code> property.
077: * </li>
078: * </ul>
079: * </p>
080: * <p>
081: * Another typical usage might look like:
082: * <code><pre>
083: * // create the closure
084: * BeanPropertyValueEqualsPredicate predicate =
085: * new BeanPropertyValueEqualsPredicate( "personId", "456-12-1234" );
086: *
087: * // search the Collection
088: * CollectionUtils.find( peopleCollection, predicate );
089: * </pre><code>
090: * </p>
091: * <p>
092: * This would search a <code>Collection</code> of person objects and return the first object whose
093: * <code>personId</code> property value equals <code>456-12-1234</code>. Assuming...
094: * <ul>
095: * <li>
096: * The top level object in the <code>peeopleCollection</code> is an object which represents a
097: * person.
098: * </li>
099: * <li>
100: * The person object has a <code>getPersonId()</code> method which returns
101: * the value for the object's <code>personId</code> property.
102: * </li>
103: * </ul>
104: * </p>
105: *
106: * @author Norm Deane
107: * @see org.apache.commons.beanutils.PropertyUtils
108: * @see org.apache.commons.collections.Predicate
109: */
110: public class BeanPropertyValueEqualsPredicate implements Predicate {
111:
112: /** For logging. */
113: private final Log log = LogFactory.getLog(this .getClass());
114:
115: /**
116: * The name of the property which will be evaluated when this <code>Predicate</code> is executed.
117: */
118: private String propertyName;
119:
120: /**
121: * The value that the property specified by <code>propertyName</code>
122: * will be compared to when this <code>Predicate</code> executes.
123: */
124: private Object propertyValue;
125:
126: /**
127: * <p>Should <code>null</code> objects in the property path be ignored?</p>
128: * <p>
129: * Determines whether <code>null</code> objects in the property path will genenerate an
130: * <code>IllegalArgumentException</code> or not. If set to <code>true</code> then if any objects
131: * in the property path evaluate to <code>null</code> then the
132: * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
133: * not rethrown and <code>false</code> will be returned. If set to <code>false</code> then if
134: * any objects in the property path evaluate to <code>null</code> then the
135: * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
136: * rethrown.
137: * </p>
138: */
139: private boolean ignoreNull;
140:
141: /**
142: * Constructor which takes the name of the property, its expected value to be used in evaluation,
143: * and assumes <code>ignoreNull</code> to be <code>false</code>.
144: *
145: * @param propertyName The name of the property that will be evaluated against the expected value.
146: * @param propertyValue The value to use in object evaluation.
147: * @throws IllegalArgumentException If the property name provided is null or empty.
148: */
149: public BeanPropertyValueEqualsPredicate(String propertyName,
150: 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,
166: Object propertyValue, boolean ignoreNull) {
167: super ();
168:
169: if ((propertyName != null) && (propertyName.length() > 0)) {
170: this .propertyName = propertyName;
171: this .propertyValue = propertyValue;
172: this .ignoreNull = ignoreNull;
173: } else {
174: throw new IllegalArgumentException(
175: "propertyName cannot be null or empty");
176: }
177: }
178:
179: /**
180: * Evaulates the object provided against the criteria specified when this
181: * <code>BeanPropertyValueEqualsPredicate</code> was constructed. Equality is based on
182: * either reference or logical equality as defined by the property object's equals method. If
183: * any object in the property path leading up to the target property is <code>null</code> then
184: * the outcome will be based on the value of the <code>ignoreNull</code> attribute. By default,
185: * <code>ignoreNull</code> is <code>false</code> and would result in an
186: * <code>IllegalArgumentException</code> if an object in the property path leading up to the
187: * target property is <code>null</code>.
188: *
189: * @param object The object to be evaluated.
190: * @return True if the object provided meets all the criteria for this <code>Predicate</code>;
191: * false otherwise.
192: * @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or
193: * NoSuchMethodException is thrown when trying to access the property specified on the object
194: * provided. Or if an object in the property path provided is <code>null</code> and
195: * <code>ignoreNull</code> is set to <code>false</code>.
196: */
197: public boolean evaluate(Object object) {
198:
199: boolean evaluation = false;
200:
201: try {
202: evaluation = evaluateValue(propertyValue, PropertyUtils
203: .getProperty(object, propertyName));
204: } catch (IllegalArgumentException e) {
205: final String errorMsg = "Problem during evaluation. Null value encountered in property path...";
206:
207: if (ignoreNull) {
208: log.warn("WARNING: " + errorMsg, e);
209: } else {
210: log.error("ERROR: " + errorMsg, e);
211: throw e;
212: }
213: } catch (IllegalAccessException e) {
214: final String errorMsg = "Unable to access the property provided.";
215: log.error(errorMsg, e);
216: throw new IllegalArgumentException(errorMsg);
217: } catch (InvocationTargetException e) {
218: final String errorMsg = "Exception occurred in property's getter";
219: log.error(errorMsg, e);
220: throw new IllegalArgumentException(errorMsg);
221: } catch (NoSuchMethodException e) {
222: final String errorMsg = "Property not found.";
223: log.error(errorMsg, e);
224: throw new IllegalArgumentException(errorMsg);
225: }
226:
227: return evaluation;
228: }
229:
230: /**
231: * Utility method which evaluates whether the actual property value equals the expected property
232: * value.
233: *
234: * @param expected The expected value.
235: * @param actual The actual value.
236: * @return True if they are equal; false otherwise.
237: */
238: private boolean evaluateValue(Object expected, Object actual) {
239: return (expected == actual)
240: || ((expected != null) && expected.equals(actual));
241: }
242:
243: /**
244: * Returns the name of the property which will be evaluated when this <code>Predicate</code> is
245: * executed.
246: *
247: * @return The name of the property which will be evaluated when this <code>Predicate</code> is
248: * executed.
249: */
250: public String getPropertyName() {
251: return propertyName;
252: }
253:
254: /**
255: * Returns the value that the property specified by <code>propertyName</code> will be compared to
256: * when this <code>Predicate</code> executes.
257: *
258: * @return The value that the property specified by <code>propertyName</code> will be compared to
259: * when this <code>Predicate</code> executes.
260: */
261: public Object getPropertyValue() {
262: return propertyValue;
263: }
264:
265: /**
266: * Returns the flag which determines whether <code>null</code> objects in the property path will
267: * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then
268: * if 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 but
270: * not rethrown and <code>false</code> will be returned. If set to <code>false</code> then if
271: * any objects in the property path evaluate to <code>null</code> then the
272: * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
273: * rethrown.
274: *
275: * @return The flag which determines whether <code>null</code> objects in the property path will
276: * genenerate an <code>IllegalArgumentException</code> or not.
277: */
278: public boolean isIgnoreNull() {
279: return ignoreNull;
280: }
281: }
|