org.dcm4che3.conf.core.util.ConfigIterators.java Source code

Java tutorial

Introduction

Here is the source code for org.dcm4che3.conf.core.util.ConfigIterators.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.util;

import org.apache.commons.beanutils.PropertyUtils;
import org.dcm4che3.conf.core.AnnotatedConfigurableProperty;
import org.dcm4che3.conf.core.api.ConfigurableClass;
import org.dcm4che3.conf.core.api.ConfigurableProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

public class ConfigIterators {

    private static Logger LOG = LoggerFactory.getLogger(ConfigIterators.class);

    private static final Map<Class, List<AnnotatedConfigurableProperty>> configurableFieldsCache = Collections
            .synchronizedMap(new HashMap<Class, List<AnnotatedConfigurableProperty>>());
    private static final Map<Class, List<AnnotatedSetter>> configurableSettersCache = Collections
            .synchronizedMap(new HashMap<Class, List<AnnotatedSetter>>());

    public static Class<?> getExtensionClassBySimpleName(String extensionSimpleName,
            List<Class<?>> allExtensionClasses) throws ClassNotFoundException {

        List<Class<?>> extensionClasses = allExtensionClasses;

        for (Class<?> aClass : extensionClasses) {
            if (aClass.getSimpleName().equals(extensionSimpleName))
                return aClass;
        }

        throw new ClassNotFoundException();
    }

    public static class AnnotatedSetter {
        private Map<Type, Annotation> annotations;
        private List<AnnotatedConfigurableProperty> parameters;
        private Method method;

        public Map<Type, Annotation> getAnnotations() {
            return annotations;
        }

        public void setAnnotations(Map<Type, Annotation> annotations) {
            this.annotations = annotations;
        }

        public <T> T getAnnotation(Class<T> annotationType) {
            return (T) annotations.get(annotationType);
        }

        public List<AnnotatedConfigurableProperty> getParameters() {
            return parameters;
        }

        public void setParameters(List<AnnotatedConfigurableProperty> parameters) {
            this.parameters = parameters;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }
    }

    public static List<AnnotatedConfigurableProperty> getAllConfigurableFieldsAndSetterParameters(Class clazz) {
        List<AnnotatedConfigurableProperty> fields = getAllConfigurableFields(clazz);
        for (AnnotatedSetter s : getAllConfigurableSetters(clazz))
            fields.addAll(s.getParameters());
        return fields;
    }

    public static List<AnnotatedSetter> getAllConfigurableSetters(Class clazz) {
        List<AnnotatedSetter> list = configurableSettersCache.get(clazz);
        if (list != null)
            return list;

        processAndCacheAnnotationsForClass(clazz);

        return configurableSettersCache.get(clazz);
    }

    public static List<AnnotatedConfigurableProperty> getAllConfigurableFields(Class clazz) {

        //check cache
        List<AnnotatedConfigurableProperty> l = configurableFieldsCache.get(clazz);
        if (l != null)
            return l;
        processAndCacheAnnotationsForClass(clazz);

        return configurableFieldsCache.get(clazz);

    }

    private static void processAndCacheAnnotationsForClass(Class clazz) {
        processAnnotatedSetters(clazz);
        processAnnotatedProperties(clazz);

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

    private static List<AnnotatedSetter> processAnnotatedSetters(Class clazz) {
        List<AnnotatedSetter> list;
        list = new ArrayList<AnnotatedSetter>();

        // scan all methods including superclasses, assume each is a config-setter
        for (Method m : clazz.getMethods()) {
            AnnotatedSetter annotatedSetter = new AnnotatedSetter();
            annotatedSetter.setParameters(new ArrayList<AnnotatedConfigurableProperty>());

            Annotation[][] parameterAnnotations = m.getParameterAnnotations();
            Type[] genericParameterTypes = m.getGenericParameterTypes();

            // if method is no-arg, then it is not a setter
            boolean thisMethodIsNotASetter = true;

            for (int i = 0; i < parameterAnnotations.length; i++) {

                thisMethodIsNotASetter = false;

                AnnotatedConfigurableProperty property = new AnnotatedConfigurableProperty();
                property.setAnnotations(annotationsArrayToMap(parameterAnnotations[i]));
                property.setType(genericParameterTypes[i]);

                annotatedSetter.getParameters().add(property);

                // make sure all the parameters of this setter-wannabe are annotated
                if (property.getAnnotation(ConfigurableProperty.class) == null) {
                    thisMethodIsNotASetter = true;
                    break;
                }
            }

            // filter out non-setters
            if (thisMethodIsNotASetter)
                continue;

            list.add(annotatedSetter);
            annotatedSetter.setAnnotations(annotationsArrayToMap(m.getAnnotations()));
            annotatedSetter.setMethod(m);
        }

        configurableSettersCache.put(clazz, list);
        return list;
    }

    private static List<AnnotatedConfigurableProperty> processAnnotatedProperties(Class clazz) {
        List<AnnotatedConfigurableProperty> l;
        l = new ArrayList<AnnotatedConfigurableProperty>();

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

                AnnotatedConfigurableProperty ap = new AnnotatedConfigurableProperty();
                ap.setAnnotations(annotationsArrayToMap(field.getAnnotations()));
                ap.setType(field.getGenericType());
                ap.setName(field.getName());

                l.add(ap);
            }
        }

        configurableFieldsCache.put(clazz, l);
        return l;
    }

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

    /**
     * Iterates over the whole hierarchy of classes starting from the startClass.
     * Taken from http://stackoverflow.com/questions/16966629/what-is-the-difference-between-getfields-getdeclaredfields-in-java-reflection
     */
    public static Iterable<Field> getFieldsUpTo(Class<?> startClass, Class<?> exclusiveParent) {

        List<Field> currentClassFields = new ArrayList<Field>();
        currentClassFields.addAll(Arrays.asList(startClass.getDeclaredFields()));

        Class<?> parentClass = startClass.getSuperclass();

        if (parentClass != null && (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
            List<Field> parentClassFields = (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
            currentClassFields.addAll(parentClassFields);
        }

        return currentClassFields;
    }

    public static void reconfigure(Object source, Object target, Class configurableClass) {
        for (AnnotatedConfigurableProperty property : getAllConfigurableFields(configurableClass)) {
            try {
                PropertyUtils.setSimpleProperty(target, property.getName(),
                        PropertyUtils.getSimpleProperty(source, property.getName()));
            } catch (Exception e) {
                throw new RuntimeException("Unable to reconfigure instance of class " + property.getRawClass(), e);
            }
        }
    }

}