PropertyElementAnnotationMetadata.java
/*
* Copyright 2017-2023 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.ast.annotation;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.AnnotationValueBuilder;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
import io.micronaut.inject.annotation.MutableAnnotationMetadata;
import io.micronaut.inject.ast.ClassElement;
import io.micronaut.inject.ast.FieldElement;
import io.micronaut.inject.ast.MethodElement;
import io.micronaut.inject.ast.ParameterElement;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The element annotation metadata for property element.
*
* @author Denis Stepanov
* @since 4.0.0
*/
public final class PropertyElementAnnotationMetadata implements ElementAnnotationMetadata {
private final io.micronaut.inject.ast.Element thisElement;
private final List<MutableAnnotationMetadataDelegate<?>> elements;
private final AnnotationMetadata propertyAnnotationMetadata;
private final AnnotationMetadata propertyReadAnnotationMetadata;
private final AnnotationMetadata propertyWriteAnnotationMetadata;
public PropertyElementAnnotationMetadata(@NonNull
io.micronaut.inject.ast.Element thisElement,
@Nullable
MethodElement getter,
@Nullable
MethodElement setter,
@Nullable
FieldElement field,
@Nullable
ParameterElement constructorParameter,
boolean includeSynthetic) {
this.thisElement = thisElement;
List<MutableAnnotationMetadataDelegate<?>> elements = new ArrayList<>(3);
List<MutableAnnotationMetadataDelegate<?>> readElements = new ArrayList<>(3);
List<MutableAnnotationMetadataDelegate<?>> writeElements = new ArrayList<>(3);
if (setter != null && (!setter.isSynthetic() || includeSynthetic)) {
elements.add(setter.getMethodAnnotationMetadata());
writeElements.add(setter.getMethodAnnotationMetadata());
ParameterElement[] parameters = setter.getParameters();
if (parameters.length > 0) {
ParameterElement parameter = parameters[0];
MutableAnnotationMetadataDelegate<?> typeAnnotationMetadata = parameter.getType().getTypeAnnotationMetadata();
if (!typeAnnotationMetadata.isEmpty()) {
elements.add(typeAnnotationMetadata);
writeElements.add(typeAnnotationMetadata);
}
}
}
if (constructorParameter != null) {
elements.add(constructorParameter);
MutableAnnotationMetadataDelegate<?> typeAnnotationMetadata = constructorParameter.getType().getTypeAnnotationMetadata();
if (!typeAnnotationMetadata.isEmpty()) {
elements.add(typeAnnotationMetadata);
writeElements.add(typeAnnotationMetadata);
}
}
if (field != null && (!field.isSynthetic() || includeSynthetic)) {
ClassElement genericFieldType = field.getGenericType();
if (getter != null && getter.getGenericReturnType().isAssignable(Optional.class) && !genericFieldType.isAssignable(Optional.class)) {
// The case with an Optional getter and a wrapped container field with annotations
// We need to copy all the annotations from the field to the type argument of the optional
// In the future we might want to support all kind of containers, not just Optional
ClassElement wrappedArgument = getter.getGenericReturnType().getTypeArguments(Optional.class).get("T");
if (wrappedArgument != null) {
AnnotationMetadata wrappedArgumentAnnotationMetadata = wrappedArgument.getTargetAnnotationMetadata();
AnnotationMetadata fieldAnnotationMetadata = genericFieldType.getAnnotationMetadata().getTargetAnnotationMetadata();
if (wrappedArgumentAnnotationMetadata instanceof MutableAnnotationMetadata mutableAnnotationMetadata
&& fieldAnnotationMetadata instanceof MutableAnnotationMetadata fieldMutableAnnotationMetadata) {
mutableAnnotationMetadata.addAnnotationMetadata(fieldMutableAnnotationMetadata);
}
}
} else {
elements.add(field);
writeElements.add(field);
readElements.add(field);
MutableAnnotationMetadataDelegate<?> typeAnnotationMetadata = field.getType().getTypeAnnotationMetadata();
if (!typeAnnotationMetadata.isEmpty()) {
elements.add(typeAnnotationMetadata);
writeElements.add(typeAnnotationMetadata);
readElements.add(typeAnnotationMetadata);
}
}
}
if (getter != null && (!getter.isSynthetic() || includeSynthetic)) {
elements.add(getter.getMethodAnnotationMetadata());
readElements.add(getter.getMethodAnnotationMetadata());
MutableAnnotationMetadataDelegate<?> typeAnnotationMetadata = getter.getReturnType().getTypeAnnotationMetadata();
if (!typeAnnotationMetadata.isEmpty()) {
elements.add(typeAnnotationMetadata);
readElements.add(typeAnnotationMetadata);
}
}
// The instance AnnotationMetadata of each element can change after a modification
// Set annotation metadata as actual elements so the changes are reflected
AnnotationMetadata[] hierarchy = elements.toArray(AnnotationMetadata[]::new);
this.propertyAnnotationMetadata =
hierarchy.length == 1 ? hierarchy[0] : new AnnotationMetadataHierarchy(true, hierarchy);
AnnotationMetadata[] readHierarchy = readElements.toArray(AnnotationMetadata[]::new);
this.propertyReadAnnotationMetadata =
readHierarchy.length == 1 ? readHierarchy[0] : new AnnotationMetadataHierarchy(true, readHierarchy);
AnnotationMetadata[] writeHierarchy = writeElements.toArray(AnnotationMetadata[]::new);
this.propertyWriteAnnotationMetadata =
writeHierarchy.length == 1 ? writeHierarchy[0] : new AnnotationMetadataHierarchy(true, writeHierarchy);
this.elements = elements;
}
@Override
public <T extends Annotation> io.micronaut.inject.ast.Element annotate(AnnotationValue<T> annotationValue) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.annotate(annotationValue);
}
return thisElement;
}
@Override
public <T extends Annotation> io.micronaut.inject.ast.Element annotate(String annotationType, Consumer<AnnotationValueBuilder<T>> consumer) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.annotate(annotationType, consumer);
}
return thisElement;
}
@Override
public <T extends Annotation> io.micronaut.inject.ast.Element annotate(Class<T> annotationType) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.annotate(annotationType);
}
return thisElement;
}
@Override
public io.micronaut.inject.ast.Element annotate(String annotationType) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.annotate(annotationType);
}
return thisElement;
}
@Override
public <T extends Annotation> io.micronaut.inject.ast.Element annotate(Class<T> annotationType, Consumer<AnnotationValueBuilder<T>> consumer) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.annotate(annotationType, consumer);
}
return thisElement;
}
@Override
public io.micronaut.inject.ast.Element removeAnnotation(String annotationType) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.removeAnnotation(annotationType);
}
return thisElement;
}
@Override
public <T extends Annotation> io.micronaut.inject.ast.Element removeAnnotationIf(Predicate<AnnotationValue<T>> predicate) {
for (MutableAnnotationMetadataDelegate<?> am : elements) {
am.removeAnnotationIf(predicate);
}
return thisElement;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return propertyAnnotationMetadata;
}
/**
* @return The read annotation metadata
*/
public AnnotationMetadata getReadAnnotationMetadata() {
return propertyReadAnnotationMetadata;
}
/**
* @return The write annotation metadata
*/
public AnnotationMetadata getWriteAnnotationMetadata() {
return propertyWriteAnnotationMetadata;
}
}