KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > beans > BeanUtils


1 /*
2  * Copyright 2002-2006 the original author or authors.
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
17 package org.springframework.beans;
18
19 import java.beans.PropertyDescriptor JavaDoc;
20 import java.lang.reflect.Constructor JavaDoc;
21 import java.lang.reflect.InvocationTargetException JavaDoc;
22 import java.lang.reflect.Method JavaDoc;
23 import java.lang.reflect.Modifier JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.List JavaDoc;
26
27 import org.springframework.util.Assert;
28 import org.springframework.util.ClassUtils;
29 import org.springframework.util.StringUtils;
30
31 /**
32  * Static convenience methods for JavaBeans: for instantiating beans,
33  * checking bean property types, copying bean properties, etc.
34  *
35  * <p>Mainly for use within the framework, but to some degree also
36  * useful for application classes.
37  *
38  * @author Rod Johnson
39  * @author Juergen Hoeller
40  * @author Rob Harrop
41  */

42 public abstract class BeanUtils {
43
44     /**
45      * Convenience method to instantiate a class using its no-arg constructor.
46      * As this method doesn't try to load classes by name, it should avoid
47      * class-loading issues.
48      * <p>Note that this method tries to set the constructor accessible
49      * if given a non-accessible (that is, non-public) constructor.
50      * @param clazz class to instantiate
51      * @return the new instance
52      * @throws BeanInstantiationException if the bean cannot be instantiated
53      */

54     public static Object JavaDoc instantiateClass(Class JavaDoc clazz) throws BeanInstantiationException {
55         Assert.notNull(clazz, "Class must not be null");
56         if (clazz.isInterface()) {
57             throw new BeanInstantiationException(clazz, "Specified class is an interface");
58         }
59         try {
60             return instantiateClass(clazz.getDeclaredConstructor((Class JavaDoc[]) null), null);
61         }
62         catch (NoSuchMethodException JavaDoc ex) {
63             throw new BeanInstantiationException(clazz, "No default constructor found", ex);
64         }
65     }
66
67     /**
68      * Convenience method to instantiate a class using the given constructor.
69      * As this method doesn't try to load classes by name, it should avoid
70      * class-loading issues.
71      * <p>Note that this method tries to set the constructor accessible
72      * if given a non-accessible (that is, non-public) constructor.
73      * @param ctor constructor to instantiate
74      * @return the new instance
75      * @throws BeanInstantiationException if the bean cannot be instantiated
76      */

77     public static Object JavaDoc instantiateClass(Constructor JavaDoc ctor, Object JavaDoc[] args) throws BeanInstantiationException {
78         Assert.notNull(ctor, "Constructor must not be null");
79         try {
80             if (!Modifier.isPublic(ctor.getModifiers()) ||
81                     !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) {
82                 ctor.setAccessible(true);
83             }
84             return ctor.newInstance(args);
85         }
86         catch (InstantiationException JavaDoc ex) {
87             throw new BeanInstantiationException(ctor.getDeclaringClass(),
88                     "Is it an abstract class?", ex);
89         }
90         catch (IllegalAccessException JavaDoc ex) {
91             throw new BeanInstantiationException(ctor.getDeclaringClass(),
92                     "Has the class definition changed? Is the constructor accessible?", ex);
93         }
94         catch (IllegalArgumentException JavaDoc ex) {
95             throw new BeanInstantiationException(ctor.getDeclaringClass(),
96                     "Illegal arguments for constructor", ex);
97         }
98         catch (InvocationTargetException JavaDoc ex) {
99             throw new BeanInstantiationException(ctor.getDeclaringClass(),
100                     "Constructor threw exception", ex.getTargetException());
101         }
102     }
103
104     /**
105      * Find a method with the given method name and the given parameter types,
106      * declared on the given class or one of its superclasses. Prefers public methods,
107      * but will return a protected, package access, or private method too.
108      * <p>Checks <code>Class.getMethod</code> first, falling back to
109      * <code>findDeclaredMethod</code>. This allows to find public methods
110      * without issues even in environments with restricted Java security settings.
111      * @param clazz the class to check
112      * @param methodName the name of the method to find
113      * @param paramTypes the parameter types of the method to find
114      * @return the Method object, or <code>null</code> if not found
115      * @see java.lang.Class#getMethod
116      * @see #findDeclaredMethod
117      */

118     public static Method JavaDoc findMethod(Class JavaDoc clazz, String JavaDoc methodName, Class JavaDoc[] paramTypes) {
119         try {
120             return clazz.getMethod(methodName, paramTypes);
121         }
122         catch (NoSuchMethodException JavaDoc ex) {
123             return findDeclaredMethod(clazz, methodName, paramTypes);
124         }
125     }
126
127     /**
128      * Find a method with the given method name and the given parameter types,
129      * declared on the given class or one of its superclasses. Will return a public,
130      * protected, package access, or private method.
131      * <p>Checks <code>Class.getDeclaredMethod</code>, cascading upwards to all superclasses.
132      * @param clazz the class to check
133      * @param methodName the name of the method to find
134      * @param paramTypes the parameter types of the method to find
135      * @return the Method object, or <code>null</code> if not found
136      * @see java.lang.Class#getDeclaredMethod
137      */

138     public static Method JavaDoc findDeclaredMethod(Class JavaDoc clazz, String JavaDoc methodName, Class JavaDoc[] paramTypes) {
139         try {
140             return clazz.getDeclaredMethod(methodName, paramTypes);
141         }
142         catch (NoSuchMethodException JavaDoc ex) {
143             if (clazz.getSuperclass() != null) {
144                 return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
145             }
146             return null;
147         }
148     }
149
150     /**
151      * Find a method with the given method name and minimal parameters (best case: none),
152      * declared on the given class or one of its superclasses. Prefers public methods,
153      * but will return a protected, package access, or private method too.
154      * <p>Checks <code>Class.getMethods</code> first, falling back to
155      * <code>findDeclaredMethodWithMinimalParameters</code>. This allows to find public
156      * methods without issues even in environments with restricted Java security settings.
157      * @param clazz the class to check
158      * @param methodName the name of the method to find
159      * @return the Method object, or <code>null</code> if not found
160      * @throws IllegalArgumentException if methods of the given name were found but
161      * could not be resolved to a unique method with minimal parameters
162      * @see java.lang.Class#getMethods
163      * @see #findDeclaredMethodWithMinimalParameters
164      */

165     public static Method JavaDoc findMethodWithMinimalParameters(Class JavaDoc clazz, String JavaDoc methodName)
166             throws IllegalArgumentException JavaDoc {
167
168         Method JavaDoc targetMethod = doFindMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
169         if (targetMethod == null) {
170             return findDeclaredMethodWithMinimalParameters(clazz, methodName);
171         }
172         return targetMethod;
173     }
174
175     /**
176      * Find a method with the given method name and minimal parameters (best case: none),
177      * declared on the given class or one of its superclasses. Will return a public,
178      * protected, package access, or private method.
179      * <p>Checks <code>Class.getDeclaredMethods</code>, cascading upwards to all superclasses.
180      * @param clazz the class to check
181      * @param methodName the name of the method to find
182      * @return the Method object, or <code>null</code> if not found
183      * @throws IllegalArgumentException if methods of the given name were found but
184      * could not be resolved to a unique method with minimal parameters
185      * @see java.lang.Class#getDeclaredMethods
186      */

187     public static Method JavaDoc findDeclaredMethodWithMinimalParameters(Class JavaDoc clazz, String JavaDoc methodName)
188             throws IllegalArgumentException JavaDoc {
189
190         Method JavaDoc targetMethod = doFindMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
191         if (targetMethod == null && clazz.getSuperclass() != null) {
192             return findDeclaredMethodWithMinimalParameters(clazz.getSuperclass(), methodName);
193         }
194         return targetMethod;
195     }
196
197     /**
198      * Find a method with the given method name and minimal parameters (best case: none)
199      * in the given list of methods.
200      * @param methods the methods to check
201      * @param methodName the name of the method to find
202      * @return the Method object, or <code>null</code> if not found
203      * @throws IllegalArgumentException if methods of the given name were found but
204      * could not be resolved to a unique method with minimal parameters
205      */

206     private static Method JavaDoc doFindMethodWithMinimalParameters(Method JavaDoc[] methods, String JavaDoc methodName)
207             throws IllegalArgumentException JavaDoc {
208
209         Method JavaDoc targetMethod = null;
210         int numMethodsFoundWithCurrentMinimumArgs = 0;
211         for (int i = 0; i < methods.length; i++) {
212             if (methods[i].getName().equals(methodName)) {
213                 int numParams = methods[i].getParameterTypes().length;
214                 if (targetMethod == null ||
215                         numParams < targetMethod.getParameterTypes().length) {
216                     targetMethod = methods[i];
217                     numMethodsFoundWithCurrentMinimumArgs = 1;
218                 }
219                 else {
220                     if (targetMethod.getParameterTypes().length == numParams) {
221                         // Additional candidate with same length.
222
numMethodsFoundWithCurrentMinimumArgs++;
223                     }
224                 }
225             }
226         }
227         if (numMethodsFoundWithCurrentMinimumArgs > 1) {
228             throw new IllegalArgumentException JavaDoc("Cannot resolve method '" + methodName +
229                     "' to a unique method. Attempted to resolve to overloaded method with " +
230                     "the least number of parameters, but there were " +
231                     numMethodsFoundWithCurrentMinimumArgs + " candidates.");
232         }
233         return targetMethod;
234     }
235
236     /**
237      * Parse a method signature in the form <code>methodName[([arg_list])]</code>,
238      * where <code>arg_list</code> is an optional, comma-separated list of fully-qualified
239      * type names, and attempts to resolve that signature against the supplied <code>Class</code>.
240      * <p>When not supplying an argument list (<code>methodName</code>) the method whose name
241      * matches and has the least number of parameters will be returned. When supplying an
242      * argument type list, only the method whose name and argument types match will be returned.
243      * <p>Note then that <code>methodName</code> and <code>methodName()</code> are <strong>not</strong>
244      * resolved in the same way. The signature <code>methodName</code> means the method called
245      * <code>methodName</code> with the least number of arguments, whereas <code>methodName()</code>
246      * means the method called <code>methodName</code> with exactly 0 arguments.
247      * <p>If no method can be found, then <code>null</code> is returned.
248      * @see #findMethod
249      * @see #findMethodWithMinimalParameters
250      */

251     public static Method JavaDoc resolveSignature(String JavaDoc signature, Class JavaDoc clazz) {
252         Assert.hasText(signature, "'signature' must not be empty");
253         Assert.notNull(clazz, "Class must not be null");
254
255         int firstParen = signature.indexOf("(");
256         int lastParen = signature.indexOf(")");
257
258         if (firstParen > -1 && lastParen == -1) {
259             throw new IllegalArgumentException JavaDoc("Invalid method signature '" + signature +
260                     "': expected closing ')' for args list");
261         }
262         else if (lastParen > -1 && firstParen == -1) {
263             throw new IllegalArgumentException JavaDoc("Invalid method signature '" + signature +
264                     "': expected opening '(' for args list");
265         }
266         else if (firstParen == -1 && lastParen == -1) {
267             return findMethodWithMinimalParameters(clazz, signature);
268         }
269         else {
270             String JavaDoc methodName = signature.substring(0, firstParen);
271             String JavaDoc[] parameterTypeNames =
272                     StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen));
273             Class JavaDoc[] parameterTypes = new Class JavaDoc[parameterTypeNames.length];
274             for (int i = 0; i < parameterTypeNames.length; i++) {
275                 String JavaDoc parameterTypeName = parameterTypeNames[i].trim();
276                 try {
277                     parameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader());
278                 }
279                 catch (Throwable JavaDoc ex) {
280                     throw new IllegalArgumentException JavaDoc("Invalid method signature: unable to resolve type [" +
281                             parameterTypeName + "] for argument " + i + ". Root cause: " + ex);
282                 }
283             }
284             return findMethod(clazz, methodName, parameterTypes);
285         }
286     }
287
288
289     /**
290      * Retrieve the JavaBeans <code>PropertyDescriptor</code>s of a given class.
291      * @param clazz the Class to retrieve the PropertyDescriptors for
292      * @return an array of <code>PropertyDescriptors</code> for the given class
293      * @throws BeansException if PropertyDescriptor look fails
294      */

