Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.inject.annotation.MutableAnnotationMetadata Maven / Gradle / Ivy
/*
* 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.env.DefaultPropertyPlaceholderResolver;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.expressions.EvaluatedExpressionReference;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* A mutable various of {@link DefaultAnnotationMetadata} that is used only at build time.
*
* @author graemerocher
* @since 2.4.0
*/
public class MutableAnnotationMetadata extends DefaultAnnotationMetadata {
private boolean hasPropertyExpressions = false;
private boolean hasEvaluatedExpressions = false;
@Nullable
Map> annotationDefaultValues;
@Nullable
private Set sourceRetentionAnnotations;
@Nullable
private Map> sourceAnnotationDefaultValues;
@Nullable
Map annotationRepeatableContainer;
/**
* Default constructor.
*/
public MutableAnnotationMetadata() {
}
private MutableAnnotationMetadata(@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);
}
private MutableAnnotationMetadata(@Nullable Map> declaredAnnotations,
@Nullable Map> declaredStereotypes,
@Nullable Map> allStereotypes,
@Nullable Map> allAnnotations,
@Nullable Map> annotationsByStereotype,
boolean hasPropertyExpressions,
boolean hasEvaluatedExpressions) {
super(declaredAnnotations,
declaredStereotypes,
allStereotypes,
allAnnotations,
annotationsByStereotype,
hasPropertyExpressions);
this.hasPropertyExpressions = hasPropertyExpressions;
this.hasEvaluatedExpressions = hasEvaluatedExpressions;
}
public static MutableAnnotationMetadata of(AnnotationMetadata annotationMetadata) {
if (annotationMetadata.isEmpty()) {
return new MutableAnnotationMetadata();
}
annotationMetadata = annotationMetadata.getTargetAnnotationMetadata();
if (annotationMetadata instanceof AnnotationMetadataHierarchy hierarchy) {
return hierarchy.merge();
} else if (annotationMetadata instanceof MutableAnnotationMetadata metadata) {
return metadata.clone();
} else if (annotationMetadata instanceof DefaultAnnotationMetadata defaultAnnotationMetadata) {
MutableAnnotationMetadata metadata = new MutableAnnotationMetadata();
metadata.addAnnotationMetadata(defaultAnnotationMetadata);
return metadata;
} else {
throw new IllegalStateException("Unknown annotation metadata: " + annotationMetadata);
}
}
@Override
public boolean hasPropertyExpressions() {
return hasPropertyExpressions;
}
@Override
public boolean hasEvaluatedExpressions() {
return hasEvaluatedExpressions;
}
@Override
public MutableAnnotationMetadata clone() {
final MutableAnnotationMetadata cloned = new MutableAnnotationMetadata(
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,
hasEvaluatedExpressions
);
if (annotationDefaultValues != null) {
cloned.annotationDefaultValues = new LinkedHashMap<>(annotationDefaultValues);
}
if (annotationRepeatableContainer != null) {
cloned.annotationRepeatableContainer = new HashMap<>(annotationRepeatableContainer);
}
if (sourceRetentionAnnotations != null) {
cloned.sourceRetentionAnnotations = new HashSet<>(sourceRetentionAnnotations);
}
if (annotationDefaultValues != null) {
cloned.annotationDefaultValues = cloneMapOfMapValue(annotationDefaultValues);
}
if (sourceAnnotationDefaultValues != null) {
cloned.sourceAnnotationDefaultValues = cloneMapOfMapValue(sourceAnnotationDefaultValues);
}
cloned.hasPropertyExpressions = hasPropertyExpressions;
cloned.hasEvaluatedExpressions = hasEvaluatedExpressions;
return cloned;
}
@NonNull
@Override
public Map getDefaultValues(@NonNull String annotation) {
Map values = super.getDefaultValues(annotation);
if (!values.isEmpty() || annotationDefaultValues == null) {
return values;
}
final Map compileTimeDefaults = annotationDefaultValues.get(annotation);
if (compileTimeDefaults != null && !compileTimeDefaults.isEmpty()) {
return compileTimeDefaults.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue));
}
return values;
}
private boolean computeHasPropertyExpressions(Map values, RetentionPolicy retentionPolicy) {
return hasPropertyExpressions || values != null && retentionPolicy == RetentionPolicy.RUNTIME && hasPropertyExpressions(values);
}
private boolean computeHasEvaluatedExpressions(Map values, RetentionPolicy retentionPolicy) {
return hasEvaluatedExpressions || values != null && retentionPolicy == RetentionPolicy.RUNTIME && hasEvaluatedExpressions(values);
}
private boolean hasPropertyExpressions(Map values) {
if (CollectionUtils.isEmpty(values)) {
return false;
}
return values.values().stream().anyMatch(v -> {
if (v instanceof CharSequence) {
return v.toString().contains(DefaultPropertyPlaceholderResolver.PREFIX);
} else if (v instanceof String[] strings) {
return Arrays.stream(strings).anyMatch(s -> s.contains(DefaultPropertyPlaceholderResolver.PREFIX));
} else if (v instanceof AnnotationValue> annotationValue) {
return hasPropertyExpressions(annotationValue.getValues());
} else if (v instanceof AnnotationValue>[] annotationValues) {
if (annotationValues.length > 0) {
return Arrays.stream(annotationValues).anyMatch(av -> hasPropertyExpressions(av.getValues()));
} else {
return false;
}
} else {
return false;
}
});
}
/**
* @return The annotations that are source retention.
*/
@Internal
Set getSourceRetentionAnnotations() {
if (sourceRetentionAnnotations != null) {
return Collections.unmodifiableSet(sourceRetentionAnnotations);
}
return Collections.emptySet();
}
/**
* Adds an annotation and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param annotation The annotation
* @param values The values
*/
@SuppressWarnings("WeakerAccess")
public void addAnnotation(String annotation, Map values) {
addAnnotation(annotation, values, RetentionPolicy.RUNTIME);
}
/**
* Adds an annotation and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param annotation The annotation
* @param values The values
* @param retentionPolicy The retention policy
*/
@SuppressWarnings("WeakerAccess")
public void addAnnotation(String annotation, Map values, RetentionPolicy retentionPolicy) {
if (annotation == null) {
return;
}
if (isRepeatableAnnotationContainer(annotation)) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue>[] annotationValues) {
for (io.micronaut.core.annotation.AnnotationValue> annotationValue : annotationValues) {
addRepeatable(annotation, annotationValue);
}
} else if (v instanceof Iterable> iterable) {
for (Object o : iterable) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue> annotationValue) {
addRepeatable(annotation, annotationValue);
}
}
}
} else {
Map> allAnnotations = getAllAnnotations();
addAnnotation(annotation, values, null, allAnnotations, false, retentionPolicy);
}
}
/**
* Adds an annotation directly declared on the element and its member values, if the annotation already exists the
* data will be merged with existing values replaced.
*
* @param annotation The annotation
* @param values The values
*/
public final void addDefaultAnnotationValues(String annotation, Map values) {
addDefaultAnnotationValues(annotation, values, RetentionPolicy.RUNTIME);
}
/**
* Adds an annotation directly declared on the element and its member values, if the annotation already exists the
* data will be merged with existing values replaced.
*
* @param annotation The annotation
* @param values The values
* @param retentionPolicy The retention policy
*/
public final void addDefaultAnnotationValues(String annotation, Map values, RetentionPolicy retentionPolicy) {
if (annotation == null) {
return;
}
Map> annotationDefaults;
if (retentionPolicy == RetentionPolicy.RUNTIME) {
annotationDefaults = this.annotationDefaultValues;
if (annotationDefaults == null) {
this.annotationDefaultValues = new LinkedHashMap<>();
annotationDefaults = this.annotationDefaultValues;
}
} else {
annotationDefaults = this.sourceAnnotationDefaultValues;
if (annotationDefaults == null) {
this.sourceAnnotationDefaultValues = new LinkedHashMap<>();
annotationDefaults = this.sourceAnnotationDefaultValues;
}
}
putValues(annotation, values, annotationDefaults);
}
/**
* Adds a repeatable annotation value. If a value already exists will be added
*
* @param annotationName The annotation name
* @param annotationValue The annotation value
*/
public void addRepeatable(String annotationName, AnnotationValue> annotationValue) {
addRepeatable(annotationName, annotationValue, annotationValue.getRetentionPolicy());
}
/**
* Adds a repeatable annotation value. If a value already exists will be added
*
* @param annotationName The annotation name
* @param annotationValue The annotation value
* @param retentionPolicy The retention policy
*/
public void addRepeatable(String annotationName, AnnotationValue> annotationValue, RetentionPolicy retentionPolicy) {
if (StringUtils.isNotEmpty(annotationName) && annotationValue != null) {
Map> allAnnotations = getAllAnnotations();
addRepeatableInternal(annotationName, annotationValue, allAnnotations, retentionPolicy);
}
}
/**
* Adds a repeatable stereotype value. If a value already exists will be added
*
* @param parents The parent annotations
* @param stereotype The annotation name
* @param annotationValue The annotation value
*/
public void addRepeatableStereotype(List parents, String stereotype, AnnotationValue> annotationValue) {
Map> allStereotypes = getAllStereotypes();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
for (String parentAnnotation : parents) {
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
addRepeatableInternal(stereotype, annotationValue, allStereotypes, RetentionPolicy.RUNTIME);
}
/**
* Adds a repeatable declared stereotype value. If a value already exists will be added
*
* @param parents The parent annotations
* @param stereotype The annotation name
* @param annotationValue The annotation value
*/
public void addDeclaredRepeatableStereotype(List parents, String stereotype, AnnotationValue> annotationValue) {
Map> declaredStereotypes = getDeclaredStereotypesInternal();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
for (String parentAnnotation : parents) {
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
addRepeatableInternal(stereotype, annotationValue, declaredStereotypes, RetentionPolicy.RUNTIME);
addRepeatableInternal(stereotype, annotationValue, getAllStereotypes(), RetentionPolicy.RUNTIME);
}
/**
* Adds a repeatable annotation value. If a value already exists will be added
*
* @param annotationName The annotation name
* @param annotationValue The annotation value
*/
public void addDeclaredRepeatable(String annotationName, AnnotationValue> annotationValue) {
addDeclaredRepeatable(annotationName, annotationValue, annotationValue.getRetentionPolicy());
}
/**
* Adds a repeatable annotation value. If a value already exists will be added
*
* @param annotationName The annotation name
* @param annotationValue The annotation value
* @param retentionPolicy The retention policy
*/
public void addDeclaredRepeatable(String annotationName, AnnotationValue> annotationValue, RetentionPolicy retentionPolicy) {
if (StringUtils.isNotEmpty(annotationName) && annotationValue != null) {
Map> allAnnotations = getDeclaredAnnotationsInternal();
addRepeatableInternal(annotationName, annotationValue, allAnnotations, retentionPolicy);
addRepeatable(annotationName, annotationValue);
}
}
/**
* Adds a stereotype and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param parentAnnotations The parent annotations
* @param stereotype The annotation
* @param values The values
*/
@SuppressWarnings("WeakerAccess")
public final void addStereotype(List parentAnnotations, String stereotype, Map values) {
addStereotype(parentAnnotations, stereotype, values, RetentionPolicy.RUNTIME);
}
/**
* Adds a stereotype and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param parentAnnotations The parent annotations
* @param stereotype The annotation
* @param values The values
* @param retentionPolicy The retention policy
*/
@SuppressWarnings("WeakerAccess")
public final void addStereotype(List parentAnnotations, String stereotype, Map values, RetentionPolicy retentionPolicy) {
if (stereotype == null) {
return;
}
if (isRepeatableAnnotationContainer(stereotype)) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue>[] annotationValues) {
for (io.micronaut.core.annotation.AnnotationValue> annotationValue : annotationValues) {
addRepeatableStereotype(parentAnnotations, stereotype, annotationValue);
}
} else if (v instanceof Iterable> iterable) {
for (Object o : iterable) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue> annotationValue) {
addRepeatableStereotype(parentAnnotations, stereotype, annotationValue);
}
}
}
} else {
Map> allStereotypes = getAllStereotypes();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
if (!parentAnnotations.isEmpty()) {
final String parentAnnotation = CollectionUtils.last(parentAnnotations);
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
// add to stereotypes
addAnnotation(
stereotype,
values,
null,
allStereotypes,
false,
retentionPolicy
);
}
}
/**
* Adds a stereotype and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param parentAnnotations The parent annotations
* @param stereotype The annotation
* @param values The values
*/
@SuppressWarnings("WeakerAccess")
public void addDeclaredStereotype(List parentAnnotations, String stereotype, Map values) {
addDeclaredStereotype(parentAnnotations, stereotype, values, RetentionPolicy.RUNTIME);
}
/**
* Adds a stereotype and its member values, if the annotation already exists the data will be merged with existing
* values replaced.
*
* @param parentAnnotations The parent annotations
* @param stereotype The annotation
* @param values The values
* @param retentionPolicy The retention policy
*/
@SuppressWarnings("WeakerAccess")
public void addDeclaredStereotype(List parentAnnotations, String stereotype, Map values, RetentionPolicy retentionPolicy) {
if (stereotype == null) {
return;
}
if (isRepeatableAnnotationContainer(stereotype)) {
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v instanceof io.micronaut.core.annotation.AnnotationValue>[] annotationValues) {
for (io.micronaut.core.annotation.AnnotationValue> annotationValue : annotationValues) {
addDeclaredRepeatableStereotype(parentAnnotations, stereotype, annotationValue);
}
} else if (v instanceof Iterable> iterable) {
for (Object o : iterable) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue> annotationValue) {
addDeclaredRepeatableStereotype(parentAnnotations, stereotype, annotationValue);
}
}
}
} else {
Map> declaredStereotypes = getDeclaredStereotypesInternal();
Map> allStereotypes = getAllStereotypes();
List annotationList = getAnnotationsByStereotypeInternal(stereotype);
if (!parentAnnotations.isEmpty()) {
final String parentAnnotation = CollectionUtils.last(parentAnnotations);
if (!annotationList.contains(parentAnnotation)) {
annotationList.add(parentAnnotation);
}
}
addAnnotation(
stereotype,
values,
declaredStereotypes,
allStereotypes,
true,
retentionPolicy
);
}
}
/**
* Adds an annotation directly declared on the element and its member values, if the annotation already exists the
* data will be merged with existing values replaced.
*
* @param annotation The annotation
* @param values The values
*/
public void addDeclaredAnnotation(String annotation, Map values) {
addDeclaredAnnotation(annotation, values, RetentionPolicy.RUNTIME);
}
/**
* Adds an annotation directly declared on the element and its member values, if the annotation already exists the
* data will be merged with existing values replaced.
*
* @param annotation The annotation
* @param values The values
* @param retentionPolicy The retention policy
*/
public void addDeclaredAnnotation(String annotation, Map values, RetentionPolicy retentionPolicy) {
if (annotation == null) {
return;
}
boolean hasOtherMembers = false;
boolean repeatableAnnotationContainer = isRepeatableAnnotationContainer(annotation);
if (isRepeatableAnnotationContainer(annotation)) {
for (Map.Entry entry : values.entrySet()) {
if (entry.getKey().equals(AnnotationMetadata.VALUE_MEMBER)) {
Object v = entry.getValue();
if (v instanceof io.micronaut.core.annotation.AnnotationValue>[] annotationValues) {
for (io.micronaut.core.annotation.AnnotationValue> annotationValue : annotationValues) {
addDeclaredRepeatable(annotation, annotationValue);
}
} else if (v instanceof Iterable> iterable) {
for (Object o : iterable) {
if (o instanceof io.micronaut.core.annotation.AnnotationValue> annotationValue) {
addDeclaredRepeatable(annotation, annotationValue);
}
}
}
} else {
hasOtherMembers = true;
}
}
}
if (!repeatableAnnotationContainer || hasOtherMembers) {
Map> declaredAnnotations = getDeclaredAnnotationsInternal();
Map> allAnnotations = getAllAnnotations();
addAnnotation(annotation, values, declaredAnnotations, allAnnotations, true, retentionPolicy);
}
}
@SuppressWarnings("java:S2259") // false positive
private void addAnnotation(String annotation,
Map values,
Map> declaredAnnotations,
Map> allAnnotations,
boolean isDeclared,
RetentionPolicy retentionPolicy) {
hasPropertyExpressions = computeHasPropertyExpressions(values, retentionPolicy);
hasEvaluatedExpressions = computeHasEvaluatedExpressions(values, retentionPolicy);
if (isDeclared && declaredAnnotations != null) {
putValues(annotation, values, declaredAnnotations);
}
putValues(annotation, values, allAnnotations);
// Annotations with retention CLASS need not be retained at run time
if (retentionPolicy == RetentionPolicy.SOURCE || retentionPolicy == RetentionPolicy.CLASS) {
addSourceRetentionAnnotation(annotation);
}
}
private void addSourceRetentionAnnotation(String annotation) {
if (sourceRetentionAnnotations == null) {
sourceRetentionAnnotations = new HashSet<>(5);
}
sourceRetentionAnnotations.add(annotation);
}
@SuppressWarnings("java:S2259")
private void putValues(String annotation, Map values, Map> currentAnnotationValues) {
Map existing = currentAnnotationValues.get(annotation);
boolean hasValues = CollectionUtils.isNotEmpty(values);
if (existing != null && hasValues) {
if (existing.isEmpty()) {
existing = new LinkedHashMap<>();
currentAnnotationValues.put(annotation, existing);
}
for (CharSequence key : values.keySet()) {
if (!existing.containsKey(key)) {
existing.put(key, values.get(key));
}
}
} else {
if (!hasValues) {
existing = existing == null ? CollectionUtils.newLinkedHashMap(3) : existing;
} else {
existing = CollectionUtils.newLinkedHashMap(values.size());
existing.putAll(values);
}
currentAnnotationValues.put(annotation, existing);
}
}
@SuppressWarnings("MagicNumber")
private Map> getAllStereotypes() {
Map> stereotypes = this.allStereotypes;
if (stereotypes == null) {
stereotypes = new HashMap<>(3);
this.allStereotypes = stereotypes;
}
return stereotypes;
}
@SuppressWarnings("MagicNumber")
private Map> getDeclaredStereotypesInternal() {
Map> stereotypes = this.declaredStereotypes;
if (stereotypes == null) {
stereotypes = new HashMap<>(3);
this.declaredStereotypes = stereotypes;
}
return stereotypes;
}
@SuppressWarnings("MagicNumber")
private Map> getAllAnnotations() {
Map> annotations = this.allAnnotations;
if (annotations == null) {
annotations = new HashMap<>(3);
this.allAnnotations = annotations;
}
return annotations;
}
@SuppressWarnings("MagicNumber")
private Map> getDeclaredAnnotationsInternal() {
Map> annotations = this.declaredAnnotations;
if (annotations == null) {
annotations = new HashMap<>(3);
this.declaredAnnotations = annotations;
}
return annotations;
}
private List getAnnotationsByStereotypeInternal(String stereotype) {
return getAnnotationsByStereotypeInternal().computeIfAbsent(stereotype, s -> new ArrayList<>());
}
@SuppressWarnings("MagicNumber")
private Map> getAnnotationsByStereotypeInternal() {
Map> annotations = this.annotationsByStereotype;
if (annotations == null) {
annotations = new HashMap<>(3);
this.annotationsByStereotype = annotations;
}
return annotations;
}
private void addRepeatableInternal(String repeatableAnnotationContainer,
AnnotationValue> annotationValue,
Map> allAnnotations,
RetentionPolicy retentionPolicy) {
hasPropertyExpressions = computeHasPropertyExpressions(annotationValue.getValues(), retentionPolicy);
hasEvaluatedExpressions = computeHasEvaluatedExpressions(annotationValue.getValues(), retentionPolicy);
if (annotationRepeatableContainer == null) {
annotationRepeatableContainer = new HashMap<>(2);
}
annotationRepeatableContainer.put(annotationValue.getAnnotationName(), repeatableAnnotationContainer);
if (retentionPolicy == RetentionPolicy.SOURCE) {
addSourceRetentionAnnotation(repeatableAnnotationContainer);
}
Map values = allAnnotations.computeIfAbsent(repeatableAnnotationContainer, s -> new HashMap<>());
Object v = values.get(AnnotationMetadata.VALUE_MEMBER);
if (v != null) {
if (v.getClass().isArray()) {
Object[] array = (Object[]) v;
Set newValues = CollectionUtils.newLinkedHashSet(array.length + 1);
newValues.addAll(Arrays.asList(array));
newValues.add(annotationValue);
values.put(AnnotationMetadata.VALUE_MEMBER, newValues);
} else if (v instanceof Collection collection) {
collection.add(annotationValue);
}
} else {
Set newValues = new LinkedHashSet<>(2);
newValues.add(annotationValue);
values.put(AnnotationMetadata.VALUE_MEMBER, newValues);
}
}
/**
* Sets a member of the given {@link AnnotationMetadata} return a new annotation metadata instance without
* mutating the existing.
*
* WARNING: for internal use only be the framework
*
* @param annotationMetadata The metadata
* @param annotationName The annotation name
* @param member The member
* @param value The value
* @return The metadata
*/
@Internal
public static MutableAnnotationMetadata mutateMember(MutableAnnotationMetadata annotationMetadata,
String annotationName,
String member,
Object value) {
return mutateMember(annotationMetadata, annotationName, Collections.singletonMap(member, value));
}
@Override
protected AnnotationValue newAnnotationValue(String annotationType, Map values) {
Map defaultValues = null;
if (annotationDefaultValues != null) {
defaultValues = annotationDefaultValues.get(annotationType);
}
if (defaultValues == null && sourceAnnotationDefaultValues != null) {
defaultValues = sourceAnnotationDefaultValues.get(annotationType);
}
if (defaultValues == null) {
defaultValues = AnnotationMetadataSupport.getDefaultValuesOrNull(annotationType);
}
return new AnnotationValue<>(annotationType, values, defaultValues);
}
/**
* Include the annotation metadata from the other instance of {@link DefaultAnnotationMetadata}.
*
* @param annotationMetadata The annotation metadata
* @since 4.0.0
*/
@Internal
public void addAnnotationMetadata(DefaultAnnotationMetadata annotationMetadata) {
hasPropertyExpressions |= annotationMetadata.hasPropertyExpressions();
hasEvaluatedExpressions |= annotationMetadata.hasEvaluatedExpressions();
if (annotationMetadata.declaredAnnotations != null && !annotationMetadata.declaredAnnotations.isEmpty()) {
if (declaredAnnotations == null) {
declaredAnnotations = new LinkedHashMap<>();
}
for (Map.Entry> entry : annotationMetadata.declaredAnnotations.entrySet()) {
putValues(entry.getKey(), entry.getValue(), declaredAnnotations);
}
}
if (annotationMetadata.declaredStereotypes != null && !annotationMetadata.declaredStereotypes.isEmpty()) {
if (declaredStereotypes == null) {
declaredStereotypes = new LinkedHashMap<>();
}
for (Map.Entry> entry : annotationMetadata.declaredStereotypes.entrySet()) {
putValues(entry.getKey(), entry.getValue(), declaredStereotypes);
}
}
if (annotationMetadata.allStereotypes != null && !annotationMetadata.allStereotypes.isEmpty()) {
if (allStereotypes == null) {
allStereotypes = new LinkedHashMap<>();
}
for (Map.Entry> entry : annotationMetadata.allStereotypes.entrySet()) {
putValues(entry.getKey(), entry.getValue(), allStereotypes);
}
}
if (annotationMetadata.allAnnotations != null && !annotationMetadata.allAnnotations.isEmpty()) {
if (allAnnotations == null) {
allAnnotations = new LinkedHashMap<>();
}
for (Map.Entry> entry : annotationMetadata.allAnnotations.entrySet()) {
putValues(entry.getKey(), entry.getValue(), allAnnotations);
}
}
Map> source = annotationMetadata.annotationsByStereotype;
if (source != null && !source.isEmpty()) {
if (annotationsByStereotype == null) {
annotationsByStereotype = new LinkedHashMap<>();
}
for (Map.Entry> entry : source.entrySet()) {
String ann = entry.getKey();
List prevValues = annotationsByStereotype.get(ann);
if (prevValues == null) {
annotationsByStereotype.put(ann, new ArrayList<>(entry.getValue()));
} else {
Set prevValuesSet = new LinkedHashSet<>(prevValues);
prevValuesSet.addAll(entry.getValue());
annotationsByStereotype.put(ann, new ArrayList<>(prevValuesSet));
}
}
}
}
/**
* Include the annotation metadata from the other instance of {@link DefaultAnnotationMetadata}.
*
* @param annotationMetadata The annotation metadata
* @since 4.0.0
*/
@Internal
public void addAnnotationMetadata(MutableAnnotationMetadata annotationMetadata) {
addAnnotationMetadata((DefaultAnnotationMetadata) annotationMetadata);
hasPropertyExpressions |= annotationMetadata.hasPropertyExpressions;
hasEvaluatedExpressions |= annotationMetadata.hasEvaluatedExpressions;
if (annotationMetadata.sourceRetentionAnnotations != null) {
if (sourceRetentionAnnotations == null) {
sourceRetentionAnnotations = new HashSet<>(annotationMetadata.sourceRetentionAnnotations);
} else {
sourceRetentionAnnotations.addAll(annotationMetadata.sourceRetentionAnnotations);
}
}
if (annotationMetadata.annotationDefaultValues != null) {
if (annotationDefaultValues == null) {
annotationDefaultValues = new LinkedHashMap<>(annotationMetadata.annotationDefaultValues);
} else {
// No need to merge values
annotationDefaultValues.putAll(annotationMetadata.annotationDefaultValues);
}
}
if (annotationMetadata.sourceAnnotationDefaultValues != null) {
if (sourceAnnotationDefaultValues == null) {
sourceAnnotationDefaultValues = new LinkedHashMap<>(annotationMetadata.sourceAnnotationDefaultValues);
} else {
// No need to merge values
sourceAnnotationDefaultValues.putAll(annotationMetadata.sourceAnnotationDefaultValues);
}
}
if (annotationMetadata.annotationRepeatableContainer != null) {
if (annotationRepeatableContainer == null) {
annotationRepeatableContainer = new LinkedHashMap<>(annotationMetadata.annotationRepeatableContainer);
} else {
annotationRepeatableContainer.putAll(annotationMetadata.annotationRepeatableContainer);
}
}
}
/**
* Contributes defaults to the given target.
*
* WARNING: for internal use only be the framework
*
* @param target The target
* @param source The source
*/
@Internal
public static void contributeDefaults(AnnotationMetadata target, AnnotationMetadata source) {
source = source.getTargetAnnotationMetadata();
if (source instanceof AnnotationMetadataHierarchy annotationMetadataHierarchy) {
contributeDefaults(target, annotationMetadataHierarchy);
return;
}
if (target instanceof MutableAnnotationMetadata damTarget && source instanceof MutableAnnotationMetadata damSource) {
final Map> existingDefaults = damTarget.annotationDefaultValues;
final Map> additionalDefaults = damSource.annotationDefaultValues;
if (existingDefaults != null) {
if (additionalDefaults != null) {
existingDefaults.putAll(
additionalDefaults
);
}
} else {
if (additionalDefaults != null) {
additionalDefaults.forEach(damTarget::addDefaultAnnotationValues);
}
}
}
// We don't need to contribute the default source annotation
contributeRepeatable(target, source);
}
/**
* Contributes defaults to the given target.
*
* WARNING: for internal use only be the framework
*
* @param target The target
* @param source The source
* @since 4.0.0
*/
@Internal
public static void contributeDefaults(AnnotationMetadata target, AnnotationMetadataHierarchy source) {
for (AnnotationMetadata annotationMetadata : source) {
if (annotationMetadata instanceof AnnotationMetadataReference) {
continue;
}
contributeDefaults(target, annotationMetadata);
}
}
/**
* Contributes repeatable annotation metadata to the given target.
*
* WARNING: for internal use only be the framework
*
* @param target The target
* @param source The source
*/
@Internal
public static void contributeRepeatable(AnnotationMetadata target, AnnotationMetadata source) {
source = source.getTargetAnnotationMetadata();
if (source instanceof AnnotationMetadataHierarchy hierarchy) {
source = hierarchy.merge();
}
if (target instanceof MutableAnnotationMetadata damTarget && source instanceof MutableAnnotationMetadata damSource) {
if (damSource.annotationRepeatableContainer != null && !damSource.annotationRepeatableContainer.isEmpty()) {
if (damTarget.annotationRepeatableContainer == null) {
damTarget.annotationRepeatableContainer = new HashMap<>(damSource.annotationRepeatableContainer);
} else {
damTarget.annotationRepeatableContainer.putAll(damSource.annotationRepeatableContainer);
}
}
}
}
/**
* Sets a member of the given {@link AnnotationMetadata} return a new annotation metadata instance without
* mutating the existing.
*
* WARNING: for internal use only be the framework
*
* @param annotationMetadata The metadata
* @param annotationName The annotation name
* @param members The key/value set of members and values
* @return The metadata
*/
@Internal
public static MutableAnnotationMetadata mutateMember(MutableAnnotationMetadata annotationMetadata,
String annotationName,
Map members) {
if (StringUtils.isEmpty(annotationName)) {
throw new IllegalArgumentException("Argument [annotationName] cannot be blank");
}
if (!members.isEmpty()) {
for (Map.Entry entry : members.entrySet()) {
if (StringUtils.isEmpty(entry.getKey())) {
throw new IllegalArgumentException("Argument [members] cannot have a blank key");
}
if (entry.getValue() == null) {
throw new IllegalArgumentException("Argument [members] cannot have a null value. Key [" + entry.getKey() + "]");
}
}
}
annotationMetadata = annotationMetadata.clone();
annotationMetadata.addDeclaredAnnotation(annotationName, members);
return annotationMetadata;
}
/**
* Removes an annotation for the given predicate.
*
* @param predicate The predicate
* @param The annotation
*/
public void removeAnnotationIf(@NonNull Predicate> predicate) {
removeAnnotationsIf(predicate, this.declaredAnnotations);
removeAnnotationsIf(predicate, this.allAnnotations);
}
private void removeAnnotationsIf(@NonNull Predicate> predicate, Map> annotations) {
if (annotations == null) {
return;
}
annotations.entrySet().removeIf(entry -> {
final String annotationName = entry.getKey();
if (predicate.test(newAnnotationValue(annotationName, entry.getValue()))) {
removeFromStereotypes(annotationName);
return true;
}
return false;
});
}
/**
* Removes an annotation for the given annotation type.
*
* @param annotationType The annotation type
* @since 3.0.0
*/
public void removeAnnotation(String annotationType) {
if (annotationType == null) {
return;
}
if (annotationDefaultValues != null) {
annotationDefaultValues.remove(annotationType);
}
if (allAnnotations != null) {
allAnnotations.remove(annotationType);
}
if (declaredAnnotations != null) {
declaredAnnotations.remove(annotationType);
removeFromStereotypes(annotationType);
}
if (annotationRepeatableContainer != null) {
annotationRepeatableContainer.remove(annotationType);
}
}
/**
* Removes a stereotype annotation for the given annotation type.
*
* @param annotationType The annotation type
* @since 3.0.0
*/
public void removeStereotype(String annotationType) {
if (annotationType == null) {
return;
}
if (annotationsByStereotype == null || annotationsByStereotype.remove(annotationType) == null) {
return;
}
if (allStereotypes != null) {
allStereotypes.remove(annotationType);
}
if (declaredStereotypes != null) {
declaredStereotypes.remove(annotationType);
}
final Iterator>> i = annotationsByStereotype.entrySet().iterator();
while (i.hasNext()) {
Map.Entry> entry = i.next();
final List value = entry.getValue();
if (value.remove(annotationType) && value.isEmpty()) {
i.remove();
}
}
}
private void removeFromStereotypes(String annotationType) {
if (annotationsByStereotype == null || annotationsByStereotype.isEmpty()) {
return;
}
final Iterator>> i = annotationsByStereotype.entrySet().iterator();
Set removeNext = new LinkedHashSet<>();
while (i.hasNext()) {
final Map.Entry> entry = i.next();
final String stereotypeName = entry.getKey();
final List value = entry.getValue();
if (value.remove(annotationType)) {
if (value.isEmpty()) {
removeNext.add(stereotypeName);
i.remove();
if (allStereotypes != null) {
this.allStereotypes.remove(stereotypeName);
}
if (declaredStereotypes != null) {
this.declaredStereotypes.remove(stereotypeName);
}
if (annotationDefaultValues != null) {
annotationDefaultValues.remove(stereotypeName);
}
removeNext.add(stereotypeName);
}
}
}
for (String stereotype : removeNext) {
removeFromStereotypes(stereotype);
}
}
private boolean isRepeatableAnnotationContainer(String annotation) {
return annotationRepeatableContainer != null && annotationRepeatableContainer.containsValue(annotation);
}
@Override
protected String findRepeatableAnnotationContainerInternal(String annotation) {
if (annotationRepeatableContainer != null) {
String repeatedName = annotationRepeatableContainer.get(annotation);
if (repeatedName != null) {
return repeatedName;
}
}
return AnnotationMetadataSupport.getRepeatableAnnotation(annotation);
}
private boolean hasEvaluatedExpressions(Map annotationValues) {
if (CollectionUtils.isEmpty(annotationValues)) {
return false;
}
return annotationValues.values().stream().anyMatch(value -> {
if (value instanceof EvaluatedExpressionReference) {
return true;
} else if (value instanceof AnnotationValue av) {
return hasEvaluatedExpressions(av.getValues());
} else if (value instanceof AnnotationValue[] avArray) {
return Arrays.stream(avArray)
.map(AnnotationValue::getValues)
.anyMatch(this::hasEvaluatedExpressions);
} else {
return false;
}
});
}
}