org.dcm4che3.conf.core.api.internal.ConfigReflection.java Source code

Java tutorial

Introduction

Here is the source code for org.dcm4che3.conf.core.api.internal.ConfigReflection.java

Source

/*
 * **** BEGIN LICENSE BLOCK *****
 *  Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 *  The contents of this file are subject to the Mozilla Public License Version
 *  1.1 (the "License"); you may not use this file except in compliance with
 *  the License. You may obtain a copy of the License at
 *  http://www.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 *  for the specific language governing rights and limitations under the
 *  License.
 *
 *  The Original Code is part of dcm4che, an implementation of DICOM(TM) in
 *  Java(TM), hosted at https://github.com/gunterze/dcm4che.
 *
 *  The Initial Developer of the Original Code is
 *  Agfa Healthcare.
 *  Portions created by the Initial Developer are Copyright (C) 2014
 *  the Initial Developer. All Rights Reserved.
 *
 *  Contributor(s):
 *  See @authors listed below
 *
 *  Alternatively, the contents of this file may be used under the terms of
 *  either the GNU General Public License Version 2 or later (the "GPL"), or
 *  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 *  in which case the provisions of the GPL or the LGPL are applicable instead
 *  of those above. If you wish to allow use of your version of this file only
 *  under the terms of either the GPL or the LGPL, and not to allow others to
 *  use your version of this file under the terms of the MPL, indicate your
 *  decision by deleting the provisions above and replace them with the notice
 *  and other provisions required by the GPL or the LGPL. If you do not delete
 *  the provisions above, a recipient may use your version of this file under
 *  the terms of any one of the MPL, the GPL or the LGPL.
 *
 *  ***** END LICENSE BLOCK *****
 */
package org.dcm4che3.conf.core.api.internal;

import org.apache.commons.beanutils.PropertyUtils;
import org.dcm4che3.conf.core.api.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.*;

/**
 * This class shall NOT be referenced externally, it will be removed/renamed/refactored without notice.
 * Caches to minimize reflection access.
 *
 * @author Roman K
 */
public class ConfigReflection {

    private static final Map<Class, ClassInfo> classInfoCache = Collections
            .synchronizedMap(new HashMap<Class, ClassInfo>());
    private static final Map<Class, Boolean> isClassConfigurable = Collections
            .synchronizedMap(new HashMap<Class, Boolean>());

    private static final Map<Class, ConfigProperty> dummyPropsCache = Collections
            .synchronizedMap(new HashMap<Class, ConfigProperty>());

    public static List<ConfigProperty> getAllConfigurableFields(Class clazz) {
        return getClassInfo(clazz).configurableProperties;
    }

    public static ConfigProperty getDummyPropertyForClass(Class clazz) {
        ConfigProperty found = dummyPropsCache.get(clazz);

        if (found != null) {
            return found;
        } else {
            ConfigProperty property = new ConfigProperty(clazz);
            dummyPropsCache.put(clazz, property);
            return property;
        }
    }

    private static ClassInfo getClassInfo(Class clazz) {
        ClassInfo classInfo = classInfoCache.get(clazz);

        if (classInfo != null) {
            return classInfo;
        } else {
            return processAndCacheClassInfo(clazz);
        }
    }

    public static boolean isConfigurableClass(Class clazz) {

        if (isClassConfigurable.containsKey(clazz))
            return isClassConfigurable.get(clazz);

        boolean isItForReal = clazz.getAnnotation(ConfigurableClass.class) != null;
        isClassConfigurable.put(clazz, isItForReal);

        return isItForReal;
    }

    public static ConfigProperty getUUIDPropertyForClass(Class clazz) {
        return getClassInfo(clazz).uuidProperty;
    }

    private static ClassInfo processAndCacheClassInfo(Class clazz) {

        ConfigurableClass configClassAnno = (ConfigurableClass) clazz.getAnnotation(ConfigurableClass.class);
        if (configClassAnno == null)
            throw new IllegalArgumentException("Class '" + clazz.getName()
                    + "' is not a configurable class. Make sure the a dependency to org.dcm4che.conf.core-api exists.");

        ClassInfo classInfo = scanClass(clazz);

        // some restrictions on extensions
        if (ConfigurableClassExtension.class.isAssignableFrom(clazz)) {

            if (configClassAnno.referable()) {
                throw new IllegalArgumentException(
                        "A configurable extension class MUST NOT be referable - violated by class "
                                + clazz.getName());
            }

            if (classInfo.uuidProperty != null) {
                throw new IllegalArgumentException(
                        "A configurable extension class MUST NOT have a uuid - violated by class "
                                + clazz.getName());
            }
        }

        classInfoCache.put(clazz, classInfo);

        return classInfo;
    }

