1 package org.apache.commons.beanutils2;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.beans.IntrospectionException;
23 import java.beans.PropertyDescriptor;
24 import java.lang.ref.Reference;
25 import java.lang.ref.SoftReference;
26 import java.lang.ref.WeakReference;
27 import java.lang.reflect.Method;
28 import java.lang.reflect.Modifier;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class MappedPropertyDescriptor
48 extends PropertyDescriptor
49 {
50
51
52
53
54
55 private Reference<Class<?>> mappedPropertyTypeRef;
56
57
58
59
60 private MappedMethodReference mappedReadMethodRef;
61
62
63
64
65 private MappedMethodReference mappedWriteMethodRef;
66
67
68
69
70 @SuppressWarnings( "unchecked" )
71
72 private static final Class<String>[] STRING_CLASS_PARAMETER = new Class[] { String.class };
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public MappedPropertyDescriptor( String propertyName, Class<?> beanClass )
87 throws IntrospectionException
88 {
89
90 super( propertyName, null, null );
91
92 if ( propertyName == null || propertyName.length() == 0 )
93 {
94 throw new IntrospectionException( "bad property name: " + propertyName + " on class: "
95 + beanClass.getClass().getName() );
96 }
97
98 setName( propertyName );
99 String base = capitalizePropertyName( propertyName );
100
101
102 Method mappedReadMethod = null;
103 Method mappedWriteMethod = null;
104 try
105 {
106 try
107 {
108 mappedReadMethod = getMethod( beanClass, "get" + base, STRING_CLASS_PARAMETER );
109 }
110 catch ( IntrospectionException e )
111 {
112 mappedReadMethod = getMethod( beanClass, "is" + base, STRING_CLASS_PARAMETER );
113 }
114 Class<?>[] params = { String.class, mappedReadMethod.getReturnType() };
115 mappedWriteMethod = getMethod( beanClass, "set" + base, params );
116 }
117 catch ( IntrospectionException e )
118 {
119
120
121
122 }
123
124
125 if ( mappedReadMethod == null )
126 {
127 mappedWriteMethod = getMethod( beanClass, "set" + base, 2 );
128 }
129
130 if ( ( mappedReadMethod == null ) && ( mappedWriteMethod == null ) )
131 {
132 throw new IntrospectionException( "Property '" + propertyName + "' not found on " + beanClass.getName() );
133 }
134 mappedReadMethodRef = new MappedMethodReference( mappedReadMethod );
135 mappedWriteMethodRef = new MappedMethodReference( mappedWriteMethod );
136
137 findMappedPropertyType();
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151 public MappedPropertyDescriptor( String propertyName, Class<?> beanClass, String mappedGetterName,
152 String mappedSetterName )
153 throws IntrospectionException
154 {
155
156 super( propertyName, null, null );
157
158 if ( propertyName == null || propertyName.length() == 0 )
159 {
160 throw new IntrospectionException( "bad property name: " + propertyName );
161 }
162 setName( propertyName );
163
164
165 Method mappedReadMethod = null;
166 Method mappedWriteMethod = null;
167 mappedReadMethod = getMethod( beanClass, mappedGetterName, STRING_CLASS_PARAMETER );
168
169 if ( mappedReadMethod != null )
170 {
171 Class<?>[] params = { String.class, mappedReadMethod.getReturnType() };
172 mappedWriteMethod = getMethod( beanClass, mappedSetterName, params );
173 }
174 else
175 {
176 mappedWriteMethod = getMethod( beanClass, mappedSetterName, 2 );
177 }
178 mappedReadMethodRef = new MappedMethodReference( mappedReadMethod );
179 mappedWriteMethodRef = new MappedMethodReference( mappedWriteMethod );
180
181 findMappedPropertyType();
182 }
183
184
185
186
187
188
189
190
191
192
193
194 public MappedPropertyDescriptor( String propertyName, Method mappedGetter, Method mappedSetter )
195 throws IntrospectionException
196 {
197
198 super( propertyName, mappedGetter, mappedSetter );
199
200 if ( propertyName == null || propertyName.length() == 0 )
201 {
202 throw new IntrospectionException( "bad property name: " + propertyName );
203 }
204
205 setName( propertyName );
206 mappedReadMethodRef = new MappedMethodReference( mappedGetter );
207 mappedWriteMethodRef = new MappedMethodReference( mappedSetter );
208 findMappedPropertyType();
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222 public Class<?> getMappedPropertyType()
223 {
224 return mappedPropertyTypeRef.get();
225 }
226
227
228
229
230
231
232 public Method getMappedReadMethod()
233 {
234 return mappedReadMethodRef.get();
235 }
236
237
238
239
240
241
242
243 public void setMappedReadMethod( Method mappedGetter )
244 throws IntrospectionException
245 {
246 mappedReadMethodRef = new MappedMethodReference( mappedGetter );
247 findMappedPropertyType();
248 }
249
250
251
252
253
254
255
256 public Method getMappedWriteMethod()
257 {
258 return mappedWriteMethodRef.get();
259 }
260
261
262
263
264
265
266
267 public void setMappedWriteMethod( Method mappedSetter )
268 throws IntrospectionException
269 {
270 mappedWriteMethodRef = new MappedMethodReference( mappedSetter );
271 findMappedPropertyType();
272 }
273
274
275
276
277
278
279 private void findMappedPropertyType()
280 throws IntrospectionException
281 {
282 try
283 {
284 Method mappedReadMethod = getMappedReadMethod();
285 Method mappedWriteMethod = getMappedWriteMethod();
286 Class<?> mappedPropertyType = null;
287 if ( mappedReadMethod != null )
288 {
289 if ( mappedReadMethod.getParameterTypes().length != 1 )
290 {
291 throw new IntrospectionException( "bad mapped read method arg count" );
292 }
293 mappedPropertyType = mappedReadMethod.getReturnType();
294 if ( mappedPropertyType == Void.TYPE )
295 {
296 throw new IntrospectionException( "mapped read method " + mappedReadMethod.getName()
297 + " returns void" );
298 }
299 }
300
301 if ( mappedWriteMethod != null )
302 {
303 Class<?>[] params = mappedWriteMethod.getParameterTypes();
304 if ( params.length != 2 )
305 {
306 throw new IntrospectionException( "bad mapped write method arg count" );
307 }
308 if ( mappedPropertyType != null && mappedPropertyType != params[1] )
309 {
310 throw new IntrospectionException( "type mismatch between mapped read and write methods" );
311 }
312 mappedPropertyType = params[1];
313 }
314 mappedPropertyTypeRef = new SoftReference<Class<?>>( mappedPropertyType );
315 }
316 catch ( IntrospectionException ex )
317 {
318 throw ex;
319 }
320 }
321
322
323
324
325
326
327 private static String capitalizePropertyName( String s )
328 {
329 if ( s.length() == 0 )
330 {
331 return s;
332 }
333
334 char[] chars = s.toCharArray();
335 chars[0] = Character.toUpperCase( chars[0] );
336 return new String( chars );
337 }
338
339
340
341
342 private static Method internalGetMethod( Class<?> initial, String methodName, int parameterCount )
343 {
344
345
346 for ( Class<?> clazz = initial; clazz != null; clazz = clazz.getSuperclass() )
347 {
348 Method[] methods = clazz.getDeclaredMethods();
349 for ( int i = 0; i < methods.length; i++ )
350 {
351 Method method = methods[i];
352 if ( method == null )
353 {
354 continue;
355 }
356
357 int mods = method.getModifiers();
358 if ( !Modifier.isPublic( mods ) || Modifier.isStatic( mods ) )
359 {
360 continue;
361 }
362 if ( method.getName().equals( methodName ) && method.getParameterTypes().length == parameterCount )
363 {
364 return method;
365 }
366 }
367 }
368
369
370
371
372 Class<?>[] interfaces = initial.getInterfaces();
373 for ( int i = 0; i < interfaces.length; i++ )
374 {
375 Method method = internalGetMethod( interfaces[i], methodName, parameterCount );
376 if ( method != null )
377 {
378 return method;
379 }
380 }
381
382 return null;
383 }
384
385
386
387
388 private static Method getMethod( Class<?> clazz, String methodName, int parameterCount )
389 throws IntrospectionException
390 {
391 if ( methodName == null )
392 {
393 return null;
394 }
395
396 Method method = internalGetMethod( clazz, methodName, parameterCount );
397 if ( method != null )
398 {
399 return method;
400 }
401
402
403 throw new IntrospectionException( "No method \"" + methodName + "\" with " + parameterCount + " parameter(s)" );
404 }
405
406
407
408
409 private static Method getMethod( Class<?> clazz, String methodName, Class<?>[] parameterTypes )
410 throws IntrospectionException
411 {
412 if ( methodName == null )
413 {
414 return null;
415 }
416
417 Method method = AccessibleObjectsRegistry.getMethodsRegistry().get( true, clazz, methodName, parameterTypes );
418 if ( method != null )
419 {
420 return method;
421 }
422
423 int parameterCount = ( parameterTypes == null ) ? 0 : parameterTypes.length;
424
425
426 throw new IntrospectionException( "No method \"" + methodName + "\" with " + parameterCount
427 + " parameter(s) of matching types." );
428 }
429
430
431
432
433
434
435 private static class MappedMethodReference
436 {
437 private String className;
438
439 private String methodName;
440
441 private Reference<Method> methodRef;
442
443 private Reference<Class<?>> classRef;
444
445 private Reference<Class<?>> writeParamTypeRef0;
446
447 private Reference<Class<?>> writeParamTypeRef1;
448
449 private String[] writeParamClassNames;
450
451 MappedMethodReference( Method m )
452 {
453 if ( m != null )
454 {
455 className = m.getDeclaringClass().getName();
456 methodName = m.getName();
457 methodRef = new SoftReference<Method>( m );
458 classRef = new WeakReference<Class<?>>( m.getDeclaringClass() );
459 Class<?>[] types = m.getParameterTypes();
460 if ( types.length == 2 )
461 {
462 writeParamTypeRef0 = new WeakReference<Class<?>>( types[0] );
463 writeParamTypeRef1 = new WeakReference<Class<?>>( types[1] );
464 writeParamClassNames = new String[2];
465 writeParamClassNames[0] = types[0].getName();
466 writeParamClassNames[1] = types[1].getName();
467 }
468 }
469 }
470
471 private Method get()
472 {
473 if ( methodRef == null )
474 {
475 return null;
476 }
477 Method m = methodRef.get();
478 if ( m == null )
479 {
480 Class<?> clazz = classRef.get();
481 if ( clazz == null )
482 {
483 clazz = reLoadClass();
484 if ( clazz != null )
485 {
486 classRef = new WeakReference<Class<?>>( clazz );
487 }
488 }
489 if ( clazz == null )
490 {
491 throw new RuntimeException( "Method " + methodName + " for " + className
492 + " could not be reconstructed - class reference has gone" );
493 }
494 Class<?>[] paramTypes = null;
495 if ( writeParamClassNames != null )
496 {
497 paramTypes = new Class[2];
498 paramTypes[0] = writeParamTypeRef0.get();
499 if ( paramTypes[0] == null )
500 {
501 paramTypes[0] = reLoadClass( writeParamClassNames[0] );
502 if ( paramTypes[0] != null )
503 {
504 writeParamTypeRef0 = new WeakReference<Class<?>>( paramTypes[0] );
505 }
506 }
507 paramTypes[1] = writeParamTypeRef1.get();
508 if ( paramTypes[1] == null )
509 {
510 paramTypes[1] = reLoadClass( writeParamClassNames[1] );
511 if ( paramTypes[1] != null )
512 {
513 writeParamTypeRef1 = new WeakReference<Class<?>>( paramTypes[1] );
514 }
515 }
516 }
517 else
518 {
519 paramTypes = STRING_CLASS_PARAMETER;
520 }
521 try
522 {
523 m = clazz.getMethod( methodName, paramTypes );
524
525
526 }
527 catch ( NoSuchMethodException e )
528 {
529 throw new RuntimeException( "Method " + methodName + " for " + className
530 + " could not be reconstructed - method not found" );
531 }
532 methodRef = new SoftReference<Method>( m );
533 }
534 return m;
535 }
536
537
538
539
540 private Class<?> reLoadClass()
541 {
542 return reLoadClass( className );
543 }
544
545
546
547
548 private Class<?> reLoadClass( String name )
549 {
550
551 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
552
553
554 if ( classLoader != null )
555 {
556 try
557 {
558 return classLoader.loadClass( name );
559 }
560 catch ( ClassNotFoundException e )
561 {
562
563 }
564 }
565
566
567 classLoader = MappedPropertyDescriptor.class.getClassLoader();
568 try
569 {
570 return classLoader.loadClass( name );
571 }
572 catch ( ClassNotFoundException e )
573 {
574 return null;
575 }
576 }
577 }
578 }