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.beans;
016
017import java.lang.reflect.Method;
018import java.util.HashMap;
019import java.util.Map;
020
021import ch.qos.logback.core.Context;
022import ch.qos.logback.core.spi.ContextAwareBase;
023
024/**
025 * Encapsulates creation of {@link BeanDescription} instances. This factory is
026 * kind of a lightweight Introspector as described in the Java Beans API
027 * specification. The given class is only analyzed for its public getters,
028 * setters and adders methods. Implementations of the BeanInfo interface are not
029 * taken into account for analysis. Therefore this class is only partially
030 * compatible with the Java Beans API specification.
031 *
032 *
033 * @author urechm
034 */
035public class BeanDescriptionFactory extends ContextAwareBase {
036
037    BeanDescriptionFactory(Context context) {
038        setContext(context);
039    }
040
041    /**
042     *
043     * @param clazz to create a {@link BeanDescription} for.
044     * @return a {@link BeanDescription} for the given class.
045     */
046    public BeanDescription create(Class<?> clazz) {
047        Map<String, Method> propertyNameToGetter = new HashMap<String, Method>();
048        Map<String, Method> propertyNameToSetter = new HashMap<String, Method>();
049        Map<String, Method> propertyNameToAdder = new HashMap<String, Method>();
050        Method[] methods = clazz.getMethods();
051        for (Method method : methods) {
052            if (method.isBridge()) {
053                // we can safely ignore bridge methods
054                continue;
055            }
056            if (BeanUtil.isGetter(method)) {
057                String propertyName = BeanUtil.getPropertyName(method);
058                Method oldGetter = propertyNameToGetter.put(propertyName, method);
059                if (oldGetter != null) {
060                    if (oldGetter.getName().startsWith(BeanUtil.PREFIX_GETTER_IS)) {
061                        propertyNameToGetter.put(propertyName, oldGetter);
062                    }
063                    String message = String.format("Class '%s' contains multiple getters for the same property '%s'.",
064                            clazz.getCanonicalName(), propertyName);
065                    addWarn(message);
066                }
067            } else if (BeanUtil.isSetter(method)) {
068                String propertyName = BeanUtil.getPropertyName(method);
069                Method oldSetter = propertyNameToSetter.put(propertyName, method);
070                if (oldSetter != null) {
071                    String message = String.format("Class '%s' contains multiple setters for the same property '%s'.",
072                            clazz.getCanonicalName(), propertyName);
073                    addWarn(message);
074                }
075            } else if (BeanUtil.isAdder(method)) {
076                String propertyName = BeanUtil.getPropertyName(method);
077                Method oldAdder = propertyNameToAdder.put(propertyName, method);
078                if (oldAdder != null) {
079                    String message = String.format("Class '%s' contains multiple adders for the same property '%s'.",
080                            clazz.getCanonicalName(), propertyName);
081                    addWarn(message);
082                }
083            }
084        }
085        return new BeanDescription(clazz, propertyNameToGetter, propertyNameToSetter, propertyNameToAdder);
086    }
087}