View Javadoc
1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2024, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.core.joran.util;
16  
17  import ch.qos.logback.core.joran.spi.DefaultClass;
18  import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
19  import ch.qos.logback.core.joran.util.beans.BeanDescription;
20  import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
21  import ch.qos.logback.core.joran.util.beans.BeanUtil;
22  import ch.qos.logback.core.spi.ContextAwareBase;
23  import ch.qos.logback.core.util.AggregationType;
24  import ch.qos.logback.core.util.StringUtil;
25  
26  import java.lang.annotation.Annotation;
27  import java.lang.reflect.InvocationTargetException;
28  import java.lang.reflect.Method;
29  
30  /**
31   *
32   * Various utility methods for computing the {@link AggregationType} of a given property or
33   * the class name of a property given implicit rules.
34   *
35   * <p>This class was extracted from {@link PropertySetter}. </p>
36   *
37   * @since 1.5.1
38   */
39  public class AggregationAssessor extends ContextAwareBase  {
40  
41      protected final Class<?> objClass;
42      protected final BeanDescription beanDescription;
43  
44      public AggregationAssessor(BeanDescriptionCache beanDescriptionCache, Class objClass) {
45          this.objClass = objClass;
46          this.beanDescription = beanDescriptionCache.getBeanDescription(objClass);
47      }
48  
49      /**
50       *  Given a property name, this method computes/assesses {@link AggregationType}
51       *  for the property for the class passed to the constructor.
52       *
53       * @param name
54       * @return the computed {@link AggregationType}
55       */
56      public AggregationType computeAggregationType(String name) {
57          String cName = StringUtil.capitalizeFirstLetter(name);
58  
59          Method addMethod = findAdderMethod(cName);
60  
61          if (addMethod != null) {
62              AggregationType type = computeRawAggregationType(addMethod);
63              switch (type) {
64              case NOT_FOUND:
65                  return AggregationType.NOT_FOUND;
66              case AS_BASIC_PROPERTY:
67                  return AggregationType.AS_BASIC_PROPERTY_COLLECTION;
68  
69              case AS_COMPLEX_PROPERTY:
70                  return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION;
71              case AS_BASIC_PROPERTY_COLLECTION:
72              case AS_COMPLEX_PROPERTY_COLLECTION:
73                  addError("Unexpected AggregationType " + type);
74              }
75          }
76  
77          Method setter = findSetterMethod(name);
78          if (setter != null) {
79              return computeRawAggregationType(setter);
80          } else {
81              // we have failed
82              return AggregationType.NOT_FOUND;
83          }
84      }
85  
86  
87  //    String capitalizeFirstLetter(String name) {
88  //        return StringUtil.capitalizeFirstLetter(name);
89  //    }
90  
91      public Method findAdderMethod(String name) {
92          String propertyName = BeanUtil.toLowerCamelCase(name);
93          return beanDescription.getAdder(propertyName);
94      }
95  
96      public Method findSetterMethod(String name) {
97          String propertyName = BeanUtil.toLowerCamelCase(name);
98          return beanDescription.getSetter(propertyName);
99      }
100 
101     private AggregationType computeRawAggregationType(Method method) {
102         Class<?> parameterClass = getParameterClassForMethod(method);
103         if (parameterClass == null) {
104             return AggregationType.NOT_FOUND;
105         }
106         if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) {
107             return AggregationType.AS_BASIC_PROPERTY;
108         } else {
109             return AggregationType.AS_COMPLEX_PROPERTY;
110         }
111     }
112 
113     private Class<?> getParameterClassForMethod(Method method) {
114         if (method == null) {
115             return null;
116         }
117         Class<?>[] classArray = method.getParameterTypes();
118         if (classArray.length != 1) {
119             return null;
120         } else {
121             return classArray[0];
122         }
123     }
124 
125     public Class<?> getClassNameViaImplicitRules(String name, AggregationType aggregationType,
126             DefaultNestedComponentRegistry registry) {
127 
128         Class<?> registryResult = registry.findDefaultComponentType(objClass, name);
129         if (registryResult != null) {
130             return registryResult;
131         }
132         // find the relevant method for the given property name and aggregationType
133         Method relevantMethod = getRelevantMethod(name, aggregationType);
134         if (relevantMethod == null) {
135             return null;
136         }
137         Class<?> byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod);
138         if (byAnnotation != null) {
139             return byAnnotation;
140         }
141         return getByConcreteType(name, relevantMethod);
142     }
143 
144     <T extends Annotation> T getAnnotation(String name, Class<T> annonationClass, Method relevantMethod) {
145 
146         if (relevantMethod != null) {
147             return relevantMethod.getAnnotation(annonationClass);
148         } else {
149             return null;
150         }
151     }
152 
153     Class<?> getDefaultClassNameByAnnonation(String name, Method relevantMethod) {
154         DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class, relevantMethod);
155         if (defaultClassAnnon != null) {
156             return defaultClassAnnon.value();
157         }
158         return null;
159     }
160     Method getRelevantMethod(String name, AggregationType aggregationType) {
161         Method relevantMethod;
162         if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) {
163             relevantMethod = findAdderMethod(name);
164         } else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) {
165             relevantMethod = findSetterMethod(name);
166         } else {
167             throw new IllegalStateException(aggregationType + " not allowed here");
168         }
169         return relevantMethod;
170     }
171 
172     Class<?> getByConcreteType(String name, Method relevantMethod) {
173 
174         Class<?> paramType = getParameterClassForMethod(relevantMethod);
175         if (paramType == null) {
176             return null;
177         }
178 
179         boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType);
180         if (isUnequivocallyInstantiable) {
181             return paramType;
182         } else {
183             return null;
184         }
185     }
186 
187     /**
188      * Can the given clazz instantiable with certainty?
189      *
190      * @param clazz The class to test for instantiability
191      * @return true if clazz can be instantiated, and false otherwise.
192      */
193     private boolean isUnequivocallyInstantiable(Class<?> clazz) {
194         if (clazz.isInterface()) {
195             return false;
196         }
197         // checking for constructors would be more elegant, but in
198         // classes without any declared constructors, Class.getConstructor()
199         // returns null.
200         Object o;
201         try {
202             o = clazz.getDeclaredConstructor().newInstance();
203             if (o != null) {
204                 return true;
205             } else {
206                 return false;
207             }
208         } catch (InstantiationException | IllegalAccessException | InvocationTargetException
209                 | NoSuchMethodException e) {
210             return false;
211         }
212     }
213 }