/*
* 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.annotation.processing;
import io.micronaut.annotation.processing.visitor.JavaVisitorContext;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.convert.value.MutableConvertibleValues;
import io.micronaut.core.convert.value.MutableConvertibleValuesMap;
import io.micronaut.core.io.service.ServiceDefinition;
import io.micronaut.core.io.service.SoftServiceLoader;
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap;
import io.micronaut.inject.annotation.AnnotatedElementValidator;
import io.micronaut.inject.annotation.AbstractAnnotationMetadataBuilder;
import edu.umd.cs.findbugs.annotations.Nullable;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.lang.annotation.Annotation;
import java.util.*;
Utility methods for annotations.
Author: Graeme Rocher, Dean Wette
/**
* Utility methods for annotations.
*
* @author Graeme Rocher
* @author Dean Wette
*/
@SuppressWarnings("ConstantName")
@Internal
public class AnnotationUtils {
private static final int CACHE_SIZE = 100;
private static final Map<Element, AnnotationMetadata> annotationMetadataCache
= new ConcurrentLinkedHashMap.Builder<Element, AnnotationMetadata>().maximumWeightedCapacity(CACHE_SIZE).build();
private final Elements elementUtils;
private final Messager messager;
private final Types types;
private final ModelUtils modelUtils;
private final Filer filer;
private final MutableConvertibleValues<Object> visitorAttributes;
private final ProcessingEnvironment processingEnv;
private final AnnotatedElementValidator elementValidator;
private JavaAnnotationMetadataBuilder javaAnnotationMetadataBuilder;
private final GenericUtils genericUtils;
Default constructor.
Params: - processingEnv – The processing env
- elementUtils – The elements
- messager – The messager
- types – The types
- modelUtils – The model utils
- genericUtils – The generic utils
- filer – The filer
- visitorAttributes – The visitor attributes
/**
* Default constructor.
*
* @param processingEnv The processing env
* @param elementUtils The elements
* @param messager The messager
* @param types The types
* @param modelUtils The model utils
* @param genericUtils The generic utils
* @param filer The filer
* @param visitorAttributes The visitor attributes
*/
protected AnnotationUtils(
ProcessingEnvironment processingEnv,
Elements elementUtils,
Messager messager,
Types types,
ModelUtils modelUtils,
GenericUtils genericUtils,
Filer filer,
MutableConvertibleValues<Object> visitorAttributes) {
this.elementUtils = elementUtils;
this.messager = messager;
this.types = types;
this.modelUtils = modelUtils;
this.genericUtils = genericUtils;
this.filer = filer;
this.visitorAttributes = visitorAttributes;
this.processingEnv = processingEnv;
final SoftServiceLoader<AnnotatedElementValidator> validators = SoftServiceLoader.load(AnnotatedElementValidator.class);
final Iterator<ServiceDefinition<AnnotatedElementValidator>> i = validators.iterator();
AnnotatedElementValidator elementValidator = null;
while (i.hasNext()) {
final ServiceDefinition<AnnotatedElementValidator> validator = i.next();
if (validator.isPresent()) {
elementValidator = validator.load();
break;
}
}
this.javaAnnotationMetadataBuilder = newAnnotationBuilder();
this.elementValidator = elementValidator;
}
Default constructor.
Params: - processingEnv – The processing env
- elementUtils – The elements
- messager – The messager
- types – The types
- modelUtils – The model utils
- genericUtils – The generic utils
- filer – The filer
/**
* Default constructor.
*
* @param processingEnv The processing env
* @param elementUtils The elements
* @param messager The messager
* @param types The types
* @param modelUtils The model utils
* @param genericUtils The generic utils
* @param filer The filer
*/
protected AnnotationUtils(
ProcessingEnvironment processingEnv,
Elements elementUtils,
Messager messager,
Types types,
ModelUtils modelUtils,
GenericUtils genericUtils,
Filer filer) {
this(processingEnv, elementUtils, messager, types, modelUtils, genericUtils, filer, new MutableConvertibleValuesMap<>());
}
The AnnotatedElementValidator
instance. Can be null. Returns: The validator instance
/**
* The {@link AnnotatedElementValidator} instance. Can be null.
* @return The validator instance
*/
public @Nullable AnnotatedElementValidator getElementValidator() {
return elementValidator;
}
Return whether the given element is annotated with the given annotation stereotype.
Params: - element – The element
- stereotype – The stereotype
Returns: True if it is
/**
* Return whether the given element is annotated with the given annotation stereotype.
*
* @param element The element
* @param stereotype The stereotype
* @return True if it is
*/
protected boolean hasStereotype(Element element, Class<? extends Annotation> stereotype) {
return hasStereotype(element, stereotype.getName());
}
Return whether the given element is annotated with the given annotation stereotypes.
Params: - element – The element
- stereotypes – The stereotypes
Returns: True if it is
/**
* Return whether the given element is annotated with the given annotation stereotypes.
*
* @param element The element
* @param stereotypes The stereotypes
* @return True if it is
*/
protected boolean hasStereotype(Element element, String... stereotypes) {
return hasStereotype(element, Arrays.asList(stereotypes));
}
Return whether the given element is annotated with any of the given annotation stereotypes.
Params: - element – The element
- stereotypes – The stereotypes
Returns: True if it is
/**
* Return whether the given element is annotated with any of the given annotation stereotypes.
*
* @param element The element
* @param stereotypes The stereotypes
* @return True if it is
*/
protected boolean hasStereotype(Element element, List<String> stereotypes) {
if (element == null) {
return false;
}
if (stereotypes.contains(element.toString())) {
return true;
}
AnnotationMetadata annotationMetadata = getAnnotationMetadata(element);
for (String stereotype : stereotypes) {
if (annotationMetadata.hasStereotype(stereotype)) {
return true;
}
}
return false;
}
Get the annotation metadata for the given element.
Params: - element – The element
Returns: The AnnotationMetadata
/**
* Get the annotation metadata for the given element.
*
* @param element The element
* @return The {@link AnnotationMetadata}
*/
public AnnotationMetadata getAnnotationMetadata(Element element) {
AnnotationMetadata metadata = annotationMetadataCache.get(element);
if (metadata == null) {
metadata = newAnnotationBuilder().buildOverridden(element);
annotationMetadataCache.put(element, metadata);
}
return metadata;
}
Get the declared annotation metadata for the given element.
Params: - element – The element
Returns: The AnnotationMetadata
/**
* Get the declared annotation metadata for the given element.
*
* @param element The element
* @return The {@link AnnotationMetadata}
*/
public AnnotationMetadata getDeclaredAnnotationMetadata(Element element) {
return javaAnnotationMetadataBuilder.buildDeclared(element);
}
Get the annotation metadata for the given element and the given parent.
This method is used for cases when you need to combine annotation metadata for
two elements, for example a JavaBean property where the field and the method metadata
need to be combined.
Params: - parent – The parent
- element – The element
Returns: The AnnotationMetadata
/**
* Get the annotation metadata for the given element and the given parent.
* This method is used for cases when you need to combine annotation metadata for
* two elements, for example a JavaBean property where the field and the method metadata
* need to be combined.
*
* @param parent The parent
* @param element The element
* @return The {@link AnnotationMetadata}
*/
public AnnotationMetadata getAnnotationMetadata(Element parent, Element element) {
return newAnnotationBuilder().buildForParent(parent, element);
}
Check whether the method is annotated.
Params: - declaringType – The declaring type
- method – The method
Returns: True if it is annotated with non internal annotations
/**
* Check whether the method is annotated.
*
* @param declaringType The declaring type
* @param method The method
* @return True if it is annotated with non internal annotations
*/
public boolean isAnnotated(String declaringType, ExecutableElement method) {
if (AbstractAnnotationMetadataBuilder.isMetadataMutated(declaringType, method)) {
return true;
}
List<? extends AnnotationMirror> annotationMirrors = method.getAnnotationMirrors();
for (AnnotationMirror annotationMirror : annotationMirrors) {
String typeName = annotationMirror.getAnnotationType().toString();
if (!AnnotationUtil.INTERNAL_ANNOTATION_NAMES.contains(typeName)) {
return true;
}
}
return false;
}
Creates a new annotation builder.
Returns: The builder
/**
* Creates a new annotation builder.
*
* @return The builder
*/
public JavaAnnotationMetadataBuilder newAnnotationBuilder() {
return new JavaAnnotationMetadataBuilder(
elementUtils,
messager,
this,
modelUtils
);
}
Creates a new JavaVisitorContext
. Returns: The visitor context
/**
* Creates a new {@link JavaVisitorContext}.
*
* @return The visitor context
*/
public JavaVisitorContext newVisitorContext() {
return new JavaVisitorContext(
processingEnv,
messager,
elementUtils,
this,
types,
modelUtils,
genericUtils,
filer,
visitorAttributes
);
}
Invalidates any cached metadata.
/**
* Invalidates any cached metadata.
*/
@Internal
static void invalidateCache() {
annotationMetadataCache.clear();
}
Invalidates any cached metadata.
Params: - element – The element
/**
* Invalidates any cached metadata.
*
* @param element The element
*/
@Internal
public void invalidateMetadata(Element element) {
if (element != null) {
annotationMetadataCache.remove(element);
}
}
}