private void addBeanInfoToClassIntrospectionData()

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)
    }