    private static ClassInfo scanClass(Class clazz) {

        ClassInfo classInfo = new ClassInfo();
        classInfo.configurableProperties = new ArrayList<ConfigProperty>();

        // scan all fields from this class and superclasses
        for (Field field : getAllFields(clazz)) {
            if (field.getAnnotation(ConfigurableProperty.class) != null) {

                ConfigProperty ap = new ConfigProperty(annotationsArrayToMap(field.getAnnotations()),
                        field.getName(), field.getGenericType());
                classInfo.configurableProperties.add(ap);

                if (ap.isUuid()) {
                    if (classInfo.uuidProperty != null) {
                        throw new IllegalArgumentException(
                                "A configurable class MUST NOT have more than one UUID field - violated by class "
                                        + clazz.getName());
                    }

                    classInfo.uuidProperty = ap;
                }

                if (ap.isOlockHash()) {
                    if (classInfo.olockHashProperty != null) {
                        throw new IllegalArgumentException(
                                "A configurable class MUST NOT have more than one optimistic locking hash field - violated by class "
                                        + clazz.getName());
                    }

                    classInfo.olockHashProperty = ap;
                }
            } else if (field.getAnnotation(Parent.class) != null) {
                if (classInfo.parentField != null) {
                    throw new IllegalArgumentException(
                            "A configurable class MUST NOT have more than one field annotated with @Parent - violated by class "
                                    + clazz.getName());
                }

                classInfo.parentField = field;
            }
        }

        if (!ConfigurableClassExtension.class.isAssignableFrom(clazz) && classInfo.uuidProperty == null
                && classInfo.parentField != null) {
            throw new IllegalArgumentException(
                    "A configurable class that refers to a @Parent must have a uuid property defined (except the extensions). Violated by "
                            + clazz.getName());
        }

        return classInfo;
    }

    public static Map<Type, Annotation> annotationsArrayToMap(Annotation[] annos) {
        HashMap<Type, Annotation> annotations = new HashMap<Type, Annotation>();
        for (Annotation anno : annos)
            annotations.put(anno.annotationType(), anno);
        return annotations;
    }

    public static Field getParentPropertyForClass(Class<?> extensionClass) {
        return getClassInfo(extensionClass).parentField;
    }

    /**
     * Gets all the fields for the class and it's superclass(es)
     */
    private static List<Field> getAllFields(Class clazz) {

        List<Field> fields = new ArrayList<Field>();

        // get all fields of the current class (includes public, protected, default, and private fields)
        fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

        // go to the parent recursively
        Class<?> parent = clazz.getSuperclass();
        if (parent != null)
            fields.addAll(getAllFields(parent));

        return fields;
    }

    public static void setProperty(Object object, ConfigProperty property, Object value)
            throws ReflectionAccessException {
        setProperty(object, property.getName(), value);
    }

    public static void setProperty(Object object, String propertyName, Object value)
            throws ReflectionAccessException {
        try {
            PropertyUtils.setSimpleProperty(object, propertyName, value);
        } catch (IllegalAccessException e) {
            throw new ReflectionAccessException(
                    "Could not set property " + propertyName + " in class " + object.getClass().toString(), e);
        } catch (InvocationTargetException e) {
            throw new ReflectionAccessException(
                    "Could not set property " + propertyName + " in class " + object.getClass().toString(), e);
        } catch (NoSuchMethodException e) {
            throw new ReflectionAccessException(
                    "Could not set property " + propertyName + " in class " + object.getClass().toString(), e);
        }
    }

    public static Object getProperty(Object object, ConfigProperty property) throws ReflectionAccessException {
        try {
            return PropertyUtils.getSimpleProperty(object, property.getName());
        } catch (IllegalAccessException e) {
            throw new ReflectionAccessException(
                    "Could not get property " + property + " in class " + object.getClass().toString(), e);
        } catch (InvocationTargetException e) {
            throw new ReflectionAccessException(
                    "Could not get property " + property + " in class " + object.getClass().toString(), e);
        } catch (NoSuchMethodException e) {
            throw new ReflectionAccessException(
                    "Could not get property " + property + " in class " + object.getClass().toString(), e);
        }
    }

    private static class ClassInfo {

        List<ConfigProperty> configurableProperties;
        ConfigProperty uuidProperty;
        ConfigProperty olockHashProperty;
        Field parentField;

    }
}