295     public static PropertyDescriptor JavaDoc[] getPropertyDescriptors(Class JavaDoc clazz) throws BeansException {
296         CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
297         return cr.getBeanInfo().getPropertyDescriptors();
298     }
299
300     /**
301      * Retrieve the JavaBeans <code>PropertyDescriptors</code> for the given property.
302      * @param clazz the Class to retrieve the PropertyDescriptor for
303      * @param propertyName the name of the property
304      * @return the corresponding PropertyDescriptor, or <code>null</code> if none
305      * @throws BeansException if PropertyDescriptor lookup fails
306      */

307     public static PropertyDescriptor JavaDoc getPropertyDescriptor(Class JavaDoc clazz, String JavaDoc propertyName)
308             throws BeansException {
309
310         CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
311         return cr.getPropertyDescriptor(propertyName);
312     }
313
314     /**
315      * Find a JavaBeans <code>PropertyDescriptor</code> for the given method,
316      * with the method either being the read method or the write method for
317      * that bean property.
318      * @param method the method to find a corresponding PropertyDescriptor for
319      * @return the corresponding PropertyDescriptor, or <code>null</code> if none
320      * @throws BeansException if PropertyDescriptor lookup fails
321      */

322     public static PropertyDescriptor JavaDoc findPropertyForMethod(Method JavaDoc method) throws BeansException {
323         Assert.notNull(method, "Method must not be null");
324         PropertyDescriptor JavaDoc[] pds = getPropertyDescriptors(method.getDeclaringClass());
325         for (int i = 0; i < pds.length; i++) {
326             if (method.equals(pds[i].getReadMethod()) || method.equals(pds[i].getWriteMethod())) {
327                 return pds[i];
328             }
329         }
330         return null;
331     }
332
333     /**
334      * Determine the bean property type for the given property from the
335      * given classes/interfaces, if possible.
336      * @param propertyName the name of the bean property
337      * @param beanClasses the classes to check against
338      * @return the property type, or <code>Object.class</code> as fallback
339      */

