AnnotationMetadata.java
/*
* Copyright 2017-2020 original authors
*
* Licensed 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
*
* https://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.micronaut.core.annotation;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.OptionalValues;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
/**
* <p>An interface implemented at compile time by Micronaut that allows the inspection of annotation metadata and
* stereotypes (meta-annotations)</p>.
*
* <p>This interface exposes fast and efficient means to expose annotation data at runtime without requiring reflective
* tricks to read the annotation metadata</p>
*
* <p>Users of Micronaut should in general avoid the methods of the {@link java.lang.reflect.AnnotatedElement}
* interface and use this interface instead to obtain maximum efficiency</p>
*
* <p>Core framework types such as {@code io.micronaut.inject.BeanDefinition} and
* {@code io.micronaut.inject.ExecutableMethod} implement this interface</p>
*
* @author Graeme Rocher
* @since 1.0
*/
public interface AnnotationMetadata extends AnnotationSource {
/**
* A constant for representing empty metadata.
*/
AnnotationMetadata EMPTY_METADATA = new EmptyAnnotationMetadata();
/**
* The default {@code value()} member.
*/
String VALUE_MEMBER = "value";
/**
* The suffix used when saving compiled metadata to classes.
*/
String CLASS_NAME_SUFFIX = "$$AnnotationMetadata";
/**
* Gets the declared metadata without inherited metdata.
* @return The declared metadata
* @since 3.0.0
*/
default @NonNull AnnotationMetadata getDeclaredMetadata() {
return this;
}
/**
* Does the metadata contain any property expressions like {@code ${foo.bar}}. Note
* this by default returns {@code true} as previous versions of Micronaut must assume metadata
* is present. The compilation time this is computed in order to decide whether to instrument
* annotation metadata with environment specific logic.
*
* @return True if property expressions are present
*/
default boolean hasPropertyExpressions() {
return true;
}
/**
* Does the metadata contain any evaluated expressions like {@code #{ T(java.lang.Math).random() }}.
*
* @return True if evaluated expressions are present
* @since 4.0.0
*/
default boolean hasEvaluatedExpressions() {
return false;
}
/**
* Resolve all annotation names that feature the given stereotype.
*
* @param stereotype The annotation names
* @return A set of annotation names
*/
default @NonNull
List<String> getAnnotationNamesByStereotype(@Nullable String stereotype) {
return Collections.emptyList();
}
/**
* Resolve all annotation values that feature the given stereotype.
*
* @param stereotype The annotation names
* @param <T> The annotation type
* @return A set of annotation names
* @since 3.5.2
*/
@NonNull
default <T extends Annotation> List<AnnotationValue<T>> getAnnotationValuesByStereotype(@Nullable String stereotype) {
return Collections.emptyList();
}
/**
* All the annotation names this metadata declares.
*
* @return All the annotation names this metadata declares
*/
default @NonNull Set<String> getAnnotationNames() {
return Collections.emptySet();
}
/**
* Returns the names of the annotations which are stereotypes.
*
* <p>A stereotype is a meta-annotation (an annotation declared on another annotation).</p>
* @return The names of the stereotype annotations
* @since 3.4.1
* @see #getDeclaredStereotypeAnnotationNames()
*/
default @NonNull Set<String> getStereotypeAnnotationNames() {
return Collections.emptySet();
}
/**
* Returns the names of the annotations which are declared stereotypes.
*
* <p>A stereotype is a meta-annotation (an annotation declared on another annotation).</p>
*
* <p>A stereotype is considered declared when it it is a meta-annotation that is present on an annotation directly declared on the element and not inherited from a super class.</p>
* @return The names of the stereotype annotations
* @since 3.4.1
* @see #getStereotypeAnnotationNames()
* @see #getDeclaredAnnotationNames()
*/
default @NonNull Set<String> getDeclaredStereotypeAnnotationNames() {
return Collections.emptySet();
}
/**
* All the declared annotation names this metadata declares.
*
* @return All the declared annotation names this metadata declares
*/
default @NonNull Set<String> getDeclaredAnnotationNames() {
return Collections.emptySet();
}
/**
* Resolve all annotations names for the given stereotype that are declared annotations.
*
* @param stereotype The stereotype
* @return The declared annotations
*/
default @NonNull List<String> getDeclaredAnnotationNamesByStereotype(@Nullable String stereotype) {
return Collections.emptyList();
}
/**
* Get all values for the given annotation and type of the underlying values.
*
* @param annotation The annotation name
* @param valueType valueType
* @param <T> Generic type
* @return The {@link OptionalValues}
*/
default @NonNull <T> OptionalValues<T> getValues(@NonNull String annotation, @NonNull Class<T> valueType) {
return OptionalValues.empty();
}
/**
* Get all values for the given annotation and type of the underlying values.
*
* @param annotation The annotation name
* @return An immutable map of values
*/
default @NonNull Map<CharSequence, Object> getValues(@NonNull String annotation) {
final AnnotationValue<Annotation> ann = getAnnotation(annotation);
if (ann != null) {
return ann.getValues();
} else {
return Collections.emptyMap();
}
}
/**
* Return the default value for the given annotation member.
*
* @param annotation The annotation
* @param member The member
* @param requiredType The required type
* @param <T> The required generic type
* @return An optional value
*/
default <T> Optional<T> getDefaultValue(@NonNull String annotation, @NonNull String member, @NonNull Argument<T> requiredType) {
return Optional.empty();
}
/**
* Gets all the annotation values by the given repeatable type.
*
* @param annotationType The annotation type
* @param <T> The annotation type
* @return A list of values
*/
default @NonNull <T extends Annotation> List<AnnotationValue<T>> getAnnotationValuesByType(@NonNull Class<T> annotationType) {
return Collections.emptyList();
}
/**
* Gets all the annotation values by the given repeatable type name.
*
* @param annotationType The annotation type
* @param <T> The annotation type
* @return A list of values
* @since 3.2.4
*/
default @NonNull <T extends Annotation> List<AnnotationValue<T>> getAnnotationValuesByName(@NonNull String annotationType) {
return Collections.emptyList();
}
/**
* Gets only declared annotation values by the given repeatable type.
*
* @param annotationType The annotation type
* @param <T> The annotation type
* @return A list of values
*/
default @NonNull <T extends Annotation> List<AnnotationValue<T>> getDeclaredAnnotationValuesByType(@NonNull Class<T> annotationType) {
return Collections.emptyList();
}
/**
* Gets only declared annotation values by the given repeatable type name.
*
* @param annotationType The annotation type
* @param <T> The annotation type
* @return A list of values
* @since 3.2.4
*/
default @NonNull <T extends Annotation> List<AnnotationValue<T>> getDeclaredAnnotationValuesByName(@NonNull String annotationType) {
return Collections.emptyList();
}
/**
* Checks whether this object has the given annotation directly declared on the object.
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasDeclaredAnnotation(@Nullable String annotation) {
return false;
}
/**
* Checks whether this object has the given annotation on the object itself or inherited from a parent.
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasAnnotation(@Nullable String annotation) {
return false;
}
/**
* Checks whether the given annotation simple name (name without the package) is present in the annotations.
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasSimpleAnnotation(@Nullable String annotation) {
if (annotation == null) {
return false;
}
for (String a : getAnnotationNames()) {
if (NameUtils.getSimpleName(a).equalsIgnoreCase(annotation)) {
return true;
}
}
return false;
}
/**
* Checks whether the given annotation simple name (name without the package) is present in the declared annotations.
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasSimpleDeclaredAnnotation(@Nullable String annotation) {
if (annotation == null) {
return false;
}
for (String a : getDeclaredAnnotationNames()) {
if (NameUtils.getSimpleName(a).equalsIgnoreCase(annotation)) {
return true;
}
}
return false;
}
/**
* <p>Checks whether this object has the given annotation stereotype on the object itself or inherited from a parent</p>.
*
* <p>An annotation stereotype is a meta annotation potentially applied to another annotation</p>
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasStereotype(@Nullable String annotation) {
return false;
}
/**
* <p>Checks whether this object has the given annotation stereotype on the object itself and not inherited from a parent</p>.
*
* <p>An annotation stereotype is a meta annotation potentially applied to another annotation</p>
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasDeclaredStereotype(@Nullable String annotation) {
return false;
}
/**
* Checks whether this object has any of the given stereotype directly declared on the object.
*
* @param annotations The annotations
* @return True if any of the given stereotypes are present
* @since 2.3.3
*/
@SuppressWarnings("unchecked")
default boolean hasDeclaredStereotype(@Nullable String... annotations) {
if (ArrayUtils.isEmpty(annotations)) {
return false;
}
for (String annotation : annotations) {
if (hasDeclaredStereotype(annotation)) {
return true;
}
}
return false;
}
/**
* Return the default values for the given annotation name.
* @param annotation The annotation name
* @return The default values
*/
default @NonNull Map<CharSequence, Object> getDefaultValues(@NonNull String annotation) {
return Collections.emptyMap();
}
/**
* Return the default value for the given annotation member.
*
* @param annotation The annotation
* @param member The member
* @param requiredType The required type
* @param <T> The required generic type
* @return An optional value
*/
default <T> Optional<T> getDefaultValue(@NonNull String annotation, @NonNull String member, @NonNull Class<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getDefaultValue(annotation, member, Argument.of(requiredType));
}
/**
* Return the default value for the given annotation member.
*
* @param annotation The annotation
* @param member The member
* @param requiredType The required type
* @param <T> The required generic type
* @return An optional value
*/
default <T> Optional<T> getDefaultValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member, @NonNull Argument<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getDefaultValue(annotation.getName(), member, requiredType);
}
/**
* @see AnnotationSource#isAnnotationPresent(Class)
*/
@Override
@SuppressWarnings("java:S2583")
default boolean isAnnotationPresent(@NonNull Class<? extends Annotation> annotationClass) {
//noinspection ConstantConditions
if (annotationClass == null) {
return false;
}
return hasAnnotation(annotationClass);
}
/**
* @see AnnotationSource#isAnnotationPresent(Class)
*/
@Override
@SuppressWarnings("java:S2583")
default boolean isDeclaredAnnotationPresent(@NonNull Class<? extends Annotation> annotationClass) {
//noinspection ConstantConditions
if (annotationClass == null) {
return false;
}
return hasDeclaredAnnotation(annotationClass);
}
/**
* @see AnnotationSource#isAnnotationPresent(String)
*/
@Override
@SuppressWarnings("java:S2583")
default boolean isAnnotationPresent(@NonNull String annotationName) {
//noinspection ConstantConditions
if (annotationName == null) {
return false;
}
return hasAnnotation(annotationName);
}
/**
* @see AnnotationSource#isAnnotationPresent(String)
*/
@Override
@SuppressWarnings("java:S2583")
default boolean isDeclaredAnnotationPresent(@NonNull String annotationName) {
//noinspection ConstantConditions
if (annotationName == null) {
return false;
}
return hasDeclaredAnnotation(annotationName);
}
/**
* Return the default value for the given annotation member.
*
* @param annotation The annotation
* @param member The member
* @param requiredType The required type
* @param <T> The required generic type
* @return An optional value
*/
default <T> Optional<T> getDefaultValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member, @NonNull Class<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
return getDefaultValue(annotation.getName(), member, requiredType);
}
/**
* Get the value of the given annotation member.
*
* @param annotation The annotation class
* @param member The annotation member
* @param requiredType The required type
* @param <T> The value
* @return An {@link Optional} of the value
*/
default <T> Optional<T> getValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member, @NonNull Class<T> requiredType) {
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getValue(annotation, member, Argument.of(requiredType));
}
/**
* Get the value of the given annotation member.
*
* @param annotation The annotation class
* @param member The annotation member
* @param requiredType The required type
* @param <T> The value
* @return An {@link Optional} of the value
*/
default <T> Optional<T> getValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member, @NonNull Argument<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
if (isRepeatableAnnotation(annotation)) {
List<? extends AnnotationValue<? extends Annotation>> values = getAnnotationValuesByType(annotation);
if (!values.isEmpty()) {
return values.iterator().next().get(member, requiredType);
} else {
return Optional.empty();
}
} else {
Optional<? extends AnnotationValue<? extends Annotation>> values = findAnnotation(annotation);
Optional<T> value = values.flatMap(av -> av.get(member, requiredType));
if (!value.isPresent() && hasStereotype(annotation)) {
return getDefaultValue(annotation, member, requiredType);
}
return value;
}
}
/**
* Find the first annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<String> getAnnotationNameByStereotype(@Nullable String stereotype) {
List<String> annotationNamesByStereotype = getAnnotationNamesByStereotype(stereotype);
if (annotationNamesByStereotype.isEmpty()) {
return Optional.empty();
}
return Optional.of(annotationNamesByStereotype.get(0));
}
/**
* Find the first annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<String> getDeclaredAnnotationNameByStereotype(@Nullable String stereotype) {
List<String> declaredAnnotationNamesByStereotype = getDeclaredAnnotationNamesByStereotype(stereotype);
if (declaredAnnotationNamesByStereotype.isEmpty()) {
return Optional.empty();
}
return Optional.of(declaredAnnotationNamesByStereotype.get(0));
}
/**
* Find the first annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<Class<? extends Annotation>> getAnnotationTypeByStereotype(@NonNull Class<? extends Annotation> stereotype) {
ArgumentUtils.requireNonNull("stereotype", stereotype);
return getAnnotationTypeByStereotype(stereotype.getName());
}
/**
* Find the first declared annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<Class<? extends Annotation>> getDeclaredAnnotationTypeByStereotype(@NonNull Class<? extends Annotation> stereotype) {
ArgumentUtils.requireNonNull("stereotype", stereotype);
return getDeclaredAnnotationTypeByStereotype(stereotype.getName());
}
/**
* Find the first annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<Class<? extends Annotation>> getDeclaredAnnotationTypeByStereotype(@Nullable String stereotype) {
return getDeclaredAnnotationNameByStereotype(stereotype).flatMap(this::getAnnotationType);
}
/**
* Gets the type for a given annotation if it is present on the classpath. Subclasses can potentially override to provide optimized loading.
* @param name The type name
* @param classLoader The ClassLoader to load the type
* @return The type if present
*/
default Optional<Class<? extends Annotation>> getAnnotationType(@NonNull String name, @NonNull ClassLoader classLoader) {
ArgumentUtils.requireNonNull("name", name);
final Optional<Class<?>> aClass = ClassUtils.forName(name, classLoader);
Class<?> clazz = aClass.orElse(null);
if (clazz != null && Annotation.class.isAssignableFrom(clazz)) {
//noinspection unchecked
return (Optional) aClass;
}
return Optional.empty();
}
/**
* Gets the type for a given annotation if it is present on the classpath. Subclasses can potentially override to provide optimized loading.
* @param name The type name
* @return The type if present
*/
default Optional<Class<? extends Annotation>> getAnnotationType(@NonNull String name) {
return getAnnotationType(name, getClass().getClassLoader());
}
/**
* Find the first annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<Class<? extends Annotation>> getAnnotationTypeByStereotype(@Nullable String stereotype) {
return getAnnotationNameByStereotype(stereotype).flatMap(this::getAnnotationType);
}
/**
* Find the first annotation name for the given stereotype.
*
* @param stereotype The stereotype
* @return The annotation name
*/
default Optional<String> getAnnotationNameByStereotype(@NonNull Class<? extends Annotation> stereotype) {
ArgumentUtils.requireNonNull("stereotype", stereotype);
return getAnnotationNameByStereotype(stereotype.getName());
}
/**
* Get all values for the given annotation.
*
* @param annotation The annotation name
* @param valueType valueType
* @param <T> Generic type
* @return The {@link OptionalValues}
*/
default @NonNull <T> OptionalValues<T> getValues(@NonNull Class<? extends Annotation> annotation, @NonNull Class<T> valueType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("valueType", valueType);
return getValues(annotation.getName(), valueType);
}
/**
* Resolve all annotation names that feature the given stereotype.
*
* @param stereotype The annotation names
* @return A set of annotation names
*/
default @NonNull List<String> getAnnotationNamesByStereotype(@NonNull Class<? extends Annotation> stereotype) {
ArgumentUtils.requireNonNull("stereotype", stereotype);
return getAnnotationNamesByStereotype(stereotype.getName());
}
/**
* Resolve all annotation names that feature the given stereotype.
*
* @param stereotype The annotation names
* @return A set of annotation names
*/
default @NonNull List<Class<? extends Annotation>> getAnnotationTypesByStereotype(@NonNull Class<? extends Annotation> stereotype) {
return getAnnotationTypesByStereotype(stereotype.getName());
}
/**
* Resolve all annotation names that feature the given stereotype.
*
* @param stereotype The annotation names
* @return A set of annotation names
*/
default @NonNull List<Class<? extends Annotation>> getAnnotationTypesByStereotype(@NonNull String stereotype) {
ArgumentUtils.requireNonNull("stereotype", stereotype);
List<String> names = getAnnotationNamesByStereotype(stereotype);
List<Class<? extends Annotation>> list = new ArrayList<>(names.size());
for (String name : names) {
Optional<Class<? extends Annotation>> opt = getAnnotationType(name);
if (opt.isPresent()) {
Class<? extends Annotation> aClass = opt.get();
list.add(aClass);
}
}
return list;
}
/**
* Resolve all annotation names that feature the given stereotype.
*
* @param stereotype The annotation names
* @param classLoader The classloader to load annotation type
* @return A set of annotation names
*/
default @NonNull List<Class<? extends Annotation>> getAnnotationTypesByStereotype(@NonNull Class<? extends Annotation> stereotype, @NonNull ClassLoader classLoader) {
ArgumentUtils.requireNonNull("stereotype", stereotype);
List<String> names = getAnnotationNamesByStereotype(stereotype.getName());
List<Class<? extends Annotation>> list = new ArrayList<>(names.size());
for (String name : names) {
Optional<Class<? extends Annotation>> opt = getAnnotationType(name, classLoader);
if (opt.isPresent()) {
Class<? extends Annotation> aClass = opt.get();
list.add(aClass);
}
}
return list;
}
/**
* Get all values for the given annotation.
*
* @param annotationClass The annotation name
* @param <T> The annotation type
* @return The {@link AnnotationValue}
*/
@Override
default <T extends Annotation> Optional<AnnotationValue<T>> findAnnotation(@NonNull Class<T> annotationClass) {
ArgumentUtils.requireNonNull("annotationClass", annotationClass);
if (isRepeatableAnnotation(annotationClass)) {
List<AnnotationValue<T>> values = getAnnotationValuesByType(annotationClass);
if (values.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(values.get(0));
}
} else {
return this.findAnnotation(annotationClass.getName());
}
}
@Override
default <T extends Annotation> Optional<AnnotationValue<T>> findDeclaredAnnotation(@NonNull Class<T> annotationClass) {
ArgumentUtils.requireNonNull("annotationClass", annotationClass);
if (isRepeatableAnnotation(annotationClass)) {
List<AnnotationValue<T>> values = getDeclaredAnnotationValuesByType(annotationClass);
if (values.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(values.get(0));
}
} else {
return this.findDeclaredAnnotation(annotationClass.getName());
}
}
/**
* Get the value of the given annotation member.
*
* @param annotation The annotation class
* @param member The annotation member
* @param requiredType The required type
* @param <T> The value
* @return An {@link Optional} of the value
*/
default <T> Optional<T> getValue(@NonNull String annotation, @NonNull String member, @NonNull Class<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getValue(annotation, member, Argument.of(requiredType));
}
/**
* Get the value of the given annotation member.
*
* @param annotation The annotation class
* @param member The annotation member
* @param requiredType The required type
* @param <T> The value
* @return An {@link Optional} of the value
*/
default <T> Optional<T> getValue(@NonNull String annotation, @NonNull String member, @NonNull Argument<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
ArgumentUtils.requireNonNull("requiredType", requiredType);
Optional<T> value = findAnnotation(annotation).flatMap(av -> av.get(member, requiredType));
if (!value.isPresent() && hasStereotype(annotation)) {
return getDefaultValue(annotation, member, requiredType);
}
return value;
}
/**
* The value as an {@link OptionalLong} for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return THe {@link OptionalLong} value
*/
default OptionalLong longValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
Optional<Long> result = getValue(annotation, member, Long.class);
return result.map(OptionalLong::of).orElseGet(OptionalLong::empty);
}
/**
* The value as an {@link OptionalLong} for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return THe {@link OptionalLong} value
*/
default OptionalLong longValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
return longValue(annotation.getName(), member);
}
/**
* The value of the given enum.
*
* @param annotation The annotation
* @param enumType The enum type
* @param <E> The enum type
* @return An {@link Optional} enum value
*/
default <E extends Enum<E>> Optional<E> enumValue(@NonNull String annotation, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
return enumValue(annotation, VALUE_MEMBER, enumType);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param member The annotation member
* @param enumType The enum type
* @param <E> The enum type
* @return An {@link Optional} class
*/
default <E extends Enum<E>> Optional<E> enumValue(@NonNull String annotation, @NonNull String member, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, enumType);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param enumType The enum type
* @param <E> The enum type
* @return An {@link Optional} class
*/
default <E extends Enum<E>> Optional<E> enumValue(@NonNull Class<? extends Annotation> annotation, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
return enumValue(annotation, VALUE_MEMBER, enumType);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param member The annotation member
* @param enumType The enum type
* @param <E> The enum type
* @return An {@link Optional} class
*/
default <E extends Enum<E>> Optional<E> enumValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return enumValue(annotation.getName(), member, enumType);
}
/**
* The enum values for the given annotation.
*
* @param annotation The annotation
* @param enumType The enum type
* @param <E> The enum type
* @return An array of enum values
*/
default <E extends Enum<E>> E[] enumValues(@NonNull String annotation, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
return enumValues(annotation, VALUE_MEMBER, enumType);
}
/**
* The enum values for the given annotation.
*
* @param annotation The annotation
* @param member The annotation member
* @param enumType The enum type
* @param <E> The enum type
* @return An array of enum values
*/
default <E extends Enum<E>> E[] enumValues(@NonNull String annotation, @NonNull String member, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return (E[]) Array.newInstance(enumType, 0);
}
/**
* The enum values for the given annotation.
*
* @param annotation The annotation
* @param member The annotation member
* @param enumType The enum type
* @param <E> The enum type
* @return An enum set of enum values
* @since 4.0.0
*/
default <E extends Enum<E>> EnumSet<E> enumValuesSet(@NonNull String annotation, @NonNull String member, Class<E> enumType) {
E[] values = enumValues(annotation, member, enumType);
return values.length == 0 ? EnumSet.noneOf(enumType) : EnumSet.copyOf(Arrays.asList(values));
}
/**
* The enum values for the given annotation.
*
* @param annotation The annotation
* @param enumType The enum type
* @param <E> The enum type
* @return An array of enum values
*/
default <E extends Enum<E>> E[] enumValues(@NonNull Class<? extends Annotation> annotation, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
return enumValues(annotation, VALUE_MEMBER, enumType);
}
/**
* The enum values for the given annotation.
*
* @param annotation The annotation
* @param member The annotation member
* @param enumType The enum type
* @param <E> The enum type
* @return An array of enum values
*/
default <E extends Enum<E>> E[] enumValues(@NonNull Class<? extends Annotation> annotation, @NonNull String member, Class<E> enumType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return enumValues(annotation.getName(), member, enumType);
}
/**
* The enum values for the given annotation.
*
* @param annotation The annotation
* @param member The annotation member
* @param enumType The enum type
* @param <E> The enum type
* @return An enum set of enum values
* @since 4.0.0
*/
default <E extends Enum<E>> EnumSet<E> enumValuesSet(@NonNull Class<? extends Annotation> annotation, @NonNull String member, Class<E> enumType) {
return enumValuesSet(annotation.getName(), member, enumType);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @return An {@link Optional} class
* @param <T> The type of the class
*/
default @NonNull <T> Class<T>[] classValues(@NonNull String annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return classValues(annotation, VALUE_MEMBER);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param member The annotation member
* @return An {@link Optional} class
* @param <T> The type of the class
*/
default @NonNull <T> Class<T>[] classValues(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, Class[].class).orElse(ReflectionUtils.EMPTY_CLASS_ARRAY);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @return An {@link Optional} class
* @param <T> The type of the class
*/
default @NonNull <T> Class<T>[] classValues(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return classValues(annotation, VALUE_MEMBER);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param member The annotation member
* @return An {@link Optional} class
* @param <T> The type of the class
*/
default @NonNull <T> Class<T>[] classValues(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return classValues(annotation.getName(), member);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @return An {@link Optional} class
*/
default Optional<Class> classValue(@NonNull String annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return classValue(annotation, VALUE_MEMBER);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param member The annotation member
* @return An {@link Optional} class
*/
default Optional<Class> classValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, Class.class);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @return An {@link Optional} class
*/
default Optional<Class> classValue(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return classValue(annotation, VALUE_MEMBER);
}
/**
* The value of the annotation as a Class.
*
* @param annotation The annotation
* @param member The annotation member
* @return An {@link Optional} class
*/
default Optional<Class> classValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return classValue(annotation.getName(), member);
}
/**
* The value as an {@link OptionalInt} for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return THe {@link OptionalInt} value
*/
default OptionalInt intValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
Optional<Integer> result = getValue(annotation, member, Integer.class);
return result.map(OptionalInt::of).orElseGet(OptionalInt::empty);
}
/**
* The value as an {@link OptionalInt} for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return THe {@link OptionalInt} value
*/
default OptionalInt intValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
return intValue(annotation.getName(), member);
}
/**
* The value as an {@link OptionalInt} for the given annotation and member.
*
* @param annotation The annotation
* @return THe {@link OptionalInt} value
*/
default OptionalInt intValue(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return intValue(annotation, VALUE_MEMBER);
}
/**
* The value as an optional string for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return The string value if it is present
*/
default Optional<String> stringValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, String.class);
}
/**
* The value as an optional string for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return The string value if it is present
*/
default Optional<String> stringValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
return stringValue(annotation.getName(), member);
}
/**
* The value as an optional string for the given annotation and member.
*
* @param annotation The annotation
* @return The string value if it is present
*/
default @NonNull Optional<String> stringValue(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return stringValue(annotation, VALUE_MEMBER);
}
/**
* The value as an optional string for the given annotation and member.
*
* @param annotation The annotation
* @return The string value if it is present
*/
default @NonNull Optional<String> stringValue(@NonNull String annotation) {
return stringValue(annotation, VALUE_MEMBER);
}
/**
* The value as an optional boolean for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return The string value if it is present
*/
default Optional<Boolean> booleanValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, Boolean.class);
}
/**
* The value as an optional boolean for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return The string value if it is present
*/
default Optional<Boolean> booleanValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
return booleanValue(annotation.getName(), member);
}
/**
* The value as an optional boolean for the given annotation and member.
*
* @param annotation The annotation
* @return The string value if it is present
*/
default @NonNull Optional<Boolean> booleanValue(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return booleanValue(annotation, VALUE_MEMBER);
}
/**
* The value as an optional boolean for the given annotation and member.
*
* @param annotation The annotation
* @return The string value if it is present
*/
default @NonNull Optional<Boolean> booleanValue(@NonNull String annotation) {
return booleanValue(annotation, VALUE_MEMBER);
}
/**
* The values as string array for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return The string values if it is present
*/
default @NonNull String[] stringValues(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
return StringUtils.EMPTY_STRING_ARRAY;
}
/**
* The values as string array for the given annotation and member.
*
* @param annotation The annotation
* @return The string values if it is present
*/
default @NonNull String[] stringValues(@NonNull Class<? extends Annotation> annotation) {
return stringValues(annotation, VALUE_MEMBER);
}
/**
* The values as string array for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return The string values if it is present
*/
default @NonNull String[] stringValues(@NonNull String annotation, @NonNull String member) {
return StringUtils.EMPTY_STRING_ARRAY;
}
/**
* The values as string array for the given annotation and member.
*
* @param annotation The annotation
* @return The string values if it is present
*/
default @NonNull String[] stringValues(@NonNull String annotation) {
return stringValues(annotation, VALUE_MEMBER);
}
/**
* The value as an {@link OptionalDouble} for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return THe {@link OptionalDouble} value
*/
default @NonNull OptionalDouble doubleValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
Optional<Double> result = getValue(annotation, member, Double.class);
return result.map(OptionalDouble::of).orElseGet(OptionalDouble::empty);
}
/**
* The value as an {@link OptionalDouble} for the given annotation and member.
*
* @param annotation The annotation
* @param member The member
* @return THe {@link OptionalDouble} value
*/
default @NonNull OptionalDouble doubleValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
return doubleValue(annotation.getName(), member);
}
/**
* The value as an {@link OptionalDouble} for the given annotation and member.
*
* @param annotation The annotation
* @return THe {@link OptionalDouble} value
*/
default @NonNull OptionalDouble doubleValue(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return doubleValue(annotation, VALUE_MEMBER);
}
/**
* Get the value of default "value" the given annotation.
*
* @param annotation The annotation class
* @param requiredType The required type
* @param <T> The value
* @return An {@link Optional} of the value
*/
default @NonNull <T> Optional<T> getValue(@NonNull String annotation, @NonNull Class<T> requiredType) {
return getValue(annotation, VALUE_MEMBER, requiredType);
}
/**
* Get the value of the given annotation member.
*
* @param annotation The annotation class
* @param member The annotation member
* @return An {@link Optional} of the value
*/
default @NonNull Optional<Object> getValue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, Object.class);
}
/**
* Get the value of the given annotation member.
*
* @param annotation The annotation class
* @param member The annotation member
* @return An {@link Optional} of the value
*/
default @NonNull Optional<Object> getValue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, Object.class);
}
/**
* Returns whether the value of the given member is <em>true</em>.
*
* @param annotation The annotation class
* @param member The annotation member
* @return True if the value is true
*/
default boolean isTrue(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation, member, Boolean.class).orElse(false);
}
/**
* Returns whether the value of the given member is <em>true</em>.
*
* @param annotation The annotation class
* @param member The annotation member
* @return True if the value is true
*/
default boolean isTrue(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return getValue(annotation.getName(), member, Boolean.class).orElse(false);
}
/**
* Returns whether the value of the given member is present.
*
* @param annotation The annotation class
* @param member The annotation member
* @return True if the value is true
*/
default boolean isPresent(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return findAnnotation(annotation).map(av -> av.contains(member)).orElse(false);
}
/**
* Returns whether the value of the given member is present.
*
* @param annotation The annotation class
* @param member The annotation member
* @return True if the value is true
*/
default boolean isPresent(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return isPresent(annotation.getName(), member);
}
/**
* Returns whether the value of the given member is <em>true</em>.
*
* @param annotation The annotation class
* @param member The annotation member
* @return True if the value is true
*/
default boolean isFalse(@NonNull Class<? extends Annotation> annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return !isTrue(annotation, member);
}
/**
* Returns whether the value of the given member is <em>true</em>.
*
* @param annotation The annotation class
* @param member The annotation member
* @return True if the value is true
*/
default boolean isFalse(@NonNull String annotation, @NonNull String member) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("member", member);
return !isTrue(annotation, member);
}
/**
* Get the value of default "value" the given annotation.
*
* @param annotation The annotation class
* @return An {@link Optional} of the value
*/
default @NonNull Optional<Object> getValue(@NonNull String annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return getValue(annotation, Object.class);
}
/**
* Get the value of default "value" the given annotation.
*
* @param annotation The annotation class
* @return An {@link Optional} of the value
*/
default @NonNull Optional<Object> getValue(@NonNull Class<? extends Annotation> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return getValue(annotation, AnnotationMetadata.VALUE_MEMBER, Object.class);
}
/**
* Get the value of default "value" the given annotation.
*
* @param annotation The annotation class
* @param requiredType requiredType
* @param <T> Generic type
* @return An {@link Optional} of the value
*/
default @NonNull <T> Optional<T> getValue(@NonNull Class<? extends Annotation> annotation, @NonNull Class<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getValue(annotation, AnnotationMetadata.VALUE_MEMBER, requiredType);
}
/**
* Get the value of default "value" the given annotation.
*
* @param annotation The annotation class
* @param requiredType requiredType
* @param <T> Generic type
* @return An {@link Optional} of the value
*/
default @NonNull <T> Optional<T> getValue(@NonNull Class<? extends Annotation> annotation, @NonNull Argument<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getValue(annotation, AnnotationMetadata.VALUE_MEMBER, requiredType);
}
/**
* Get the value of default "value" the given annotation.
*
* @param annotation The annotation class
* @param requiredType requiredType
* @param <T> Generic type
* @return An {@link Optional} of the value
*/
default @NonNull <T> Optional<T> getValue(@NonNull String annotation, @NonNull Argument<T> requiredType) {
ArgumentUtils.requireNonNull("annotation", annotation);
ArgumentUtils.requireNonNull("requiredType", requiredType);
return getValue(annotation, AnnotationMetadata.VALUE_MEMBER, requiredType);
}
/**
* Checks whether this object has the given annotation on the object itself or inherited from a parent.
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasAnnotation(@Nullable Class<? extends Annotation> annotation) {
if (annotation != null) {
return findRepeatableAnnotation(annotation)
.map(this::hasAnnotation)
.orElseGet(() -> hasAnnotation(annotation.getName()));
}
return false;
}
/**
* <p>Checks whether this object has the given annotation stereotype on the object itself or inherited from a parent</p>.
*
* <p>An annotation stereotype is a meta annotation potentially applied to another annotation</p>
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasStereotype(@Nullable Class<? extends Annotation> annotation) {
if (annotation != null) {
return findRepeatableAnnotation(annotation)
.map(this::hasStereotype)
.orElseGet(() -> hasStereotype(annotation.getName()));
}
return false;
}
/**
* Faster version of {@link #hasStereotype(Class)} that does not support repeatable
* annotations.
*
* @param annotation The annotation type
* @return Whether this metadata has the given stereotype
*/
@Internal
default boolean hasStereotypeNonRepeating(@NonNull Class<? extends Annotation> annotation) {
return hasStereotype(annotation.getName());
}
/**
* Check whether any of the given stereotypes is present.
*
* @param annotations The annotations
* @return True if any of the given stereotypes are present
*/
@SuppressWarnings("unchecked")
default boolean hasStereotype(@Nullable Class<? extends Annotation>... annotations) {
if (ArrayUtils.isEmpty(annotations)) {
return false;
}
for (Class<? extends Annotation> annotation : annotations) {
if (hasStereotype(annotation)) {
return true;
}
}
return false;
}
/**
* Check whether any of the given stereotypes is present.
*
* @param annotations The annotations
* @return True if any of the given stereotypes are present
*/
default boolean hasStereotype(@Nullable String[] annotations) {
if (ArrayUtils.isEmpty(annotations)) {
return false;
}
for (String annotation : annotations) {
if (hasStereotype(annotation)) {
return true;
}
}
return false;
}
/**
* Checks whether this object has the given annotation directly declared on the object.
*
* @param annotation The annotation
* @return True if the annotation is present
*/
default boolean hasDeclaredAnnotation(@Nullable Class<? extends Annotation> annotation) {
if (annotation != null) {
return findRepeatableAnnotation(annotation)
.map(this::hasDeclaredAnnotation)
.orElseGet(() -> hasDeclaredAnnotation(annotation.getName()));
}
return false;
}
/**
* Checks whether this object has the given stereotype directly declared on the object.
*
* @param stereotype The annotation
* @return True if the annotation is present
*/
default boolean hasDeclaredStereotype(@Nullable Class<? extends Annotation> stereotype) {
if (stereotype != null) {
return findRepeatableAnnotation(stereotype)
.map(this::hasDeclaredStereotype)
.orElseGet(() -> hasDeclaredStereotype(stereotype.getName()));
}
return false;
}
/**
* Checks whether this object has any of the given stereotype directly declared on the object.
*
* @param annotations The annotations
* @return True if any of the given stereotypes are present
*/
@SuppressWarnings("unchecked")
default boolean hasDeclaredStereotype(@Nullable Class<? extends Annotation>... annotations) {
if (ArrayUtils.isEmpty(annotations)) {
return false;
}
for (Class<? extends Annotation> annotation : annotations) {
if (hasDeclaredStereotype(annotation)) {
return true;
}
}
return false;
}
/**
* Is repeatable annotation?
* @param annotation The annotation
* @return true if repeatable
* @since 3.1
*/
default boolean isRepeatableAnnotation(@NonNull Class<? extends Annotation> annotation) {
return annotation.getAnnotation(Repeatable.class) != null;
}
/**
* Is repeatable annotation?
* @param annotation The annotation
* @return true if repeatable
* @since 3.1
*/
default boolean isRepeatableAnnotation(@NonNull String annotation) {
return false;
}
/**
* Find repeatable annotation container.
* @param annotation The annotation
* @return optional repeatable annotation container
* @since 3.1
*/
default Optional<String> findRepeatableAnnotation(@NonNull Class<? extends Annotation> annotation) {
return Optional.ofNullable(annotation.getAnnotation(Repeatable.class))
.map(repeatable -> repeatable.value().getName());
}
/**
* Find repeatable annotation container.
* @param annotation The annotation
* @return optional repeatable annotation container
* @since 3.1
*/
default Optional<String> findRepeatableAnnotation(@NonNull String annotation) {
return Optional.empty();
}
/**
* Is the annotation metadata empty.
*
* @return True if it is
*/
default boolean isEmpty() {
return this == AnnotationMetadata.EMPTY_METADATA;
}
/**
* Makes a copy of the annotation or returns this.
*
* @return the copy
* @since 4.0.0
*/
@NonNull
default AnnotationMetadata copyAnnotationMetadata() {
return this;
}
/**
* Unwraps possible a possible delegate or a provider, returns this otherwise.
*
* @return unwrapped
* @see AnnotationMetadataDelegate
* @see AnnotationMetadataProvider
* @since 4.0.0
*/
@Override
@NonNull
default AnnotationMetadata getTargetAnnotationMetadata() {
return this;
}
}