| /* |
| * 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 org.apache.dubbo.common.utils; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.Documented; |
| import java.lang.annotation.ElementType; |
| 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.Collections; |
| 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.Set; |
| import java.util.function.Predicate; |
| |
| import static java.util.Arrays.asList; |
| import static java.util.Collections.emptyList; |
| import static java.util.Collections.unmodifiableList; |
| import static org.apache.dubbo.common.function.Predicates.and; |
| import static org.apache.dubbo.common.function.Streams.filterAll; |
| import static org.apache.dubbo.common.function.Streams.filterFirst; |
| import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes; |
| import static org.apache.dubbo.common.utils.ClassUtils.resolveClass; |
| import static org.apache.dubbo.common.utils.CollectionUtils.first; |
| import static org.apache.dubbo.common.utils.MethodUtils.findMethod; |
| import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod; |
| |
| /** |
| * Commons Annotation Utilities class |
| * |
| * @since 2.7.6 |
| */ |
| public interface AnnotationUtils { |
| |
| /** |
| * Resolve the annotation type by the annotated element and resolved class name |
| * |
| * @param annotatedElement the annotated element |
| * @param annotationClassName the class name of annotation |
| * @param <A> the type of annotation |
| * @return If resolved, return the type of annotation, or <code>null</code> |
| */ |
| static <A extends Annotation> Class<A> resolveAnnotationType(AnnotatedElement annotatedElement, |
| String annotationClassName) { |
| ClassLoader classLoader = annotatedElement.getClass().getClassLoader(); |
| Class<?> annotationType = resolveClass(annotationClassName, classLoader); |
| if (annotationType == null || !Annotation.class.isAssignableFrom(annotationType)) { |
| return null; |
| } |
| return (Class<A>) annotationType; |
| } |
| |
| /** |
| * Is the specified type a generic {@link Class type} |
| * |
| * @param annotatedElement the annotated element |
| * @return if <code>annotatedElement</code> is the {@link Class}, return <code>true</code>, or <code>false</code> |
| * @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 <code>true</code>, or <code>false</code> |
| */ |
| static boolean isSameType(Annotation annotation, Class<? extends Annotation> annotationType) { |
| if (annotation == null || annotationType == null) { |
| return false; |
| } |
| return Objects.equals(annotation.annotationType(), annotationType); |
| } |
| |
| /** |
| * Build an instance of {@link Predicate} to excluded annotation type |
| * |
| * @param excludedAnnotationType excluded annotation type |
| * @return non-null |
| */ |
| static Predicate<Annotation> excludedType(Class<? extends Annotation> excludedAnnotationType) { |
| return annotation -> !isSameType(annotation, excludedAnnotationType); |
| } |
| |
| /** |
| * Get the attribute from the specified {@link Annotation annotation} |
| * |
| * @param annotation the specified {@link Annotation annotation} |
| * @param attributeName the attribute name |
| * @param <T> the type of attribute |
| * @return the attribute value |
| * @throws IllegalArgumentException If the attribute name can't be found |
| */ |
| static <T> T getAttribute(Annotation annotation, String attributeName) throws IllegalArgumentException { |
| return annotation == null ? null : invokeMethod(annotation, attributeName); |
| } |
| |
| /** |
| * Get the "value" attribute from the specified {@link Annotation annotation} |
| * |
| * @param annotation the specified {@link Annotation annotation} |
| * @param <T> the type of attribute |
| * @return the value of "value" attribute |
| * @throws IllegalArgumentException If the attribute name can't be found |
| */ |
| static <T> T getValue(Annotation annotation) throws IllegalArgumentException { |
| return getAttribute(annotation, "value"); |
| } |
| |
| /** |
| * Get the {@link Annotation} from the specified {@link AnnotatedElement the annotated element} and |
| * {@link Annotation annotation} class name |
| * |
| * @param annotatedElement {@link AnnotatedElement} |
| * @param annotationClassName the class name of annotation |
| * @param <A> The type of {@link Annotation} |
| * @return the {@link Annotation} if found |
| * @throws ClassCastException If the {@link Annotation annotation} type that client requires can't match actual type |
| */ |
| static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, String annotationClassName) |
| throws ClassCastException { |
| Class<? extends Annotation> annotationType = resolveAnnotationType(annotatedElement, annotationClassName); |
| if (annotationType == null) { |
| return null; |
| } |
| return (A) annotatedElement.getAnnotation(annotationType); |
| } |
| |
| /** |
| * Get annotations that are <em>directly present</em> 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} |
| */ |
| static List<Annotation> getDeclaredAnnotations(AnnotatedElement annotatedElement, |
| Predicate<Annotation>... annotationsToFilter) { |
| if (annotatedElement == null) { |
| return emptyList(); |
| } |
| |
| return unmodifiableList(filterAll(asList(annotatedElement.getDeclaredAnnotations()), annotationsToFilter)); |
| } |
| |
| /** |
| * 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} |
| */ |
| static List<Annotation> getAllDeclaredAnnotations(AnnotatedElement annotatedElement, |
| Predicate<Annotation>... annotationsToFilter) { |
| if (isType(annotatedElement)) { |
| return getAllDeclaredAnnotations((Class) annotatedElement, annotationsToFilter); |
| } else { |
| return getDeclaredAnnotations(annotatedElement, 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} |
| */ |
| static List<Annotation> getAllDeclaredAnnotations(Class<?> type, Predicate<Annotation>... annotationsToFilter) { |
| |
| if (type == null) { |
| return emptyList(); |
| } |
| |
| List<Annotation> allAnnotations = new LinkedList<>(); |
| |
| // All types |
| Set<Class<?>> 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, annotationsToFilter)); |
| } |
| |
| return unmodifiableList(allAnnotations); |
| } |
| |
| |
| /** |
| * Get the meta-annotated {@link Annotation annotations} directly, excluding {@link Target}, {@link Retention} |
| * and {@link Documented} |
| * |
| * @param annotationType the {@link Annotation annotation} type |
| * @param metaAnnotationsToFilter the meta annotations to filter |
| * @return non-null read-only {@link List} |
| */ |
| static List<Annotation> getMetaAnnotations(Class<? extends Annotation> annotationType, |
| Predicate<Annotation>... metaAnnotationsToFilter) { |
| return getDeclaredAnnotations(annotationType, |
| // Excludes the Java native annotation types or it causes the stack overflow, e.g, |
| // @Target annotates itself |
| excludedType(Target.class), |
| excludedType(Retention.class), |
| excludedType(Documented.class), |
| // Add other predicates |
| and(metaAnnotationsToFilter) |
| ); |
| } |
| |
| /** |
| * Get all meta annotations from the specified {@link Annotation annotation} type |
| * |
| * @param annotationType the {@link Annotation annotation} type |
| * @param annotationsToFilter the annotations to filter |
| * @return non-null read-only {@link List} |
| */ |
| static List<Annotation> getAllMetaAnnotations(Class<? extends Annotation> annotationType, |
| Predicate<Annotation>... annotationsToFilter) { |
| |
| List<Annotation> allMetaAnnotations = new LinkedList<>(); |
| |
| List<Annotation> metaAnnotations = getMetaAnnotations(annotationType); |
| |
| allMetaAnnotations.addAll(metaAnnotations); |
| |
| for (Annotation metaAnnotation : metaAnnotations) { |
| // Get the nested meta annotations recursively |
| allMetaAnnotations.addAll(getAllMetaAnnotations(metaAnnotation.annotationType())); |
| } |
| |
| return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter)); |
| } |
| |
| /** |
| * Find the annotation that is annotated on the specified element may be a meta-annotation |
| * |
| * @param annotatedElement the annotated element |
| * @param annotationClassName the class name of annotation |
| * @param <A> the required type of annotation |
| * @return If found, return first matched-type {@link Annotation annotation}, or <code>null</code> |
| */ |
| static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, String annotationClassName) { |
| return findAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, annotationClassName)); |
| } |
| |
| /** |
| * 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 <A> the required type of annotation |
| * @return If found, return first matched-type {@link Annotation annotation}, or <code>null</code> |
| */ |
| static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { |
| return (A) filterFirst(getAllDeclaredAnnotations(annotatedElement), a -> isSameType(a, annotationType)); |
| } |
| |
| /** |
| * Find the meta annotations from the the {@link Annotation annotation} type by meta annotation type |
| * |
| * @param annotationType the {@link Annotation annotation} type |
| * @param metaAnnotationType the meta annotation type |
| * @param <A> the type of required annotation |
| * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list} |
| */ |
| static <A extends Annotation> List<A> findMetaAnnotations(Class<? extends Annotation> annotationType, |
| Class<A> metaAnnotationType) { |
| return (List<A>) getAllMetaAnnotations(annotationType, a -> isSameType(a, metaAnnotationType)); |
| } |
| |
| /** |
| * Find the meta annotations from the the the annotated element by meta annotation type |
| * |
| * @param annotatedElement the annotated element |
| * @param metaAnnotationType the meta annotation type |
| * @param <A> the type of required annotation |
| * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list} |
| */ |
| static <A extends Annotation> List<A> findMetaAnnotations(AnnotatedElement annotatedElement, |
| Class<A> metaAnnotationType) { |
| List<A> metaAnnotations = new LinkedList<>(); |
| |
| for (Annotation annotation : getAllDeclaredAnnotations(annotatedElement)) { |
| metaAnnotations.addAll(findMetaAnnotations(annotation.annotationType(), metaAnnotationType)); |
| } |
| |
| return unmodifiableList(metaAnnotations); |
| } |
| |
| /** |
| * Find the meta annotation from the annotated element by meta annotation type |
| * |
| * @param annotatedElement the annotated element |
| * @param metaAnnotationClassName the class name of meta annotation |
| * @param <A> the type of required annotation |
| * @return {@link #findMetaAnnotation(Class, Class)} |
| */ |
| static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement, |
| String metaAnnotationClassName) { |
| return findMetaAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, metaAnnotationClassName)); |
| } |
| |
| /** |
| * Find the meta annotation from the annotation type by meta annotation type |
| * |
| * @param annotationType the {@link Annotation annotation} type |
| * @param metaAnnotationType the meta annotation type |
| * @param <A> the type of required annotation |
| * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return <code>null</code>. |
| * If it requires more result, please consider to use {@link #findMetaAnnotations(Class, Class)} |
| * @see #findMetaAnnotations(Class, Class) |
| */ |
| static <A extends Annotation> A findMetaAnnotation(Class<? extends Annotation> annotationType, |
| Class<A> metaAnnotationType) { |
| return first(findMetaAnnotations(annotationType, metaAnnotationType)); |
| } |
| |
| /** |
| * Find the meta annotation from the annotated element by meta annotation type |
| * |
| * @param annotatedElement the annotated element |
| * @param metaAnnotationType the meta annotation type |
| * @param <A> the type of required annotation |
| * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return <code>null</code>. |
| * If it requires more result, please consider to use {@link #findMetaAnnotations(AnnotatedElement, Class)} |
| * @see #findMetaAnnotations(AnnotatedElement, Class) |
| */ |
| static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement, Class<A> metaAnnotationType) { |
| return first(findMetaAnnotations(annotatedElement, metaAnnotationType)); |
| } |
| |
| /** |
| * Tests the annotated element is annotated the specified annotations or not |
| * |
| * @param type the annotated type |
| * @param matchAll If <code>true</code>, checking all annotation types are present or not, or match any |
| * @param annotationTypes the specified annotation types |
| * @return If the specified annotation types are present, return <code>true</code>, or <code>false</code> |
| */ |
| static boolean isAnnotationPresent(Class<?> type, |
| boolean matchAll, |
| Class<? extends Annotation>... annotationTypes) { |
| |
| int size = annotationTypes == null ? 0 : annotationTypes.length; |
| |
| if (size < 1) { |
| return false; |
| } |
| |
| int presentCount = 0; |
| |
| for (int i = 0; i < size; i++) { |
| Class<? extends Annotation> annotationType = annotationTypes[i]; |
| if (findAnnotation(type, annotationType) != null || findMetaAnnotation(type, annotationType) != null) { |
| presentCount++; |
| } |
| } |
| |
| return matchAll ? presentCount == size : presentCount > 0; |
| } |
| |
| /** |
| * Tests the annotated element is annotated the specified annotation or not |
| * |
| * @param type the annotated type |
| * @param annotationType the class of annotation |
| * @return If the specified annotation type is present, return <code>true</code>, or <code>false</code> |
| */ |
| static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType) { |
| return isAnnotationPresent(type, true, annotationType); |
| } |
| |
| /** |
| * Tests the annotated element is present any specified annotation types |
| * |
| * @param annotatedElement the annotated element |
| * @param annotationClassName the class name of annotation |
| * @return If any specified annotation types are present, return <code>true</code> |
| */ |
| static boolean isAnnotationPresent(AnnotatedElement annotatedElement, String annotationClassName) { |
| ClassLoader classLoader = annotatedElement.getClass().getClassLoader(); |
| Class<?> resolvedType = resolveClass(annotationClassName, classLoader); |
| if (!Annotation.class.isAssignableFrom(resolvedType)) { |
| return false; |
| } |
| return isAnnotationPresent(annotatedElement, (Class<? extends Annotation>) resolvedType); |
| } |
| |
| /** |
| * Tests the annotated element is present any specified annotation types |
| * |
| * @param annotatedElement the annotated element |
| * @param annotationType the class of annotation |
| * @return If any specified annotation types are present, return <code>true</code> |
| */ |
| static boolean isAnnotationPresent(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType) { |
| if (isType(annotatedElement)) { |
| return isAnnotationPresent((Class) annotatedElement, annotationType); |
| } else { |
| return annotatedElement.isAnnotationPresent(annotationType) || |
| findMetaAnnotation(annotatedElement, annotationType) != null; // to find meta-annotation |
| } |
| } |
| |
| /** |
| * Tests the annotated element is annotated all specified annotations or not |
| * |
| * @param type the annotated type |
| * @param annotationTypes the specified annotation types |
| * @return If the specified annotation types are present, return <code>true</code>, or <code>false</code> |
| */ |
| static boolean isAllAnnotationPresent(Class<?> type, Class<? extends Annotation>... annotationTypes) { |
| return isAnnotationPresent(type, true, annotationTypes); |
| } |
| |
| /** |
| * Tests the annotated element is present any specified annotation types |
| * |
| * @param type the annotated type |
| * @param annotationTypes the specified annotation types |
| * @return If any specified annotation types are present, return <code>true</code> |
| */ |
| static boolean isAnyAnnotationPresent(Class<?> type, |
| Class<? extends Annotation>... annotationTypes) { |
| return isAnnotationPresent(type, false, annotationTypes); |
| } |
| |
| |
| /** |
| * Get the default value of attribute on the specified annotation |
| * |
| * @param annotation {@link Annotation} object |
| * @param attributeName the name of attribute |
| * @param <T> the type of value |
| * @return <code>null</code> if not found |
| * @since 2.7.9 |
| */ |
| static <T> T getDefaultValue(Annotation annotation, String attributeName) { |
| return getDefaultValue(annotation.annotationType(), attributeName); |
| } |
| |
| /** |
| * Get the default value of attribute on the specified annotation |
| * |
| * @param annotationType the type of {@link Annotation} |
| * @param attributeName the name of attribute |
| * @param <T> the type of value |
| * @return <code>null</code> if not found |
| * @since 2.7.9 |
| */ |
| static <T> T getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) { |
| Method method = findMethod(annotationType, attributeName); |
| return (T) (method == null ? null : method.getDefaultValue()); |
| } |
| |
| /** |
| * Filter default value of Annotation type |
| * @param annotationType annotation type from {@link Annotation#annotationType()} |
| * @param attributes |
| * @return |
| */ |
| static Map<String, Object> filterDefaultValues(Class<? extends Annotation> annotationType, Map<String, Object> attributes) { |
| Map<String, Object> filteredAttributes = new LinkedHashMap<>(attributes.size()); |
| attributes.forEach((key,val) -> { |
| if (!Objects.deepEquals(val, getDefaultValue(annotationType, key))) { |
| filteredAttributes.put(key, val); |
| } |
| }); |
| // remove void class, compatible with spring 3.x |
| Object interfaceClassValue = filteredAttributes.get("interfaceClass"); |
| if (interfaceClassValue instanceof String && StringUtils.isEquals((String) interfaceClassValue, "void")) { |
| filteredAttributes.remove("interfaceClass"); |
| } |
| return filteredAttributes; |
| } |
| |
| /** |
| * Filter default value of Annotation type |
| * @param annotation |
| * @param attributes |
| * @return |
| */ |
| static Map<String, Object> filterDefaultValues(Annotation annotation, Map<String, Object> attributes) { |
| return filterDefaultValues(annotation.annotationType(), attributes); |
| } |
| |
| /** |
| * Get attributes of annotation |
| * @param annotation |
| * @return |
| */ |
| static Map<String, Object> getAttributes(Annotation annotation, boolean filterDefaultValue) { |
| Class<?> annotationType = annotation.annotationType(); |
| Method[] methods = annotationType.getMethods(); |
| Map<String, Object> attributes = new LinkedHashMap<>(methods.length); |
| for (Method method : methods) { |
| try { |
| if (method.getDeclaringClass() == Annotation.class) { |
| continue; |
| } |
| String name = method.getName(); |
| Object value = method.invoke(annotation); |
| if (!filterDefaultValue || !Objects.deepEquals(value, method.getDefaultValue())) { |
| attributes.put(name, value); |
| } |
| } catch (Exception e) { |
| throw new IllegalStateException("get attribute value of annotation failed: " + method, e); |
| } |
| } |
| return attributes; |
| } |
| } |