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 * https://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 * <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 * </p> 036 * <dl> 037 * <dt> 038 * <strong><code> 039 * public BeanPropertyValueEqualsPredicate(String propertyName, Object propertyValue) 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 * <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 * </p> 058 * <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> 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 * </p> 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 * Another typical usage might look like: 082 * </p> 083 * <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> 091 * <p> 092 * This would search a <code>Collection</code> of person objects and return the first object whose 093 * </p> 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 * 106 * @see org.apache.commons.beanutils.PropertyUtils 107 * @see org.apache.commons.collections.Predicate 108 */ 109public class BeanPropertyValueEqualsPredicate implements Predicate { 110 111 /** For logging. */ 112 private final Log log = LogFactory.getLog(this.getClass()); 113 114 /** 115 * The name of the property which will be evaluated when this <code>Predicate</code> is executed. 116 */ 117 private final String propertyName; 118 119 /** 120 * The value that the property specified by <code>propertyName</code> 121 * will be compared to when this <code>Predicate</code> executes. 122 */ 123 private final Object propertyValue; 124 125 /** 126 * <p>Should <code>null</code> objects in the property path be ignored?</p> 127 * <p> 128 * Determines whether <code>null</code> objects in the property path will genenerate an 129 * <code>IllegalArgumentException</code> or not. If set to <code>true</code> then if any objects 130 * in the property path evaluate to <code>null</code> then the 131 * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but 132 * not rethrown and <code>false</code> will be returned. If set to <code>false</code> then if 133 * any objects in the property path evaluate to <code>null</code> then the 134 * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and 135 * rethrown. 136 * </p> 137 */ 138 private final boolean ignoreNull; 139 140 /** 141 * Constructor which takes the name of the property, its expected value to be used in evaluation, 142 * and assumes <code>ignoreNull</code> to be <code>false</code>. 143 * 144 * @param propertyName The name of the property that will be evaluated against the expected value. 145 * @param propertyValue The value to use in object evaluation. 146 * @throws IllegalArgumentException If the property name provided is null or empty. 147 */ 148 public BeanPropertyValueEqualsPredicate(final String propertyName, final Object propertyValue) { 149 this(propertyName, propertyValue, false); 150 } 151 152 /** 153 * Constructor which takes the name of the property, its expected value 154 * to be used in evaluation, and a boolean which determines whether <code>null</code> objects in 155 * the property path will genenerate an <code>IllegalArgumentException</code> or not. 156 * 157 * @param propertyName The name of the property that will be evaluated against the expected value. 158 * @param propertyValue The value to use in object evaluation. 159 * @param ignoreNull Determines whether <code>null</code> objects in the property path will 160 * genenerate an <code>IllegalArgumentException</code> or not. 161 * @throws IllegalArgumentException If the property name provided is null or empty. 162 */ 163 public BeanPropertyValueEqualsPredicate(final String propertyName, final Object propertyValue, final boolean ignoreNull) { 164 if (propertyName == null || propertyName.length() <= 0) { 165 throw new IllegalArgumentException("propertyName cannot be null or empty"); 166 } 167 this.propertyName = propertyName; 168 this.propertyValue = propertyValue; 169 this.ignoreNull = ignoreNull; 170 } 171 172 /** 173 * Evaulates the object provided against the criteria specified when this 174 * <code>BeanPropertyValueEqualsPredicate</code> was constructed. Equality is based on 175 * either reference or logical equality as defined by the property object's equals method. If 176 * any object in the property path leading up to the target property is <code>null</code> then 177 * the outcome will be based on the value of the <code>ignoreNull</code> attribute. By default, 178 * <code>ignoreNull</code> is <code>false</code> and would result in an 179 * <code>IllegalArgumentException</code> if an object in the property path leading up to the 180 * target property is <code>null</code>. 181 * 182 * @param object The object to be evaluated. 183 * @return True if the object provided meets all the criteria for this <code>Predicate</code>; 184 * false otherwise. 185 * @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or 186 * NoSuchMethodException is thrown when trying to access the property specified on the object 187 * provided. Or if an object in the property path provided is <code>null</code> and 188 * <code>ignoreNull</code> is set to <code>false</code>. 189 */ 190 @Override 191 public boolean evaluate(final Object object) { 192 193 boolean evaluation = false; 194 195 try { 196 evaluation = evaluateValue(propertyValue, 197 PropertyUtils.getProperty(object, propertyName)); 198 } catch (final IllegalArgumentException e) { 199 final String errorMsg = "Problem during evaluation. Null value encountered in property path..."; 200 201 if (!ignoreNull) { 202 throw new IllegalArgumentException(errorMsg, e); 203 } 204 log.warn("WARNING: " + errorMsg + e); 205 } catch (final IllegalAccessException e) { 206 final String errorMsg = "Unable to access the property provided."; 207 throw new IllegalArgumentException(errorMsg, e); 208 } catch (final InvocationTargetException e) { 209 final String errorMsg = "Exception occurred in property's getter"; 210 throw new IllegalArgumentException(errorMsg, e); 211 } catch (final NoSuchMethodException e) { 212 final String errorMsg = "Property not found."; 213 throw new IllegalArgumentException(errorMsg, e); 214 } 215 216 return evaluation; 217 } 218 219 /** 220 * Utility method which evaluates whether the actual property value equals the expected property 221 * value. 222 * 223 * @param expected The expected value. 224 * @param actual The actual value. 225 * @return True if they are equal; false otherwise. 226 */ 227 protected boolean evaluateValue(final Object expected, final Object actual) { 228 return expected == actual || expected != null && expected.equals(actual); 229 } 230 231 /** 232 * Returns the name of the property which will be evaluated when this <code>Predicate</code> is 233 * executed. 234 * 235 * @return The name of the property which will be evaluated when this <code>Predicate</code> is 236 * executed. 237 */ 238 public String getPropertyName() { 239 return propertyName; 240 } 241 242 /** 243 * Returns the value that the property specified by <code>propertyName</code> will be compared to 244 * when this <code>Predicate</code> executes. 245 * 246 * @return The value that the property specified by <code>propertyName</code> will be compared to 247 * when this <code>Predicate</code> executes. 248 */ 249 public Object getPropertyValue() { 250 return propertyValue; 251 } 252 253 /** 254 * Returns the flag which determines whether <code>null</code> objects in the property path will 255 * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then 256 * if any objects in the property path evaluate to <code>null</code> then the 257 * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but 258 * not rethrown and <code>false</code> will be returned. If set to <code>false</code> then if 259 * any objects in the property path evaluate to <code>null</code> then the 260 * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and 261 * rethrown. 262 * 263 * @return The flag which determines whether <code>null</code> objects in the property path will 264 * genenerate an <code>IllegalArgumentException</code> or not. 265 */ 266 public boolean isIgnoreNull() { 267 return ignoreNull; 268 } 269}