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  
19  package org.apache.commons.beanutils;
20  
21  import java.io.Serializable;
22  import java.lang.reflect.InvocationTargetException;
23  
24  
25  /**
26   * <p>Implementation of <code>DynaBean</code> that wraps a standard JavaBean
27   * instance, so that DynaBean APIs can be used to access its properties.</p>
28   *
29   * <p>
30   * The most common use cases for this class involve wrapping an existing java bean.
31   * (This makes it different from the typical use cases for other <code>DynaBean</code>'s.)
32   * For example:
33   * </p>
34   * <code><pre>
35   *  Object aJavaBean = ...;
36   *  ...
37   *  DynaBean db = new WrapDynaBean(aJavaBean);
38   *  ...
39   * </pre></code>
40   *
41   * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation does not
42   * support the <code>contains()</code> and <code>remove()</code> methods.</p>
43   *
44   * @version $Id$
45   */
46  
47  public class WrapDynaBean implements DynaBean, Serializable {
48  
49  
50      // ---------------------------------------------------------- Constructors
51  
52  
53      /**
54       * Construct a new <code>DynaBean</code> associated with the specified
55       * JavaBean instance.
56       *
57       * @param instance JavaBean instance to be wrapped
58       */
59      public WrapDynaBean(final Object instance) {
60  
61          this(instance, null);
62  
63      }
64  
65      /**
66       * Creates a new instance of {@code WrapDynaBean}, associates it with the specified
67       * JavaBean instance, and initializes the bean's {@code DynaClass}. Using this
68       * constructor this {@code WrapDynaBean} instance can be assigned a class which has
69       * been configured externally. If no {@code WrapDynaClass} is provided, a new one is
70       * created using a standard mechanism.
71       *
72       * @param instance JavaBean instance to be wrapped
73       * @param cls the optional {@code WrapDynaClass} to be used for this bean
74       * @since 1.9
75       */
76      public WrapDynaBean(final Object instance, final WrapDynaClass cls) {
77  
78          this.instance = instance;
79          this.dynaClass = (cls != null) ? cls : (WrapDynaClass) getDynaClass();
80  
81      }
82  
83      // ---------------------------------------------------- Instance Variables
84  
85  
86      /**
87       * The <code>DynaClass</code> "base class" that this DynaBean
88       * is associated with.
89       */
90      protected transient WrapDynaClass dynaClass = null;
91  
92  
93      /**
94       * The JavaBean instance wrapped by this WrapDynaBean.
95       */
96      protected Object instance = null;
97  
98  
99      // ------------------------------------------------------ DynaBean Methods
100 
101 
102     /**
103      * Does the specified mapped property contain a value for the specified
104      * key value?
105      *
106      * @param name Name of the property to check
107      * @param key Name of the key to check
108      * @return <code>true<code> if the mapped property contains a value for
109      * the specified key, otherwise <code>false</code>
110      *
111      * @throws IllegalArgumentException if there is no property
112      *  of the specified name
113      */
114     public boolean contains(final String name, final String key) {
115 
116         throw new UnsupportedOperationException
117                 ("WrapDynaBean does not support contains()");
118 
119     }
120 
121 
122     /**
123      * Return the value of a simple property with the specified name.
124      *
125      * @param name Name of the property whose value is to be retrieved
126      * @return The property's value
127      *
128      * @throws IllegalArgumentException if there is no property
129      *  of the specified name
130      */
131     public Object get(final String name) {
132 
133         Object value = null;
134         try {
135             value = getPropertyUtils().getSimpleProperty(instance, name);
136         } catch (final InvocationTargetException ite) {
137             final Throwable cause = ite.getTargetException();
138             throw new IllegalArgumentException
139                     ("Error reading property '" + name +
140                               "' nested exception - " + cause);
141         } catch (final Throwable t) {
142             throw new IllegalArgumentException
143                     ("Error reading property '" + name +
144                               "', exception - " + t);
145         }
146         return (value);
147 
148     }
149 
150 
151     /**
152      * Return the value of an indexed property with the specified name.
153      *
154      * @param name Name of the property whose value is to be retrieved
155      * @param index Index of the value to be retrieved
156      * @return The indexed property's value
157      *
158      * @throws IllegalArgumentException if there is no property
159      *  of the specified name
160      * @throws IllegalArgumentException if the specified property
161      *  exists, but is not indexed
162      * @throws IndexOutOfBoundsException if the specified index
163      *  is outside the range of the underlying property
164      * @throws NullPointerException if no array or List has been
165      *  initialized for this property
166      */
167     public Object get(final String name, final int index) {
168 
169         Object value = null;
170         try {
171             value = getPropertyUtils().getIndexedProperty(instance, name, index);
172         } catch (final IndexOutOfBoundsException e) {
173             throw e;
174         } catch (final InvocationTargetException ite) {
175             final Throwable cause = ite.getTargetException();
176             throw new IllegalArgumentException
177                     ("Error reading indexed property '" + name +
178                               "' nested exception - " + cause);
179         } catch (final Throwable t) {
180             throw new IllegalArgumentException
181                     ("Error reading indexed property '" + name +
182                               "', exception - " + t);
183         }
184         return (value);
185 
186     }
187 
188 
189     /**
190      * Return the value of a mapped property with the specified name,
191      * or <code>null</code> if there is no value for the specified key.
192      *
193      * @param name Name of the property whose value is to be retrieved
194      * @param key Key of the value to be retrieved
195      * @return The mapped property's value
196      *
197      * @throws IllegalArgumentException if there is no property
198      *  of the specified name
199      * @throws IllegalArgumentException if the specified property
200      *  exists, but is not mapped
201      */
202     public Object get(final String name, final String key) {
203 
204         Object value = null;
205         try {
206             value = getPropertyUtils().getMappedProperty(instance, name, key);
207         } catch (final InvocationTargetException ite) {
208             final Throwable cause = ite.getTargetException();
209             throw new IllegalArgumentException
210                     ("Error reading mapped property '" + name +
211                               "' nested exception - " + cause);
212         } catch (final Throwable t) {
213             throw new IllegalArgumentException
214                     ("Error reading mapped property '" + name +
215                               "', exception - " + t);
216         }
217         return (value);
218 
219     }
220 
221 
222     /**
223      * Return the <code>DynaClass</code> instance that describes the set of
224      * properties available for this DynaBean.
225      * @return The associated DynaClass
226      */
227     public DynaClass getDynaClass() {
228 
229         if (dynaClass == null) {
230             dynaClass = WrapDynaClass.createDynaClass(instance.getClass());
231         }
232 
233         return (this.dynaClass);
234 
235     }
236 
237 
238     /**
239      * Remove any existing value for the specified key on the
240      * specified mapped property.
241      *
242      * @param name Name of the property for which a value is to
243      *  be removed
244      * @param key Key of the value to be removed
245      *
246      * @throws IllegalArgumentException if there is no property
247      *  of the specified name
248      */
249     public void remove(final String name, final String key) {
250 
251 
252         throw new UnsupportedOperationException
253                 ("WrapDynaBean does not support remove()");
254 
255     }
256 
257 
258     /**
259      * Set the value of a simple property with the specified name.
260      *
261      * @param name Name of the property whose value is to be set
262      * @param value Value to which this property is to be set
263      *
264      * @throws ConversionException if the specified value cannot be
265      *  converted to the type required for this property
266      * @throws IllegalArgumentException if there is no property
267      *  of the specified name
268      * @throws NullPointerException if an attempt is made to set a
269      *  primitive property to null
270      */
271     public void set(final String name, final Object value) {
272 
273         try {
274             getPropertyUtils().setSimpleProperty(instance, name, value);
275         } catch (final InvocationTargetException ite) {
276             final Throwable cause = ite.getTargetException();
277             throw new IllegalArgumentException
278                     ("Error setting property '" + name +
279                               "' nested exception -" + cause);
280         } catch (final Throwable t) {
281             throw new IllegalArgumentException
282                     ("Error setting property '" + name +
283                               "', exception - " + t);
284         }
285 
286     }
287 
288 
289     /**
290      * Set the value of an indexed property with the specified name.
291      *
292      * @param name Name of the property whose value is to be set
293      * @param index Index of the property to be set
294      * @param value Value to which this property is to be set
295      *
296      * @throws ConversionException if the specified value cannot be
297      *  converted to the type required for this property
298      * @throws IllegalArgumentException if there is no property
299      *  of the specified name
300      * @throws IllegalArgumentException if the specified property
301      *  exists, but is not indexed
302      * @throws IndexOutOfBoundsException if the specified index
303      *  is outside the range of the underlying property
304      */
305     public void set(final String name, final int index, final Object value) {
306 
307         try {
308             getPropertyUtils().setIndexedProperty(instance, name, index, value);
309         } catch (final IndexOutOfBoundsException e) {
310             throw e;
311         } catch (final InvocationTargetException ite) {
312             final Throwable cause = ite.getTargetException();
313             throw new IllegalArgumentException
314                     ("Error setting indexed property '" + name +
315                               "' nested exception - " + cause);
316         } catch (final Throwable t) {
317             throw new IllegalArgumentException
318                     ("Error setting indexed property '" + name +
319                               "', exception - " + t);
320         }
321 
322     }
323 
324 
325     /**
326      * Set the value of a mapped property with the specified name.
327      *
328      * @param name Name of the property whose value is to be set
329      * @param key Key of the property to be set
330      * @param value Value to which this property is to be set
331      *
332      * @throws ConversionException if the specified value cannot be
333      *  converted to the type required for this property
334      * @throws IllegalArgumentException if there is no property
335      *  of the specified name
336      * @throws IllegalArgumentException if the specified property
337      *  exists, but is not mapped
338      */
339     public void set(final String name, final String key, final Object value) {
340 
341         try {
342             getPropertyUtils().setMappedProperty(instance, name, key, value);
343         } catch (final InvocationTargetException ite) {
344             final Throwable cause = ite.getTargetException();
345             throw new IllegalArgumentException
346                     ("Error setting mapped property '" + name +
347                               "' nested exception - " + cause);
348         } catch (final Throwable t) {
349             throw new IllegalArgumentException
350                     ("Error setting mapped property '" + name +
351                               "', exception - " + t);
352         }
353 
354     }
355 
356     /**
357      * Gets the bean instance wrapped by this DynaBean.
358      * For most common use cases,
359      * this object should already be known
360      * and this method safely be ignored.
361      * But some creators of frameworks using <code>DynaBean</code>'s may
362      * find this useful.
363      *
364      * @return the java bean Object wrapped by this <code>DynaBean</code>
365      */
366     public Object getInstance() {
367         return instance;
368     }
369 
370 
371     // ------------------------------------------------------ Protected Methods
372 
373 
374     /**
375      * Return the property descriptor for the specified property name.
376      *
377      * @param name Name of the property for which to retrieve the descriptor
378      * @return The descriptor for the specified property
379      *
380      * @throws IllegalArgumentException if this is not a valid property
381      *  name for our DynaClass
382      */
383     protected DynaProperty getDynaProperty(final String name) {
384 
385         final DynaProperty descriptor = getDynaClass().getDynaProperty(name);
386         if (descriptor == null) {
387             throw new IllegalArgumentException
388                     ("Invalid property name '" + name + "'");
389         }
390         return (descriptor);
391 
392     }
393 
394     /**
395      * Returns the {@code PropertyUtilsBean} instance to be used for accessing properties.
396      * If available, this object is obtained from the associated {@code WrapDynaClass}.
397      *
398      * @return the associated {@code PropertyUtilsBean}
399      */
400     private PropertyUtilsBean getPropertyUtils() {
401 
402         PropertyUtilsBean propUtils = null;
403         if (dynaClass != null) {
404             propUtils = dynaClass.getPropertyUtilsBean();
405         }
406         return (propUtils != null) ? propUtils : PropertyUtilsBean.getInstance();
407 
408     }
409 }