org.seedstack.seed.core.utils.BaseClassSpecifications.java Source code

Java tutorial

Introduction

Here is the source code for org.seedstack.seed.core.utils.BaseClassSpecifications.java

Source

/**
 * Copyright (c) 2013-2015 by The SeedStack authors. All rights reserved.
 *
 * This file is part of SeedStack, An enterprise-oriented full development stack.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.seed.core.utils;

import org.apache.commons.lang.ArrayUtils;
import org.kametic.specifications.*;
import org.kametic.specifications.reflect.ClassMethodsAnnotatedWith;
import org.kametic.specifications.reflect.DescendantOfSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
 */
public class BaseClassSpecifications {

    private static final Logger logger = LoggerFactory.getLogger(BaseClassSpecifications.class);

    public static Specification<Class<?>> classIs(final Class<?> attendee) {
        return new AbstractSpecification<Class<?>>() {

            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                return candidate != null && candidate.equals(attendee);
            }

        };
    }

    public static Specification<Class<?>> classAnnotatedWith(final Class<? extends Annotation> klass) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                return candidate != null && candidate.getAnnotation(klass) != null;
            }
        };
    }

    public static Specification<Class<?>> descendantOf(Class<?> ancestor) {
        return new DescendantOfSpecification(ancestor);
    }

    /**
     * @param modifier the expected modifier
     * @return a specification which checks the class modifier
     */
    public static Specification<Class<?>> classModifierIs(final int modifier) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                return (candidate.getModifiers() & modifier) != 0;

            }
        };
    }

    public static Specification<Class<?>> classImplements(final Class<?> klass) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                if (candidate != null && klass.isInterface()) {
                    for (Class<?> i : candidate.getInterfaces()) {
                        if (i.equals(klass)) {
                            return true;
                        }
                    }
                }
                return false;
            }
        };
    }

    /**
     * @return a specification which check if a least one constructor is public
     */
    public static Specification<Class<?>> classConstructorIsPublic() {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                for (Constructor<?> constructor : candidate.getDeclaredConstructors()) {
                    if (Modifier.isPublic(constructor.getModifiers())) {
                        return true;
                    }
                }
                return false;
            }
        };
    }

    /**
     * @param classes the list of class to check
     * @return a specification which check if classes contain the candidate class
     */
    public static Specification<Class<?>> classIsIn(final Collection<Class<?>> classes) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                return candidate != null && classes != null && classes.contains(candidate);
            }
        };
    }

    /**
     * @param interfaceClass the requested interface
     * @return a specification which check if one candidate ancestor implements the given interface
     */
    public static Specification<Class<?>> ancestorImplements(final Class<?> interfaceClass) {
        return new AbstractSpecification<Class<?>>() {

            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                if (candidate == null) {
                    return false;
                }

                boolean result = false;

                Class<?>[] allInterfacesAndClasses = getAllInterfacesAndClasses(candidate);

                for (Class<?> clazz : allInterfacesAndClasses) {
                    if (!clazz.isInterface()) {
                        for (Class<?> i : clazz.getInterfaces()) {
                            if (i.equals(interfaceClass)) {
                                result = true;
                                break;
                            }
                        }
                    }
                }

                return result;
            }

        };
    }

    /**
     * Checks if the candidate has one field annotated or meta annotated by the given annotation.
     *
     * @param annotationClass the requested annotation
     * @return the specification
     */
    public static Specification<Class<?>> fieldDeepAnnotatedWith(
            final Class<? extends Annotation> annotationClass) {

        return new AbstractSpecification<Class<?>>() {

            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                try {
                    if (candidate != null) {
                        do {
                            for (Field field : candidate.getDeclaredFields()) {
                                if (field.isAnnotationPresent(annotationClass)) {
                                    return true;
                                }
                            }
                            candidate = candidate.getSuperclass();
                        } while (candidate != null && candidate != Object.class);
                    }
                } catch (Throwable error) { // NOSONAR
                    logger.trace(String.format(
                            "Warning in Specification fieldAnnotatedWith. Candidate: %s, annotation: %s",
                            candidate.getSimpleName(), annotationClass.getSimpleName()), error);
                }

                return false;
            }
        };
    }

    /**
     * Checks if the candidate inherits from the given class.
     *
     * @param klass the requested class
     * @return the specification
     */
    public static Specification<Class<?>> classInherits(final Class<?> klass) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                return candidate != null && klass.isAssignableFrom(candidate);
            }
        };
    }

    public static Specification<Class<?>> classMethodsAnnotatedWith(
            final Class<? extends Annotation> annotationClass) {
        return new ClassMethodsAnnotatedWith(annotationClass);
    }

    /**
     * Checks if the candidate or an ancestor is annotated or meta annotated by the given annotation.
     *
     * @param anoKlass the requested annotation
     * @return the specification
     */
    public static Specification<Class<?>> ancestorMetaAnnotatedWith(final Class<? extends Annotation> anoKlass) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                if (candidate == null) {
                    return false;
                }

                boolean result = false;

                Class<?>[] allInterfacesAndClasses = getAllInterfacesAndClasses(candidate);

                for (Class<?> clazz : allInterfacesAndClasses) {
                    boolean satisfiedBy = classMetaAnnotatedWith(anoKlass).isSatisfiedBy(clazz);
                    if (satisfiedBy) {
                        result = true;
                        break;
                    }
                }

                return result;
            }
        };
    }

    /**
     * Checks if the candidate is annotated or meta annotated by the given annotation.
     *
     * @param klass the requested annotation
     * @return the specification
     */
    public static Specification<Class<?>> classMetaAnnotatedWith(final Class<? extends Annotation> klass) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                return candidate != null && hasAnnotationDeep(candidate, klass);
            }
        };

    }

    /**
     * Checks if the given class is annotated or meta annotated with the given annotation.
     *
     * @param aClass          the class to check
     * @param annotationClass the requested annotation
     * @return true if the class if annotated or meta annotated, false otherwise
     */
    public static boolean hasAnnotationDeep(Class<?> aClass, Class<? extends Annotation> annotationClass) {
        if (aClass.equals(annotationClass)) {
            return true;
        }

        for (Annotation anno : aClass.getAnnotations()) {
            Class<? extends Annotation> annoClass = anno.annotationType();
            if (!annoClass.getPackage().getName().startsWith("java.lang")
                    && hasAnnotationDeep(annoClass, annotationClass)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Checks if the candidate is an interface.
     *
     * @return the specification
     */
    public static Specification<Class<?>> classIsInterface() {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                return candidate != null && candidate.isInterface();
            }
        };
    }

    /**
     * Checks if the candidate equals to the given class.
     *
     * @param notCandidate the class to check
     * @return the specification
     */
    public static Specification<Class<?>> classIsNot(final Class<?> notCandidate) {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                return candidate != null && !candidate.equals(notCandidate);
            }
        };
    }

    /**
     * Checks if the candidate has interface.
     *
     * @return the specification
     */
    public static Specification<Class<?>> classHasSuperInterfaces() {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {
                Class<?>[] interfaces = {};
                if (candidate != null) {
                    interfaces = candidate.getInterfaces();
                }
                return candidate != null && interfaces != null && interfaces.length > 0;
            }
        };
    }

    /**
     * Checks if the class is an annotation
     *
     * @return the specification
     */
    public static Specification<Class<?>> classIsAnnotation() {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                return candidate != null && candidate.isAnnotation();
            }
        };
    }

    /**
     * Checks if the class is abstract.
     *
     * @return the specification
     */
    public static Specification<Class<?>> classIsAbstract() {
        return new AbstractSpecification<Class<?>>() {
            @Override
            public boolean isSatisfiedBy(Class<?> candidate) {

                return candidate != null && Modifier.isAbstract(candidate.getModifiers());
            }
        };
    }

    /**
     * Checks if at least one method of the class is annotated with the annotation class.
     *
     * @param annotationClass the requested annotation
     * @return the specification
     */
    public static Specification<Class<?>> methodAnnotatedWith(final Class<? extends Annotation> annotationClass) {
        return new ClassMethodsAnnotatedWith(annotationClass);
    }

    static Class<?>[] getAllInterfacesAndClasses(Class<?> clazz) {
        return getAllInterfacesAndClasses(new Class[] { clazz });
    }

    /**
     * This method walks up the inheritance hierarchy to make sure we get every class/interface/superclass/interface's superclass that could
     * possibly contain the declaration of the annotated method we're looking for.
     *
     * @param classes array of class
     * @return array of class
     */
    @SuppressWarnings("unchecked")
    static Class<?>[] getAllInterfacesAndClasses(Class<?>[] classes) {
        if (0 == classes.length) {
            return classes;
        } else {
            List<Class<?>> extendedClasses = new ArrayList<Class<?>>();
            // all interfaces hierarchy
            for (Class<?> clazz : classes) {
                if (clazz != null) {
                    Class<?>[] interfaces = clazz.getInterfaces();
                    if (interfaces != null) {
                        extendedClasses.addAll(Arrays.asList(interfaces));
                    }
                    Class<?> superclass = clazz.getSuperclass();
                    if (superclass != null && superclass != Object.class) {
                        extendedClasses.addAll(Arrays.asList(superclass));
                    }
                }
            }

            // Class::getInterfaces() gets only interfaces/classes
            // implemented/extended directly by a given class.
            // We need to walk the whole way up the tree.
            return (Class[]) ArrayUtils.addAll(classes,
                    getAllInterfacesAndClasses(extendedClasses.toArray(new Class[extendedClasses.size()])));
        }
    }

    /**
     * Logical OR between the specifications.
     *
     * @param participants array of specification
     * @return the specification
     */
    public static Specification<Class<?>> or(Specification<Class<?>>... participants) {
        return new OrSpecification<Class<?>>(participants);
    }

    /**
     * Logical AND between the specifications.
     *
     * @param participants array of specification
     * @return the specification
     */
    public static Specification<Class<?>> and(Specification<Class<?>>... participants) {
        return new AndSpecification<Class<?>>(participants);
    }

    /**
     * The negation of the given specification
     *
     * @param participant the specification
     * @return the specification
     */
    public static Specification<Class<?>> not(Specification<Class<?>> participant) {
        return new NotSpecification<Class<?>>(participant);
    }
}