All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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 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 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 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 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 annotation,
            final String attributeName) {
        for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
            final DeclaredType annotationType = mirror.getAnnotationType();
            if (annotationType.toString().equals(annotation.getName())) {
                final Map 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 annotation, @SuppressWarnings("SameParameterValue") final String attributeName) {
        for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
            final DeclaredType annotationType = mirror.getAnnotationType();
            if (annotationType.toString().equals(annotation.getName())) {
                final Map 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 groupedAnnotation, final Class annotation) { final Collection result = new ArrayList<>(); final List 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 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 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 values = (List) 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