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