1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.property;
26
27 import java.beans.Introspector;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.util.Map;
31
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import org.hibernate.HibernateException;
36 import org.hibernate.PropertyAccessException;
37 import org.hibernate.PropertyNotFoundException;
38 import org.hibernate.engine.SessionFactoryImplementor;
39 import org.hibernate.engine.SessionImplementor;
40 import org.hibernate.util.ReflectHelper;
41
42 /**
43 * Accesses property values via a get/set pair, which may be nonpublic.
44 * The default (and recommended strategy).
45 *
46 * @author Gavin King
47 */
48 public class BasicPropertyAccessor implements PropertyAccessor {
49
50 private static final Logger log = LoggerFactory.getLogger(BasicPropertyAccessor.class);
51
52 public static final class BasicSetter implements Setter {
53 private Class clazz;
54 private final transient Method method;
55 private final String propertyName;
56
57 private BasicSetter(Class clazz, Method method, String propertyName) {
58 this.clazz=clazz;
59 this.method=method;
60 this.propertyName=propertyName;
61 }
62
63 public void set(Object target, Object value, SessionFactoryImplementor factory)
64 throws HibernateException {
65 try {
66 method.invoke( target, new Object[] { value } );
67 }
68 catch (NullPointerException npe) {
69 if ( value==null && method.getParameterTypes()[0].isPrimitive() ) {
70 throw new PropertyAccessException(
71 npe,
72 "Null value was assigned to a property of primitive type",
73 true,
74 clazz,
75 propertyName
76 );
77 }
78 else {
79 throw new PropertyAccessException(
80 npe,
81 "NullPointerException occurred while calling",
82 true,
83 clazz,
84 propertyName
85 );
86 }
87 }
88 catch (InvocationTargetException ite) {
89 throw new PropertyAccessException(
90 ite,
91 "Exception occurred inside",
92 true,
93 clazz,
94 propertyName
95 );
96 }
97 catch (IllegalAccessException iae) {
98 throw new PropertyAccessException(
99 iae,
100 "IllegalAccessException occurred while calling",
101 true,
102 clazz,
103 propertyName
104 );
105 //cannot occur
106 }
107 catch (IllegalArgumentException iae) {
108 if ( value==null && method.getParameterTypes()[0].isPrimitive() ) {
109 throw new PropertyAccessException(
110 iae,
111 "Null value was assigned to a property of primitive type",
112 true,
113 clazz,
114 propertyName
115 );
116 }
117 else {
118 log.error(
119 "IllegalArgumentException in class: " + clazz.getName() +
120 ", setter method of property: " + propertyName
121 );
122 log.error(
123 "expected type: " +
124 method.getParameterTypes()[0].getName() +
125 ", actual value: " +
126 ( value==null ? null : value.getClass().getName() )
127 );
128 throw new PropertyAccessException(
129 iae,
130 "IllegalArgumentException occurred while calling",
131 true,
132 clazz,
133 propertyName
134 );
135 }
136 }
137 }
138
139 public Method getMethod() {
140 return method;
141 }
142
143 public String getMethodName() {
144 return method.getName();
145 }
146
147 Object readResolve() {
148 return createSetter(clazz, propertyName);
149 }
150
151 public String toString() {
152 return "BasicSetter(" + clazz.getName() + '.' + propertyName + ')';
153 }
154 }
155
156 public static final class BasicGetter implements Getter {
157 private Class clazz;
158 private final transient Method method;
159 private final String propertyName;
160
161 private BasicGetter(Class clazz, Method method, String propertyName) {
162 this.clazz=clazz;
163 this.method=method;
164 this.propertyName=propertyName;
165 }
166
167 public Object get(Object target) throws HibernateException {
168 try {
169 return method.invoke(target, null);
170 }
171 catch (InvocationTargetException ite) {
172 throw new PropertyAccessException(
173 ite,
174 "Exception occurred inside",
175 false,
176 clazz,
177 propertyName
178 );
179 }
180 catch (IllegalAccessException iae) {
181 throw new PropertyAccessException(
182 iae,
183 "IllegalAccessException occurred while calling",
184 false,
185 clazz,
186 propertyName
187 );
188 //cannot occur
189 }
190 catch (IllegalArgumentException iae) {
191 log.error(
192 "IllegalArgumentException in class: " + clazz.getName() +
193 ", getter method of property: " + propertyName
194 );
195 throw new PropertyAccessException(
196 iae,
197 "IllegalArgumentException occurred calling",
198 false,
199 clazz,
200 propertyName
201 );
202 }
203 }
204
205 public Object getForInsert(Object target, Map mergeMap, SessionImplementor session) {
206 return get( target );
207 }
208
209 public Class getReturnType() {
210 return method.getReturnType();
211 }
212
213 public Method getMethod() {
214 return method;
215 }
216
217 public String getMethodName() {
218 return method.getName();
219 }
220
221 public String toString() {
222 return "BasicGetter(" + clazz.getName() + '.' + propertyName + ')';
223 }
224
225 Object readResolve() {
226 return createGetter(clazz, propertyName);
227 }
228 }
229
230
231 public Setter getSetter(Class theClass, String propertyName)
232 throws PropertyNotFoundException {
233 return createSetter(theClass, propertyName);
234 }
235
236 private static Setter createSetter(Class theClass, String propertyName)
237 throws PropertyNotFoundException {
238 BasicSetter result = getSetterOrNull(theClass, propertyName);
239 if (result==null) {
240 throw new PropertyNotFoundException(
241 "Could not find a setter for property " +
242 propertyName +
243 " in class " +
244 theClass.getName()
245 );
246 }
247 return result;
248 }
249
250 private static BasicSetter getSetterOrNull(Class theClass, String propertyName) {
251
252 if (theClass==Object.class || theClass==null) return null;
253
254 Method method = setterMethod(theClass, propertyName);
255
256 if (method!=null) {
257 if ( !ReflectHelper.isPublic(theClass, method) ) method.setAccessible(true);
258 return new BasicSetter(theClass, method, propertyName);
259 }
260 else {
261 BasicSetter setter = getSetterOrNull( theClass.getSuperclass(), propertyName );
262 if (setter==null) {
263 Class[] interfaces = theClass.getInterfaces();
264 for ( int i=0; setter==null && i<interfaces.length; i++ ) {
265 setter=getSetterOrNull( interfaces[i], propertyName );
266 }
267 }
268 return setter;
269 }
270
271 }
272
273 private static Method setterMethod(Class theClass, String propertyName) {
274
275 BasicGetter getter = getGetterOrNull(theClass, propertyName);
276 Class returnType = (getter==null) ? null : getter.getReturnType();
277
278 Method[] methods = theClass.getDeclaredMethods();
279 Method potentialSetter = null;
280 for (int i=0; i<methods.length; i++) {
281 String methodName = methods[i].getName();
282
283 if ( methods[i].getParameterTypes().length==1 && methodName.startsWith("set") ) {
284 String testStdMethod = Introspector.decapitalize( methodName.substring(3) );
285 String testOldMethod = methodName.substring(3);
286 if ( testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName) ) {
287 potentialSetter = methods[i];
288 if ( returnType==null || methods[i].getParameterTypes()[0].equals(returnType) ) {
289 return potentialSetter;
290 }
291 }
292 }
293 }
294 return potentialSetter;
295 }
296
297 public Getter getGetter(Class theClass, String propertyName)
298 throws PropertyNotFoundException {
299 return createGetter(theClass, propertyName);
300 }
301
302 public static Getter createGetter(Class theClass, String propertyName)
303 throws PropertyNotFoundException {
304 BasicGetter result = getGetterOrNull(theClass, propertyName);
305 if (result==null) {
306 throw new PropertyNotFoundException(
307 "Could not find a getter for " +
308 propertyName +
309 " in class " +
310 theClass.getName()
311 );
312 }
313 return result;
314
315 }
316
317 private static BasicGetter getGetterOrNull(Class theClass, String propertyName) {
318
319 if (theClass==Object.class || theClass==null) return null;
320
321 Method method = getterMethod(theClass, propertyName);
322
323 if (method!=null) {
324 if ( !ReflectHelper.isPublic(theClass, method) ) method.setAccessible(true);
325 return new BasicGetter(theClass, method, propertyName);
326 }
327 else {
328 BasicGetter getter = getGetterOrNull( theClass.getSuperclass(), propertyName );
329 if (getter==null) {
330 Class[] interfaces = theClass.getInterfaces();
331 for ( int i=0; getter==null && i<interfaces.length; i++ ) {
332 getter=getGetterOrNull( interfaces[i], propertyName );
333 }
334 }
335 return getter;
336 }
337 }
338
339 private static Method getterMethod(Class theClass, String propertyName) {
340
341 Method[] methods = theClass.getDeclaredMethods();
342 for (int i=0; i<methods.length; i++) {
343 // only carry on if the method has no parameters
344 if ( methods[i].getParameterTypes().length == 0 ) {
345 String methodName = methods[i].getName();
346
347 // try "get"
348 if ( methodName.startsWith("get") ) {
349 String testStdMethod = Introspector.decapitalize( methodName.substring(3) );
350 String testOldMethod = methodName.substring(3);
351 if ( testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName) ) {
352 return methods[i];
353 }
354
355 }
356
357 // if not "get", then try "is"
358 if ( methodName.startsWith("is") ) {
359 String testStdMethod = Introspector.decapitalize( methodName.substring(2) );
360 String testOldMethod = methodName.substring(2);
361 if ( testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName) ) {
362 return methods[i];
363 }
364 }
365 }
366 }
367 return null;
368 }
369
370 }