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 extends Annotation> CALLER_SENSITIVE_ANNOTATION_CLASS = (Class extends Annotation>) 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 extends Annotation> 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 extends Annotation>... 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 extends Annotation> annotationType,
Class extends Annotation>... metaAnnotationTypes) {
return isMetaAnnotation(annotationType, asList(metaAnnotationTypes));
}
public static boolean isMetaAnnotation(Class extends Annotation> 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 extends Annotation> 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 extends Annotation> 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 extends Annotation> 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 extends Annotation> 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 extends Annotation> annotationType) {
if (annotatedElement == null || annotationType == null) {
return false;
}
// annotated directly
return annotatedElement.isAnnotationPresent(annotationType);
}
public static boolean isAnnotationPresent(Annotation annotation, Class extends Annotation> 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 extends Annotation> 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 extends Annotation> annotationType = annotation.annotationType();
return Stream.of(annotationType.getMethods())
.filter(NON_INHERITED_OBJECT_METHOD_PREDICATE
.and(NON_ANNOTATION_METHOD_PREDICATE)
.and(and(attributesToFilter)));
}
}