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.Closure;
  18. import org.apache.commons.logging.Log;
  19. import org.apache.commons.logging.LogFactory;
  20. import java.lang.reflect.InvocationTargetException;
  21. /**
  22. * <p><code>Closure</code> that sets a property.</p>
  23. * <p>
  24. * An implementation of <code>org.apache.commons.collections.Closure</code> that updates
  25. * a specified property on the object provided with a specified value.
  26. * The <code>BeanPropertyValueChangeClosure</code> constructor takes two parameters which determine
  27. * what property will be updated and with what value.
  28. * <dl>
  29. * <dt>
  30. * <b><code><pre>public BeanPropertyValueChangeClosure( String propertyName, Object propertyValue )</pre></code></b>
  31. * </dt>
  32. * <dd>
  33. * Will create a <code>Closure</code> that will update an object by setting the property
  34. * specified by <code>propertyName</code> to the value specified by <code>propertyValue</code>.
  35. * </dd>
  36. * </dl>
  37. *
  38. * <p/>
  39. * <strong>Note:</strong> Property names can be a simple, nested, indexed, or mapped property as defined by
  40. * <code>org.apache.commons.beanutils.PropertyUtils</code>. If any object in the property path
  41. * specified by <code>propertyName</code> is <code>null</code> then the outcome is based on the
  42. * value of the <code>ignoreNull</code> attribute.
  43. *
  44. * <p/>
  45. * A typical usage might look like:
  46. * <code><pre>
  47. * // create the closure
  48. * BeanPropertyValueChangeClosure closure =
  49. * new BeanPropertyValueChangeClosure( "activeEmployee", Boolean.TRUE );
  50. *
  51. * // update the Collection
  52. * CollectionUtils.forAllDo( peopleCollection, closure );
  53. * </pre></code>
  54. * <p/>
  55. *
  56. * This would take a <code>Collection</code> of person objects and update the
  57. * <code>activeEmployee</code> property of each object in the <code>Collection</code> to
  58. * <code>true</code>. Assuming...
  59. * <ul>
  60. * <li>
  61. * The top level object in the <code>peopleCollection</code> is an object which represents a
  62. * person.
  63. * </li>
  64. * <li>
  65. * The person object has a <code>setActiveEmployee( boolean )</code> method which updates
  66. * the value for the object's <code>activeEmployee</code> property.
  67. * </li>
  68. * </ul>
  69. *
  70. * @author Norm Deane
  71. * @see org.apache.commons.beanutils.PropertyUtils
  72. * @see org.apache.commons.collections.Closure
  73. */
  74. public class BeanPropertyValueChangeClosure implements Closure {
  75. /** For logging. */
  76. private final Log log = LogFactory.getLog(this.getClass());
  77. /**
  78. * The name of the property which will be updated when this <code>Closure</code> executes.
  79. */
  80. private String propertyName;
  81. /**
  82. * The value that the property specified by <code>propertyName</code>
  83. * will be updated to when this <code>Closure</code> executes.
  84. */
  85. private Object propertyValue;
  86. /**
  87. * Determines whether <code>null</code> objects in the property path will genenerate an
  88. * <code>IllegalArgumentException</code> or not. If set to <code>true</code> then if any objects
  89. * in the property path leading up to the target property evaluate to <code>null</code> then the
  90. * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
  91. * not rethrown. If set to <code>false</code> then if any objects in the property path leading
  92. * up to the target property evaluate to <code>null</code> then the
  93. * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
  94. * rethrown.
  95. */
  96. private boolean ignoreNull;
  97. /**
  98. * Constructor which takes the name of the property to be changed, the new value to set
  99. * the property to, and assumes <code>ignoreNull</code> to be <code>false</code>.
  100. *
  101. * @param propertyName The name of the property that will be updated with the value specified by
  102. * <code>propertyValue</code>.
  103. * @param propertyValue The value that <code>propertyName</code> will be set to on the target
  104. * object.
  105. * @throws IllegalArgumentException If the propertyName provided is null or empty.
  106. */
  107. public BeanPropertyValueChangeClosure(String propertyName, Object propertyValue) {
  108. this(propertyName, propertyValue, false);
  109. }
  110. /**
  111. * Constructor which takes the name of the property to be changed, the new value to set
  112. * the property to and a boolean which determines whether <code>null</code> objects in the
  113. * property path will genenerate an <code>IllegalArgumentException</code> or not.
  114. *
  115. * @param propertyName The name of the property that will be updated with the value specified by
  116. * <code>propertyValue</code>.
  117. * @param propertyValue The value that <code>propertyName</code> will be set to on the target
  118. * object.
  119. * @param ignoreNull Determines whether <code>null</code> objects in the property path will
  120. * genenerate an <code>IllegalArgumentException</code> or not.
  121. * @throws IllegalArgumentException If the propertyName provided is null or empty.
  122. */
  123. public BeanPropertyValueChangeClosure(String propertyName, Object propertyValue, boolean ignoreNull) {
  124. super();
  125. if ((propertyName != null) && (propertyName.length() > 0)) {
  126. this.propertyName = propertyName;
  127. this.propertyValue = propertyValue;
  128. this.ignoreNull = ignoreNull;
  129. } else {
  130. throw new IllegalArgumentException("propertyName cannot be null or empty");
  131. }
  132. }
  133. /**
  134. * Updates the target object provided using the property update criteria provided when this
  135. * <code>BeanPropertyValueChangeClosure</code> was constructed. If any object in the property
  136. * path leading up to the target property is <code>null</code> then the outcome will be based on
  137. * the value of the <code>ignoreNull</code> attribute. By default, <code>ignoreNull</code> is
  138. * <code>false</code> and would result in an <code>IllegalArgumentException</code> if an object
  139. * in the property path leading up to the target property is <code>null</code>.
  140. *
  141. * @param object The object to be updated.
  142. * @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or
  143. * NoSuchMethodException is thrown when trying to access the property specified on the object
  144. * provided. Or if an object in the property path provided is <code>null</code> and
  145. * <code>ignoreNull</code> is set to <code>false</code>.
  146. */
  147. public void execute(Object object) {
  148. try {
  149. PropertyUtils.setProperty(object, propertyName, propertyValue);
  150. } catch (IllegalArgumentException e) {
  151. final String errorMsg = "Unable to execute Closure. Null value encountered in property path...";
  152. if (ignoreNull) {
  153. log.warn("WARNING: " + errorMsg, e);
  154. } else {
  155. log.error("ERROR: " + errorMsg, e);
  156. throw e;
  157. }
  158. } catch (IllegalAccessException e) {
  159. final String errorMsg = "Unable to access the property provided.";
  160. log.error(errorMsg, e);
  161. throw new IllegalArgumentException(errorMsg);
  162. } catch (InvocationTargetException e) {
  163. final String errorMsg = "Exception occurred in property's getter";
  164. log.error(errorMsg, e);
  165. throw new IllegalArgumentException(errorMsg);
  166. } catch (NoSuchMethodException e) {
  167. final String errorMsg = "Property not found";
  168. log.error(errorMsg, e);
  169. throw new IllegalArgumentException(errorMsg);
  170. }
  171. }
  172. /**
  173. * Returns the name of the property which will be updated when this <code>Closure</code> executes.
  174. *
  175. * @return The name of the property which will be updated when this <code>Closure</code> executes.
  176. */
  177. public String getPropertyName() {
  178. return propertyName;
  179. }
  180. /**
  181. * Returns the value that the property specified by <code>propertyName</code>
  182. * will be updated to when this <code>Closure</code> executes.
  183. *
  184. * @return The value that the property specified by <code>propertyName</code>
  185. * will be updated to when this <code>Closure</code> executes.
  186. */
  187. public Object getPropertyValue() {
  188. return propertyValue;
  189. }
  190. /**
  191. * Returns the flag that determines whether <code>null</code> objects in the property path will
  192. * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then
  193. * if any objects in the property path leading up to the target property evaluate to
  194. * <code>null</code> then the <code>IllegalArgumentException</code> throw by
  195. * <code>PropertyUtils</code> will be logged but not rethrown. If set to <code>false</code> then
  196. * if any objects in the property path leading up to the target property evaluate to
  197. * <code>null</code> then the <code>IllegalArgumentException</code> throw by
  198. * <code>PropertyUtils</code> will be logged and rethrown.
  199. *
  200. * @return The flag that determines whether <code>null</code> objects in the property path will
  201. * genenerate an <code>IllegalArgumentException</code> or not.
  202. */
  203. public boolean isIgnoreNull() {
  204. return ignoreNull;
  205. }
  206. }