001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v2.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014 015package ch.qos.logback.core.joran.util; 016 017import ch.qos.logback.core.joran.spi.DefaultClass; 018import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; 019import ch.qos.logback.core.joran.util.beans.BeanDescription; 020import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; 021import ch.qos.logback.core.joran.util.beans.BeanUtil; 022import ch.qos.logback.core.spi.ContextAwareBase; 023import ch.qos.logback.core.util.AggregationType; 024import ch.qos.logback.core.util.StringUtil; 025 026import java.lang.annotation.Annotation; 027import java.lang.reflect.InvocationTargetException; 028import java.lang.reflect.Method; 029 030/** 031 * 032 * Various utility methods for computing the {@link AggregationType} of a given property or 033 * the class name of a property given implicit rules. 034 * 035 * <p>This class was extracted from {@link PropertySetter}. </p> 036 * 037 * @since 1.5.1 038 */ 039public class AggregationAssessor extends ContextAwareBase { 040 041 protected final Class<?> objClass; 042 protected final BeanDescription beanDescription; 043 044 public AggregationAssessor(BeanDescriptionCache beanDescriptionCache, Class objClass) { 045 this.objClass = objClass; 046 this.beanDescription = beanDescriptionCache.getBeanDescription(objClass); 047 } 048 049 /** 050 * Given a property name, this method computes/assesses {@link AggregationType} 051 * for the property for the class passed to the constructor. 052 * 053 * @param name 054 * @return the computed {@link AggregationType} 055 */ 056 public AggregationType computeAggregationType(String name) { 057 String cName = StringUtil.capitalizeFirstLetter(name); 058 059 Method addMethod = findAdderMethod(cName); 060 061 if (addMethod != null) { 062 AggregationType type = computeRawAggregationType(addMethod); 063 switch (type) { 064 case NOT_FOUND: 065 return AggregationType.NOT_FOUND; 066 case AS_BASIC_PROPERTY: 067 return AggregationType.AS_BASIC_PROPERTY_COLLECTION; 068 069 case AS_COMPLEX_PROPERTY: 070 return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION; 071 072 // computeRawAggregationType cannot return these values 073 case AS_BASIC_PROPERTY_COLLECTION: 074 case AS_COMPLEX_PROPERTY_COLLECTION: 075 addError("Unexpected AggregationType " + type); 076 } 077 } 078 079 Method setter = findSetterMethod(name); 080 if (setter != null) { 081 return computeRawAggregationType(setter); 082 } else { 083 // we have failed 084 return AggregationType.NOT_FOUND; 085 } 086 } 087 088 089// String capitalizeFirstLetter(String name) { 090// return StringUtil.capitalizeFirstLetter(name); 091// } 092 093 public Method findAdderMethod(String name) { 094 String propertyName = BeanUtil.toLowerCamelCase(name); 095 return beanDescription.getAdder(propertyName); 096 } 097 098 public Method findSetterMethod(String name) { 099 String propertyName = BeanUtil.toLowerCamelCase(name); 100 return beanDescription.getSetter(propertyName); 101 } 102 103 private AggregationType computeRawAggregationType(Method method) { 104 Class<?> parameterClass = getParameterClassForMethod(method); 105 if (parameterClass == null) { 106 return AggregationType.NOT_FOUND; 107 } 108 if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) { 109 return AggregationType.AS_BASIC_PROPERTY; 110 } else { 111 return AggregationType.AS_COMPLEX_PROPERTY; 112 } 113 } 114 115 private Class<?> getParameterClassForMethod(Method method) { 116 if (method == null) { 117 return null; 118 } 119 Class<?>[] classArray = method.getParameterTypes(); 120 if (classArray.length != 1) { 121 return null; 122 } else { 123 return classArray[0]; 124 } 125 } 126 127 public Class<?> getClassNameViaImplicitRules(String name, AggregationType aggregationType, 128 DefaultNestedComponentRegistry registry) { 129 130 Class<?> registryResult = registry.findDefaultComponentType(objClass, name); 131 if (registryResult != null) { 132 return registryResult; 133 } 134 // find the relevant method for the given property name and aggregationType 135 Method relevantMethod = getRelevantMethod(name, aggregationType); 136 if (relevantMethod == null) { 137 return null; 138 } 139 Class<?> byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod); 140 if (byAnnotation != null) { 141 return byAnnotation; 142 } 143 return getByConcreteType(name, relevantMethod); 144 } 145 146 <T extends Annotation> T getAnnotation(String name, Class<T> annonationClass, Method relevantMethod) { 147 148 if (relevantMethod != null) { 149 return relevantMethod.getAnnotation(annonationClass); 150 } else { 151 return null; 152 } 153 } 154 155 Class<?> getDefaultClassNameByAnnonation(String name, Method relevantMethod) { 156 DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class, relevantMethod); 157 if (defaultClassAnnon != null) { 158 return defaultClassAnnon.value(); 159 } 160 return null; 161 } 162 Method getRelevantMethod(String name, AggregationType aggregationType) { 163 Method relevantMethod; 164 if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) { 165 relevantMethod = findAdderMethod(name); 166 } else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) { 167 relevantMethod = findSetterMethod(name); 168 } else { 169 throw new IllegalStateException(aggregationType + " not allowed here"); 170 } 171 return relevantMethod; 172 } 173 174 Class<?> getByConcreteType(String name, Method relevantMethod) { 175 176 Class<?> paramType = getParameterClassForMethod(relevantMethod); 177 if (paramType == null) { 178 return null; 179 } 180 181 boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType); 182 if (isUnequivocallyInstantiable) { 183 return paramType; 184 } else { 185 return null; 186 } 187 } 188 189 /** 190 * Can the given clazz instantiable with certainty? 191 * 192 * @param clazz The class to test for instantiability 193 * @return true if clazz can be instantiated, and false otherwise. 194 */ 195 private boolean isUnequivocallyInstantiable(Class<?> clazz) { 196 if (clazz.isInterface()) { 197 return false; 198 } 199 // checking for constructors would be more elegant, but in 200 // classes without any declared constructors, Class.getConstructor() 201 // returns null. 202 Object o; 203 try { 204 o = clazz.getDeclaredConstructor().newInstance(); 205 if (o != null) { 206 return true; 207 } else { 208 return false; 209 } 210 } catch (InstantiationException | IllegalAccessException | InvocationTargetException 211 | NoSuchMethodException e) { 212 return false; 213 } 214 } 215}