AnnotationValue.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.convert.ArgumentConversionContext;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.value.ConvertibleValues;
import io.micronaut.core.expressions.EvaluatedExpression;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
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;
import java.util.function.Function;
import java.util.stream.Collectors;
import static io.micronaut.core.reflect.ReflectionUtils.EMPTY_CLASS_ARRAY;
/**
* A runtime representation of the annotation and its values.
*
* <p>This class implements the {@link AnnotationValueResolver} interface and methods such as {@link AnnotationValueResolver#get(CharSequence, Class)} can be used to retrieve the values of annotation members.</p>
*
* <p>If a member is not present then the methods of the class will attempt to resolve the default value for a given annotation member. In this sense the behaviour of this class is similar to how
* a implementation of {@link Annotation} behaves.</p>
*
* NOTE: During the mapping or remapping, nullable stereotypes value means that
* the stereotypes will be filled from the annotation definition, when empty collection will skip it.
*
* @param <A> The annotation type
* @author Graeme Rocher
* @since 1.0
*/
public class AnnotationValue<A extends Annotation> implements AnnotationValueResolver {
private final String annotationName;
private final ConvertibleValues<Object> convertibleValues;
private final Map<CharSequence, Object> values;
@Nullable
private Map<CharSequence, Object> defaultValues;
@Nullable
private final Function<Object, Object> valueMapper;
private final RetentionPolicy retentionPolicy;
@Nullable
private final List<AnnotationValue<?>> stereotypes;
@Nullable
private final AnnotationDefaultValuesProvider defaultValuesProvider;
/**
* @param annotationName The annotation name
* @param values The values
*/
@UsedByGeneratedCode
@Internal
public AnnotationValue(String annotationName, Map<CharSequence, Object> values) {
this(annotationName, values, null, RetentionPolicy.RUNTIME);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param retentionPolicy The retention policy
*/
@Internal
public AnnotationValue(String annotationName, Map<CharSequence, Object> values, RetentionPolicy retentionPolicy) {
this(annotationName, values, null, retentionPolicy, null);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param retentionPolicy The retention policy
* @param stereotypes The stereotypes of the annotation
*/
@Internal
public AnnotationValue(String annotationName, Map<CharSequence, Object> values, RetentionPolicy retentionPolicy, List<AnnotationValue<?>> stereotypes) {
this(annotationName, values, null, retentionPolicy, stereotypes);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param defaultValues The default values
*/
@UsedByGeneratedCode
@Internal
public AnnotationValue(String annotationName, Map<CharSequence, Object> values, Map<CharSequence, Object> defaultValues) {
this(annotationName, values, defaultValues, RetentionPolicy.RUNTIME, null);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param defaultValuesProvider The default values provider
* @since 4.2.0
*/
@UsedByGeneratedCode
@Internal
public AnnotationValue(String annotationName, Map<CharSequence, Object> values, AnnotationDefaultValuesProvider defaultValuesProvider) {
this(annotationName, values, null, RetentionPolicy.RUNTIME, null, defaultValuesProvider);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param defaultValues The default values
* @param retentionPolicy The retention policy
*/
@UsedByGeneratedCode
@Internal
public AnnotationValue(String annotationName, Map<CharSequence, Object> values, Map<CharSequence, Object> defaultValues, RetentionPolicy retentionPolicy) {
this(annotationName, values, defaultValues, retentionPolicy, null);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param defaultValues The default values
* @param retentionPolicy The retention policy
* @param stereotypes The stereotypes of the annotation
*/
@Internal
public AnnotationValue(String annotationName,
Map<CharSequence, Object> values,
Map<CharSequence, Object> defaultValues,
RetentionPolicy retentionPolicy,
List<AnnotationValue<?>> stereotypes) {
this(annotationName, values, defaultValues, retentionPolicy, stereotypes, null);
}
/**
* @param annotationName The annotation name
* @param values The values
* @param defaultValues The default values
* @param retentionPolicy The retention policy
* @param stereotypes The stereotypes of the annotation
* @param defaultValuesProvider The defaults values provider
*/
@Internal
public AnnotationValue(String annotationName,
Map<CharSequence, Object> values,
Map<CharSequence, Object> defaultValues,
RetentionPolicy retentionPolicy,
List<AnnotationValue<?>> stereotypes,
AnnotationDefaultValuesProvider defaultValuesProvider) {
this.annotationName = annotationName;
this.convertibleValues = newConvertibleValues(values);
this.values = values;
this.defaultValues = defaultValues;
this.valueMapper = null;
this.retentionPolicy = retentionPolicy != null ? retentionPolicy : RetentionPolicy.RUNTIME;
this.stereotypes = stereotypes;
this.defaultValuesProvider = defaultValuesProvider;
}
/**
* @param annotationName The annotation name
*/
@UsedByGeneratedCode
@Internal
public AnnotationValue(String annotationName) {
this(annotationName, Collections.emptyMap(), Collections.emptyMap());
}
/**
* @param annotationName The annotation name
* @param convertibleValues The convertible values
*/
@Internal
public AnnotationValue(String annotationName, ConvertibleValues<Object> convertibleValues) {
this.annotationName = annotationName;
this.convertibleValues = convertibleValues;
this.values = new LinkedHashMap<>(convertibleValues.asMap());
this.defaultValues = null;
this.valueMapper = null;
this.retentionPolicy = RetentionPolicy.RUNTIME;
this.stereotypes = null;
this.defaultValuesProvider = null;
}
/**
* @param target The target
* @param defaultValues The default values
* @param convertibleValues The convertible values
* @param valueMapper The value mapper
*/
@Internal
@UsedByGeneratedCode
public AnnotationValue(AnnotationValue<A> target,
Map<CharSequence, Object> defaultValues,
ConvertibleValues<Object> convertibleValues,
Function<Object, Object> valueMapper) {
this.annotationName = target.annotationName;
this.defaultValues = defaultValues;
this.values = target.values;
this.convertibleValues = convertibleValues;
this.valueMapper = valueMapper;
this.retentionPolicy = RetentionPolicy.RUNTIME;
this.stereotypes = target.stereotypes;
this.defaultValuesProvider = null;
}
/**
* @return The value mapper.
* @since 4.0.2
*/
protected @Nullable Function<Object, Object> getValueMapper() {
return valueMapper;
}
/**
* Creates a builder with the initial value of this annotation.
*
* @return The builder with this annotation value
* @since 4.0.0
*/
public AnnotationValueBuilder<A> mutate() {
return builder(this);
}
/**
* @return The retention policy.
*/
@NonNull
public final RetentionPolicy getRetentionPolicy() {
return retentionPolicy;
}
/**
* @return The stereotypes of the annotation
*/
@Nullable
public List<AnnotationValue<?>> getStereotypes() {
return stereotypes;
}
/**
* The default values.
* @return The default of the annotation or null if not specified.
* @since 4.0.0
*/
@Nullable
public Map<CharSequence, Object> getDefaultValues() {
if (defaultValues == null && defaultValuesProvider != null) {
defaultValues = defaultValuesProvider.provide(annotationName);
}
return defaultValues;
}
/**
* Resolves a map of properties for a member that is an array of annotations that have members called "name" or "key" to represent the key and "value" to represent the value.
*
* <p>For example consider the following annotation definition:</p>
*
* <pre class="code">
* @PropertySource({ @Property(name = "one", value = "1"), @Property(name = "two", value = "2")})
* public class MyBean {
* ...
* }</pre>
*
* <p>You can use this method to resolve the values of the {@code PropertySource} annotation such that the following assertion is true:</p>
*
* <pre class="code">
* annotationValue.getProperties("value") == [one:1, two:2]
* </pre>
*
* @param member The member
* @return The properties as an immutable map.
*/
@NonNull
public Map<String, String> getProperties(@NonNull String member) {
return getProperties(member, "name");
}
/**
* Resolve properties with a custom key member.
*
* @param member The member to resolve the properties from
* @param keyMember The member of the sub annotation that represents the key.
* @return The properties.
* @see #getProperties(String)
*/
public Map<String, String> getProperties(@NonNull String member, String keyMember) {
ArgumentUtils.requireNonNull("keyMember", keyMember);
if (StringUtils.isEmpty(member)) {
return Collections.emptyMap();
}
List<AnnotationValue<Annotation>> values = getAnnotations(member);
if (CollectionUtils.isEmpty(values)) {
return Collections.emptyMap();
}
Map<String, String> props = CollectionUtils.newLinkedHashMap(values.size());
for (AnnotationValue<Annotation> av : values) {
String name = av.stringValue(keyMember).orElse(null);
if (StringUtils.isNotEmpty(name)) {
av.stringValue(AnnotationMetadata.VALUE_MEMBER, valueMapper).ifPresent(v -> props.put(name, v));
}
}
return Collections.unmodifiableMap(props);
}
/**
* Return the enum value of the given member of the given enum type.
*
* @param member The annotation member
* @param enumType The required type
* @param <E> The enum type
* @return An {@link Optional} of the enum value
*/
@Override
public <E extends Enum> Optional<E> enumValue(@NonNull String member, @NonNull Class<E> enumType) {
return enumValue(member, enumType, valueMapper);
}
/**
* Return the enum value of the given member of the given enum type.
*
* @param member The annotation member
* @param enumType The required type
* @param valueMapper The value mapper
* @param <E> The enum type
* @return An {@link Optional} of the enum value
*/
public <E extends Enum> Optional<E> enumValue(@NonNull String member, @NonNull Class<E> enumType, Function<Object, Object> valueMapper) {
ArgumentUtils.requireNonNull("enumType", enumType);
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o != null) {
return convertToEnum(enumType, o);
}
return Optional.empty();
}
/**
* Return the enum values of the given member of the given enum type.
*
* @param member The annotation member
* @param enumType The required type
* @param <E> The enum type
* @return An array of enum values
*/
@Override
@SuppressWarnings("unchecked")
public <E extends Enum> E[] enumValues(@NonNull String member, @NonNull Class<E> enumType) {
ArgumentUtils.requireNonNull("enumType", enumType);
if (StringUtils.isEmpty(member)) {
return (E[]) Array.newInstance(enumType, 0);
}
Object rawValue = values.get(member);
return resolveEnumValues(enumType, rawValue);
}
/**
* The value of the annotation as a Class.
*
* @return An {@link Optional} class
*/
@Override
@NonNull
public Optional<Class<?>> classValue() {
return classValue(AnnotationMetadata.VALUE_MEMBER);
}
/**
* The value of the given annotation member as a Class.
*
* @param member The annotation member
* @return An {@link Optional} class
*/
@Override
public Optional<Class<?>> classValue(@NonNull String member) {
return classValue(member, valueMapper);
}
/**
* The value of the given annotation member as a Class.
*
* @param member The annotation member
* @param requiredType The required type
* @param <T> The required type
* @return An {@link Optional} class
*/
@Override
@SuppressWarnings("unchecked")
public <T> Optional<Class<? extends T>> classValue(@NonNull String member, @NonNull Class<T> requiredType) {
ArgumentUtils.requireNonNull("requiredType", requiredType);
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof AnnotationClassValue<?> annotationClassValue) {
Class<?> t = annotationClassValue.getType().orElse(null);
if (t != null && requiredType.isAssignableFrom(t)) {
return Optional.of((Class<? extends T>) t);
}
return Optional.empty();
}
if (o instanceof Class<?> t) {
if (requiredType.isAssignableFrom(t)) {
return Optional.of((Class<? extends T>) t);
}
return Optional.empty();
}
if (o != null) {
Class<?> t = ClassUtils.forName(o.toString(), getClass().getClassLoader()).orElse(null);
if (t != null && requiredType.isAssignableFrom(t)) {
return Optional.of((Class<? extends T>) t);
}
}
return Optional.empty();
}
/**
* The value of the given annotation member as a Class.
*
* @param member The annotation member
* @param valueMapper The raw value mapper
* @return An {@link Optional} class
*/
public Optional<Class<?>> classValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof AnnotationClassValue annotationClassValue) {
return annotationClassValue.getType();
} else if (o instanceof Class<?> aClass) {
return Optional.of(aClass);
}
return Optional.empty();
}
@NonNull
@Override
public String[] stringValues(@NonNull String member) {
Function<Object, Object> valueMapper = this.valueMapper;
return stringValues(member, valueMapper);
}
@Override
public boolean[] booleanValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_BOOLEAN_ARRAY;
}
if (v instanceof boolean[] booleans) {
return booleans;
}
if (v instanceof Boolean aBoolean) {
return new boolean[]{aBoolean};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_BOOLEAN_ARRAY;
}
boolean[] booleans = new boolean[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
booleans[i] = Boolean.parseBoolean(string);
}
return booleans;
}
@Override
public byte[] byteValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_BYTE_ARRAY;
}
if (v instanceof byte[] bytes) {
return bytes;
}
if (v instanceof Number number) {
return new byte[]{number.byteValue()};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_BYTE_ARRAY;
}
byte[] bytes = new byte[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
bytes[i] = Byte.parseByte(string);
}
return bytes;
}
@Override
public char[] charValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_CHAR_ARRAY;
}
if (v instanceof char[] chars) {
return chars;
}
if (v instanceof Character[] v2) {
char[] chars = new char[v2.length];
for (int i = 0; i < v2.length; i++) {
Character character = v2[i];
chars[i] = character;
}
return chars;
}
if (v instanceof Character character) {
return new char[]{character};
}
return ArrayUtils.EMPTY_CHAR_ARRAY;
}
@Override
public int[] intValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_INT_ARRAY;
}
if (v instanceof int[] ints) {
return ints;
}
if (v instanceof Number number) {
return new int[]{number.intValue()};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_INT_ARRAY;
}
int[] integers = new int[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
integers[i] = Integer.parseInt(string);
}
return integers;
}
@Override
public double[] doubleValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_DOUBLE_ARRAY;
}
if (v instanceof double[] doubles) {
return doubles;
}
if (v instanceof Number number) {
return new double[]{number.doubleValue()};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_DOUBLE_ARRAY;
}
double[] doubles = new double[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
doubles[i] = Double.parseDouble(string);
}
return doubles;
}
@Override
public long[] longValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_LONG_ARRAY;
}
if (v instanceof long[] longs) {
return longs;
}
if (v instanceof Number number) {
return new long[]{number.longValue()};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_LONG_ARRAY;
}
long[] longs = new long[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
longs[i] = Long.parseLong(string);
}
return longs;
}
@Override
public float[] floatValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_FLOAT_ARRAY;
}
if (v instanceof float[] floats) {
return floats;
}
if (v instanceof Number number) {
return new float[]{number.floatValue()};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_FLOAT_ARRAY;
}
float[] floats = new float[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
floats[i] = Float.parseFloat(string);
}
return floats;
}
@Override
public short[] shortValues(String member) {
Object v = values.get(member);
if (v == null) {
return ArrayUtils.EMPTY_SHORT_ARRAY;
}
if (v instanceof short[] shorts) {
return shorts;
}
if (v instanceof Number number) {
return new short[]{number.shortValue()};
}
String[] strings = resolveStringValues(v, this.valueMapper);
if (ArrayUtils.isEmpty(strings)) {
return ArrayUtils.EMPTY_SHORT_ARRAY;
}
short[] shorts = new short[strings.length];
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
shorts[i] = Short.parseShort(string);
}
return shorts;
}
/**
* The string values for the given member and mapper.
*
* @param member The member
* @param valueMapper The mapper
* @return The string values
*/
public String[] stringValues(@NonNull String member, Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return StringUtils.EMPTY_STRING_ARRAY;
}
Object o = values.get(member);
String[] strs = resolveStringValues(o, valueMapper);
if (strs != null) {
return strs;
}
return StringUtils.EMPTY_STRING_ARRAY;
}
@Override
public Class<?>[] classValues(@NonNull String member) {
if (StringUtils.isEmpty(member)) {
return EMPTY_CLASS_ARRAY;
}
Object o = values.get(member);
Class<?>[] type = resolveClassValues(o);
if (type != null) {
return type;
}
return EMPTY_CLASS_ARRAY;
}
@NonNull
@Override
public AnnotationClassValue<?>[] annotationClassValues(@NonNull String member) {
if (StringUtils.isEmpty(member)) {
return AnnotationClassValue.ZERO_ANNOTATION_CLASS_VALUES;
}
Object o = values.get(member);
if (o instanceof AnnotationClassValue<?> annotationClassValue) {
return new AnnotationClassValue[]{annotationClassValue};
}
if (o instanceof AnnotationClassValue<?>[] annotationClassValues) {
return annotationClassValues;
}
if (o instanceof String className) {
return new AnnotationClassValue[] { getAnnotationClassValue(className) };
}
if (o instanceof String[] classNames) {
return Arrays.stream(classNames).map(AnnotationValue::getAnnotationClassValue).toArray(AnnotationClassValue[]::new);
}
return AnnotationClassValue.ZERO_ANNOTATION_CLASS_VALUES;
}
@Override
public Optional<AnnotationClassValue<?>> annotationClassValue(@NonNull String member) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = values.get(member);
if (o instanceof AnnotationClassValue<?> annotationClassValue) {
annotationClassValue.failIfError();
return Optional.of(annotationClassValue);
}
if (o instanceof AnnotationClassValue<?>[] annotationClassValues) {
if (annotationClassValues.length > 0) {
AnnotationClassValue<?> acv = annotationClassValues[0];
acv.failIfError();
return Optional.of(acv);
}
}
if (o instanceof String className) {
return Optional.of(getAnnotationClassValue(className));
}
if (o instanceof String[] classNames && classNames.length > 0) {
return Optional.of(getAnnotationClassValue(classNames[0]));
}
return Optional.empty();
}
/**
* The integer value of the given member.
*
* @param member The annotation member
* @return An {@link OptionalInt}
*/
@Override
public OptionalInt intValue(@NonNull String member) {
return intValue(member, valueMapper);
}
/**
* The integer value of the given member.
*
* @param member The annotation member
* @param valueMapper The value mapper
* @return An {@link OptionalInt}
*/
public OptionalInt intValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return OptionalInt.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Number number) {
return OptionalInt.of(number.intValue());
}
if (o instanceof String s) {
try {
return OptionalInt.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return OptionalInt.empty();
}
}
if (o instanceof CharSequence charSequence) {
try {
return OptionalInt.of(Integer.parseInt(charSequence.toString()));
} catch (NumberFormatException e) {
return OptionalInt.empty();
}
}
return OptionalInt.empty();
}
@Override
public Optional<Byte> byteValue(String member) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Number number) {
return Optional.of(number.byteValue());
}
if (o instanceof CharSequence charSequence) {
try {
return Optional.of(Byte.parseByte(charSequence.toString()));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
return Optional.empty();
}
@Override
public Optional<Character> charValue(String member) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Character character) {
return Optional.of(character);
}
return Optional.empty();
}
/**
* The integer value of the given member.
*
* @return An {@link OptionalInt}
*/
@Override
public OptionalInt intValue() {
return intValue(AnnotationMetadata.VALUE_MEMBER);
}
@Override
public OptionalLong longValue(@NonNull String member) {
return longValue(member, valueMapper);
}
/**
* The long value of the given member.
*
* @param member The annotation member
* @param valueMapper The value mapper
* @return An {@link OptionalLong}
*/
public OptionalLong longValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return OptionalLong.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Number number) {
return OptionalLong.of(number.longValue());
}
if (o instanceof String s) {
try {
return OptionalLong.of(Long.parseLong(s));
} catch (NumberFormatException e) {
return OptionalLong.empty();
}
}
if (o instanceof CharSequence charSequence) {
try {
return OptionalLong.of(Long.parseLong(charSequence.toString()));
} catch (NumberFormatException e) {
return OptionalLong.empty();
}
}
return OptionalLong.empty();
}
@Override
public Optional<Short> shortValue(@NonNull String member) {
return shortValue(member, valueMapper);
}
/**
* The short value of the given member.
*
* @param member The annotation member
* @param valueMapper The value mapper
* @return An {@link Optional} of {@link Short}
*/
public Optional<Short> shortValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Number number) {
return Optional.of(number.shortValue());
}
if (o instanceof CharSequence charSequence) {
try {
return Optional.of(Short.parseShort(charSequence.toString()));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
return Optional.empty();
}
/**
* The boolean value of the given member.
*
* @param member The annotation member
* @param valueMapper The value mapper
* @return An {@link Optional} boolean
*/
public Optional<Boolean> booleanValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Boolean aBoolean) {
return Optional.of(aBoolean);
}
if (o instanceof String s) {
return Optional.of(StringUtils.isTrue(s));
}
if (o instanceof CharSequence charSequence) {
return Optional.of(StringUtils.isTrue(charSequence.toString()));
}
return Optional.empty();
}
/**
* The double value of the given member.
*
* @param member The annotation member
* @return An {@link OptionalDouble}
*/
@Override
public OptionalDouble doubleValue(@NonNull String member) {
return doubleValue(member, valueMapper);
}
/**
* The double value of the given member.
*
* @param member The annotation member
* @param valueMapper The value mapper
* @return An {@link OptionalDouble}
*/
public OptionalDouble doubleValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return OptionalDouble.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Number number) {
return OptionalDouble.of(number.doubleValue());
}
if (o instanceof String s) {
try {
return OptionalDouble.of(Double.parseDouble(s));
} catch (NumberFormatException e) {
return OptionalDouble.empty();
}
}
if (o instanceof CharSequence charSequence) {
try {
return OptionalDouble.of(Double.parseDouble(charSequence.toString()));
} catch (NumberFormatException e) {
return OptionalDouble.empty();
}
}
return OptionalDouble.empty();
}
@Override
public Optional<Float> floatValue(String member) {
return floatValue(member, valueMapper);
}
/**
* The double value of the given member.
*
* @param member The annotation member
* @param valueMapper The value mapper
* @return An {@link OptionalDouble}
*/
public Optional<Float> floatValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Number number) {
return Optional.of(number.floatValue());
}
if (o instanceof CharSequence charSequence) {
try {
return Optional.of(Float.parseFloat(charSequence.toString()));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
return Optional.empty();
}
/**
* The double value of the given member.
*
* @return An {@link OptionalDouble}
*/
@Override
public OptionalDouble doubleValue() {
return doubleValue(AnnotationMetadata.VALUE_MEMBER);
}
/**
* The string value of the given member.
*
* @param member The annotation member
* @return An {@link OptionalInt}
*/
@Override
public Optional<String> stringValue(@NonNull String member) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o != null) {
return Optional.of(o.toString());
}
return Optional.empty();
}
/**
* The string value of the given member.
*
* @param member The annotation member
* @param valueMapper An optional raw value mapper
* @return An {@link OptionalInt}
*/
public Optional<String> stringValue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return Optional.empty();
}
Object o = getRawSingleValue(member, valueMapper);
if (o != null) {
return Optional.of(o.toString());
}
return Optional.empty();
}
/**
* The double value of the given member.
*
* @return An {@link OptionalInt}
*/
@Override
public Optional<String> stringValue() {
return stringValue(AnnotationMetadata.VALUE_MEMBER);
}
@Override
public Optional<Boolean> booleanValue(@NonNull String member) {
return booleanValue(member, valueMapper);
}
/**
* Is the given member present.
*
* @param member The member
* @return True if it is
*/
@Override
public final boolean isPresent(CharSequence member) {
if (StringUtils.isNotEmpty(member)) {
return values.containsKey(member);
}
return false;
}
/**
* @return Is the value of the annotation true.
*/
@Override
public boolean isTrue() {
return isTrue(AnnotationMetadata.VALUE_MEMBER);
}
/**
* @param member The member
* @return Is the value of the annotation true.
*/
@Override
public boolean isTrue(String member) {
return isTrue(member, valueMapper);
}
/**
* @param member The member
* @param valueMapper The value mapper
* @return Is the value of the annotation true.
*/
public boolean isTrue(@NonNull String member, @Nullable Function<Object, Object> valueMapper) {
if (StringUtils.isEmpty(member)) {
return false;
}
Object o = getRawSingleValue(member, valueMapper);
if (o instanceof Boolean aBoolean) {
return aBoolean;
} else if (o != null) {
return StringUtils.isTrue(o.toString());
}
return false;
}
/**
* @return Is the value of the annotation true.
*/
@Override
public boolean isFalse() {
return !isTrue(AnnotationMetadata.VALUE_MEMBER);
}
/**
* @param member The member
* @return Is the value of the annotation true.
*/
@Override
public boolean isFalse(String member) {
return !isTrue(member);
}
/**
* The annotation name.
*
* @return The annotation name
*/
@NonNull
public final String getAnnotationName() {
return annotationName;
}
/**
* Whether a particular member is present.
*
* @param member The member
* @return True if it is
*/
public final boolean contains(String member) {
return isPresent(member);
}
/**
* Resolves the names of all the present annotation members.
*
* @return The names of the members
*/
@NonNull
public final Set<CharSequence> getMemberNames() {
return values.keySet();
}
/**
* @return The attribute values
*/
@Override
@NonNull
public Map<CharSequence, Object> getValues() {
return Collections.unmodifiableMap(values);
}
/**
* @return The convertible values
*/
@NonNull
public ConvertibleValues<Object> getConvertibleValues() {
return convertibleValues;
}
@Override
public <T> Optional<T> get(CharSequence member, ArgumentConversionContext<T> conversionContext) {
Optional<T> result = convertibleValues.get(member, conversionContext);
if (result.isPresent()) {
return result;
}
Map<CharSequence, Object> defaults = getDefaultValues();
if (defaults != null) {
Object dv = defaults.get(member.toString());
if (dv != null) {
return ConversionService.SHARED.convert(dv, conversionContext);
}
}
return result;
}
/**
* Get the value of the {@code value} member of the annotation.
*
* @param conversionContext The conversion context
* @param <T> The type
* @return The result
*/
public <T> Optional<T> getValue(ArgumentConversionContext<T> conversionContext) {
return get(AnnotationMetadata.VALUE_MEMBER, conversionContext);
}
/**
* Get the value of the {@code value} member of the annotation.
*
* @param argument The argument
* @param <T> The type
* @return The result
*/
public final <T> Optional<T> getValue(Argument<T> argument) {
return getValue(ConversionContext.of(argument));
}
/**
* Get the value of the {@code value} member of the annotation.
*
* @param type The type
* @param <T> The type
* @return The result
*/
public final <T> Optional<T> getValue(Class<T> type) {
return getValue(ConversionContext.of(type));
}
/**
* Get the value of the {@code value} member of the annotation.
*
* @param type The type
* @param <T> The type
* @return The result
* @throws IllegalStateException If no member is available that conforms to the given type
*/
@NonNull
public final <T> T getRequiredValue(Class<T> type) {
return getRequiredValue(AnnotationMetadata.VALUE_MEMBER, type);
}
/**
* Get the value of the {@code value} member of the annotation.
*
* @param member The member
* @param type The type
* @param <T> The type
* @return The result
* @throws IllegalStateException If no member is available that conforms to the given name and type
*/
@NonNull
public final <T> T getRequiredValue(String member, Class<T> type) {
return get(member, ConversionContext.of(type)).orElseThrow(() -> new IllegalStateException("No value available for annotation member @" + annotationName + "[" + member + "] of type: " + type));
}
/**
* Gets a list of {@link AnnotationValue} for the given member.
*
* @param member The member
* @param type The type
* @param <T> The type
* @return The result
* @throws IllegalStateException If no member is available that conforms to the given name and type
*/
@NonNull
@SuppressWarnings("java:S2259") // false positive
public <T extends Annotation> List<AnnotationValue<T>> getAnnotations(String member, Class<T> type) {
ArgumentUtils.requireNonNull("type", type);
String typeName = type.getName();
ArgumentUtils.requireNonNull("member", member);
Object v = values.get(member);
Collection<AnnotationValue<?>> values = null;
if (v instanceof AnnotationValue<?> annotationValue) {
values = Collections.singletonList(annotationValue);
} else if (v instanceof AnnotationValue<?>[] annotationValues) {
values = Arrays.asList(annotationValues);
} else if (v instanceof Collection<?> collection) {
final Iterator<?> i = collection.iterator();
if (i.hasNext()) {
final Object o = i.next();
if (o instanceof AnnotationValue) {
values = (Collection<AnnotationValue<?>>) collection;
}
}
}
if (CollectionUtils.isEmpty(values)) {
return Collections.emptyList();
} else {
List<AnnotationValue<T>> list = new ArrayList<>(values.size());
for (AnnotationValue<?> value : values) {
if (value == null) {
continue;
}
if (value.getAnnotationName().equals(typeName)) {
list.add((AnnotationValue<T>) value);
}
}
return list;
}
}
/**
* Gets a list of {@link AnnotationValue} for the given member.
*
* @param member The member
* @param <T> The type
* @return The result
* @throws IllegalStateException If no member is available that conforms to the given name and type
*/
@SuppressWarnings("unchecked")
@NonNull
public <T extends Annotation> List<AnnotationValue<T>> getAnnotations(String member) {
ArgumentUtils.requireNonNull("member", member);
Object v = values.get(member);
if (v instanceof AnnotationValue annotationValue) {
return Collections.singletonList(annotationValue);
}
if (v instanceof AnnotationValue[] annotationValues) {
return Arrays.asList(annotationValues);
}
if (v instanceof Collection<?> collection) {
final Iterator<?> i = collection.iterator();
if (i.hasNext()) {
final Object o = i.next();
if (o instanceof AnnotationValue) {
return new ArrayList<>((Collection<? extends AnnotationValue<T>>) v);
}
}
}
return Collections.emptyList();
}
/**
* Gets a list of {@link AnnotationValue} for the given member.
*
* @param member The member
* @param type The type
* @param <T> The type
* @return The result
* @throws IllegalStateException If no member is available that conforms to the given name and type
*/
@NonNull
public <T extends Annotation> Optional<AnnotationValue<T>> getAnnotation(String member, Class<T> type) {
ArgumentUtils.requireNonNull("type", type);
String typeName = type.getName();
ArgumentUtils.requireNonNull("member", member);
Object v = values.get(member);
if (v instanceof AnnotationValue av) {
if (av.getAnnotationName().equals(typeName)) {
return Optional.of(av);
}
return Optional.empty();
}
if (v instanceof AnnotationValue[] values) {
if (ArrayUtils.isNotEmpty(values)) {
final AnnotationValue value = values[0];
if (value.getAnnotationName().equals(typeName)) {
return Optional.of(value);
}
}
return Optional.empty();
}
return Optional.empty();
}
/**
* Gets a list of {@link AnnotationValue} for the given member.
*
* @param member The member
* @param <T> The type
* @return The result
* @throws IllegalStateException If no member is available that conforms to the given name and type
* @since 3.3.0
*/
@NonNull
public <T extends Annotation> Optional<AnnotationValue<T>> getAnnotation(@NonNull String member) {
ArgumentUtils.requireNonNull("member", member);
Object v = values.get(member);
if (v instanceof AnnotationValue av) {
return Optional.of(av);
}
if (v instanceof AnnotationValue[] values) {
if (ArrayUtils.isNotEmpty(values)) {
return Optional.of(values[0]);
}
return Optional.empty();
}
return Optional.empty();
}
/**
* If this AnnotationValue contains Evaluated Expressions.
*
* @return true if it is
* @since 4.0.0
*/
public boolean hasEvaluatedExpressions() {
return values.values().stream()
.anyMatch(value -> value instanceof EvaluatedExpression);
}
@Override
public String toString() {
if (values.isEmpty()) {
return "@" + annotationName;
} else {
return "@" + annotationName + "(" + values.entrySet().stream().map(entry -> entry.getKey() + "=" + toStringValue(entry.getValue())).collect(
Collectors.joining(", ")) + ")";
}
}
private String toStringValue(Object object) {
if (object == null) {
return "null";
}
if (object instanceof Object[] object1s) {
return Arrays.deepToString(object1s);
}
return object.toString();
}
@Override
public int hashCode() {
return 31 * annotationName.hashCode() + AnnotationUtil.calculateHashCode(getValues());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof AnnotationValue<?> other)) {
return false;
}
if (!annotationName.equals(other.getAnnotationName())) {
return false;
}
Map<CharSequence, Object> otherValues = other.getValues();
Map<CharSequence, Object> values = getValues();
if (values.size() != otherValues.size()) {
return false;
}
// compare annotation member values
for (Map.Entry<CharSequence, Object> member : values.entrySet()) {
Object value = member.getValue();
Object otherValue = otherValues.get(member.getKey());
if (!AnnotationUtil.areEqual(value, otherValue)) {
return false;
}
}
return true;
}
/**
* Start building a new annotation for the given name.
*
* @param annotationName The annotation name
* @param <T> The annotation type
* @return The builder
*/
public static <T extends Annotation> AnnotationValueBuilder<T> builder(String annotationName) {
return new AnnotationValueBuilder<>(annotationName);
}
/**
* Start building a new annotation for the given name.
*
* @param annotationName The annotation name
* @param retentionPolicy The retention policy
* @param <T> The annotation type
* @return The builder
* @since 2.4.0
*/
public static <T extends Annotation> AnnotationValueBuilder<T> builder(String annotationName, RetentionPolicy retentionPolicy) {
return new AnnotationValueBuilder<>(annotationName, retentionPolicy);
}
/**
* Start building a new annotation for the given name.
*
* @param annotation The annotation name
* @param <T> The annotation type
* @return The builder
*/
public static <T extends Annotation> AnnotationValueBuilder<T> builder(Class<T> annotation) {
return new AnnotationValueBuilder<>(annotation);
}
/**
* Start building a new annotation existing value.
*
* @param annotation The annotation name
* @param <T> The annotation type
* @return The builder
* @since 4.0.0
*/
public static <T extends Annotation> AnnotationValueBuilder<T> builder(@NonNull AnnotationValue<T> annotation) {
ArgumentUtils.requireNonNull("annotation", annotation);
return new AnnotationValueBuilder<>(annotation, annotation.getRetentionPolicy());
}
/**
* Start building a new annotation existing value and retention policy.
*
* @param annotation The annotation name
* @param retentionPolicy The retention policy. Defaults to runtime.
* @param <T> The annotation type
* @return The builder
*/
public static <T extends Annotation> AnnotationValueBuilder<T> builder(@NonNull AnnotationValue<T> annotation, @Nullable RetentionPolicy retentionPolicy) {
ArgumentUtils.requireNonNull("annotation", annotation);
return new AnnotationValueBuilder<>(annotation, retentionPolicy);
}
/**
* The string values for the given value.
*
* @param value The value
* @param valueMapper The value mapper
* @return The string[] or null
*/
@Internal
@Nullable
public static String[] resolveStringValues(@Nullable Object value, @Nullable Function<Object, Object> valueMapper) {
if (value == null) {
return null;
}
if (valueMapper != null) {
value = valueMapper.apply(value);
}
if (value instanceof String s) {
return new String[]{s};
}
if (value instanceof String[] existing) {
return Arrays.copyOf(existing, existing.length);
}
if (value instanceof CharSequence) {
return new String[]{value.toString()};
}
if (value != null) {
if (value.getClass().isArray()) {
int len = Array.getLength(value);
String[] newArray = new String[len];
for (int i = 0; i < newArray.length; i++) {
Object entry = Array.get(value, i);
if (entry != null) {
newArray[i] = entry.toString();
}
}
return newArray;
} else {
return new String[]{value.toString()};
}
}
return null;
}
/**
* The enum values for the given enum type and raw value.
*
* @param enumType The enum type
* @param rawValue The raw value
* @param <E> The enum generic type
* @return An array of enum values
*/
@Internal
@NonNull
public static <E extends Enum> E[] resolveEnumValues(@NonNull Class<E> enumType, @Nullable Object rawValue) {
if (rawValue == null) {
return (E[]) Array.newInstance(enumType, 0);
}
List<E> list = new ArrayList<>();
if (rawValue.getClass().isArray()) {
int len = Array.getLength(rawValue);
for (int i = 0; i < len; i++) {
convertToEnum(enumType, Array.get(rawValue, i)).ifPresent(list::add);
}
} else if (rawValue instanceof Iterable<?> iterable) {
for (Object o : iterable) {
convertToEnum(enumType, o).ifPresent(list::add);
}
} else if (enumType.isAssignableFrom(rawValue.getClass())) {
list.add((E) rawValue);
} else {
convertToEnum(enumType, rawValue).ifPresent(list::add);
}
return list.toArray((E[]) Array.newInstance(enumType, 0));
}
/**
* The string[] values for the given value.
*
* @param strs The strings
* @param valueMapper The value mapper
* @return The string[] or the original string
*/
@Internal
public static String[] resolveStringArray(String[] strs, @Nullable Function<Object, Object> valueMapper) {
if (valueMapper != null) {
String[] newStrs = new String[strs.length];
for (int i = 0; i < strs.length; i++) {
String str = strs[i];
newStrs[i] = valueMapper.apply(str).toString();
}
return newStrs;
} else {
return strs;
}
}
/**
* The class values for the given value.
*
* @param value The value
* @return The class values or null
*/
@Internal
@Nullable
public static Class<?>[] resolveClassValues(@Nullable Object value) {
// conditional branches ordered from most likely to least likely
// generally at runtime values are always AnnotationClassValue
// A class can be present at compilation time
if (value == null) {
return null;
}
if (value instanceof AnnotationClassValue<?> annotationClassValue) {
Class<?> type = annotationClassValue.getType().orElse(null);
if (type != null) {
return new Class<?>[]{type};
}
return null;
}
if (value instanceof AnnotationValue<?>[] annotationValues) {
int len = annotationValues.length;
if (len > 0) {
if (len == 1) {
return annotationValues[0].classValues();
} else {
List<Class<?>> list = new ArrayList<>(5);
for (AnnotationValue<?> annotationValue : annotationValues) {
list.addAll(Arrays.asList(annotationValue.classValues()));
}
return list.toArray(EMPTY_CLASS_ARRAY);
}
}
return null;
}
if (value instanceof AnnotationValue<?> annotationValue) {
return annotationValue.classValues();
}
if (value instanceof Object[] values) {
if (values instanceof Class<?>[] classes) {
return classes;
} else {
List<Class<?>> list = new ArrayList<>(5);
for (Object o : values) {
if (o instanceof AnnotationClassValue<?> annotationClassValue) {
if (annotationClassValue.theClass != null) {
list.add(annotationClassValue.theClass);
}
} else if (o instanceof Class<?> aClass) {
list.add(aClass);
}
}
return list.toArray(EMPTY_CLASS_ARRAY);
}
}
if (value instanceof Class<?> aClass) {
return new Class<?>[]{aClass};
}
return null;
}
/**
* Subclasses can override to provide a custom convertible values instance.
*
* @param values The values
* @return The instance
*/
private ConvertibleValues<Object> newConvertibleValues(Map<CharSequence, Object> values) {
if (CollectionUtils.isEmpty(values)) {
return ConvertibleValues.EMPTY;
} else {
return ConvertibleValues.of(values);
}
}
@Nullable
private Object getRawSingleValue(@NonNull String member, Function<Object, Object> valueMapper) {
Object rawValue = values.get(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 String || rawValue instanceof EvaluatedExpression)) {
return valueMapper.apply(rawValue);
}
return rawValue;
}
private static <T extends Enum> Optional<T> convertToEnum(Class<T> enumType, Object o) {
if (enumType.isInstance(o)) {
return Optional.of((T) o);
} else {
try {
T t = (T) Enum.valueOf(enumType, o.toString());
return Optional.of(t);
} catch (IllegalArgumentException ex) {
return Optional.empty();
}
}
}
/**
* Creates {@link AnnotationClassValue} from the class name.
*
* @param className The class name
* @return AnnotationClassValue for given class name
*/
private static AnnotationClassValue<?> getAnnotationClassValue(String className) {
Optional<Class<?>> theClass = ClassUtils.forName(className, null);
if (theClass.isPresent()) {
return new AnnotationClassValue<>(theClass.get());
}
return new AnnotationClassValue<>(className);
}
}