
org.jboss.logging.processor.util.ElementHelper Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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
*
* http://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 org.jboss.logging.processor.util;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.AnnotatedConstruct;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* An utility class to work with element.
*
* @author Kevin Pollet - SERLI - ([email protected])
* @author James R. Perkins
*/
public final class ElementHelper {
/**
* Disable instantiation.
*/
private ElementHelper() {
}
/**
* Returns a single type argument for the element. If more than one type is found only the first one is returned.
*
* @param element the element to get the type arguments for
*
* @return an optional type argument
*/
public static Optional getTypeArgument(final Element element) {
final List extends TypeMirror> types = getTypeArguments(element);
return types.isEmpty() ? Optional.empty() : Optional.of(types.get(0));
}
/**
* Returns the type arguments for the element. If the elements {@linkplain Element#asType() type} is not a
* {@link DeclaredType} or the element does not have any type arguments an empty list is returned.
*
* @param element the element to get the type arguments for
*
* @return the type arguments or an empty list
*/
public static List extends TypeMirror> getTypeArguments(final Element element) {
return getTypeArguments(element.asType());
}
/**
* Returns the type arguments for the type. If the type is not a {@link DeclaredType} or the type does not have any
* type arguments an empty list is returned.
*
* @param type the type to get the type arguments for
*
* @return the type arguments or an empty list
*/
public static List extends TypeMirror> getTypeArguments(final TypeMirror type) {
if (type instanceof DeclaredType) {
return ((DeclaredType) type).getTypeArguments();
}
return Collections.emptyList();
}
/**
* Check if an element is annotated with the given annotation.
*
* @param annotatedConstruct the object to look for the annotation on.
* @param clazz the annotation class
*
* @return {@code true} if the element is annotated, otherwise {@code false}
*
* @throws IllegalArgumentException if element parameter is null
*/
public static boolean isAnnotatedWith(final AnnotatedConstruct annotatedConstruct,
final Class extends Annotation> clazz) {
if (annotatedConstruct == null) {
throw new IllegalArgumentException("The element parameter is null");
}
Annotation annotation = annotatedConstruct.getAnnotation(clazz);
return (annotation != null);
}
/**
* Retrieves the first attribute value from the annotation and assumes it's a {@link Class class} type.
*
* @param element the element the annotation is on
* @param annotation the annotation to get the value from
*
* @return a {@link TypeElement} representing the value for the first annotation attribute or {@code null} if no
* attributes were found
*/
public static TypeElement getClassAnnotationValue(final Element element, final Class extends Annotation> annotation) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
final DeclaredType annotationType = mirror.getAnnotationType();
if (annotationType.toString().equals(annotation.getName())) {
final AnnotationValue value = mirror.getElementValues().values().iterator().next();
return ((TypeElement) (((DeclaredType) value.getValue()).asElement()));
}
}
return null;
}
/**
* Retrieves the attribute value from the annotation and assumes it's a {@link Class class} type.
*
* @param element the element the annotation is on
* @param annotation the annotation to get the value from
* @param attributeName the name of the attribute to retrieve the class value for
*
* @return a {@link TypeElement} representing the value for the annotation attribute or {@code null} if the
* attribute was not found
*/
@SuppressWarnings({ "StaticMethodOnlyUsedInOneClass", "SameParameterValue" })
public static TypeElement getClassAnnotationValue(final Element element, final Class extends Annotation> annotation,
final String attributeName) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
final DeclaredType annotationType = mirror.getAnnotationType();
if (annotationType.toString().equals(annotation.getName())) {
final Map extends ExecutableElement, ? extends AnnotationValue> map = mirror.getElementValues();
for (ExecutableElement key : map.keySet()) {
if (key.getSimpleName().contentEquals(attributeName)) {
return ((TypeElement) (((DeclaredType) map.get(key).getValue()).asElement()));
}
}
}
}
return null;
}
/**
* Retrieves the attribute value from the annotation and assumes it's an array {@link Class classes}.
*
* @param element the element the annotation is on
* @param annotation the annotation to get the value from
* @param attributeName the name of the attribute to retrieve the class value array for
*
* @return a list of {@link TypeMirror} representing the value for the annotation attribute or an empty list
*/
public static List getClassArrayAnnotationValue(final Element element,
final Class extends Annotation> annotation, @SuppressWarnings("SameParameterValue") final String attributeName) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
final DeclaredType annotationType = mirror.getAnnotationType();
if (annotationType.toString().equals(annotation.getName())) {
final Map extends ExecutableElement, ? extends AnnotationValue> map = mirror.getElementValues();
for (ExecutableElement key : map.keySet()) {
if (key.getSimpleName().contentEquals(attributeName)) {
@SuppressWarnings("unchecked")
final List annotationValues = (List) map.get(key).getValue();
final List result = new ArrayList<>(annotationValues.size());
for (AnnotationValue value : annotationValues) {
result.add((TypeMirror) value.getValue());
}
return result;
}
}
}
}
return Collections.emptyList();
}
/**
* Returns annotations that are associated with the element that match the {@code annotation} parameter type. If the
* {@code groupedAnnotation} is not {@code null} then any repeated annotations that math the {@code annotation}
* parameter type are also returned.
*
*
* The {@code groupedAnnotation} must have a value attribute that includes an array of annotations that math the
* {@code annotation} parameter type.
*
*
* @param element the element to search for annotations
* @param groupedAnnotation the grouped annotation, e.g. collector for repeatable annotations, or {@code null} if not a
* repeatable annotation
* @param annotation the annotation to search for
*
* @return a collection matched annotations
*/
public static Collection getAnnotations(final Element element,
final Class extends Annotation> groupedAnnotation, final Class extends Annotation> annotation) {
final Collection result = new ArrayList<>();
final List extends AnnotationMirror> annotations = element.getAnnotationMirrors();
for (AnnotationMirror annotationMirror : annotations) {
if (isSameType(groupedAnnotation, annotationMirror.getAnnotationType())) {
result.addAll(getContainingAnnotations(annotationMirror));
} else if (isSameType(annotation, annotationMirror.getAnnotationType())) {
result.add(annotationMirror);
}
}
return result;
}
/**
* Checks whether or not a constructor matching the parameters exists.
*
* @param types the type utility used to compare the type arguments
* @param element the element that contains the constructors
* @param args the arguments the constructor should match
*
* @return {@code true} if a matching constructor was found otherwise {@code false}
*/
public static boolean hasConstructor(final Types types, final Element element, final List args) {
final int len = args.size();
final List constructors = ElementFilter.constructorsIn(element.getEnclosedElements());
for (ExecutableElement constructor : constructors) {
final List extends VariableElement> parameters = constructor.getParameters();
if (len == parameters.size()) {
boolean match = false;
for (int i = 0; i < len; i++) {
final TypeMirror type = args.get(i);
final VariableElement parameter = parameters.get(i);
if (types.isSameType(type, parameter.asType())) {
match = true;
} else {
match = false;
break;
}
}
if (match) {
return true;
}
}
}
return false;
}
/**
* Returns the type as a {@link TypeMirror}.
*
* @param processingEnv the processing environment to get the elements utility
* @param type the type to create the {@link TypeMirror} for
*
* @return the type
*/
public static TypeElement toTypeElement(final ProcessingEnvironment processingEnv, final Class> type) {
return toTypeElement(processingEnv.getElementUtils(), type);
}
/**
* Returns the type as a {@link TypeMirror}.
*
* @param elements the element utility used to generate the tye type
* @param type the type to create the {@link TypeMirror} for
*
* @return the type
*/
public static TypeElement toTypeElement(final Elements elements, final Class> type) {
return elements.getTypeElement(type.getCanonicalName());
}
/**
* Returns the type as a {@link TypeMirror}.
*
* @param processingEnv the processing environment to get the elements utility
* @param type the type to create the {@link TypeMirror} for
*
* @return the type
*/
public static TypeMirror toType(final ProcessingEnvironment processingEnv, final Class> type) {
return toType(processingEnv.getElementUtils(), type);
}
/**
* Returns the type as a {@link TypeMirror}.
*
* @param elements the element utility used to generate the tye type
* @param type the type to create the {@link TypeMirror} for
*
* @return the type
*/
public static TypeMirror toType(final Elements elements, final Class> type) {
return toTypeElement(elements, type).asType();
}
private static boolean isSameType(final Class> c, final TypeMirror type) {
return c != null && c.getCanonicalName().equals(type.toString());
}
@SuppressWarnings("unchecked")
private static Collection getContainingAnnotations(final AnnotationMirror annotation) {
final Collection result = new ArrayList<>();
// Return any child annotations
final Map extends ExecutableElement, ? extends AnnotationValue> childAnnotations = annotation.getElementValues();
childAnnotations.entrySet().stream().filter(entry -> entry.getKey().getSimpleName().contentEquals("value"))
.forEach(entry -> {
final Object value = entry.getValue().getValue();
if (value instanceof List) {
final List extends AnnotationValue> values = (List extends AnnotationValue>) value;
for (AnnotationValue subValue : values) {
if (subValue instanceof AnnotationMirror) {
result.add((AnnotationMirror) subValue);
} else {
result.add((AnnotationMirror) subValue.getValue());
}
}
}
});
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy