All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.microsphere.util.AnnotationUtils Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.microsphere.util;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Native;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static io.microsphere.lang.function.Predicates.and;
import static io.microsphere.lang.function.Streams.filterAll;
import static io.microsphere.lang.function.Streams.filterFirst;
import static io.microsphere.lang.function.ThrowableSupplier.execute;
import static io.microsphere.reflect.MethodUtils.OBJECT_METHODS;
import static io.microsphere.reflect.MethodUtils.overrides;
import static io.microsphere.util.ArrayUtils.length;
import static io.microsphere.util.ClassLoaderUtils.resolveClass;
import static io.microsphere.util.ClassUtils.getAllInheritedTypes;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Optional.ofNullable;

/**
 * {@link Annotation} Utilities class
 *
 * @author Mercy
 * @since 1.0.0
 */
public abstract class AnnotationUtils extends BaseUtils {

    public final static List> NATIVE_ANNOTATION_TYPES = unmodifiableList(asList
            (Target.class, Retention.class, Documented.class, Inherited.class, Native.class, Repeatable.class));

    private static final Predicate INHERITED_OBJECT_METHOD_PREDICATE = AnnotationUtils::isInheritedObjectMethod;

    private static final Predicate NON_INHERITED_OBJECT_METHOD_PREDICATE = INHERITED_OBJECT_METHOD_PREDICATE.negate();

    private static final Predicate ANNOTATION_METHOD_PREDICATE = AnnotationUtils::isAnnotationMethod;

    private static final Predicate NON_ANNOTATION_METHOD_PREDICATE = ANNOTATION_METHOD_PREDICATE.negate();

    /**
     * The annotation class name of {@linkplain jdk.internal.reflect.CallerSensitive}
     */
    public static final String CALLER_SENSITIVE_ANNOTATION_CLASS_NAME = "jdk.internal.reflect.CallerSensitive";

    /**
     * The annotation {@link Class} of {@linkplain jdk.internal.reflect.CallerSensitive} that may be null
     */
    public static final Class CALLER_SENSITIVE_ANNOTATION_CLASS = (Class) resolveClass(CALLER_SENSITIVE_ANNOTATION_CLASS_NAME);

    /**
     * Is the specified type a generic {@link Class type}
     *
     * @param annotatedElement the annotated element
     * @return if annotatedElement is the {@link Class}, return true, or false
     * @see ElementType#TYPE
     */
    static boolean isType(AnnotatedElement annotatedElement) {
        return annotatedElement instanceof Class;
    }

    /**
     * Is the type of specified annotation same to the expected type?
     *
     * @param annotation     the specified {@link Annotation}
     * @param annotationType the expected annotation type
     * @return if same, return true, or false
     */
    static boolean isSameType(Annotation annotation, Class annotationType) {
        if (annotation == null || annotationType == null) {
            return false;
        }
        return Objects.equals(annotation.annotationType(), annotationType);
    }