340     public static Class JavaDoc findPropertyType(String JavaDoc propertyName, Class JavaDoc[] beanClasses) {
341         if (beanClasses != null) {
342             for (int i = 0; i < beanClasses.length; i++) {
343                 PropertyDescriptor JavaDoc pd = BeanUtils.getPropertyDescriptor(beanClasses[i], propertyName);
344                 if (pd != null) {
345                     return pd.getPropertyType();
346                 }
347             }
348         }
349         return Object JavaDoc.class;
350     }
351
352     /**
353      * Check if the given class represents a "simple" property:
354      * a primitive, a String, a Class, or a corresponding array.
355      * <p>Used to determine properties to check for a "simple" dependency-check.
356      * @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
357      * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
358      */

359     public static boolean isSimpleProperty(Class JavaDoc clazz) {
360         Assert.notNull(clazz, "Class must not be null");
361         return clazz.isPrimitive() || ClassUtils.isPrimitiveArray(clazz) ||
362                 ClassUtils.isPrimitiveWrapper(clazz) || ClassUtils.isPrimitiveWrapperArray(clazz) ||
363                 clazz.equals(String JavaDoc.class) || clazz.equals(String JavaDoc[].class) ||
364                 clazz.equals(Class JavaDoc.class) || clazz.equals(Class JavaDoc[].class);
365     }
366
367     /**
368      * Determine if the given target type is assignable from the given value
369      * type, assuming setting by reflection. Considers primitive wrapper
370      * classes as assignable to the corresponding primitive types.
371      * @param targetType the target type
372      * @param valueType the value type that should be assigned to the target type
373      * @return if the target type is assignable from the value type
374      * @deprecated as of Spring 2.0, in favor of <code>ClassUtils.isAssignable</code>
375      * @see org.springframework.util.ClassUtils#isAssignable(Class, Class)
376      */

