package ch.qos.logback.core.joran.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import ch.qos.logback.core.joran.spi.DefaultClass;
import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
import ch.qos.logback.core.joran.util.beans.BeanDescription;
import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
import ch.qos.logback.core.joran.util.beans.BeanUtil;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.util.AggregationType;
import ch.qos.logback.core.util.PropertySetterException;
public class PropertySetter extends ContextAwareBase {
protected final Object obj;
protected final Class<?> objClass;
protected final BeanDescription beanDescription;
public PropertySetter(BeanDescriptionCache beanDescriptionCache, Object obj) {
this.obj = obj;
this.objClass = obj.getClass();
this.beanDescription = beanDescriptionCache.getBeanDescription(objClass);
}
public void setProperty(String name, String value) {
if (value == null) {
return;
}
Method setter = findSetterMethod(name);
if (setter == null) {
addWarn("No setter for property [" + name + "] in " + objClass.getName() + ".");
} else {
try {
setProperty(setter, name, value);
} catch (PropertySetterException ex) {
addWarn("Failed to set property [" + name + "] to value \"" + value + "\". ", ex);
}
}
}
private void setProperty(Method setter, String name, String value) throws PropertySetterException {
Class<?>[] paramTypes = setter.getParameterTypes();
Object arg;
try {
arg = StringToObjectConverter.convertArg(this, value, paramTypes[0]);
} catch (Throwable t) {
throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed. ", t);
}
if (arg == null) {
throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed.");
}
try {
setter.invoke(obj, arg);
} catch (Exception ex) {
throw new PropertySetterException(ex);
}
}
public AggregationType computeAggregationType(String name) {
String cName = capitalizeFirstLetter(name);
Method addMethod = findAdderMethod(cName);
if (addMethod != null) {
AggregationType type = computeRawAggregationType(addMethod);
switch (type) {
case NOT_FOUND:
return AggregationType.NOT_FOUND;
case AS_BASIC_PROPERTY:
return AggregationType.AS_BASIC_PROPERTY_COLLECTION;
case AS_COMPLEX_PROPERTY:
return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION;
case AS_BASIC_PROPERTY_COLLECTION:
case AS_COMPLEX_PROPERTY_COLLECTION:
addError("Unexpected AggregationType " + type);
}
}
Method setter = findSetterMethod(name);
if (setter != null) {
return computeRawAggregationType(setter);
} else {
return AggregationType.NOT_FOUND;
}
}
private Method findAdderMethod(String name) {
String propertyName = BeanUtil.toLowerCamelCase(name);
return beanDescription.getAdder(propertyName);
}
private Method findSetterMethod(String name) {
String propertyName = BeanUtil.toLowerCamelCase(name);
return beanDescription.getSetter(propertyName);
}
private Class<?> getParameterClassForMethod(Method method) {
if (method == null) {
return null;
}
Class<?>[] classArray = method.getParameterTypes();
if (classArray.length != 1) {
return null;
} else {
return classArray[0];
}
}
private AggregationType computeRawAggregationType(Method method) {
Class<?> parameterClass = getParameterClassForMethod(method);
if (parameterClass == null) {
return AggregationType.NOT_FOUND;
}
if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) {
return AggregationType.AS_BASIC_PROPERTY;
} else {
return AggregationType.AS_COMPLEX_PROPERTY;
}
}
private boolean isUnequivocallyInstantiable(Class<?> clazz) {
if (clazz.isInterface()) {
return false;
}
Object o;
try {
o = clazz.newInstance();
if (o != null) {
return true;
} else {
return false;
}
} catch (InstantiationException e) {
return false;
} catch (IllegalAccessException e) {
return false;
}
}
public Class<?> getObjClass() {
return objClass;
}
public void addComplexProperty(String name, Object complexProperty) {
Method adderMethod = findAdderMethod(name);
if (adderMethod != null) {
Class<?>[] paramTypes = adderMethod.getParameterTypes();
if (!isSanityCheckSuccessful(name, adderMethod, paramTypes, complexProperty)) {
return;
}
invokeMethodWithSingleParameterOnThisObject(adderMethod, complexProperty);
} else {
addError("Could not find method [" + "add" + name + "] in class [" + objClass.getName() + "].");
}
}
void invokeMethodWithSingleParameterOnThisObject(Method method, Object parameter) {
Class<?> ccc = parameter.getClass();
try {
method.invoke(this.obj, parameter);
} catch (Exception e) {
addError("Could not invoke method " + method.getName() + " in class " + obj.getClass().getName() + " with parameter of type " + ccc.getName(), e);
}
}
public void addBasicProperty(String name, String strValue) {
if (strValue == null) {
return;
}
name = capitalizeFirstLetter(name);
Method adderMethod = findAdderMethod(name);
if (adderMethod == null) {
addError("No adder for property [" + name + "].");
return;
}
Class<?>[] paramTypes = adderMethod.getParameterTypes();
isSanityCheckSuccessful(name, adderMethod, paramTypes, strValue);
Object arg;
try {
arg = StringToObjectConverter.convertArg(this, strValue, paramTypes[0]);
} catch (Throwable t) {
addError("Conversion to type [" + paramTypes[0] + "] failed. ", t);
return;
}
if (arg != null) {
invokeMethodWithSingleParameterOnThisObject(adderMethod, strValue);
}
}
public void setComplexProperty(String name, Object complexProperty) {
Method setter = findSetterMethod(name);
if (setter == null) {
addWarn("Not setter method for property [" + name + "] in " + obj.getClass().getName());
return;
}
Class<?>[] paramTypes = setter.getParameterTypes();
if (!isSanityCheckSuccessful(name, setter, paramTypes, complexProperty)) {
return;
}
try {
invokeMethodWithSingleParameterOnThisObject(setter, complexProperty);
} catch (Exception e) {
addError("Could not set component " + obj + " for parent component " + obj, e);
}
}
private boolean isSanityCheckSuccessful(String name, Method method, Class<?>[] params, Object complexProperty) {
Class<?> ccc = complexProperty.getClass();
if (params.length != 1) {
addError("Wrong number of parameters in setter method for property [" + name + "] in " + obj.getClass().getName());
return false;
}
if (!params[0].isAssignableFrom(complexProperty.getClass())) {
addError("A \"" + ccc.getName() + "\" object is not assignable to a \"" + params[0].getName() + "\" variable.");
addError("The class \"" + params[0].getName() + "\" was loaded by ");
addError("[" + params[0].getClassLoader() + "] whereas object of type ");
addError("\"" + ccc.getName() + "\" was loaded by [" + ccc.getClassLoader() + "].");
return false;
}
return true;
}
private String capitalizeFirstLetter(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
public Object getObj() {
return obj;
}
Method getRelevantMethod(String name, AggregationType aggregationType) {
Method relevantMethod;
if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) {
relevantMethod = findAdderMethod(name);
} else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) {
relevantMethod = findSetterMethod(name);
} else {
throw new IllegalStateException(aggregationType + " not allowed here");
}
return relevantMethod;
}
<T extends Annotation> T getAnnotation(String name, Class<T> annonationClass, Method relevantMethod) {
if (relevantMethod != null) {
return relevantMethod.getAnnotation(annonationClass);
} else {
return null;
}
}
Class<?> getDefaultClassNameByAnnonation(String name, Method relevantMethod) {
DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class, relevantMethod);
if (defaultClassAnnon != null) {
return defaultClassAnnon.value();
}
return null;
}
Class<?> getByConcreteType(String name, Method relevantMethod) {
Class<?> paramType = getParameterClassForMethod(relevantMethod);
if (paramType == null) {
return null;
}
boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType);
if (isUnequivocallyInstantiable) {
return paramType;
} else {
return null;
}
}
public Class<?> getClassNameViaImplicitRules(String name, AggregationType aggregationType, DefaultNestedComponentRegistry registry) {
Class<?> registryResult = registry.findDefaultComponentType(obj.getClass(), name);
if (registryResult != null) {
return registryResult;
}
Method relevantMethod = getRelevantMethod(name, aggregationType);
if (relevantMethod == null) {
return null;
}
Class<?> byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
if (byAnnotation != null) {
return byAnnotation;
}
return getByConcreteType(name, relevantMethod);
}
}