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 018package org.apache.commons.beanutils; 019 020import java.lang.reflect.InvocationTargetException; 021 022import org.apache.commons.collections.Predicate; 023import org.apache.commons.logging.Log; 024import org.apache.commons.logging.LogFactory; 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 * @version $Id$ 108 * @see org.apache.commons.beanutils.PropertyUtils 109 * @see org.apache.commons.collections.Predicate 110 */ 111public 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}