377     public static boolean isAssignable(Class JavaDoc targetType, Class JavaDoc valueType) {
378         return ClassUtils.isAssignable(targetType, valueType);
379     }
380
381     /**
382      * Determine if the given type is assignable from the given value,
383      * assuming setting by reflection. Considers primitive wrapper classes
384      * as assignable to the corresponding primitive types.
385      * @param type the target type
386      * @param value the value that should be assigned to the type
387      * @return if the type is assignable from the value
388      * @deprecated as of Spring 2.0, in favor of <code>ClassUtils.isAssignableValue</code>
389      * @see org.springframework.util.ClassUtils#isAssignableValue(Class, Object)
390      */

391     public static boolean isAssignable(Class JavaDoc type, Object JavaDoc value) {
392         return ClassUtils.isAssignableValue(type, value);
393     }
394
395
396     /**
397      * Copy the property values of the given source bean into the target bean.
398      * <p>Note: The source and target classes do not have to match or even be derived
399      * from each other, as long as the properties match. Any bean properties that the
400      * source bean exposes but the target bean does not will silently be ignored.
401      * <p>This is just a convenience method. For more complex transfer needs,
402      * consider using a full BeanWrapper.
403      * @param source the source bean
404      * @param target the target bean
405      * @throws BeansException if the copying failed
406      * @see BeanWrapper
407      */

