View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2008 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.util.codingsupport;
18  
19  import java.beans.BeanInfo;
20  import java.beans.IntrospectionException;
21  import java.beans.Introspector;
22  import java.beans.PropertyDescriptor;
23  import java.lang.reflect.Array;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.List;
29  
30  /**
31   * Utility class to get/set properties generically.
32   *
33   * @svnLink $Revision: 3873 $;$Date: 2009-08-04 13:59:45 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/core/src/main/java/ch/elca/el4j/util/codingsupport/BeanPropertyUtils.java $
34   *
35   * @author David Bernhard (DBD)
36   */
37  public final class BeanPropertyUtils {
38  	
39  	/**
40  	 * Utility class -> no instances.
41  	 */
42  	private BeanPropertyUtils() { }
43  
44  	/**
45  	 * Generically get a property.
46  	 * @param entity The entity to get a property on.
47  	 * @param propertyName The property name to get.
48  	 * @return The property value as returned from reading the property.
49  	 */
50  	public static Object getProperty(Object entity, String propertyName) {
51  		PropertyDescriptor descriptor = getDescriptor(entity, propertyName);
52  		Method reader = descriptor.getReadMethod();
53  		if (reader == null) {
54  			throw new IllegalArgumentException("Property " + propertyName
55  				+ "is not readable.");
56  		}
57  
58  		boolean accessible = reader.isAccessible();
59  		try {
60  			reader.setAccessible(true);
61  			return reader.invoke(entity, new Object[0]);
62  		} catch (IllegalAccessException e) {
63  			throw new RuntimeException(e);
64  		} catch (InvocationTargetException e) {
65  			throw new RuntimeException(e);
66  		} finally {
67  			reader.setAccessible(accessible);
68  		}
69  	}
70  	
71  	/**
72  	 * Generically get a property, casting arrays to collections.
73  	 * @param entity The entity to get a property on.
74  	 * @param propertyName The property name to get.
75  	 * @return The property value. If it was an array, it is returned as a collection.
76  	 */
77  	public static Object getPropertySimplified(Object entity, String propertyName) {
78  		Object value = getProperty(entity, propertyName);
79  		if (value.getClass().isArray()) {
80  			// Unlike generics, arrays luckily ARE covariant.
81  			List<Object> list = new ArrayList<Object>(((Object[]) value).length);
82  			for (Object entry : (Object[]) value) {
83  				list.add(entry);
84  			}
85  			return list;
86  		} else {
87  			return value;
88  		}
89  	}
90  	
91  	/**
92  	 * Get the property descriptor for a property by name.
93  	 * @param entity The entity to get the property for.
94  	 * @param propertyName The property name.
95  	 * @return The property descriptor.
96  	 */
97  	private static PropertyDescriptor getDescriptor(Object entity, String propertyName) {
98  		Class<?> entityClass = entity.getClass();
99  		return getDescriptorByClass(entityClass, propertyName);
100 	}
101 	
102 	/**
103 	 * Get the property descriptor for a property by name and class.
104 	 * @param entityClass The entity class to get the property for.
105 	 * @param propertyName The property name.
106 	 * @return The property descriptor.
107 	 */
108 	private static PropertyDescriptor getDescriptorByClass(Class<?> entityClass, String propertyName) {
109 		BeanInfo info;
110 		try {
111 			info = Introspector.getBeanInfo(entityClass);
112 		} catch (IntrospectionException e) {
113 			throw new RuntimeException("Cannot introspect " + entityClass, e);
114 		}
115 		PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
116 		PropertyDescriptor descriptor = null;
117 		for (PropertyDescriptor candidate : descriptors) {
118 			if (propertyName.equals(candidate.getName())) {
119 				descriptor = candidate;
120 				break;
121 			}
122 		}
123 		if (descriptor == null) {
124 			throw new IllegalArgumentException("No property " + propertyName + " in class "
125 				+ entityClass);
126 		}
127 		return descriptor;
128 	}
129 	
130 	/**
131 	 * Generically set a property.
132 	 * @param entity The entity to set the property on.
133 	 * @param propertyName The property name.
134 	 * @param value The value to set.
135 	 */
136 	public static void setProperty(Object entity, String propertyName, Object value) {
137 		PropertyDescriptor descriptor = getDescriptor(entity, propertyName);
138 		Method writer = descriptor.getWriteMethod();
139 		if (writer == null) {
140 			throw new IllegalArgumentException("Property " + propertyName
141 				+ "is not writable.");
142 		}
143 		
144 		boolean accessible = writer.isAccessible();
145 		try {
146 			writer.setAccessible(true);
147 			writer.invoke(entity, value);
148 		} catch (IllegalAccessException e) {
149 			throw new RuntimeException(e);
150 		} catch (InvocationTargetException e) {
151 			throw new RuntimeException(e);
152 		} finally {
153 			writer.setAccessible(accessible);
154 		}
155 	}
156 	
157 	/**
158 	 * Generically set a property. If the setter requires an array, a collection may be
159 	 * passed too in which case it is put into an array.
160 	 * @param entity The entity to set the property on.
161 	 * @param propertyName The property name.
162 	 * @param value The value to set.
163 	 */
164 	public static void setPropertySimplified(Object entity, String propertyName, Object value) {
165 		PropertyDescriptor descriptor = getDescriptor(entity, propertyName);
166 		Method writer = descriptor.getWriteMethod();
167 		
168 		if (writer.getParameterTypes()[0].isArray() && value instanceof Collection) {
169 			Class<?> componentClass = writer.getParameterTypes()[0].getComponentType();
170 			// We need unchecked access to the collection because we have no
171 			// type information. This is safe as we only use it internally
172 			// to take objects out.
173 			// PRE: The caller has only put elements in the collection that 
174 			// can go into the array.
175 			@SuppressWarnings("unchecked")
176 			Collection collection = (Collection) value;
177 			Object array = Array.newInstance(componentClass, collection.size());
178 			int i = 0;
179 			for (Object entry : collection) {
180 				Array.set(array, i, entry);
181 				i++;
182 			}
183 			setProperty(entity, propertyName, array);
184 		} else {
185 			setProperty(entity, propertyName, value);
186 		}
187 	}
188 	
189 	/**
190 	 * Get the read method of a property.
191 	 * @param entityClass     the class of the entity
192 	 * @param propertyName    the property name
193 	 * @return                the read method to access the property
194 	 */
195 	public static Method getReadMethod(Class<?> entityClass, String propertyName) {
196 		PropertyDescriptor descriptor = getDescriptorByClass(entityClass, propertyName);
197 		return descriptor.getReadMethod();
198 	}
199 	
200 	/**
201 	 * Get the write method of a property.
202 	 * @param entityClass     the class of the entity
203 	 * @param propertyName    the property name
204 	 * @return                the write method to access the property
205 	 */
206 	public static Method getWriteMethod(Class<?> entityClass, String propertyName) {
207 		PropertyDescriptor descriptor = getDescriptorByClass(entityClass, propertyName);
208 		return descriptor.getWriteMethod();
209 	}
210 }