AggregationAssessor.java
/*
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2026, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v2.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.core.joran.util;
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.StringUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
*
* Various utility methods for computing the {@link AggregationType} of a given property or
* the class name of a property given implicit rules.
*
* <p>This class was extracted from {@link PropertySetter}. </p>
*
* @since 1.5.1
*/
public class AggregationAssessor extends ContextAwareBase {
protected final Class<?> objClass;
protected final BeanDescription beanDescription;
public AggregationAssessor(BeanDescriptionCache beanDescriptionCache, Class objClass) {
this.objClass = objClass;
this.beanDescription = beanDescriptionCache.getBeanDescription(objClass);
}
/**
* Given a property name, this method computes/assesses {@link AggregationType}
* for the property for the class passed to the constructor.
*
* @param name
* @return the computed {@link AggregationType}
*/
public AggregationType computeAggregationType(String name) {
String cName = StringUtil.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;
// computeRawAggregationType cannot return these values
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 {
// we have failed
return AggregationType.NOT_FOUND;
}
}
// String capitalizeFirstLetter(String name) {
// return StringUtil.capitalizeFirstLetter(name);
// }
public Method findAdderMethod(String name) {
String propertyName = BeanUtil.toLowerCamelCase(name);
return beanDescription.getAdder(propertyName);
}
public Method findSetterMethod(String name) {
String propertyName = BeanUtil.toLowerCamelCase(name);
return beanDescription.getSetter(propertyName);
}
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 Class<?> getParameterClassForMethod(Method method) {
if (method == null) {
return null;
}
Class<?>[] classArray = method.getParameterTypes();
if (classArray.length != 1) {
return null;
} else {
return classArray[0];
}
}
public Class<?> getClassNameViaImplicitRules(String name, AggregationType aggregationType,
DefaultNestedComponentRegistry registry) {
Class<?> registryResult = registry.findDefaultComponentType(objClass, name);
if (registryResult != null) {
return registryResult;
}
// find the relevant method for the given property name and aggregationType
Method relevantMethod = getRelevantMethod(name, aggregationType);
if (relevantMethod == null) {
return null;
}
Class<?> byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
if (byAnnotation != null) {
return byAnnotation;
}
return getByConcreteType(name, 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;
}
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;
}
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;
}
}
/**
* Can the given clazz instantiable with certainty?
*
* @param clazz The class to test for instantiability
* @return true if clazz can be instantiated, and false otherwise.
*/
private boolean isUnequivocallyInstantiable(Class<?> clazz) {
if (clazz.isInterface()) {
return false;
}
// checking for constructors would be more elegant, but in
// classes without any declared constructors, Class.getConstructor()
// returns null.
Object o;
try {
o = clazz.getDeclaredConstructor().newInstance();
if (o != null) {
return true;
} else {
return false;
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
| NoSuchMethodException e) {
return false;
}
}
}