001/*
002 *  Copyright 2019 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.runtime.util;
017
018import java.lang.annotation.Annotation;
019import java.lang.annotation.Inherited;
020import java.util.function.Function;
021import java.util.stream.Stream;
022
023/**
024 * Helper for {@link Annotation}s.
025 */
026public final class Annotations
027{
028    private Annotations()
029    {
030        // empty constructor
031    }
032    
033    /**
034     * Tests if given {@link Annotation} is present on any of the superclasses or on any of the implemented interfaces.
035     * <br>Unlike {@link Class#isAnnotationPresent}, it also checks into all the implemented interfaces.
036     * <br> The {@link Annotation} must be annotated with {@link Inherited} meta-annotation.
037     * @param classToTest The class to test
038     * @param annotationClass The {@link Annotation} class
039     * @return <code>true</code> if annotation is present on any of the superclasses or on any of the implemented interfaces.
040     */
041    public static boolean isAnnotationPresent(Class<?> classToTest, Class<? extends Annotation> annotationClass)
042    {
043        return classToTest.isAnnotationPresent(annotationClass) || _isAnnotationPresentOnInterfacesOfSuperclasses(classToTest, annotationClass);
044    }
045    
046    private static boolean _isAnnotationPresentOnInterfacesOfSuperclasses(Class<?> classToTest, Class<? extends Annotation> annotationClass)
047    {
048        return _getSuperclassesAndSelf(classToTest)
049                .anyMatch(superclass -> _isAnnotationPresentOnInterfaces(superclass, annotationClass));
050    }
051    
052    private static Stream<Class<?>> _getSuperclassesAndSelf(Class<?> classToTest)
053    {
054        if (classToTest == null)
055        {
056            return Stream.empty();
057        }
058        else
059        {
060            return Stream.concat(
061                    Stream.of(classToTest), 
062                    _getSuperclassesAndSelf(classToTest.getSuperclass()));
063        }
064    }
065    
066    private static boolean _isAnnotationPresentOnInterfaces(Class<?> classToTest, Class<? extends Annotation> annotationClass)
067    {
068        return _getAncestorAndSelfInterfaces(classToTest)
069                .anyMatch(theInterface -> theInterface.isAnnotationPresent(annotationClass));
070    }
071    
072    private static Stream<Class<?>> _getAncestorAndSelfInterfaces(Class<?> classToTest)
073    {
074        return Stream.concat(
075                Stream.of(classToTest), 
076                _getAncestorInterfaces(classToTest));
077    }
078    
079    private static Stream<Class<?>> _getAncestorInterfaces(Class<?> classToTest)
080    {
081        return _getSuperInterfaces(classToTest)
082                .map(Annotations::_getAncestorAndSelfInterfaces)
083                .flatMap(Function.identity());
084    }
085    
086    private static Stream<Class<?>> _getSuperInterfaces(Class<?> classToTest)
087    {
088        return Stream.of(classToTest.getInterfaces());
089    }
090}
091