KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > core > util > reflection > BeanUtil


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.core.util.reflection;
19
20 import java.beans.BeanInfo JavaDoc;
21 import java.beans.IntrospectionException JavaDoc;
22 import java.beans.Introspector JavaDoc;
23 import java.beans.PropertyDescriptor JavaDoc;
24 import java.lang.reflect.InvocationTargetException JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27
28
29 /**
30  * <p>This class implements the ability to get and set properties on a
31  * bean. This included the concept of embedded properties that may
32  * be referenced like <code>Bean.property.property.property</code>.</p>
33  *
34  * Copyright 2002 Sapient
35  * @since carbon 1.0
36  * @author Greg Hinkle, January 2002
37  * @version $Revision: 1.11 $ ($Author: dvoet $)
38  */

39 public final class BeanUtil {
40     /**
41      * String used to separate multiple properties inside of embedded
42      * beans.
43      */

44     private static final String JavaDoc PROPERTY_SEPARATOR = ".";
45
46     /**
47      * An empty class array used for null parameter method reflection
48      */

49     private static Class JavaDoc[] NO_PARAMETERS_ARRAY = new Class JavaDoc[] {
50     };
51     /**
52      * an empty object array used for null parameter method reflection
53      */

54     private static Object JavaDoc[] NO_ARGUMENTS_ARRAY = new Object JavaDoc[] {
55     };
56
57     /**
58      * The constructor is private so that <tt>new</tt> cannot be used.
59      */

60     private BeanUtil() {
61     }
62
63     /**
64      * Retreives a property descriptor object for a given property.
65      * <p>
66      * Uses the classes in <code>java.beans</code> to get back
67      * a descriptor for a property. Read-only and write-only
68      * properties will have a slower return time.
69      * </p>
70      *
71      * @param propertyName The programmatic name of the property
72      * @param beanClass The Class object for the target bean.
73      * For example sun.beans.OurButton.class.
74      * @return a PropertyDescriptor for a property that follows the
75      * standard Java naming conventions.
76      * @throws PropertyNotFoundException indicates that the property
77      * could not be found on the bean class.
78      */

79     private static final PropertyDescriptor JavaDoc getPropertyDescriptor(
80         String JavaDoc propertyName,
81         Class JavaDoc beanClass) {
82
83         PropertyDescriptor JavaDoc resultPropertyDescriptor = null;
84
85
86         char[] pNameArray = propertyName.toCharArray();
87         pNameArray[0] = Character.toLowerCase(pNameArray[0]);
88         propertyName = new String JavaDoc(pNameArray);
89
90         try {
91             resultPropertyDescriptor =
92                 new PropertyDescriptor JavaDoc(propertyName, beanClass);
93         } catch (IntrospectionException JavaDoc e1) {
94             // Read-only and write-only properties will throw this
95
// exception. The properties must be looked up using
96
// brute force.
97

98             // This will get the list of all properties and iterate
99
// through looking for one that matches the property
100
// name passed into the method.
101
try {
102                 BeanInfo JavaDoc beanInfo = Introspector.getBeanInfo(beanClass);
103
104                 PropertyDescriptor JavaDoc[] propertyDescriptors =
105                     beanInfo.getPropertyDescriptors();
106
107                 for (int i = 0; i < propertyDescriptors.length; i++) {
108                     if (propertyDescriptors[i]
109                         .getName()
110                         .equals(propertyName)) {
111
112                         // If the names match, this this describes the
113
// property being searched for. Break out of
114
// the iteration.
115
resultPropertyDescriptor = propertyDescriptors[i];
116                         break;
117                     }
118                 }
119             } catch (IntrospectionException JavaDoc e2) {
120                 throw new PropertyNotFoundException(
121                     BeanUtil.class,
122                     "Encountered "
123                         + "exception looking up property ["
124                         + propertyName
125                         + "] on class ["
126                         + beanClass
127                         + "]",
128                     e2);
129             }
130         }
131
132         // If no property descriptor was found, then this bean does not
133
// have a property matching the name passed in.
134
if (resultPropertyDescriptor == null) {
135             throw new PropertyNotFoundException(
136                 BeanUtil.class,
137                 "Failed to find "
138                     + "property descriptor for property ["
139                     + propertyName
140                     + "] on class ["
141                     + beanClass
142                     + "]");
143         }
144
145         return resultPropertyDescriptor;
146     }
147
148     /**
149      * <p>Gets the specified attribute from the specified object. For example,
150      * <code>getObjectAttribute(o, "address.line1")</code> will return
151      * the result of calling <code>o.getAddress().getLine1()</code>.<p>
152      *
153      * <p>The attribute specified may contain as many levels as you like. If at
154      * any time a null reference is acquired by calling one of the successive
155      * getter methods, then the return value from this method is also null.</p>
156      *
157      * <p>When reading from a boolean property the underlying bean introspector
158      * first looks for an is&lt;Property&gt; read method, not finding one it will
159      * still look for a get&lt;Property&gt; read method. Not finding either, the
160      * property is considered write-only.</p>
161      *
162      * @param bean the bean to set the property on
163      * @param propertyNames the name of the propertie(s) to retrieve. If this is
164      * null or the empty string, then <code>bean</code> will be returned.
165      * @return the object value of the bean attribute
166      *
167      * @throws PropertyNotFoundException indicates the the given property
168      * could not be found on the bean
169      * @throws NoSuchMethodException Not thrown
170      * @throws InvocationTargetException if a specified getter method throws an
171      * exception.
172      * @throws IllegalAccessException if a getter method is
173      * not public or property is write-only.
174      */

175     public static Object JavaDoc getObjectAttribute(Object JavaDoc bean, String JavaDoc propertyNames)
176         throws
177             NoSuchMethodException JavaDoc,
178             InvocationTargetException JavaDoc,
179             IllegalAccessException JavaDoc {
180
181
182         Object JavaDoc result = bean;
183
184         StringTokenizer JavaDoc propertyTokenizer =
185             new StringTokenizer JavaDoc(propertyNames, PROPERTY_SEPARATOR);
186
187         // Run through the tokens, calling get methods and
188
// replacing result with the new object each time.
189
// If the result equals null, then simply return null.
190
while (propertyTokenizer.hasMoreElements() && result != null) {
191             Class JavaDoc resultClass = result.getClass();
192             String JavaDoc currentPropertyName = propertyTokenizer.nextToken();
193
194             PropertyDescriptor JavaDoc propertyDescriptor =
195                 getPropertyDescriptor(currentPropertyName, resultClass);
196
197             Method JavaDoc readMethod = propertyDescriptor.getReadMethod();
198             if (readMethod == null) {
199                 throw new IllegalAccessException JavaDoc(
200                     "User is attempting to "
201                         + "read from a property that has no read method. "
202                         + " This is likely a write-only bean property. Caused "
203                         + "by property ["
204                         + currentPropertyName
205                         + "] on class ["
206                         + resultClass
207                         + "]");
208             }
209
210             result = readMethod.invoke(result, NO_ARGUMENTS_ARRAY);
211         }
212
213         return result;
214     }
215
216     /**
217      * <p>Sets the specified attribute on the specified object. For example,
218      * <code>getObjectAttribute(o, "address.line1", value)</code> will call
219      * <code>o.getAddress().setLine1(value)</code>.<p>
220      *
221      * <p>The attribute specified may contain as many levels as you like. If at
222      * any time a null reference is acquired by calling one of the successive
223      * getter methods, then a <code>NullPointerException</code> is thrown.</p>
224      *
225      * @param bean the bean to call the getters on
226      * @param propertyNames the name of the attribute(s) to set. If this is
227      * null or the empty string, then an exception is thrown.
228      * @param value the value of the object to set on the bean property
229      *
230      * @throws PropertyNotFoundException indicates the the given property
231      * could not be found on the bean
232      * @throws IllegalArgumentException if the supplied parameter is not of
233      * a valid type
234      * @throws NoSuchMethodException never
235      * @throws IllegalAccessException if a getter or setter method is
236      * not public or property is read-only.
237      * @throws InvocationTargetException if a specified getter method throws an
238      * exception.
239      */

240     public static void setObjectAttribute(
241         Object JavaDoc bean,
242         String JavaDoc propertyNames,
243         Object JavaDoc value)
244         throws
245             IllegalAccessException JavaDoc,
246             IllegalArgumentException JavaDoc,
247             InvocationTargetException JavaDoc,
248             NoSuchMethodException JavaDoc {
249
250
251         Object JavaDoc result = bean;
252         String JavaDoc propertyName = propertyNames;
253
254         // Check if this has some embedded properties. If it does, use the
255
// getObjectAttribute to get the proper object to call this on.
256
int indexOfLastPropertySeparator =
257             propertyName.lastIndexOf(PROPERTY_SEPARATOR);
258
259         if (indexOfLastPropertySeparator >= 0) {
260             String JavaDoc embeddedProperties =
261                 propertyName.substring(0, indexOfLastPropertySeparator);
262
263             // Grab the final property name after the last property separator
264
propertyName =
265                 propertyName.substring(
266                     indexOfLastPropertySeparator + PROPERTY_SEPARATOR.length());
267
268             result = getObjectAttribute(result, embeddedProperties);
269         }
270
271         Class JavaDoc resultClass = result.getClass();
272
273         PropertyDescriptor JavaDoc propertyDescriptor =
274             getPropertyDescriptor(propertyName, resultClass);
275
276         Method JavaDoc writeMethod = propertyDescriptor.getWriteMethod();
277         if (writeMethod == null) {
278             throw new IllegalAccessException JavaDoc(
279                 "User is attempting to write "
280                     + "to a property that has no write method. This is likely "
281                     + "a read-only bean property. Caused by property ["
282                     + propertyName
283                     + "] on class ["
284                     + resultClass
285                     + "]");
286         }
287
288         writeMethod.invoke(result, new Object JavaDoc[] { value });
289     }
290 }
291
Popular Tags