View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of Java-Common.
5    *
6    * Java-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Java-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Java-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.introspection;
21  
22  import java.beans.FeatureDescriptor;
23  import java.beans.IntrospectionException;
24  import java.beans.Introspector;
25  import java.beans.MethodDescriptor;
26  import java.beans.PropertyDescriptor;
27  import java.lang.reflect.InvocationTargetException;
28  import java.lang.reflect.Member;
29  import java.lang.reflect.Method;
30  import java.util.ArrayList;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.Map;
34  
35  
36  /**
37   * The BeanIntrospector class helps read and write property values from a Java
38   * Bean and provides methods for easier manipulation of PropertyDescriptor
39   * arrays.
40   *
41   * @author Miha Kadunc
42   * @version $id$
43   */
44  public class BeanIntrospector
45  {
46  	/**
47  	 * Gets the value of the property specified by  <code>propertyName</code>
48  	 * on the <code>target</code> object. Primitive types are returned in
49  	 * equivalent Java objects.
50  	 *
51  	 * @param target the object containing the property
52  	 * @param propertyName the name of the property to get
53  	 *
54  	 * @return Object the value of the property
55  	 *
56  	 * @throws java.beans.IntrospectionException if an illegal property name is
57  	 *         specified or the target object is not a Java bean
58  	 * @throws java.lang.reflect.InvocationTargetException when an exception is
59  	 *         thrown inside the property getter
60  	 * @throws IllegalAccessException when property access is restricted on the
61  	 *         target object
62  	 */
63  	public static Object getPropertyValue(Object target, String propertyName)
64  		throws java.beans.IntrospectionException, 
65  			java.lang.reflect.InvocationTargetException, IllegalAccessException
66  	{
67  		assert (target != null);
68  		assert (propertyName != null);
69  
70  		PropertyDescriptor pd = getPropertyDescriptor(target.getClass(),
71  			    propertyName);
72  		Method m = pd.getReadMethod();
73  		Object[] params = new Object[0];
74  
75  		if (m == null) {
76  			return null;
77  		}
78  
79  		return m.invoke(target, params);
80  	}
81  
82  	/**
83  	 * Returns a Name-Value map of all the properties on a given object
84  	 *
85  	 * @param target Object
86  	 *
87  	 * @return Map
88  	 *
89  	 * @throws java.beans.IntrospectionException
90  	 * @throws java.lang.reflect.InvocationTargetException
91  	 * @throws IllegalAccessException
92  	 * @throws NullPointerException DOCUMENT ME!
93  	 */
94  	public static Map getProperties(Object target)
95  		throws java.beans.IntrospectionException, 
96  			java.lang.reflect.InvocationTargetException, IllegalAccessException
97  	{
98  		if (target == null) {
99  			throw new NullPointerException("target");
100 		}
101 
102 		PropertyDescriptor[] pds = getPropertyDescriptors(target.getClass());
103 
104 		return getProperties(pds, target);
105 	}
106 
107 	/**
108 	 * Returns a Name-Value map of properties described by the array of type
109 	 * <code>PropertyDescriptor</code>s; Values are retrieved from  the
110 	 * <code>target</code> object.
111 	 *
112 	 * @param properties PropertyDescriptor[]
113 	 * @param target Object
114 	 *
115 	 * @return Map
116 	 *
117 	 * @throws java.beans.IntrospectionException
118 	 * @throws java.lang.reflect.InvocationTargetException
119 	 * @throws IllegalAccessException
120 	 */
121 	public static Map getProperties(PropertyDescriptor[] properties,
122 	    Object target)
123 		throws java.beans.IntrospectionException, 
124 			java.lang.reflect.InvocationTargetException, IllegalAccessException
125 	{
126 		Method m = null;
127 		Object[] params = null;
128 		HashMap ret = new HashMap(properties.length);
129 
130 		for (int i = 0; i < properties.length; i++) {
131 			m = properties[i].getReadMethod();
132 			params = new Object[0];
133 
134 			if (m != null) {
135 				ret.put(properties[i].getName(), m.invoke(target, params));
136 			}
137 		}
138 
139 		return ret;
140 	}
141 
142 	/**
143 	 * Sets the value of the specified property on the <code>target</code>
144 	 * object.
145 	 *
146 	 * @param target the object containing the property
147 	 * @param propertyName name of the property
148 	 * @param value the new value of the property
149 	 *
150 	 * @exception NoSuchMethodException if the setter does not exist
151 	 * @exception InvocationTargetException reflection exception
152 	 * @exception IllegalAccessException reflection exception
153 	 */
154 	public static void setPropertyValue(Object target, String propertyName,
155 	    Object value)
156 		throws NoSuchMethodException, InvocationTargetException, 
157 			IllegalAccessException
158 	{
159 		assert target != null;
160 		assert propertyName != null;
161 
162 		Method m = getSetterMethod(target.getClass(), propertyName);
163 
164 		if (m == null) {
165 			throw new NoSuchMethodException("Property setter for '"
166 			    + propertyName + "' does not exist on '" + target.getClass()
167 			    + "'.");
168 		}
169 
170 		Object[] args = new Object[1];
171 		args[0] = value;
172 		m.invoke(target, args);
173 	}
174 
175 	/**
176 	 * Gets a setter method for a property specified by propertyName
177 	 *
178 	 * @param inspectedBean Class
179 	 * @param propertyName String
180 	 *
181 	 * @return Method the setter method for the property
182 	 */
183 	public static Method getSetterMethod(Class inspectedBean,
184 	    String propertyName)
185 	{
186 		Method[] methods = inspectedBean.getMethods();
187 
188 		Method method = null;
189 		Method retVal = null;
190 
191 		String setName = "set" + propertyName;
192 
193 		for (int i = 0; i < methods.length; i++) {
194 			method = methods[i];
195 
196 			if (method.getName().equalsIgnoreCase(setName)
197 			    && (method.getParameterTypes().length == 1)) {
198 				if (retVal == null) {
199 					retVal = method;
200 				} else {
201 					if (retVal.getDeclaringClass().isAssignableFrom(method
202 					        .getDeclaringClass())) {
203 						retVal = method;
204 					}
205 				}
206 			}
207 		}
208 
209 		return retVal;
210 	}
211 
212 	/**
213 	 * Retrieves the <code>FeatureDescriptor</code> for a member from an array
214 	 * of <code>FeatureDescriptor</code>s. Member should be the method that is
215 	 * desribed by a MethodDescriptor or the read method of a property
216 	 * described by PropertyDescriptor.
217 	 *
218 	 * @param descriptors PropertyDescriptor[]
219 	 * @param m String
220 	 *
221 	 * @return java.beans.PropertyDescriptor
222 	 */
223 	public static FeatureDescriptor getDescriptor(
224 	    FeatureDescriptor[] descriptors, Member m)
225 	{
226 		for (int i = 0; i < descriptors.length; i++) {
227 			if (descriptors[i] instanceof MethodDescriptor) {
228 				MethodDescriptor md = (MethodDescriptor)descriptors[i];
229 
230 				if (md.getMethod().equals(m)) {
231 					return md;
232 				}
233 			} else if (descriptors[i] instanceof PropertyDescriptor) {
234 				PropertyDescriptor pd = (PropertyDescriptor)descriptors[i];
235 
236 				if ((pd.getReadMethod().equals(m))
237 				    || (pd.getWriteMethod().equals(m))) {
238 					return pd;
239 				}
240 			}
241 		}
242 
243 		return null;
244 	}
245 
246 	/**
247 	 * Retrieves the <code>FeatureDescriptor</code> for a member from an array
248 	 * of <code>FeatureDescriptor</code>s given the feature's name. Feature
249 	 * can be a property, a method, or an event set.
250 	 *
251 	 * @param descriptors FeatureDescriptor[]
252 	 * @param featureName String
253 	 *
254 	 * @return java.beans.FeatureDescriptor
255 	 */
256 	public static FeatureDescriptor getDescriptorByName(
257 	    FeatureDescriptor[] descriptors, String featureName)
258 	{
259 		for (int i = 0; i < descriptors.length; i++) {
260 			if (descriptors[i].getName().equals(featureName)) {
261 				return descriptors[i];
262 			}
263 		}
264 
265 		return null;
266 	}
267 
268 	/**
269 	 * Returns descriptors of the properties declared in interfaces that the
270 	 * current class implements, following the interface hierarchy to the
271 	 * <code>superInterface</code> specified. Descriptors of properties that
272 	 * are declared in the class itself or an interface, that is not a
273 	 * descendant of the specified interface, are not returned.
274 	 *
275 	 * @param theClass Class
276 	 * @param superInterface Class
277 	 *
278 	 * @return java.beans.PropertyDescriptor
279 	 */
280 	public static PropertyDescriptor[] getInterfacePropertyDescriptors(
281 	    Class theClass, Class superInterface)
282 	{
283 		HashSet set = new HashSet();
284 		internalGetInterfacePropertyDescriptors(theClass, superInterface, set);
285 
286 		PropertyDescriptor[] retVal = new PropertyDescriptor[set.size()];
287 		set.toArray(retVal);
288 
289 		return retVal;
290 	}
291 
292 	/**
293 	 * Returns descriptor for the property with specified property name of bean
294 	 * with <code>beanClass</code>
295 	 *
296 	 * @param beanClass the Class of the bean to introspect
297 	 * @param propertyName the property name
298 	 *
299 	 * @return java.beans.PropertyDescriptor
300 	 *
301 	 * @throws IntrospectionException
302 	 */
303 	public static PropertyDescriptor getPropertyDescriptor(Class beanClass,
304 	    String propertyName) throws IntrospectionException
305 	{
306 		return (PropertyDescriptor)getDescriptorByName(getPropertyDescriptors(
307 		        beanClass), propertyName);
308 	}
309 
310 	/**
311 	 * Returns the type of the specified property on specified bean.
312 	 *
313 	 * @param beanClass the Class of the bean to introspect
314 	 * @param propertyName the property name
315 	 *
316 	 * @return the property type
317 	 *
318 	 * @throws IntrospectionException
319 	 */
320 	public static Class getPropertyType(Class beanClass, String propertyName)
321 		throws IntrospectionException
322 	{
323 		return getPropertyDescriptor(beanClass, propertyName).getPropertyType();
324 	}
325 
326 	/**
327 	 * Returns names of all the property desriptors in the given array.
328 	 *
329 	 * @param desc PropertyDescriptor[]
330 	 *
331 	 * @return java.beans.PropertyDescriptor
332 	 */
333 	public static String[] getPropertyNames(PropertyDescriptor[] desc)
334 	{
335 		String[] names = new String[desc.length];
336 
337 		for (int i = 0; i < desc.length; i++) {
338 			names[i] = desc[i].getName();
339 		}
340 
341 		return names;
342 	}
343 
344 	/**
345 	 * Returns types of all the property descriptors in the given array.
346 	 *
347 	 * @param desc PropertyDescriptor[]
348 	 *
349 	 * @return java.beans.PropertyDescriptor
350 	 */
351 	public static Class[] getPropertyTypes(PropertyDescriptor[] desc)
352 	{
353 		Class[] names = new Class[desc.length];
354 
355 		for (int i = 0; i < desc.length; i++) {
356 			names[i] = desc[i].getPropertyType();
357 		}
358 
359 		return names;
360 	}
361 
362 	/**
363 	 * Returns types of all the property descriptors in the given array.
364 	 *
365 	 * @param desc PropertyDescriptor[]
366 	 * @param target DOCUMENT ME!
367 	 *
368 	 * @return java.beans.PropertyDescriptor
369 	 *
370 	 * @throws java.beans.IntrospectionException DOCUMENT ME!
371 	 * @throws java.lang.reflect.InvocationTargetException DOCUMENT ME!
372 	 * @throws IllegalAccessException DOCUMENT ME!
373 	 */
374 	public static Object[] getPropertyValues(PropertyDescriptor[] desc,
375 	    Object target)
376 		throws java.beans.IntrospectionException, 
377 			java.lang.reflect.InvocationTargetException, IllegalAccessException
378 	{
379 		Method m = null;
380 		Object[] params = new Object[0];
381 		Object[] ret = new Object[desc.length];
382 
383 		for (int i = 0; i < desc.length; i++) {
384 			m = desc[i].getReadMethod();
385 
386 			if (m != null) {
387 				ret[i] = m.invoke(target, params);
388 			}
389 		}
390 
391 		return ret;
392 	}
393 
394 	/**
395 	 * Selects descriptors of all writable properties from an array of
396 	 * descriptors.
397 	 *
398 	 * @param desc PropertyDescriptor[]
399 	 *
400 	 * @return java.beans.PropertyDescriptor
401 	 */
402 	public static PropertyDescriptor[] getWritable(PropertyDescriptor[] desc)
403 	{
404 		ArrayList list = new ArrayList();
405 
406 		for (int i = 0; i < desc.length; i++) {
407 			if (desc[i].getWriteMethod() != null) {
408 				list.add(desc[i]);
409 			}
410 		}
411 
412 		PropertyDescriptor[] ret = new PropertyDescriptor[list.size()];
413 		list.toArray(ret);
414 
415 		return ret;
416 	}
417 
418 	/**
419 	 * Selects descriptors of all readable properties from an array of
420 	 * descriptors.
421 	 *
422 	 * @param desc PropertyDescriptor[]
423 	 *
424 	 * @return java.beans.PropertyDescriptor
425 	 */
426 	public static PropertyDescriptor[] getReadable(PropertyDescriptor[] desc)
427 	{
428 		ArrayList list = new ArrayList();
429 
430 		for (int i = 0; i < desc.length; i++) {
431 			if (desc[i].getReadMethod() != null) {
432 				list.add(desc[i]);
433 			}
434 		}
435 
436 		PropertyDescriptor[] ret = new PropertyDescriptor[list.size()];
437 		list.toArray(ret);
438 
439 		return ret;
440 	}
441 
442 	private static void internalGetInterfacePropertyDescriptors(
443 	    Class[] interfaces, Class superInterface, HashSet set)
444 	{
445 		try {
446 			for (int i = 0; i < interfaces.length; i++) {
447 				if ((superInterface.isAssignableFrom(interfaces[i]))
448 				    && (interfaces[i] != superInterface)) {
449 					set.addAll(java.util.Arrays.asList(
450 					        java.beans.Introspector.getBeanInfo(interfaces[i])
451 					        .getPropertyDescriptors()));
452 					internalGetInterfacePropertyDescriptors(interfaces[i]
453 					    .getInterfaces(), superInterface, set);
454 				}
455 			}
456 		} catch (Exception e) {
457 			System.out.println("Exception occured " + interfaces + " "
458 			    + superInterface + " " + set);
459 		}
460 	}
461 
462 	private static void internalGetInterfacePropertyDescriptors(
463 	    Class theClass, Class superInterface, HashSet set)
464 	{
465 		if (!superInterface.isAssignableFrom(theClass)) {
466 			throw new IllegalArgumentException(DataFormatter.toString(theClass)
467 			    + " does not implement "
468 			    + DataFormatter.toString(superInterface));
469 		}
470 
471 		internalGetInterfacePropertyDescriptors(theClass.getInterfaces(),
472 		    superInterface, set);
473 	}
474 
475 	/**
476 	 * Returns all the property descriptors of <code>theClass</code>.
477 	 *
478 	 * @param theClass Class
479 	 *
480 	 * @return java.beans.PropertyDescriptor
481 	 *
482 	 * @throws IntrospectionException
483 	 */
484 	public static PropertyDescriptor[] getPropertyDescriptors(Class theClass)
485 		throws IntrospectionException
486 	{
487 		return Introspector.getBeanInfo(theClass).getPropertyDescriptors();
488 	}
489 
490 	/**
491 	 * DOCUMENT ME!
492 	 *
493 	 * @param propertyName DOCUMENT ME!
494 	 *
495 	 * @return DOCUMENT ME!
496 	 */
497 	public static String getPropertyDisplayString(String propertyName)
498 	{
499 		return getPropertyDisplayString(propertyName, true);
500 	}
501 
502 	/**
503 	 * DOCUMENT ME!
504 	 *
505 	 * @param propertyName DOCUMENT ME!
506 	 * @param titleCase DOCUMENT ME!
507 	 *
508 	 * @return DOCUMENT ME!
509 	 */
510 	public static String getPropertyDisplayString(String propertyName,
511 	    boolean titleCase)
512 	{
513 		String[] split = DataFormatter.splitStringOnCapitals(propertyName);
514 		StringBuffer ret = new StringBuffer();
515 
516 		if (titleCase) {
517 			split[0] = split[0].substring(0, 1).toUpperCase()
518 				+ split[0].substring(1);
519 		}
520 
521 		ret.append(split[0]);
522 
523 		for (int i = 1; i < split.length; i++) {
524 			if (!titleCase) {
525 				split[i] = split[i].substring(0, 1).toLowerCase()
526 					+ split[i].substring(1);
527 			}
528 
529 			ret.append(" ");
530 			ret.append(split[i]);
531 		}
532 
533 		return ret.toString();
534 	}
535 
536 	/**
537 	 * DOCUMENT ME!
538 	 *
539 	 * @param method DOCUMENT ME!
540 	 *
541 	 * @return DOCUMENT ME!
542 	 */
543 	public static boolean isGetterMethod(Method method)
544 	{
545 		return ((method.getName().startsWith("get")
546 		&& method.getParameterTypes().length == 0
547 		&& !method.getReturnType().equals(Void.TYPE))
548 		|| (method.getName().startsWith("is")
549 		&& method.getParameterTypes().length == 0
550 		&& method.getReturnType().equals(Boolean.TYPE)));
551 	}
552 }
553 
554 /* __oOo__ */