View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.beanutils;
19  
20  import org.apache.commons.collections.Transformer;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import java.lang.reflect.InvocationTargetException;
25  
26  
27  /**
28   * <p><code>Transformer</code> that outputs a property value.</p>
29   *
30   * <p>An implementation of <code>org.apache.commons.collections.Transformer</code> that transforms
31   * the object provided by returning the value of a specified property of the object.  The
32   * constructor for <code>BeanToPropertyValueTransformer</code> requires the name of the property
33   * that will be used in the transformation.  The property can be a simple, nested, indexed, or
34   * mapped property as defined by <code>org.apache.commons.beanutils.PropertyUtils</code>. If any
35   * object in the property path specified by <code>propertyName</code> is <code>null</code> then the
36   * outcome is based on the value of the <code>ignoreNull</code> attribute.
37   * </p>
38   *
39   * <p>
40   * A typical usage might look like:
41   * <code><pre>
42   * // create the transformer
43   * BeanToPropertyValueTransformer transformer = new BeanToPropertyValueTransformer( "person.address.city" );
44   *
45   * // transform the Collection
46   * Collection peoplesCities = CollectionUtils.collect( peopleCollection, transformer );
47   * </pre></code>
48   * </p>
49   *
50   * <p>
51   * This would take a <code>Collection</code> of person objects and return a <code>Collection</code>
52   * of objects which represents the cities in which each person lived. Assuming...
53   * <ul>
54   *    <li>
55   *       The top level object in the <code>peeopleCollection</code> is an object which represents a
56   *       person.
57   *    </li>
58   *    <li>
59   *       The person object has a <code>getAddress()</code> method which returns an object which
60   *       represents a person's address.
61   *    </li>
62   *    <li>
63   *       The address object has a <code>getCity()</code> method which returns an object which
64   *       represents the city in which a person lives.
65   *    </li>
66   * </ul>
67   *
68   * @version $Id$
69   * @see org.apache.commons.beanutils.PropertyUtils
70   * @see org.apache.commons.collections.Transformer
71   */
72  public class BeanToPropertyValueTransformer implements Transformer {
73  
74      /** For logging. */
75      private final Log log = LogFactory.getLog(this.getClass());
76  
77      /** The name of the property that will be used in the transformation of the object. */
78      private String propertyName;
79  
80      /**
81       * <p>Should null objects on the property path throw an <code>IllegalArgumentException</code>?</p>
82       * <p>
83       * Determines whether <code>null</code> objects in the property path will genenerate an
84       * <code>IllegalArgumentException</code> or not. If set to <code>true</code> then if any objects
85       * in the property path evaluate to <code>null</code> then the
86       * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
87       * not rethrown and <code>null</code> will be returned.  If set to <code>false</code> then if any
88       * objects in the property path evaluate to <code>null</code> then the
89       * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
90       * rethrown.
91       * </p>
92       */
93      private boolean ignoreNull;
94  
95      /**
96       * Constructs a Transformer which does not ignore nulls.
97       * Constructor which takes the name of the property that will be used in the transformation and
98       * assumes <code>ignoreNull</code> to be <code>false</code>.
99       *
100      * @param propertyName The name of the property that will be used in the transformation.
101      * @throws IllegalArgumentException If the <code>propertyName</code> is <code>null</code> or
102      * empty.
103      */
104     public BeanToPropertyValueTransformer(final String propertyName) {
105         this(propertyName, false);
106     }
107 
108     /**
109      * Constructs a Transformer and sets ignoreNull.
110      * Constructor which takes the name of the property that will be used in the transformation and
111      * a boolean which determines whether <code>null</code> objects in the property path will
112      * genenerate an <code>IllegalArgumentException</code> or not.
113      *
114      * @param propertyName The name of the property that will be used in the transformation.
115      * @param ignoreNull Determines whether <code>null</code> objects in the property path will
116      * genenerate an <code>IllegalArgumentException</code> or not.
117      * @throws IllegalArgumentException If the <code>propertyName</code> is <code>null</code> or
118      * empty.
119      */
120     public BeanToPropertyValueTransformer(final String propertyName, final boolean ignoreNull) {
121         super();
122 
123         if ((propertyName != null) && (propertyName.length() > 0)) {
124             this.propertyName = propertyName;
125             this.ignoreNull = ignoreNull;
126         } else {
127             throw new IllegalArgumentException(
128                 "propertyName cannot be null or empty");
129         }
130     }
131 
132     /**
133      * Returns the value of the property named in the transformer's constructor for
134      * the object provided. If any object in the property path leading up to the target property is
135      * <code>null</code> then the outcome will be based on the value of the <code>ignoreNull</code>
136      * attribute. By default, <code>ignoreNull</code> is <code>false</code> and would result in an
137      * <code>IllegalArgumentException</code> if an object in the property path leading up to the
138      * target property is <code>null</code>.
139      *
140      * @param object The object to be transformed.
141      * @return The value of the property named in the transformer's constructor for the object
142      * provided.
143      * @throws IllegalArgumentException If an IllegalAccessException, InvocationTargetException, or
144      * NoSuchMethodException is thrown when trying to access the property specified on the object
145      * provided. Or if an object in the property path provided is <code>null</code> and
146      * <code>ignoreNull</code> is set to <code>false</code>.
147      */
148     public Object transform(final Object object) {
149 
150         Object propertyValue = null;
151 
152         try {
153             propertyValue = PropertyUtils.getProperty(object, propertyName);
154         } catch (final IllegalArgumentException e) {
155             final String errorMsg = "Problem during transformation. Null value encountered in property path...";
156 
157             if (ignoreNull) {
158                 log.warn("WARNING: " + errorMsg + e);
159             } else {
160                 final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
161                 if (!BeanUtils.initCause(iae, e)) {
162                     log.error(errorMsg, e);
163                 }
164                 throw iae;
165             }
166         } catch (final IllegalAccessException e) {
167             final String errorMsg = "Unable to access the property provided.";
168             final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
169             if (!BeanUtils.initCause(iae, e)) {
170                 log.error(errorMsg, e);
171             }
172             throw iae;
173         } catch (final InvocationTargetException e) {
174             final String errorMsg = "Exception occurred in property's getter";
175             final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
176             if (!BeanUtils.initCause(iae, e)) {
177                 log.error(errorMsg, e);
178             }
179             throw iae;
180         } catch (final NoSuchMethodException e) {
181             final String errorMsg = "No property found for name [" +
182                 propertyName + "]";
183             final IllegalArgumentException iae = new IllegalArgumentException(errorMsg);
184             if (!BeanUtils.initCause(iae, e)) {
185                 log.error(errorMsg, e);
186             }
187             throw iae;
188         }
189 
190         return propertyValue;
191     }
192 
193     /**
194      * Returns the name of the property that will be used in the transformation of the bean.
195      *
196      * @return The name of the property that will be used in the transformation of the bean.
197      */
198     public String getPropertyName() {
199         return propertyName;
200     }
201 
202     /**
203      * Returns the flag which determines whether <code>null</code> objects in the property path will
204      * genenerate an <code>IllegalArgumentException</code> or not. If set to <code>true</code> then
205      * if any objects in the property path evaluate to <code>null</code> then the
206      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged but
207      * not rethrown and <code>null</code> will be returned.  If set to <code>false</code> then if any
208      * objects in the property path evaluate to <code>null</code> then the
209      * <code>IllegalArgumentException</code> throw by <code>PropertyUtils</code> will be logged and
210      * rethrown.
211      *
212      * @return The flag which determines whether <code>null</code> objects in the property path will
213      * genenerate an <code>IllegalArgumentException</code> or not.
214      */
215     public boolean isIgnoreNull() {
216         return ignoreNull;
217     }
218 }