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

io.micronaut.inject.annotation.DefaultAnnotationMetadata Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * 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.inject.annotation;

import io.micronaut.context.exceptions.ConfigurationException;
import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.value.OptionalValues;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Default implementation of {@link AnnotationMetadata}.
 *
 * 

* NOTE: Although required to be public This is an internal class and should not be referenced directly in user code *

* * @author Graeme Rocher * @since 1.0 */ @Internal public class DefaultAnnotationMetadata extends AbstractAnnotationMetadata implements AnnotationMetadata, Cloneable, EnvironmentAnnotationMetadata { @Nullable Map> declaredAnnotations; @Nullable Map> allAnnotations; @Nullable Map> declaredStereotypes; @Nullable Map> allStereotypes; @Nullable Map> annotationsByStereotype; private final Map annotationValuesByType = new ConcurrentHashMap<>(2); private final boolean hasPropertyExpressions; private final boolean hasEvaluatedExpressions; /** * Constructs empty annotation metadata. */ @Internal protected DefaultAnnotationMetadata() { hasPropertyExpressions = false; hasEvaluatedExpressions = false; } /** * This constructor is designed to be used by compile time produced subclasses. * * @param declaredAnnotations The directly declared annotations * @param declaredStereotypes The directly declared stereotypes * @param allStereotypes All stereotypes * @param allAnnotations All annotations * @param annotationsByStereotype The annotations by stereotype */ @Internal @UsedByGeneratedCode public DefaultAnnotationMetadata( @Nullable Map> declaredAnnotations, @Nullable Map> declaredStereotypes, @Nullable Map> allStereotypes, @Nullable Map> allAnnotations, @Nullable Map> annotationsByStereotype) { this(declaredAnnotations, declaredStereotypes, allStereotypes, allAnnotations, annotationsByStereotype, true); } /** * This constructor is designed to be used by compile time produced subclasses. * * @param declaredAnnotations The directly declared annotations * @param declaredStereotypes The directly declared stereotypes * @param allStereotypes All stereotypes * @param allAnnotations All annotations * @param annotationsByStereotype The annotations by stereotype * @param hasPropertyExpressions Whether property expressions exist in the metadata */ @Internal @UsedByGeneratedCode public DefaultAnnotationMetadata( @Nullable Map> declaredAnnotations, @Nullable Map> declaredStereotypes, @Nullable Map> allStereotypes, @Nullable Map> allAnnotations, @Nullable Map> annotationsByStereotype, boolean hasPropertyExpressions) { this(declaredAnnotations, declaredStereotypes, allStereotypes, allAnnotations, annotationsByStereotype, hasPropertyExpressions, false); } /** * This constructor is designed to be used by compile time produced subclasses. * * @param declaredAnnotations The directly declared annotations * @param declaredStereotypes The directly declared stereotypes * @param allStereotypes All stereotypes * @param allAnnotations All annotations * @param annotationsByStereotype The annotations by stereotype * @param hasPropertyExpressions Whether property expressions exist in the metadata * @param hasEvaluatedExpressions Whether evaluated expressions exist in the metadata */ @Internal @UsedByGeneratedCode public DefaultAnnotationMetadata( @Nullable Map> declaredAnnotations, @Nullable Map> declaredStereotypes, @Nullable Map> allStereotypes, @Nullable Map> allAnnotations, @Nullable Map> annotationsByStereotype, boolean hasPropertyExpressions, boolean hasEvaluatedExpressions) { this.declaredAnnotations = declaredAnnotations; this.declaredStereotypes = declaredStereotypes; this.allStereotypes = allStereotypes; this.allAnnotations = allAnnotations; this.annotationsByStereotype = annotationsByStereotype; this.hasPropertyExpressions = hasPropertyExpressions; this.hasEvaluatedExpressions = hasEvaluatedExpressions; } @NonNull @Override public AnnotationMetadata getDeclaredMetadata() { return new DefaultAnnotationMetadata( this.declaredAnnotations, this.declaredStereotypes, null, null, annotationsByStereotype, hasPropertyExpressions ); } @Override public boolean hasPropertyExpressions() { return hasPropertyExpressions; } @Override public boolean hasEvaluatedExpressions() { return hasEvaluatedExpressions; } @NonNull @Override public Map getDefaultValues(@NonNull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); return AnnotationMetadataSupport.getDefaultValues(annotation); } @Override public boolean isPresent(@NonNull String annotation, @NonNull String member) { if (allAnnotations == null || StringUtils.isEmpty(annotation)) { return false; } Map values = allAnnotations.get(annotation); if (values != null) { return values.containsKey(member); } if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { return values.containsKey(member); } } return false; } @Override public > Optional enumValue(@NonNull String annotation, Class enumType) { return enumValue(annotation, VALUE_MEMBER, enumType, null); } @Override public > Optional enumValue(@NonNull String annotation, @NonNull String member, Class enumType) { return enumValue(annotation, member, enumType, null); } @Override public > Optional enumValue(@NonNull Class annotation, Class enumType) { return enumValue(annotation, VALUE_MEMBER, enumType); } @Override public > Optional enumValue(@NonNull Class annotation, @NonNull String member, Class enumType) { return enumValue(annotation, member, enumType, null); } /** * Retrieve the class value and optionally map its value. * * @param annotation The annotation * @param member The member * @param enumType The enum type * @param valueMapper The value mapper * @param The enum type * @return The class value */ @Override @Internal public > Optional enumValue(@NonNull Class annotation, @NonNull String member, Class enumType, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, valueMapper); if (v instanceof AnnotationValue annotationValue) { return annotationValue.enumValue(member, enumType, valueMapper); } return Optional.empty(); } else { return enumValue(annotation.getName(), member, enumType, valueMapper); } } @Override public > E[] enumValues(@NonNull String annotation, Class enumType) { return enumValues(annotation, VALUE_MEMBER, enumType, null); } @Override public > E[] enumValues(@NonNull String annotation, @NonNull String member, Class enumType) { return enumValues(annotation, member, enumType, null); } @Override public > E[] enumValues(@NonNull Class annotation, Class enumType) { return enumValues(annotation, VALUE_MEMBER, enumType, null); } @Override public > E[] enumValues(@NonNull Class annotation, @NonNull String member, Class enumType) { return enumValues(annotation, member, enumType, null); } @Override public > E[] enumValues(@NonNull Class annotation, @NonNull String member, Class enumType, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("enumType", enumType); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawValue(repeatableTypeName, member); if (v instanceof AnnotationValue annotationValue) { return annotationValue.enumValues(member, enumType); } return (E[]) Array.newInstance(enumType, 0); } else { Object v = getRawValue(annotation.getName(), member); return AnnotationValue.resolveEnumValues(enumType, v); } } @Override public > E[] enumValues(@NonNull String annotation, @NonNull String member, Class enumType, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("enumType", enumType); Object v = getRawValue(annotation, member); return AnnotationValue.resolveEnumValues(enumType, v); } /** * Retrieve the class value and optionally map its value. * * @param annotation The annotation * @param member The member * @param enumType The enum type * @param valueMapper The value mapper * @param The enum type * @return The class value */ @Override @Internal public > Optional enumValue(@NonNull String annotation, @NonNull String member, Class enumType, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); return enumValueOf(enumType, rawValue); } private > Optional enumValueOf(Class enumType, Object rawValue) { if (rawValue != null) { if (enumType.isInstance(rawValue)) { return Optional.of((E) rawValue); } else { try { return Optional.of(Enum.valueOf(enumType, rawValue.toString())); } catch (Exception e) { return Optional.empty(); } } } return Optional.empty(); } @Override public Class[] classValues(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); Object rawSingleValue = getRawValue(annotation, member); //noinspection unchecked Class[] classes = (Class[]) AnnotationValue.resolveClassValues(rawSingleValue); if (classes != null) { return classes; } //noinspection unchecked return (Class[]) ReflectionUtils.EMPTY_CLASS_ARRAY; } @Override public Class[] classValues(@NonNull Class annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, member, null); if (v instanceof AnnotationValue annotationValue) { Class[] classes = annotationValue.classValues(member); return (Class[]) classes; } //noinspection unchecked return (Class[]) ReflectionUtils.EMPTY_CLASS_ARRAY; } else { return classValues(annotation.getName(), member); } } @NonNull @Override public Optional classValue(@NonNull Class annotation, @NonNull String member) { return classValue(annotation, member, null); } /** * Retrieve the class value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The class value */ @Override public Optional classValue(@NonNull Class annotation, @NonNull String member, Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, member, valueMapper); if (v instanceof AnnotationValue annotationValue) { return (Optional) (annotationValue.classValue(member, valueMapper)); } return Optional.empty(); } else { return classValue(annotation.getName(), member, valueMapper); } } @NonNull @Override public Optional classValue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return classValue(annotation, member, null); } /** * Retrieve the class value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The class value */ @Override @Internal public Optional classValue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof AnnotationClassValue annotationClassValue) { return annotationClassValue.getType(); } else if (rawValue instanceof Class class1) { return Optional.of(class1); } else if (rawValue != null) { return ConversionService.SHARED.convert(rawValue, Class.class); } return Optional.empty(); } @NonNull @Override public OptionalInt intValue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return intValue(annotation, member, null); } @NonNull @Override public OptionalInt intValue(@NonNull Class annotation, @NonNull String member) { return intValue(annotation, member, null); } /** * Retrieve the int value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The int value */ @Override @Internal public OptionalInt intValue(@NonNull Class annotation, @NonNull String member, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, valueMapper); if (v instanceof AnnotationValue annotationValue) { return annotationValue.intValue(member, valueMapper); } return OptionalInt.empty(); } else { return intValue(annotation.getName(), member, valueMapper); } } @Override public Optional booleanValue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return booleanValue(annotation, member, null); } @Override public Optional booleanValue(@NonNull Class annotation, @NonNull String member) { return booleanValue(annotation, member, null); } /** * Retrieve the boolean value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The boolean value */ @Override public Optional booleanValue(@NonNull Class annotation, @NonNull String member, Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, null); if (v instanceof AnnotationValue annotationValue) { return annotationValue.booleanValue(member, valueMapper); } return Optional.empty(); } else { return booleanValue(annotation.getName(), member, valueMapper); } } /** * Retrieve the boolean value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The boolean value */ @Override @NonNull public Optional booleanValue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof Boolean aBoolean) { return Optional.of(aBoolean); } else if (rawValue != null) { return Optional.of(StringUtils.isTrue(rawValue.toString())); } return Optional.empty(); } @NonNull @Override public OptionalLong longValue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return longValue(annotation, member, null); } @NonNull @Override public OptionalLong longValue(@NonNull Class annotation, @NonNull String member) { return longValue(annotation, member, null); } /** * Retrieve the long value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The long value */ @Override @Internal public OptionalLong longValue(@NonNull Class annotation, @NonNull String member, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, valueMapper); if (v instanceof AnnotationValue annotationValue) { return annotationValue.longValue(member, valueMapper); } return OptionalLong.empty(); } else { return longValue(annotation.getName(), member, valueMapper); } } /** * Retrieve the long value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The long value */ @Override @NonNull public OptionalLong longValue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof Number number) { return OptionalLong.of(number.longValue()); } else if (rawValue instanceof CharSequence) { final String str = rawValue.toString(); if (StringUtils.isNotEmpty(str)) { try { final long i = Long.parseLong(str); return OptionalLong.of(i); } catch (NumberFormatException e) { throw new ConfigurationException("Invalid value [" + str + "] of [" + member + "] of annotation [" + annotation + "]: " + e.getMessage(), e); } } } return OptionalLong.empty(); } /** * Retrieve the int value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The int value */ @Override @NonNull public OptionalInt intValue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof Number number) { return OptionalInt.of(number.intValue()); } else if (rawValue instanceof CharSequence) { final String str = rawValue.toString(); if (StringUtils.isNotEmpty(str)) { try { final int i = Integer.parseInt(str); return OptionalInt.of(i); } catch (NumberFormatException e) { throw new ConfigurationException("Invalid value [" + str + "] of [" + member + "] of annotation [" + annotation + "]: " + e.getMessage(), e); } } } return OptionalInt.empty(); } @NonNull @Override public Optional stringValue(@NonNull Class annotation, @NonNull String member) { return stringValue(annotation, member, null); } /** * Retrieve the string value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The int value */ @Override public Optional stringValue(@NonNull Class annotation, @NonNull String member, Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, valueMapper); if (v instanceof AnnotationValue annotationValue) { return annotationValue.stringValue(member, valueMapper); } return Optional.empty(); } else { return stringValue(annotation.getName(), member, valueMapper); } } @NonNull @Override public String[] stringValues(@NonNull Class annotation, @NonNull String member) { return stringValues(annotation.getName(), member, null); } @NonNull @Override public String[] stringValues(@NonNull String annotation, @NonNull String member) { return stringValues(annotation, member, null); } /** * Retrieve the string value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The int value */ @Override @NonNull public String[] stringValues(@NonNull Class annotation, @NonNull String member, Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawValue(repeatableTypeName, member); if (v instanceof AnnotationValue annotationValue) { return annotationValue.stringValues(member, valueMapper); } return StringUtils.EMPTY_STRING_ARRAY; } else { Object v = getRawValue(annotation.getName(), member); String[] strings = AnnotationValue.resolveStringValues(v, valueMapper); return strings != null ? strings : StringUtils.EMPTY_STRING_ARRAY; } } /** * Retrieve the string value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The int value */ @Override @NonNull public String[] stringValues(@NonNull String annotation, @NonNull String member, Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); Object v = getRawValue(annotation, member); String[] strings = AnnotationValue.resolveStringValues(v, valueMapper); return strings != null ? strings : StringUtils.EMPTY_STRING_ARRAY; } @NonNull @Override public Optional stringValue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return stringValue(annotation, member, null); } /** * Retrieve the string value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The string value */ @Override @NonNull public Optional stringValue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof String s) { // Performance optimization to check for the actual class first to avoid the type-check polution return Optional.of(s); } else if (rawValue instanceof CharSequence) { return Optional.of(rawValue.toString()); } else if (rawValue instanceof Class aClass) { String name = aClass.getName(); return Optional.of(name); } else if (rawValue != null) { return Optional.of(rawValue.toString()); } return Optional.empty(); } @Override public boolean isTrue(@NonNull Class annotation, @NonNull String member) { return isTrue(annotation, member, null); } /** * Retrieve the boolean value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The boolean value */ @Override public boolean isTrue(@NonNull Class annotation, @NonNull String member, Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, valueMapper); if (v instanceof AnnotationValue annotationValue) { return annotationValue.isTrue(member, valueMapper); } return false; } else { return isTrue(annotation.getName(), member, valueMapper); } } @Override public boolean isTrue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return isTrue(annotation, member, null); } /** * Retrieve the boolean value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The boolean value */ @Override public boolean isTrue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof Boolean aBoolean) { return aBoolean; } else if (rawValue != null) { String booleanString = rawValue.toString().toLowerCase(Locale.ENGLISH); return StringUtils.isTrue(booleanString); } return false; } @Override public boolean isFalse(@NonNull String annotation, @NonNull String member) { return !isTrue(annotation, member); } @NonNull @Override public OptionalDouble doubleValue(@NonNull String annotation, @NonNull String member) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); return doubleValue(annotation, member, null); } @NonNull @Override public OptionalDouble doubleValue(@NonNull Class annotation, @NonNull String member) { return doubleValue(annotation, member, null); } /** * Retrieve the double value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The double value */ @Override @Internal public OptionalDouble doubleValue(@NonNull Class annotation, @NonNull String member, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { Object v = getRawSingleValue(repeatableTypeName, VALUE_MEMBER, valueMapper); if (v instanceof AnnotationValue annotationValue) { return annotationValue.doubleValue(member, valueMapper); } return OptionalDouble.empty(); } else { return doubleValue(annotation.getName(), member); } } /** * Retrieve the double value and optionally map its value. * * @param annotation The annotation * @param member The member * @param valueMapper The value mapper * @return The double value */ @Override @NonNull @Internal public OptionalDouble doubleValue(@NonNull String annotation, @NonNull String member, Function valueMapper) { Object rawValue = getRawSingleValue(annotation, member, valueMapper); if (rawValue instanceof Number number) { return OptionalDouble.of(number.doubleValue()); } else if (rawValue instanceof CharSequence) { final String str = rawValue.toString(); if (StringUtils.isNotEmpty(str)) { try { final double i = Double.parseDouble(str); return OptionalDouble.of(i); } catch (NumberFormatException e) { throw new ConfigurationException("Invalid value [" + str + "] of member [" + member + "] of annotation [" + annotation + "]: " + e.getMessage(), e); } } } return OptionalDouble.empty(); } @Override public @NonNull Optional getValue(@NonNull Class annotation, @NonNull String member, @NonNull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation.getName()); if (repeatableTypeName != null) { List> values = getAnnotationValuesByType(annotation); if (!values.isEmpty()) { return values.iterator().next().get(member, requiredType); } else { return Optional.empty(); } } else { return getValue(annotation.getName(), member, requiredType); } } @Override public @NonNull Optional getValue(@NonNull String annotation, @NonNull String member, @NonNull Argument requiredType) { return getValue(annotation, member, requiredType, null); } /** * Resolves the given value performing type conversion as necessary. * * @param annotation The annotation * @param member The member * @param requiredType The required type * @param valueMapper The value mapper * @param The generic type * @return The resolved value */ @Override @NonNull public Optional getValue(@NonNull String annotation, @NonNull String member, @NonNull Argument requiredType, @Nullable Function valueMapper) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); Optional resolved = Optional.empty(); if (allAnnotations != null && StringUtils.isNotEmpty(annotation)) { Map values = allAnnotations.get(annotation); if (values != null) { Object rawValue = values.get(member); if (rawValue != null) { if (valueMapper != null) { rawValue = valueMapper.apply(rawValue); } resolved = ConversionService.SHARED.convert(rawValue, requiredType); } } else if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { Object rawValue = values.get(member); if (rawValue != null) { if (valueMapper != null) { rawValue = valueMapper.apply(rawValue); } resolved = ConversionService.SHARED.convert(rawValue, requiredType); } } } } if (resolved.isEmpty() && hasStereotype(annotation)) { return getDefaultValue(annotation, member, requiredType); } return resolved; } @Override public @NonNull Optional getDefaultValue(@NonNull String annotation, @NonNull String member, @NonNull Class requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); Map defaultValues = getDefaultValues(annotation); final Object v = defaultValues.get(member); if (v == null) { return Optional.empty(); } if (requiredType.isInstance(v)) { return (Optional) Optional.of(v); } return ConversionService.SHARED.convert(v, requiredType); } @SuppressWarnings("unchecked") @Override public @NonNull List> getAnnotationValuesByType(@Nullable Class annotationType) { if (annotationType == null) { return List.of(); } final String annotationTypeName = annotationType.getName(); List> results = annotationValuesByType.get(annotationTypeName); if (results == null) { results = resolveAnnotationValuesByType(annotationType, allAnnotations, allStereotypes); if (results != null) { return results; } else if (allAnnotations != null) { final Map values = allAnnotations.get(annotationTypeName); if (values != null) { results = List.of(newAnnotationValue(annotationTypeName, values)); } } if (results == null) { results = List.of(); } annotationValuesByType.put(annotationTypeName, results); } return results; } @Override public List> getAnnotationValuesByName(String annotationType) { if (annotationType == null) { return List.of(); } String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotationType); if (repeatableTypeName == null) { return List.of(); } List> results = resolveRepeatableAnnotations(repeatableTypeName, allAnnotations, allStereotypes); if (results != null) { return results; } if (allAnnotations != null) { final Map values = allAnnotations.get(annotationType); if (values != null) { results = List.of(newAnnotationValue(annotationType, values)); } } if (results == null) { results = List.of(); } annotationValuesByType.put(annotationType, results); return List.of(); } @NonNull protected AnnotationValue newAnnotationValue(String annotationType, Map values) { return new AnnotationValue<>(annotationType, values, AnnotationMetadataSupport.getDefaultValuesOrNull(annotationType)); } @NonNull @Override public List> getDeclaredAnnotationValuesByType(@NonNull Class annotationType) { if (annotationType == null) { return List.of(); } List> results = resolveAnnotationValuesByType(annotationType, declaredAnnotations, declaredStereotypes); if (results != null) { return results; } return List.of(); } @Override public List> getDeclaredAnnotationValuesByName(String annotationType) { if (annotationType == null) { return List.of(); } String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotationType); List> results = resolveRepeatableAnnotations(repeatableTypeName, declaredAnnotations, declaredStereotypes); if (results != null) { return results; } return List.of(); } @SuppressWarnings("unchecked") @Override public T[] synthesizeAnnotationsByType(@NonNull Class annotationClass) { if (annotationClass == null) { return (T[]) AnnotationUtil.ZERO_ANNOTATIONS; } return getAnnotationValuesByType(annotationClass).stream() .map(entries -> AnnotationMetadataSupport.buildAnnotation(annotationClass, entries)) .toArray(value -> (T[]) Array.newInstance(annotationClass, value)); } @Override public T[] synthesizeDeclaredAnnotationsByType(@NonNull Class annotationClass) { if (annotationClass == null) { return (T[]) AnnotationUtil.ZERO_ANNOTATIONS; } return getAnnotationValuesByType(annotationClass).stream() .map(entries -> AnnotationMetadataSupport.buildAnnotation(annotationClass, entries)) .toArray(value -> (T[]) Array.newInstance(annotationClass, value)); } @Override public boolean isEmpty() { return allAnnotations == null || allAnnotations.isEmpty(); } @Override public boolean hasDeclaredAnnotation(String annotation) { return declaredAnnotations != null && StringUtils.isNotEmpty(annotation) && declaredAnnotations.containsKey(annotation); } @Override public boolean hasAnnotation(String annotation) { return hasDeclaredAnnotation(annotation) || (allAnnotations != null && StringUtils.isNotEmpty(annotation) && allAnnotations.containsKey(annotation)); } @Override public boolean hasStereotype(String annotation) { return hasAnnotation(annotation) || (allStereotypes != null && StringUtils.isNotEmpty(annotation) && allStereotypes.containsKey(annotation)); } @Override public boolean hasDeclaredStereotype(String annotation) { return hasDeclaredAnnotation(annotation) || (declaredStereotypes != null && StringUtils.isNotEmpty(annotation) && declaredStereotypes.containsKey(annotation)); } @NonNull @Override public Optional> getAnnotationTypeByStereotype(@Nullable String stereotype) { if (stereotype == null) { return Optional.empty(); } if (annotationsByStereotype != null) { List annotations = annotationsByStereotype.get(stereotype); if (CollectionUtils.isNotEmpty(annotations)) { return getAnnotationType(annotations.get(0)); } } if (allAnnotations != null && allAnnotations.containsKey(stereotype)) { return getAnnotationType(stereotype); } if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) { return getAnnotationType(stereotype); } return Optional.empty(); } @NonNull @Override public Optional getAnnotationNameByStereotype(@Nullable String stereotype) { if (stereotype == null) { return Optional.empty(); } if (annotationsByStereotype != null) { List annotations = annotationsByStereotype.get(stereotype); if (CollectionUtils.isNotEmpty(annotations)) { return Optional.of(annotations.get(0)); } } if (allAnnotations != null && allAnnotations.containsKey(stereotype)) { return Optional.of(stereotype); } if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) { return Optional.of(stereotype); } return Optional.empty(); } @NonNull @Override public List getAnnotationNamesByStereotype(@Nullable String stereotype) { if (stereotype == null) { return List.of(); } if (annotationsByStereotype != null) { List annotations = annotationsByStereotype.get(stereotype); if (annotations != null) { return Collections.unmodifiableList(annotations); } } if (allAnnotations != null && allAnnotations.containsKey(stereotype)) { return List.of(stereotype); } if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) { return List.of(stereotype); } return List.of(); } @Override public List> getAnnotationValuesByStereotype(String stereotype) { if (stereotype == null) { return List.of(); } if (annotationsByStereotype != null) { List annotations = annotationsByStereotype.get(stereotype); if (annotations != null) { List> result = new ArrayList<>(annotations.size()); for (String annotation : annotations) { String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotation); if (repeatableTypeName != null) { List> results = resolveRepeatableAnnotations(repeatableTypeName, allAnnotations, allStereotypes ); if (results != null) { result.addAll(results); } } else { result.add(getAnnotation(annotation)); } } return Collections.unmodifiableList(result); } } if (allAnnotations != null) { return getAnnotationValuesByName(stereotype); } if (declaredAnnotations != null) { return getDeclaredAnnotationValuesByName(stereotype); } return List.of(); } @NonNull @Override public Set getAnnotationNames() { if (allAnnotations != null) { return Collections.unmodifiableSet(allAnnotations.keySet()); } return Set.of(); } @NonNull @Override public Set getStereotypeAnnotationNames() { if (allStereotypes != null) { return Collections.unmodifiableSet(allStereotypes.keySet()); } return Set.of(); } @NonNull @Override public Set getDeclaredStereotypeAnnotationNames() { if (declaredStereotypes != null) { return Collections.unmodifiableSet(declaredStereotypes.keySet()); } return Set.of(); } @NonNull @Override public Set getDeclaredAnnotationNames() { if (declaredAnnotations != null) { return Collections.unmodifiableSet(declaredAnnotations.keySet()); } return Set.of(); } @NonNull @Override public List getDeclaredAnnotationNamesByStereotype(@Nullable String stereotype) { if (stereotype == null) { return List.of(); } if (annotationsByStereotype != null) { List annotations = annotationsByStereotype.get(stereotype); if (annotations != null) { annotations = new ArrayList<>(annotations); if (declaredAnnotations != null) { annotations.removeIf(s -> !declaredAnnotations.containsKey(s)); return Collections.unmodifiableList(annotations); } else { // no declared return List.of(); } } } if (declaredAnnotations != null && declaredAnnotations.containsKey(stereotype)) { return List.of(stereotype); } return List.of(); } @NonNull @Override public Optional> getAnnotationType(@NonNull String name) { return AnnotationMetadataSupport.getAnnotationType(name); } @NonNull @Override public Optional> getAnnotationType(@NonNull String name, @NonNull ClassLoader classLoader) { return AnnotationMetadataSupport.getAnnotationType(name, classLoader); } @SuppressWarnings("Duplicates") @NonNull @Override public Optional> findAnnotation(@NonNull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); if (allAnnotations == null || StringUtils.isEmpty(annotation)) { return Optional.empty(); } Map values = allAnnotations.get(annotation); if (values != null) { return Optional.of(newAnnotationValue(annotation, values)); } if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { return Optional.of(newAnnotationValue(annotation, values)); } } return Optional.empty(); } @SuppressWarnings("Duplicates") @NonNull @Override public Optional> findDeclaredAnnotation(@NonNull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); if (declaredAnnotations == null || StringUtils.isEmpty(annotation)) { return Optional.empty(); } Map values = declaredAnnotations.get(annotation); if (values != null) { return Optional.of(newAnnotationValue(annotation, values)); } if (declaredStereotypes != null) { values = declaredStereotypes.get(annotation); if (values != null) { return Optional.of(newAnnotationValue(annotation, values)); } } return Optional.empty(); } @Override public @NonNull OptionalValues getValues(@NonNull String annotation, @NonNull Class valueType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("valueType", valueType); if (allAnnotations == null || StringUtils.isEmpty(annotation)) { return OptionalValues.empty(); } Map values = allAnnotations.get(annotation); if (values != null) { return OptionalValues.of(valueType, values); } if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { return OptionalValues.of(valueType, values); } } return OptionalValues.empty(); } @NonNull @Override public Map getValues(@NonNull String annotation) { ArgumentUtils.requireNonNull("annotation", annotation); if (allAnnotations == null || StringUtils.isEmpty(annotation)) { return Collections.emptyMap(); } Map values = allAnnotations.get(annotation); if (values != null) { return Collections.unmodifiableMap(values); } if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { return Collections.unmodifiableMap(values); } } return Collections.emptyMap(); } @NonNull @Override public Optional getDefaultValue(@NonNull String annotation, @NonNull String member, @NonNull Argument requiredType) { ArgumentUtils.requireNonNull("annotation", annotation); ArgumentUtils.requireNonNull("member", member); ArgumentUtils.requireNonNull("requiredType", requiredType); // Note this method should never reference the "annotationDefaultValues" field, which is used only at compile time Map defaultValues = getDefaultValues(annotation); Object value = defaultValues.get(member); if (value != null) { return ConversionService.SHARED.convert(value, requiredType); } return Optional.empty(); } @Override public boolean isRepeatableAnnotation(Class annotation) { return isRepeatableAnnotation(annotation.getName()); } @Override public boolean isRepeatableAnnotation(String annotation) { return findRepeatableAnnotationContainerInternal(annotation) != null; } @Override public Optional findRepeatableAnnotation(Class annotation) { return findRepeatableAnnotation(annotation.getName()); } @Override public Optional findRepeatableAnnotation(String annotation) { return Optional.ofNullable(findRepeatableAnnotationContainerInternal(annotation)); } @Override public AnnotationMetadata copyAnnotationMetadata() { return clone(); } @Override public DefaultAnnotationMetadata clone() { DefaultAnnotationMetadata cloned = new DefaultAnnotationMetadata( declaredAnnotations != null ? cloneMapOfMapValue(declaredAnnotations) : null, declaredStereotypes != null ? cloneMapOfMapValue(declaredStereotypes) : null, allStereotypes != null ? cloneMapOfMapValue(allStereotypes) : null, allAnnotations != null ? cloneMapOfMapValue(allAnnotations) : null, annotationsByStereotype != null ? cloneMapOfListValue(annotationsByStereotype) : null, hasPropertyExpressions ); return cloned; } protected final Map> cloneMapOfMapValue(Map> toClone) { return toClone.entrySet().stream() .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), cloneMap(e.getValue()))) .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (a, b) -> a, LinkedHashMap::new)); } protected final Map> cloneMapOfListValue(Map> toClone) { return toClone.entrySet().stream() .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), new ArrayList<>(e.getValue()))) .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (a, b) -> a, LinkedHashMap::new)); } protected final Map cloneMap(Map map) { Map newMap; if (map instanceof LinkedHashMap linkedHashMap) { newMap = (Map) linkedHashMap.clone(); } else { newMap = new LinkedHashMap<>(map); } for (Map.Entry entry : newMap.entrySet()) { if (entry.getValue() instanceof Set) { LinkedHashSet newValue = new LinkedHashSet<>((Collection) entry.getValue()); entry.setValue((V) newValue); } } return new HashMap<>(newMap); } /** * Registers annotation default values. Used by generated byte code. DO NOT REMOVE. * * @param annotation The annotation name * @param defaultValues The default values */ @SuppressWarnings("unused") @Internal @UsedByGeneratedCode public static void registerAnnotationDefaults(String annotation, Map defaultValues) { AnnotationMetadataSupport.registerDefaultValues(annotation, defaultValues); } /** * Registers annotation default values. Used by generated byte code. DO NOT REMOVE. * * @param annotation The annotation name * @param defaultValues The default values */ @SuppressWarnings("unused") @Internal @UsedByGeneratedCode public static void registerAnnotationDefaults(AnnotationClassValue annotation, Map defaultValues) { AnnotationMetadataSupport.registerDefaultValues(annotation, defaultValues); } /** * Registers annotation default values. Used by generated byte code. DO NOT REMOVE. * * @param annotation The annotation */ @SuppressWarnings("unused") @Internal @UsedByGeneratedCode public static void registerAnnotationType(AnnotationClassValue annotation) { AnnotationMetadataSupport.registerAnnotationType(annotation); } /** * Registers repeatable annotations. Annotation container -> annotations item. Used by generated byte code. DO NOT REMOVE. * * @param repeatableAnnotations The annotation */ @SuppressWarnings("unused") @Internal @UsedByGeneratedCode public static void registerRepeatableAnnotations(Map repeatableAnnotations) { AnnotationMetadataSupport.registerRepeatableAnnotations(repeatableAnnotations); } /** * Dump the values. */ @SuppressWarnings("unused") @Internal void dump() { System.out.println("declaredAnnotations = " + declaredAnnotations); System.out.println("declaredStereotypes = " + declaredStereotypes); System.out.println("allAnnotations = " + allAnnotations); System.out.println("allStereotypes = " + allStereotypes); System.out.println("annotationsByStereotype = " + annotationsByStereotype); } private List> resolveAnnotationValuesByType(Class annotationType, Map> sourceAnnotations, Map> sourceStereotypes) { String repeatableTypeName = findRepeatableAnnotationContainerInternal(annotationType.getName()); if (repeatableTypeName != null) { return resolveRepeatableAnnotations(repeatableTypeName, sourceStereotypes, sourceAnnotations ); } return null; } @Nullable private List> resolveRepeatableAnnotations(String repeatableTypeName, Map> sourceStereotypes, Map> sourceAnnotations) { if (!hasStereotype(repeatableTypeName)) { return null; } List> results = null; if (sourceAnnotations != null) { Map values = sourceAnnotations.get(repeatableTypeName); results = collectResult(results, values); } if (sourceStereotypes != null) { Map values = sourceStereotypes.get(repeatableTypeName); results = collectResult(results, values); } return results == null ? List.of() : results; } @Nullable private Object getRawSingleValue(@NonNull String annotation, @NonNull String member, @Nullable Function valueMapper) { Object rawValue = getRawValue(annotation, member); if (rawValue != null) { if (rawValue.getClass().isArray()) { int len = Array.getLength(rawValue); if (len > 0) { rawValue = Array.get(rawValue, 0); } } else if (rawValue instanceof Iterable iterable) { Iterator i = iterable.iterator(); if (i.hasNext()) { rawValue = i.next(); } } } if (valueMapper != null && rawValue instanceof CharSequence) { return valueMapper.apply(rawValue); } else { return rawValue; } } @Nullable private Object getRawValue(@NonNull String annotation, @NonNull String member) { if (allAnnotations == null || StringUtils.isEmpty(annotation)) { return null; } Map values = allAnnotations.get(annotation); if (values != null) { return values.get(member); } if (allStereotypes != null) { values = allStereotypes.get(annotation); if (values != null) { return values.get(member); } } return null; } /** * Find annotation's repeatable container. * @param annotation The annotation * @return the repeatable container or null */ @Nullable protected String findRepeatableAnnotationContainerInternal(@NonNull String annotation) { return AnnotationMetadataSupport.getRepeatableAnnotation(annotation); } private List> collectResult(List> results, Map values) { if (values != null) { Object v = values.get(AnnotationMetadata.VALUE_MEMBER); if (v instanceof AnnotationValue[] avs) { List> result = (List) Arrays.asList(avs); if (results == null) { return result; } else { return CollectionUtils.concat(results, result); } } else if (v instanceof Collection c) { List> result = new ArrayList<>(c.size()); for (Object o : c) { if (o instanceof io.micronaut.core.annotation.AnnotationValue av) { result.add((AnnotationValue) av); } } if (results == null) { return result; } else { return CollectionUtils.concat(results, result); } } } return results; } }