1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.beanutils;
  17. import org.apache.commons.collections.Predicate;
  18. import org.apache.commons.logging.Log;
  19. import org.apache.commons.logging.LogFactory;
  20. import java.lang.reflect.InvocationTargetException;
  21. /**
  22. * <p><code>Predicate</code> that evaluates a property value against a specified value.</p>
  23. * <p>
  24. * An implementation of <code>org.apache.commons.collections.Predicate</code> that evaluates a
  25. * property value on the object provided against a specified value and returns <code>true</code>
  26. * if equal; <code>false</code> otherwise.
  27. * The <code>BeanPropertyValueEqualsPredicate</code> constructor takes two parameters which
  28. * determine what property will be evaluated on the target object and what its expected value should
  29. * be.
  30. * <dl>
  31. * <dt>
  32. * <strong><code><pre>public BeanPropertyValueEqualsPredicate( String propertyName, Object propertyValue )</pre></code></strong>
  33. * </dt>
  34. * <dd>
  35. * Will create a <code>Predicate</code> that will evaluate the target object and return
  36. * <code>true</code> if the property specified by <code>propertyName</code> has a value which
  37. * is equal to the the value specified by <code>propertyValue</code>. Or return
  38. * <code>false</code> otherwise.
  39. * </dd>
  40. * </dl>
  41. * </p>
  42. * <p>
  43. * <strong>Note:</strong> Property names can be a simple, nested, indexed, or mapped property as defined by
  44. * <code>org.apache.commons.beanutils.PropertyUtils</code>. If any object in the property path
  45. * specified by <code>propertyName</code> is <code>null</code> then the outcome is based on the
  46. * value of the <code>ignoreNull</code> attribute.
  47. * </p>
  48. * <p>
  49. * A typical usage might look like:
  50. * <code><pre>
  51. * // create the closure
  52. * BeanPropertyValueEqualsPredicate predicate =
  53. * new BeanPropertyValueEqualsPredicate( "activeEmployee", Boolean.FALSE );
  54. *
  55. * // filter the Collection
  56. * CollectionUtils.filter( peopleCollection, predicate );
  57. * </pre></code>
  58. * </p>
  59. * <p>
  60. * This would take a <code>Collection</code> of person objects and filter out any people whose
  61. * <code>activeEmployee</code> property is <code>false</code>. Assuming...
  62. * <ul>
  63. * <li>
  64. * The top level object in the <code>peeopleCollection</code> is an object which represents a
  65. * person.
  66. * </li>
  67. * <li>
  68. * The person object has a <code>getActiveEmployee()</code> method which returns
  69. * the boolean value for the object's <code>activeEmployee</code> property.
  70. * </li>
  71. * </ul>
  72. * </p>
  73. * <p>
  74. * Another typical usage might look like:
  75. * <code><pre>
  76. * // create the closure
  77. * BeanPropertyValueEqualsPredicate predicate =
  78. * new BeanPropertyValueEqualsPredicate( "personId", "456-12-1234" );
  79. *
  80. * // search the Collection
  81. * CollectionUtils.find( peopleCollection, predicate );
  82. * </pre><code>
  83. * </p>
  84. * <p>
  85. * This would search a <code>Collection</code> of person objects and return the first object whose
  86. * <code>personId</code> property value equals <code>456-12-1234</code>. Assuming...
  87. * <ul>
  88. * <li>
  89. * The top level object in the <code>peeopleCollection</code> is an object which represents a
  90. * person.
  91. * </li>
  92. * <li>
  93. * The person object has a <code>getPersonId()</code> method which returns
  94. * the value for the object's <code>personId</code> property.
  95. * </li>
  96. * </ul>
  97. * </p>
  98. *
  99. * @author Norm Deane
  100. * @see org.apache.commons.beanutils.PropertyUtils
  101. * @see org.apache.commons.collections.Predicate
  102. */
  103. public class BeanPropertyValueEqualsPredicate implements Predicate {
  104. /** For logging. */
  105. private final Log log = LogFactory.getLog(this.getClass());
  106. /**
  107. * The name of the property which will be evaluated when this <code>Predicate</code> is executed.
  108. */
  109. private String propertyName;
  110. /**
  111. * The value that the property specified by <code>propertyName</code>
  112. * will be compared to when this <code>Predicate</code> executes.
  113. */
  114. private Object propertyValue;
  115. /**
  116. * <p>Should <code>null</code> objects in the property path be ignored?</p>
  117. * <p>
  118. * Determines whether <code>null</code> objects in the property path will genenerate an
  119. * <code>IllegalArgumentException</code> or not. If set to <code>true</code> then if any objects
  120. * in the property path evaluate to <code>null</code> then the
  121. * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
  122. * not rethrown and <code>false</code> will be returned. If set to <code>false</code> then if
  123. * any objects in the property path evaluate to <code>null</code> then the
  124. * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
  125. * rethrown.
  126. * </p>
  127. */
  128. private boolean ignoreNull;
  129. /**
  130. * Constructor which takes the name of the property, its expected value to be used in evaluation,
  131. * and assumes <code>ignoreNull</code> to be <code>false</code>.
  132. *
  133. * @param propertyName The name of the property that will be evaluated against the expected value.
  134. * @param propertyValue The value to use in object evaluation.
  135. * @throws IllegalArgumentException If the property name provided is null or empty.
  136. */
  137. public BeanPropertyValueEqualsPredicate(String propertyName, Object propertyValue) {
  138. this(propertyName, propertyValue, false);
  139. }
  140. /**
  141. * Constructor which takes the name of the property, its expected value
  142. * to be used in evaluation, and a boolean which determines whether <code>null</code> objects in
  143. * the property path will genenerate an <code>IllegalArgumentException</code> or not.
  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. * @param ignoreNull Determines whether <code>null</code> objects in the property path will
  148. * genenerate an <code>IllegalArgumentException</code> or not.
  149. * @throws IllegalArgumentException If the property name provided is null or empty.
  150. */
  151. public BeanPropertyValueEqualsPredicate(String propertyName, Object propertyValue, boolean ignoreNull) {
  152. super();
  153. if ((propertyName != null) && (propertyName.length() > 0)) {
  154. this.propertyName = propertyName;
  155. this.propertyValue = propertyValue;
  156. this.ignoreNull = ignoreNull;
  157. } else {
  158. throw new IllegalArgumentException("propertyName cannot be null or empty");
  159. }
  160. }
  161. /**
  162. * Evaulates the object provided against the criteria specified when this
  163. * <code>BeanPropertyValueEqualsPredicate</code> was constructed. Equality is based on
  164. * either reference or logical equality as defined by the property object's equals method. If
  165. * any object in the property path leading up to the target property is <code>null</code> then
  166. * the outcome will be based on the value of the <code>ignoreNull</code> attribute. By default,
  167. * <code>ignoreNull</code> is <code>false</code> and would result in an
  168. * <code>IllegalArgumentException</code> if an object in the property path leading up to the
  169. * target property is <code>null</code>.
  170. *
  171. * @param object The object to be evaluated.
  172. * @return True if the object provided meets all the criteria for this <code>Predicate</code>
  173. * false otherwise.
  174. * @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or
  175. * NoSuchMethodException is thrown when trying to access the property specified on the object
  176. * provided. Or if an object in the property path provided is <code>null</code> and
  177. * <code>ignoreNull</code> is set to <code>false</code>.
  178. */
  179. public boolean evaluate(Object object) {
  180. boolean evaluation = false;
  181. try {
  182. evaluation = evaluateValue(propertyValue,
  183. PropertyUtils.getProperty(object, propertyName));
  184. } catch (IllegalArgumentException e) {
  185. final String errorMsg = "Problem during evaluation. Null value encountered in property path...";
  186. if (ignoreNull) {
  187. log.warn("WARNING: " + errorMsg, e);
  188. } else {
  189. log.error("ERROR: " + errorMsg, e);
  190. throw e;
  191. }
  192. } catch (IllegalAccessException e) {
  193. final String errorMsg = "Unable to access the property provided.";
  194. log.error(errorMsg, e);
  195. throw new IllegalArgumentException(errorMsg);
  196. } catch (InvocationTargetException e) {
  197. final String errorMsg = "Exception occurred in property's getter";
  198. log.error(errorMsg, e);
  199. throw new IllegalArgumentException(errorMsg);
  200. } catch (NoSuchMethodException e) {
  201. final String errorMsg = "Property not found.";
  202. log.error(errorMsg, e);
  203. throw new IllegalArgumentException(errorMsg);
  204. }
  205. return evaluation;
  206. }
  207. /**
  208. * Utility method which evaluates whether the actual property value equals the expected property
  209. * value.
  210. *
  211. * @param expected The expected value.
  212. * @param actual The actual value.
  213. * @return True if they are equal; false otherwise.
  214. */
  215. private boolean evaluateValue(Object expected, Object actual) {
  216. return (expected == actual) || ((expected != null) && expected.equals(actual));
  217. }
  218. /**
  219. * Returns the name of the property which will be evaluated when this <code>Predicate</code> is
  220. * executed.
  221. *
  222. * @return The name of the property which will be evaluated when this <code>Predicate</code> is
  223. * executed.
  224. */
  225. public String getPropertyName() {
  226. return propertyName;
  227. }
  228. /**
  229. * Returns the value that the property specified by <code>propertyName</code> will be compared to
  230. * when this <code>Predicate</code> executes.
  231. *
  232. * @return The value that the property specified by <code>propertyName</code> will be compared to
  233. * when this <code>Predicate</code> executes.
  234. */
  235. public Object getPropertyValue() {
  236. return propertyValue;
  237. }
  238. /**
  239. * Returns the flag which determines whether <code>null</code> objects in the property path will
  240. * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then
  241. * if any objects in the property path evaluate to <code>null</code> then the
  242. * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
  243. * not rethrown and <code>false</code> will be returned. If set to <code>false</code> then if
  244. * any objects in the property path evaluate to <code>null</code> then the
  245. * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
  246. * rethrown.
  247. *
  248. * @return The flag which determines whether <code>null</code> objects in the property path will
  249. * genenerate an <code>IllegalArgumentException</code> or not.
  250. */
  251. public boolean isIgnoreNull() {
  252. return ignoreNull;
  253. }
  254. }