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