AnnotationValueBuilder.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.expressions.EvaluatedExpression;
import io.micronaut.core.expressions.EvaluatedExpressionReference;
import io.micronaut.core.reflect.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* A build for annotation values.
*
* @param <T> The annotation type
* @author graemerocher
* @since 1.0
*/
public class AnnotationValueBuilder<T extends Annotation> {
private final String annotationName;
private final Map<CharSequence, Object> values = new LinkedHashMap<>(5);
private final RetentionPolicy retentionPolicy;
@Nullable
private List<AnnotationValue<?>> stereotypes;
@Nullable
private Map<CharSequence, Object> defaultValues;
/**
* Default constructor.
*
* @param annotationName The annotation name
*/
@Internal
AnnotationValueBuilder(String annotationName) {
this(annotationName, RetentionPolicy.RUNTIME);
}
/**
* Default constructor.
*
* @param annotationName The annotation name
* @param retentionPolicy The retention policy
*/
@Internal
AnnotationValueBuilder(String annotationName, RetentionPolicy retentionPolicy) {
this.annotationName = annotationName;
this.retentionPolicy = retentionPolicy != null ? retentionPolicy : RetentionPolicy.RUNTIME;
}
/**
* Default constructor.
*
* @param annotation The annotation
*/
@Internal
AnnotationValueBuilder(Class<?> annotation) {
this(annotation.getName());
}
/**
* Default constructor.
*
* @param value An existing value
* @param retentionPolicy The retention policy
*/
@Internal
AnnotationValueBuilder(AnnotationValue<T> value, RetentionPolicy retentionPolicy) {
this.annotationName = value.getAnnotationName();
this.values.putAll(value.getValues());
this.defaultValues = value.getDefaultValues();
this.stereotypes = value.getStereotypes() == null ? null : new ArrayList<>(value.getStereotypes());
this.retentionPolicy = retentionPolicy != null ? retentionPolicy : RetentionPolicy.RUNTIME;
}
/**
* Build the actual {@link AnnotationValue}.
*
* @return The {@link AnnotationValue}
*/
@NonNull
public AnnotationValue<T> build() {
return new AnnotationValue<>(annotationName, values, defaultValues, retentionPolicy, stereotypes);
}
/**
* Adds a stereotype of the annotation.
*
* @param annotation The stereotype
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> stereotype(AnnotationValue<?> annotation) {
if (annotation != null) {
if (stereotypes == null) {
stereotypes = new ArrayList<>(10);
}
stereotypes.add(annotation);
}
return this;
}
/**
* Replaces the stereotype annotation.
*
* @param originalAnnotationValue The original annotation value
* @param newAnnotationValue The new annotation value
* @return This builder
* @since 4.0.0
*/
@NonNull
public AnnotationValueBuilder<T> replaceStereotype(@NonNull AnnotationValue<?> originalAnnotationValue,
@NonNull AnnotationValue<?> newAnnotationValue) {
Objects.requireNonNull(stereotypes);
List<AnnotationValue<?>> values = new ArrayList<>(stereotypes);
int index = values.indexOf(originalAnnotationValue);
if (index < 0) {
throw new IllegalArgumentException("Unknown original annotation value!");
}
values.set(index, newAnnotationValue);
stereotypes = values;
return this;
}
/**
* Removes the stereotype annotation.
*
* @param annotationValueToRemove The annotation value to remove
* @return This builder
* @since 4.0.0
*/
@NonNull
public AnnotationValueBuilder<T> removeStereotype(@NonNull AnnotationValue<?> annotationValueToRemove) {
Objects.requireNonNull(stereotypes);
List<AnnotationValue<?>> values = new ArrayList<>(stereotypes);
int index = values.indexOf(annotationValueToRemove);
if (index < 0) {
throw new IllegalArgumentException("Unknown annotation value!");
}
values.remove(index);
stereotypes = values;
return this;
}
/**
* Adds a stereotypes of the annotation.
*
* @param newStereotypes The stereotypes
* @return This builder
* @since 4.0.0
*/
@NonNull
public AnnotationValueBuilder<T> stereotypes(@NonNull Collection<AnnotationValue<?>> newStereotypes) {
if (stereotypes == null) {
stereotypes = new ArrayList<>(10);
}
stereotypes.addAll(newStereotypes);
return this;
}
/**
* Replaces stereotypes of the annotation.
*
* @param newStereotypes The stereotypes
* @return This builder
* @since 4.0.0
*/
@NonNull
public AnnotationValueBuilder<T> replaceStereotypes(@NonNull Collection<AnnotationValue<?>> newStereotypes) {
stereotypes = new ArrayList<>(newStereotypes);
return this;
}
/**
* Sets the default values of the annotation.
*
* @param defaultValues The default values
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> defaultValues(Map<? extends CharSequence, Object> defaultValues) {
if (defaultValues != null) {
if (this.defaultValues == null) {
this.defaultValues = new LinkedHashMap<>(defaultValues);
} else {
this.defaultValues.putAll(defaultValues);
}
}
return this;
}
/**
* Sets the value member to the given integer value.
*
* @param i The integer
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(int i) {
return member(AnnotationMetadata.VALUE_MEMBER, i);
}
/**
* Sets the value member to the given integer[] value.
*
* @param ints The integer[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable int... ints) {
return member(AnnotationMetadata.VALUE_MEMBER, ints);
}
/**
* Sets the value member to the given long value.
*
* @param i The long
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(long i) {
return member(AnnotationMetadata.VALUE_MEMBER, i);
}
/**
* Sets the value member to the given long[] value.
*
* @param longs The long[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable long... longs) {
return member(AnnotationMetadata.VALUE_MEMBER, longs);
}
/**
* Sets the value member to the given string value.
*
* @param str The string
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(@Nullable String str) {
return member(AnnotationMetadata.VALUE_MEMBER, str);
}
/**
* Sets the value member to the given String[] values.
*
* @param strings The String[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable String... strings) {
return member(AnnotationMetadata.VALUE_MEMBER, strings);
}
/**
* Sets the value member to the given boolean value.
*
* @param bool The boolean
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(boolean bool) {
return member(AnnotationMetadata.VALUE_MEMBER, bool);
}
/**
* Sets the value member to the given char value.
*
* @param character The char
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> value(char character) {
return member(AnnotationMetadata.VALUE_MEMBER, character);
}
/**
* Sets the value member to the given double value.
*
* @param number The double
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> value(double number) {
return member(AnnotationMetadata.VALUE_MEMBER, number);
}
/**
* Sets the value member to the given float value.
*
* @param f The float
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> value(float f) {
return member(AnnotationMetadata.VALUE_MEMBER, f);
}
/**
* Sets the value member to the given enum object.
*
* @param enumObj The enum
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(@Nullable Enum<?> enumObj) {
return member(AnnotationMetadata.VALUE_MEMBER, enumObj);
}
/**
* Sets the value member to the given enum objects.
*
* @param enumObjs The enum[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable Enum<?>... enumObjs) {
return member(AnnotationMetadata.VALUE_MEMBER, enumObjs);
}
/**
* Sets the value member to the given type object.
*
* @param type The type
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(@Nullable Class<?> type) {
return member(AnnotationMetadata.VALUE_MEMBER, type);
}
/**
* Sets the value member to the given type objects.
*
* @param types The type[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable Class<?>... types) {
return member(AnnotationMetadata.VALUE_MEMBER, types);
}
/**
* Sets the value member to the given type objects.
*
* @param types The type[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable AnnotationClassValue<?>... types) {
return member(AnnotationMetadata.VALUE_MEMBER, types);
}
/**
* Sets the value member to the given annotation value.
*
* @param annotation The annotation
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> value(@Nullable AnnotationValue<?> annotation) {
return member(AnnotationMetadata.VALUE_MEMBER, annotation);
}
/**
* Sets the value member to the given annotation values.
*
* @param annotations The annotation[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> values(@Nullable AnnotationValue<?>... annotations) {
return member(AnnotationMetadata.VALUE_MEMBER, annotations);
}
/**
* Sets the given member to the given integer value.
*
* @param name The name of the member
* @param i The integer
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, int i) {
values.put(name, i);
return this;
}
/**
* Sets the given member to the given byte value.
*
* @param name The name of the member
* @param b The byte
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, byte b) {
values.put(name, b);
return this;
}
/**
* Sets the given member to the given char value.
*
* @param name The name of the member
* @param c The char
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, char c) {
values.put(name, c);
return this;
}
/**
* Sets the given member to the given char[] value.
*
* @param name The name of the member
* @param chars The chars
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, char... chars) {
values.put(name, chars);
return this;
}
/**
* Sets the given member to the given double value.
*
* @param name The name of the member
* @param d The double
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, double d) {
values.put(name, d);
return this;
}
/**
* Sets the given member to the given double[] value.
*
* @param name The name of the member
* @param doubles The double[]
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, double... doubles) {
values.put(name, doubles);
return this;
}
/**
* Sets the given member to the given float value.
*
* @param name The name of the member
* @param f The float
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, float f) {
values.put(name, f);
return this;
}
/**
* Sets the given member to the given float[] value.
*
* @param name The name of the member
* @param floats The float[]
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, float... floats) {
values.put(name, floats);
return this;
}
/**
* Sets the given member to the given integer[] value.
*
* @param name The name of the member
* @param ints The integer[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable int... ints) {
if (ints != null) {
values.put(name, ints);
}
return this;
}
/**
* Sets the given member to the given byte[] value.
*
* @param name The name of the member
* @param bytes The byte[]
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable byte... bytes) {
if (bytes != null) {
values.put(name, bytes);
}
return this;
}
/**
* Sets the given member to the given long value.
*
* @param name The name of the member
* @param i The long
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, long i) {
values.put(name, i);
return this;
}
/**
* Sets the given member to the given short value.
*
* @param name The name of the member
* @param i The short
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, short i) {
values.put(name, i);
return this;
}
/**
* Sets the given member to the given short[] value.
*
* @param name The name of the member
* @param shorts The short[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, short... shorts) {
values.put(name, shorts);
return this;
}
/**
* Sets the given member to the given long[] value.
*
* @param name The name of the member
* @param longs The long[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable long... longs) {
if (longs != null) {
values.put(name, longs);
}
return this;
}
/**
* Sets the given member to the given string value.
*
* @param name The name of the member
* @param str The string
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable String str) {
if (str != null) {
values.put(name, str);
}
return this;
}
/**
* Sets the given member to the given String[] values.
*
* @param name The name of the member
* @param strings The String[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable String... strings) {
if (strings != null) {
values.put(name, strings);
}
return this;
}
/**
* Sets the given member to the given boolean value.
*
* @param name The name of the member
* @param bool The boolean
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, boolean bool) {
values.put(name, bool);
return this;
}
/**
* Sets the given member to the given boolean value array.
*
* @param name The name of the member
* @param booleans The booleans
* @return This builder
* @since 3.0.0
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, boolean... booleans) {
values.put(name, booleans);
return this;
}
/**
* Sets the given member to the given enum object.
*
* @param name The name of the member
* @param enumObj The enum
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable Enum<?> enumObj) {
if (enumObj != null) {
values.put(name, enumObj);
}
return this;
}
/**
* Sets the given member to the given enum objects.
*
* @param name The name of the member
* @param enumObjs The enum[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable Enum<?>... enumObjs) {
if (enumObjs != null) {
values.put(name, enumObjs);
}
return this;
}
/**
* Sets the given member to the given type object.
*
* @param name The name of the member
* @param type The type
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable Class<?> type) {
if (type != null) {
values.put(name, new AnnotationClassValue<>(type));
}
return this;
}
/**
* Sets the given member to the given type objects.
*
* @param name The name of the member
* @param types The type[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable Class<?>... types) {
if (types != null) {
AnnotationClassValue<?>[] classValues = new AnnotationClassValue[types.length];
for (int i = 0; i < types.length; i++) {
Class<?> type = types[i];
classValues[i] = new AnnotationClassValue<>(type);
}
values.put(name, classValues);
}
return this;
}
/**
* Sets the given member to the given annotation value.
*
* @param name The name of the member
* @param annotation The annotation
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable AnnotationValue<?> annotation) {
if (annotation != null) {
values.put(name, annotation);
}
return this;
}
/**
* Sets the given member to the given annotation values.
*
* @param name The name of the member
* @param annotations The annotation[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable AnnotationValue<?>... annotations) {
if (annotations != null) {
values.put(name, annotations);
}
return this;
}
/**
* Sets the given member to the given annotation class values.
*
* @param name The name of the member
* @param classValues The annotation[]
* @return This builder
*/
@NonNull
public AnnotationValueBuilder<T> member(@NonNull String name, @Nullable AnnotationClassValue<?>... classValues) {
if (classValues != null) {
values.put(name, classValues);
}
return this;
}
/**
* Adds the members from the provided map. All values must be primitives, enums,
* strings, annotation values, or an array of the previous types.
*
* @param members The map of members
* @return This builder
* @since 2.4.0
*/
@NonNull
public AnnotationValueBuilder<T> members(@Nullable Map<CharSequence, Object> members) {
if (members != null) {
for (Map.Entry<CharSequence, Object> entry: members.entrySet()) {
Object value = entry.getValue();
if (value != null) {
Class<?> clazz = value.getClass();
boolean isArray = clazz.isArray();
if (isArray) {
clazz = clazz.getComponentType();
}
boolean isValid = !clazz.isArray() &&
(
clazz.isPrimitive() ||
(ReflectionUtils.getPrimitiveType(clazz).isPrimitive() && !isArray) ||
clazz.isEnum() ||
clazz == Class.class ||
clazz == String.class ||
clazz == Enum.class ||
clazz == AnnotationClassValue.class ||
clazz == AnnotationValue.class ||
clazz == EvaluatedExpressionReference.class ||
clazz == EvaluatedExpression.class
);
if (!isValid) {
throw new IllegalArgumentException("The member named [" + entry.getKey().toString() + "] with type [" + value.getClass().getName() + "] is not a valid member type");
}
}
}
for (Map.Entry<CharSequence, Object> entry: members.entrySet()) {
Object value = entry.getValue();
if (value != null) {
Class<?> clazz = value.getClass();
String key = entry.getKey().toString();
if (clazz == Class.class) {
member(key, (Class<?>) value);
} else if (clazz.isArray() && clazz.getComponentType() == Class.class) {
member(key, (Class<?>[]) value);
} else {
values.put(key, value);
}
}
}
}
return this;
}
}