    /**
     * Find the annotation that is annotated on the specified element may be a meta-annotation
     *
     * @param annotatedElement the annotated element
     * @param annotationType   the type of annotation
     * @param               the required type of annotation
     * @return If found, return first matched-type {@link Annotation annotation}, or null
     */
    public static  A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) {
        return findAnnotation(annotatedElement, a -> isSameType(a, annotationType));
    }

    /**
     * Find the annotation that is annotated on the specified element may be a meta-annotation
     *
     * @param annotatedElement  the annotated element
     * @param annotationFilters the filters of annotations
     * @param                the required type of annotation
     * @return If found, return first matched-type {@link Annotation annotation}, or null
     */
    public static  A findAnnotation(AnnotatedElement annotatedElement,
                                                          Predicate... annotationFilters) {
        return (A) filterFirst(getAllDeclaredAnnotations(annotatedElement), annotationFilters);
    }

    public static boolean isMetaAnnotation(Annotation annotation,
                                           Class... metaAnnotationTypes) {
        if (metaAnnotationTypes == null) {
            return false;
        }
        return isMetaAnnotation(annotation, asList(metaAnnotationTypes));
    }

    public static boolean isMetaAnnotation(Annotation annotation,
                                           Iterable> metaAnnotationTypes) {
        if (annotation == null) {
            return false;
        }
        return isMetaAnnotation(annotation.annotationType(), metaAnnotationTypes);
    }

    public static boolean isMetaAnnotation(Class annotationType,
                                           Class... metaAnnotationTypes) {
        return isMetaAnnotation(annotationType, asList(metaAnnotationTypes));
    }

    public static boolean isMetaAnnotation(Class annotationType,
                                           Iterable> metaAnnotationTypes) {

        if (NATIVE_ANNOTATION_TYPES.contains(annotationType)) {
            return false;
        }

        if (isAnnotationPresent(annotationType, metaAnnotationTypes)) {
            return true;
        }

        boolean annotated = false;
        for (Annotation annotation : annotationType.getDeclaredAnnotations()) {
            if (isMetaAnnotation(annotation, metaAnnotationTypes)) {
                annotated = true;
                break;
            }
        }

        return annotated;
    }

    /**
     * Get all directly declared annotations of the the annotated element, not including
     * meta annotations.
     *
     * @param annotatedElement    the annotated element
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    public static List getAllDeclaredAnnotations(AnnotatedElement annotatedElement,
                                                             Predicate... annotationsToFilter) {
        if (isType(annotatedElement)) {
            return getAllDeclaredAnnotations((Class) annotatedElement, annotationsToFilter);
        } else {
            return getDeclaredAnnotations(annotatedElement, annotationsToFilter);
        }
    }

    public static > Optional filterAnnotation(S annotations,
                                                                                         Predicate... annotationsToFilter) {
        return ofNullable(filterFirst(annotations, annotationsToFilter));
    }

    public static List filterAnnotations(Annotation[] annotations,
                                                     Predicate... annotationsToFilter) {
        return filterAnnotations(asList(annotations), annotationsToFilter);
    }

    public static > S filterAnnotations(S annotations,
                                                                       Predicate... annotationsToFilter) {
        return filterAll(annotations, annotationsToFilter);
    }

    /**
     * Get all directly declared annotations of the specified type and its' all hierarchical types, not including
     * meta annotations.
     *
     * @param type                the specified type
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    public static List getAllDeclaredAnnotations(Class type, Predicate... annotationsToFilter) {

        if (type == null) {
            return emptyList();
        }

        List allAnnotations = new LinkedList<>();

        // All types
        Set> allTypes = new LinkedHashSet<>();
        // Add current type
        allTypes.add(type);
        // Add all inherited types
        allTypes.addAll(getAllInheritedTypes(type, t -> !Object.class.equals(t)));

        for (Class t : allTypes) {
            allAnnotations.addAll(getDeclaredAnnotations(t));
        }

        return filterAll(allAnnotations, annotationsToFilter);
    }

    /**
     * Get annotations that are directly present on this element.
     * This method ignores inherited annotations.
     *
     * @param annotatedElement    the annotated element
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    public static List getDeclaredAnnotations(AnnotatedElement annotatedElement,
                                                          Predicate... annotationsToFilter) {
        if (annotatedElement == null) {
            return emptyList();
        }

        return filterAll(asList(annotatedElement.getAnnotations()), annotationsToFilter);
    }

    public static  T getAttributeValue(Annotation[] annotations, String attributeName, Class returnType) {
        T attributeValue = null;
        for (Annotation annotation : annotations) {
            if (annotation != null) {
                attributeValue = getAttributeValue(annotation, attributeName, returnType);
                if (attributeValue != null) {
                    break;
                }
            }
        }
        return attributeValue;
    }

    public static  T getAttributeValue(Annotation annotation, String attributeName, Class returnType) {
        Class annotationType = annotation.annotationType();
        T attributeValue = null;
        try {
            Method method = annotationType.getMethod(attributeName);
            Object value = method.invoke(annotation);
            attributeValue = returnType.cast(value);
        } catch (Exception ignored) {
            attributeValue = null;
        }
        return attributeValue;
    }

    public static boolean contains(Collection annotations, Class annotationType) {
        if (annotations == null || annotations.isEmpty()) {
            return false;
        }
        boolean contained = false;
        for (Annotation annotation : annotations) {
            if (Objects.equals(annotationType, annotation.annotationType())) {
                contained = true;
                break;
            }
        }
        return contained;
    }

    public static boolean exists(Iterable annotations, Class annotationType) {
        if (annotations == null || annotationType == null) {
            return false;
        }

        boolean found = false;
        for (Annotation annotation : annotations) {
            if (Objects.equals(annotation.annotationType(), annotationType)) {
                found = true;
                break;
            }
        }

        return found;
    }

    public static boolean exists(Annotation[] annotations, Class annotationType) {
        int length = length(annotations);
        if (length < 1 || annotationType == null) {
            return false;
        }

        boolean found = false;
        for (int i = 0; i < length; i++) {
            if (Objects.equals(annotations[i].annotationType(), annotationType)) {
                found = true;
                break;
            }
        }

        return found;
    }

    public static boolean existsAnnotated(AnnotatedElement[] annotatedElements, Class annotationType) {
        int length = length(annotatedElements);
        if (length < 1 || annotationType == null) {
            return false;
        }

        boolean annotated = false;
        for (int i = 0; i < length; i++) {
            if (isAnnotationPresent(annotatedElements[i], annotationType)) {
                annotated = true;
                break;
            }
        }

        return annotated;
    }

    public static boolean isAnnotationPresent(AnnotatedElement annotatedElement, Class annotationType) {
        if (annotatedElement == null || annotationType == null) {
            return false;
        }
        // annotated directly
        return annotatedElement.isAnnotationPresent(annotationType);
    }

    public static boolean isAnnotationPresent(Annotation annotation, Class annotationType) {
        if (annotation == null) {
            return false;
        }
        return isAnnotationPresent(annotation.annotationType(), annotationType);
    }

    public static boolean isAnnotationPresent(AnnotatedElement annotatedElement, Iterable> annotationTypes) {
        if (annotatedElement == null || annotationTypes == null) {
            return false;
        }

        boolean annotated = true;
        for (Class annotationType : annotationTypes) {
            if (!isAnnotationPresent(annotatedElement, annotationType)) {
                annotated = false;
                break;
            }
        }
        return annotated;
    }

    public static boolean isAnnotationPresent(Annotation annotation, Iterable> annotationTypes) {
        if (annotation == null) {
            return false;
        }
        return isAnnotationPresent(annotation.annotationType(), annotationTypes);
    }

    public static Object[] getAttributeValues(Annotation annotation, Predicate... attributesToFilter) {
        return getAttributeMethods(annotation, attributesToFilter)
                .map(method -> execute(() -> method.invoke(annotation)))
                .toArray(Object[]::new);
    }

    public static Map getAttributesMap(Annotation annotation, Predicate... attributesToFilter) {
        Map attributesMap = new LinkedHashMap<>();
        getAttributeMethods(annotation, attributesToFilter)
                .forEach(method -> {
                    Object value = execute(() -> method.invoke(annotation));
                    attributesMap.put(method.getName(), value);
                });
        return attributesMap.isEmpty() ? emptyMap() : unmodifiableMap(attributesMap);
    }

    public static boolean isAnnotationMethod(Method attributeMethod) {
        return attributeMethod != null && Objects.equals(Annotation.class, attributeMethod.getDeclaringClass());
    }

    /**
     * Is {@linkplain jdk.internal.reflect.CallerSensitive} class present or not
     *
     * @return true if {@linkplain jdk.internal.reflect.CallerSensitive} presents
     * @see #CALLER_SENSITIVE_ANNOTATION_CLASS
     */
    public static boolean isCallerSensitivePresent() {
        return CALLER_SENSITIVE_ANNOTATION_CLASS != null;
    }

    private static boolean isInheritedObjectMethod(Method attributeMethod) {
        boolean inherited = false;

        for (Method method : OBJECT_METHODS) {
            if (overrides(attributeMethod, method)) {
                inherited = true;
                break;
            }
        }

        return inherited;
    }

    private static Stream getAttributeMethods(Annotation annotation, Predicate... attributesToFilter) {
        Class annotationType = annotation.annotationType();
        return Stream.of(annotationType.getMethods())
                .filter(NON_INHERITED_OBJECT_METHOD_PREDICATE
                        .and(NON_ANNOTATION_METHOD_PREDICATE)
                        .and(and(attributesToFilter)));
    }

}