408     public static void copyProperties(Object JavaDoc source, Object JavaDoc target) throws BeansException {
409         copyProperties(source, target, null, null);
410     }
411
412     /**
413      * Copy the property values of the given source bean into the given target bean,
414      * only setting properties defined in the given "editable" class (or interface).
415      * <p>Note: The source and target classes do not have to match or even be derived
416      * from each other, as long as the properties match. Any bean properties that the
417      * source bean exposes but the target bean does not will silently be ignored.
418      * <p>This is just a convenience method. For more complex transfer needs,
419      * consider using a full BeanWrapper.
420      * @param source the source bean
421      * @param target the target bean
422      * @param editable the class (or interface) to restrict property setting to
423      * @throws BeansException if the copying failed
424      * @see BeanWrapper
425      */

426     public static void copyProperties(Object JavaDoc source, Object JavaDoc target, Class JavaDoc editable)
427             throws BeansException {
428
429         copyProperties(source, target, editable, null);
430     }
431
432     /**
433      * Copy the property values of the given source bean into the given target bean,
434      * ignoring the given "ignoreProperties".
435      * <p>Note: The source and target classes do not have to match or even be derived
436      * from each other, as long as the properties match. Any bean properties that the
437      * source bean exposes but the target bean does not will silently be ignored.
438      * <p>This is just a convenience method. For more complex transfer needs,
439      * consider using a full BeanWrapper.
440      * @param source the source bean
441      * @param target the target bean
442      * @param ignoreProperties array of property names to ignore
443      * @throws BeansException if the copying failed
444      * @see BeanWrapper
445      */

446     public static void copyProperties(Object JavaDoc source, Object JavaDoc target, String JavaDoc[] ignoreProperties)
447             throws BeansException {
448
449         copyProperties(source, target, null, ignoreProperties);
450     }
451
452     /**
453      * Copy the property values of the given source bean into the given target bean.
454      * <p>Note: The source and target classes do not have to match or even be derived
455      * from each other, as long as the properties match. Any bean properties that the
456      * source bean exposes but the target bean does not will silently be ignored.
457      * @param source the source bean
458      * @param target the target bean
459      * @param editable the class (or interface) to restrict property setting to
460      * @param ignoreProperties array of property names to ignore
461      * @throws BeansException if the copying failed
462      * @see BeanWrapper
463      */

464     private static void copyProperties(Object JavaDoc source, Object JavaDoc target, Class JavaDoc editable, String JavaDoc[] ignoreProperties)
465             throws BeansException {
466
467         Assert.notNull(source, "Source must not be null");
468         Assert.notNull(target, "Target must not be null");
469
470         Class JavaDoc actualEditable = target.getClass();
471         if (editable != null) {
472             if (!editable.isInstance(target)) {
473                 throw new IllegalArgumentException JavaDoc("Target class [" + target.getClass().getName() +
474                         "] not assignable to Editable class [" + editable.getName() + "]");
475             }
476             actualEditable = editable;
477         }
478         PropertyDescriptor JavaDoc[] targetPds = getPropertyDescriptors(actualEditable);
479         List JavaDoc ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
480
481         for (int i = 0; i < targetPds.length; i++) {
482             PropertyDescriptor JavaDoc targetPd = targetPds[i];
483             if (targetPd.getWriteMethod() != null &&
484                     (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
485                 PropertyDescriptor JavaDoc sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
486                 if (sourcePd != null && sourcePd.getReadMethod() != null) {
487                     try {
488                         Method JavaDoc readMethod = sourcePd.getReadMethod();
489                         if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
490                             readMethod.setAccessible(true);
491                         }
492                         Object JavaDoc value = readMethod.invoke(source, new Object JavaDoc[0]);
493                         Method JavaDoc writeMethod = targetPd.getWriteMethod();
494                         if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
495                             writeMethod.setAccessible(true);
496                         }
497                         writeMethod.invoke(target, new Object JavaDoc[] {value});
498                     }
499                     catch (Throwable JavaDoc ex) {
500                         throw new FatalBeanException("Could not copy properties from source to target", ex);
501                     }
502                 }
503             }
504         }
505     }
506
507 }
508
Popular Tags