org.openehr.adl.rm.RmBeanReflector.java Source code

Java tutorial

Introduction

Here is the source code for org.openehr.adl.rm.RmBeanReflector.java

Source

/*
 * ADL2-core
 * Copyright (c) 2013-2014 Marand d.o.o. (www.marand.com)
 *
 * This file is part of ADL2-core.
 *
 * ADL2-core is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.openehr.adl.rm;

import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang.StringUtils;
import org.openehr.jaxb.rm.MultiplicityInterval;

import javax.annotation.Nonnull;
import javax.xml.bind.annotation.XmlElement;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.google.common.base.Preconditions.checkArgument;
import static org.openehr.adl.rm.RmObjectFactory.newMultiplicityInterval;

/**
 * @author markopi
 * @since 13.6.2013
 */
class RmBeanReflector {
    private final Map<Class, Map<String, RmAttribute>> rmClassAttributeMap;

    private static final RmBeanReflector instance;

    static {
        instance = new RmBeanReflector();
    }

    private RmBeanReflector() {
        rmClassAttributeMap = new ConcurrentHashMap<>();
    }

    private Map<String, RmAttribute> getAttributes(Class beanClass) {
        Map<String, RmAttribute> properties = rmClassAttributeMap.get(beanClass);
        if (properties == null) {
            try {
                properties = buildAttributes(beanClass);
            } catch (ReflectiveOperationException | IntrospectionException e) {
                throw new IllegalStateException(e);
            }
        }
        return properties;
    }

    private Map<String, RmAttribute> buildAttributes(Class<?> beanClass)
            throws ReflectiveOperationException, IntrospectionException {
        Map<String, RmAttribute> properties;
        properties = new LinkedHashMap<>();
        BeanInfo info = Introspector.getBeanInfo(beanClass);

        PropertyDescriptor[] pds = info.getPropertyDescriptors();
        for (PropertyDescriptor pd : pds) {
            if (pd.getName().equals("class"))
                continue;
            ;

            String attribute = getAttributeForField(pd.getName());
            MultiplicityInterval occurrences = getOccurrences(beanClass, pd);
            final Class<?> targetType = List.class.isAssignableFrom(pd.getPropertyType())
                    ? extractGenericType(beanClass, pd)
                    : pd.getPropertyType();
            RmAttribute rp = new RmAttribute(attribute, pd, occurrences, targetType);
            properties.put(attribute, rp);
        }
        rmClassAttributeMap.put(beanClass, ImmutableMap.copyOf(properties));
        return properties;
    }

    private Field getField(Class<?> beanClass, PropertyDescriptor pd) throws ReflectiveOperationException {
        final Method readMethod;
        if (Boolean.class == pd.getPropertyType()) {
            readMethod = beanClass.getMethod("is" + StringUtils.capitalize(pd.getName()));
        } else {
            readMethod = pd.getReadMethod();
        }
        Class<?> declaringClass = readMethod.getDeclaringClass();
        return declaringClass.getDeclaredField(pd.getName());

    }

    private Class<?> extractGenericType(Class<?> beanClass, PropertyDescriptor pd)
            throws ReflectiveOperationException {
        checkArgument(List.class.isAssignableFrom(pd.getPropertyType()));

        Field field = getField(beanClass, pd);
        ParameterizedType stringListType = (ParameterizedType) field.getGenericType();
        return (Class<?>) stringListType.getActualTypeArguments()[0];
    }

    private MultiplicityInterval getOccurrences(Class<?> beanClass, PropertyDescriptor pd)
            throws ReflectiveOperationException {
        Field field = getField(beanClass, pd);
        if (field.getType().isPrimitive())
            return newMultiplicityInterval(1, 1);

        XmlElement xmlElement = field.getAnnotation(XmlElement.class);
        final Integer lower;
        final Integer upper;
        if (xmlElement != null) {
            lower = xmlElement.required() ? 1 : 0;
        } else {
            lower = 0;
        }
        if (List.class.isAssignableFrom(field.getType())) {
            upper = null;
        } else {
            upper = 1;
        }

        return newMultiplicityInterval(lower, upper);
    }

    public static Iterable<RmAttribute> listProperties(Class beanClass) {
        return instance.getAttributes(beanClass).values();
    }

    public static RmAttribute getRmAttribute(Class beanClass, String attribute) {
        return instance.getAttributes(beanClass).get(attribute);
    }

    private static String getAttributeForField(@Nonnull String fieldName) {
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName);
    }

    public static class RmAttribute {
        private final PropertyDescriptor property;
        private final String attribute;
        private final MultiplicityInterval occurrences;
        private final Class<?> targetType;

        public RmAttribute(String attribute, PropertyDescriptor property, MultiplicityInterval occurrences,
                Class<?> targetType) {
            this.attribute = attribute;
            this.property = property;
            this.occurrences = occurrences;
            this.targetType = targetType;
        }

        public String getAttribute() {
            return attribute;
        }

        public PropertyDescriptor getProperty() {
            return property;
        }

        public MultiplicityInterval getOccurrences() {
            return occurrences;
        }

        public Class<?> getTargetType() {
            return targetType;
        }
    }

}