1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ch.qos.logback.core.joran.util;
16
17 import java.beans.BeanInfo;
18 import java.beans.IntrospectionException;
19 import java.beans.Introspector;
20 import java.beans.MethodDescriptor;
21 import java.beans.PropertyDescriptor;
22 import java.lang.annotation.Annotation;
23 import java.lang.reflect.Method;
24
25 import ch.qos.logback.core.joran.spi.DefaultClass;
26 import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
27 import ch.qos.logback.core.spi.ContextAwareBase;
28 import ch.qos.logback.core.util.AggregationType;
29 import ch.qos.logback.core.util.PropertySetterException;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class PropertySetter extends ContextAwareBase {
55
56 protected Object obj;
57 protected Class objClass;
58 protected PropertyDescriptor[] propertyDescriptors;
59 protected MethodDescriptor[] methodDescriptors;
60
61
62
63
64
65
66
67
68 public PropertySetter(Object obj) {
69 this.obj = obj;
70 this.objClass = obj.getClass();
71 }
72
73
74
75
76
77 protected void introspect() {
78 try {
79 BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
80 propertyDescriptors = bi.getPropertyDescriptors();
81 methodDescriptors = bi.getMethodDescriptors();
82 } catch (IntrospectionException ex) {
83 addError("Failed to introspect " + obj + ": " + ex.getMessage());
84 propertyDescriptors = new PropertyDescriptor[0];
85 methodDescriptors = new MethodDescriptor[0];
86 }
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 public void setProperty(String name, String value) {
108 if (value == null) {
109 return;
110 }
111
112 name = Introspector.decapitalize(name);
113
114 PropertyDescriptor prop = getPropertyDescriptor(name);
115
116 if (prop == null) {
117 addWarn("No such property [" + name + "] in " + objClass.getName() + ".");
118 } else {
119 try {
120 setProperty(prop, name, value);
121 } catch (PropertySetterException ex) {
122 addWarn("Failed to set property [" + name + "] to value \"" + value
123 + "\". ", ex);
124 }
125 }
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139 public void setProperty(PropertyDescriptor prop, String name, String value)
140 throws PropertySetterException {
141 Method setter = prop.getWriteMethod();
142
143 if (setter == null) {
144 throw new PropertySetterException("No setter for property [" + name
145 + "].");
146 }
147
148 Class[] paramTypes = setter.getParameterTypes();
149
150 if (paramTypes.length != 1) {
151 throw new PropertySetterException("#params for setter != 1");
152 }
153
154 Object arg;
155
156 try {
157 arg = StringToObjectConverter.convertArg(this, value, paramTypes[0]);
158 } catch (Throwable t) {
159 throw new PropertySetterException("Conversion to type [" + paramTypes[0]
160 + "] failed. ", t);
161 }
162
163 if (arg == null) {
164 throw new PropertySetterException("Conversion to type [" + paramTypes[0]
165 + "] failed.");
166 }
167 try {
168 setter.invoke(obj, arg);
169 } catch (Exception ex) {
170 throw new PropertySetterException(ex);
171 }
172 }
173
174 public AggregationType computeAggregationType(String name) {
175 String cName = capitalizeFirstLetter(name);
176
177 Method addMethod = findAdderMethod(cName);
178
179
180 if (addMethod != null) {
181 AggregationType type = computeRawAggregationType(addMethod);
182 switch (type) {
183 case NOT_FOUND:
184 return AggregationType.NOT_FOUND;
185 case AS_BASIC_PROPERTY:
186 return AggregationType.AS_BASIC_PROPERTY_COLLECTION;
187 case AS_COMPLEX_PROPERTY:
188 return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION;
189 }
190 }
191
192 Method setterMethod = findSetterMethod(name);
193 if (setterMethod != null) {
194 return computeRawAggregationType(setterMethod);
195 } else {
196
197 return AggregationType.NOT_FOUND;
198 }
199 }
200
201 private Method findAdderMethod(String name) {
202 name = capitalizeFirstLetter(name);
203 Method adderMethod = getMethod("add" + name);
204 return adderMethod;
205 }
206
207 private Method findSetterMethod(String name) {
208 String dName = Introspector.decapitalize(name);
209 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dName);
210 if (propertyDescriptor != null) {
211 return propertyDescriptor.getWriteMethod();
212 } else {
213 return null;
214 }
215 }
216
217 private Class<?> getParameterClassForMethod(Method method) {
218 if (method == null) {
219 return null;
220 }
221 Class[] classArray = method.getParameterTypes();
222 if (classArray.length != 1) {
223 return null;
224 } else {
225 return classArray[0];
226 }
227 }
228
229 private AggregationType computeRawAggregationType(Method method) {
230 Class<?> parameterClass = getParameterClassForMethod(method);
231 if (parameterClass == null) {
232 return AggregationType.NOT_FOUND;
233 }
234 if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) {
235 return AggregationType.AS_BASIC_PROPERTY;
236 } else {
237 return AggregationType.AS_COMPLEX_PROPERTY;
238 }
239 }
240
241
242
243
244
245
246
247
248 private boolean isUnequivocallyInstantiable(Class<?> clazz) {
249 if (clazz.isInterface()) {
250 return false;
251 }
252
253
254
255 Object o;
256 try {
257 o = clazz.newInstance();
258 if (o != null) {
259 return true;
260 } else {
261 return false;
262 }
263 } catch (InstantiationException e) {
264 return false;
265 } catch (IllegalAccessException e) {
266 return false;
267 }
268 }
269
270 public Class getObjClass() {
271 return objClass;
272 }
273
274 public void addComplexProperty(String name, Object complexProperty) {
275 Method adderMethod = findAdderMethod(name);
276
277 if (adderMethod != null) {
278 Class[] paramTypes = adderMethod.getParameterTypes();
279 if (!isSanityCheckSuccessful(name, adderMethod, paramTypes,
280 complexProperty)) {
281 return;
282 }
283 invokeMethodWithSingleParameterOnThisObject(adderMethod, complexProperty);
284 } else {
285 addError("Could not find method [" + "add" + name + "] in class ["
286 + objClass.getName() + "].");
287 }
288 }
289
290 void invokeMethodWithSingleParameterOnThisObject(Method method,
291 Object parameter) {
292 Class ccc = parameter.getClass();
293 try {
294 method.invoke(this.obj, parameter);
295 } catch (Exception e) {
296 addError("Could not invoke method " + method.getName() + " in class "
297 + obj.getClass().getName() + " with parameter of type "
298 + ccc.getName(), e);
299 }
300 }
301
302 public void addBasicProperty(String name, String strValue) {
303
304 if (strValue == null) {
305 return;
306 }
307
308 name = capitalizeFirstLetter(name);
309 Method adderMethod = findAdderMethod(name);
310
311 if (adderMethod == null) {
312 addError("No adder for property [" + name + "].");
313 return;
314 }
315
316 Class[] paramTypes = adderMethod.getParameterTypes();
317 isSanityCheckSuccessful(name, adderMethod, paramTypes, strValue);
318
319 Object arg;
320 try {
321 arg = StringToObjectConverter.convertArg(this, strValue, paramTypes[0]);
322 } catch (Throwable t) {
323 addError("Conversion to type [" + paramTypes[0] + "] failed. ", t);
324 return;
325 }
326 if (arg != null) {
327 invokeMethodWithSingleParameterOnThisObject(adderMethod, strValue);
328 }
329 }
330
331 public void setComplexProperty(String name, Object complexProperty) {
332 String dName = Introspector.decapitalize(name);
333 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dName);
334
335 if (propertyDescriptor == null) {
336 addWarn("Could not find PropertyDescriptor for [" + name + "] in "
337 + objClass.getName());
338
339 return;
340 }
341
342 Method setter = propertyDescriptor.getWriteMethod();
343
344 if (setter == null) {
345 addWarn("Not setter method for property [" + name + "] in "
346 + obj.getClass().getName());
347
348 return;
349 }
350
351 Class[] paramTypes = setter.getParameterTypes();
352
353 if (!isSanityCheckSuccessful(name, setter, paramTypes, complexProperty)) {
354 return;
355 }
356 try {
357 invokeMethodWithSingleParameterOnThisObject(setter, complexProperty);
358
359 } catch (Exception e) {
360 addError("Could not set component " + obj + " for parent component "
361 + obj, e);
362 }
363 }
364
365 private boolean isSanityCheckSuccessful(String name, Method method,
366 Class<?>[] params, Object complexProperty) {
367 Class ccc = complexProperty.getClass();
368 if (params.length != 1) {
369 addError("Wrong number of parameters in setter method for property ["
370 + name + "] in " + obj.getClass().getName());
371
372 return false;
373 }
374
375 if (!params[0].isAssignableFrom(complexProperty.getClass())) {
376 addError("A \"" + ccc.getName() + "\" object is not assignable to a \""
377 + params[0].getName() + "\" variable.");
378 addError("The class \"" + params[0].getName() + "\" was loaded by ");
379 addError("[" + params[0].getClassLoader() + "] whereas object of type ");
380 addError("\"" + ccc.getName() + "\" was loaded by ["
381 + ccc.getClassLoader() + "].");
382 return false;
383 }
384
385 return true;
386 }
387
388 private String capitalizeFirstLetter(String name) {
389 return name.substring(0, 1).toUpperCase() + name.substring(1);
390 }
391
392 protected Method getMethod(String methodName) {
393 if (methodDescriptors == null) {
394 introspect();
395 }
396
397 for (int i = 0; i < methodDescriptors.length; i++) {
398 if (methodName.equals(methodDescriptors[i].getName())) {
399 return methodDescriptors[i].getMethod();
400 }
401 }
402
403 return null;
404 }
405
406 protected PropertyDescriptor getPropertyDescriptor(String name) {
407 if (propertyDescriptors == null) {
408 introspect();
409 }
410
411 for (int i = 0; i < propertyDescriptors.length; i++) {
412
413
414 if (name.equals(propertyDescriptors[i].getName())) {
415
416 return propertyDescriptors[i];
417 }
418 }
419
420 return null;
421 }
422
423 public Object getObj() {
424 return obj;
425 }
426
427 Method getRelevantMethod(String name, AggregationType aggregationType) {
428 String cName = capitalizeFirstLetter(name);
429 Method relevantMethod;
430 if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) {
431 relevantMethod = findAdderMethod(cName);
432 } else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) {
433 relevantMethod = findSetterMethod(cName);
434 } else {
435 throw new IllegalStateException(aggregationType + " not allowed here");
436 }
437 return relevantMethod;
438 }
439
440 <T extends Annotation> T getAnnotation(String name, Class<T> annonationClass,
441 Method relevantMethod) {
442
443 if (relevantMethod != null) {
444 return relevantMethod.getAnnotation(annonationClass);
445 } else {
446 return null;
447 }
448 }
449
450 Class getDefaultClassNameByAnnonation(String name, Method relevantMethod) {
451 DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class,
452 relevantMethod);
453 if (defaultClassAnnon != null) {
454 Class defaultClass = defaultClassAnnon.value();
455 return defaultClass;
456 }
457 return null;
458 }
459
460 Class getByConcreteType(String name, Method relevantMethod) {
461
462 Class<?> paramType = getParameterClassForMethod(relevantMethod);
463 if (paramType == null) {
464 return null;
465 }
466
467 boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType);
468 if (isUnequivocallyInstantiable) {
469 return paramType;
470 } else {
471 return null;
472 }
473
474 }
475
476 public Class getClassNameViaImplicitRules(String name,
477 AggregationType aggregationType, DefaultNestedComponentRegistry registry) {
478
479 Class registryResult = registry.findDefaultComponentType(obj.getClass(),
480 name);
481 if (registryResult != null) {
482 return registryResult;
483 }
484
485 Method relevantMethod = getRelevantMethod(name, aggregationType);
486 if (relevantMethod == null) {
487 return null;
488 }
489 Class byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
490 if (byAnnotation != null) {
491 return byAnnotation;
492 }
493 return getByConcreteType(name, relevantMethod);
494 }
495
496 }