in freemarker-core/src/main/java/freemarker/ext/beans/ClassIntrospector.java [341:443]
private void addBeanInfoToClassIntrospectionData(
Map<Object, Object> introspData, Class<?> clazz,
Map<ExecutableMemberSignature, List<Method>> accessibleMethods,
ClassMemberAccessPolicy effClassMemberAccessPolicy) throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
boolean treatClassAsRecord = recordAware && _Java16.INSTANCE.isRecord(clazz);
ZeroArgumentNonVoidMethodPolicy zeroArgumentNonVoidMethodPolicy = treatClassAsRecord
? recordZeroArgumentNonVoidMethodPolicy
: defaultZeroArgumentNonVoidMethodPolicy;
// For real Java Beans properties only, used to exclude them from creating fake properties based on ZeroArgumentNonVoidMethod.
Set<String> beanPropertyReadMethodNameCollector = zeroArgumentNonVoidMethodPolicy != ZeroArgumentNonVoidMethodPolicy.METHOD_ONLY
? new HashSet<>()
: null;
List<PropertyDescriptor> pdas = getPropertyDescriptors(beanInfo, clazz);
int pdasLength = pdas.size();
// Reverse order shouldn't mater, but we keep it to not risk backward incompatibility.
for (int i = pdasLength - 1; i >= 0; --i) {
addPropertyDescriptorToClassIntrospectionData(
introspData, pdas.get(i), false,
accessibleMethods,
beanPropertyReadMethodNameCollector,
effClassMemberAccessPolicy);
}
if (exposureLevel < BeansWrapper.EXPOSE_PROPERTIES_ONLY) {
final MethodAppearanceDecision decision = new MethodAppearanceDecision();
MethodAppearanceDecisionInput decisionInput = null;
List<MethodDescriptor> mds = getMethodDescriptors(beanInfo, clazz);
sortMethodDescriptors(mds);
int mdsSize = mds.size();
IdentityHashMap<Method, Void> argTypesUsedByIndexerPropReaders = null;
for (int i = mdsSize - 1; i >= 0; --i) {
final Method method = getMatchingAccessibleMethod(mds.get(i).getMethod(), accessibleMethods);
if (method != null && effClassMemberAccessPolicy.isMethodExposed(method)) {
ZeroArgumentNonVoidMethodPolicy appliedZeroArgumentNonVoidMethodPolicy =
getAppliedZeroArgumentNonVoidMethodPolicy(
method, beanPropertyReadMethodNameCollector, zeroArgumentNonVoidMethodPolicy);
decision.setDefaults(method, appliedZeroArgumentNonVoidMethodPolicy);
if (methodAppearanceFineTuner != null) {
if (decisionInput == null) {
decisionInput = new MethodAppearanceDecisionInput();
}
decisionInput.setContainingClass(clazz);
decisionInput.setMethod(method);
methodAppearanceFineTuner.process(decisionInput, decision);
}
String exposedMethodName = decision.getExposeMethodAs();
PropertyDescriptor propDesc = decision.getExposeAsProperty();
if (propDesc != null &&
(decision.getReplaceExistingProperty()
|| isExistingIntropsDataNotPropertyOrNewAddsMethodSupport(introspData, propDesc.getName(), decision))) {
boolean methodInsteadOfPropertyValueBeforeCall = decision.isMethodInsteadOfPropertyValueBeforeCall();
addPropertyDescriptorToClassIntrospectionData(
introspData, propDesc, methodInsteadOfPropertyValueBeforeCall,
accessibleMethods, null, effClassMemberAccessPolicy);
if (methodInsteadOfPropertyValueBeforeCall
&& exposedMethodName != null && exposedMethodName.equals(propDesc.getName())) {
exposedMethodName = null; // We have already exposed this as property with the method name
}
}
if (exposedMethodName != null) {
Object previous = introspData.get(exposedMethodName);
if (previous instanceof Method) {
// Overloaded method - replace Method with a OverloadedMethods
OverloadedMethods overloadedMethods =
new OverloadedMethods(is2321Bugfixed());
overloadedMethods.addMethod((Method) previous);
overloadedMethods.addMethod(method);
introspData.put(exposedMethodName, overloadedMethods);
// Remove parameter type information (unless an indexed property reader needs it):
if (argTypesUsedByIndexerPropReaders == null
|| !argTypesUsedByIndexerPropReaders.containsKey(previous)) {
getArgTypesByMethod(introspData).remove(previous);
}
} else if (previous instanceof OverloadedMethods) {
// Already overloaded method - add new overload
((OverloadedMethods) previous).addMethod(method);
} else if (decision.getMethodShadowsProperty()
|| !(previous instanceof FastPropertyDescriptor)) {
// Simple method (so far)
introspData.put(exposedMethodName, method);
Class<?>[] replaced = getArgTypesByMethod(introspData).put(method,
method.getParameterTypes());
if (replaced != null) {
if (argTypesUsedByIndexerPropReaders == null) {
argTypesUsedByIndexerPropReaders = new IdentityHashMap<>();
}
argTypesUsedByIndexerPropReaders.put(method, null);
}
}
}
}
} // for each in mds
} // end if (exposureLevel < EXPOSE_PROPERTIES_ONLY)
}