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

com.globalmentor.java.model.BaseAnnotationProcessor Maven / Gradle / Ivy

Go to download

GlobalMentor Java library for working with the Java model and facilitating annotation processing.

The newest version!
/*
 * Copyright © 2023 GlobalMentor, Inc. 
 *
 * 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 com.globalmentor.java.model;

import static com.globalmentor.collections.Sets.*;
import static java.util.stream.Collectors.*;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;

import javax.annotation.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.*;
import javax.lang.model.util.*;

/**
 * A base implementation of an annotation providing convenient access to element and type utilities.
 * @implSpec This processor by default supports the latest supported source version.
 * @author Garret Wilson
 * @see ModelElements
 * @see ModelTypes
 */
public abstract class BaseAnnotationProcessor extends AbstractProcessor {

	/**
	 * The canonical names class names representing annotation type supported by this processor. These will be merged with any annotations indicated by the
	 * {@link SupportedAnnotationTypes} annotation.
	 */
	private final Set supportedAnnotationTypeCanonicalNames;

	/** Constructor specifying no specific annotation types; those may still be specified using the {@link SupportedAnnotationTypes} annotation. */
	public BaseAnnotationProcessor() {
		this(Set.of());
	}

	/**
	 * Supported annotation types constructors.
	 * @param supportedAnnotationTypes The canonical names class names representing annotation type supported by this processor, in addition to any specified by
	 *          the {@link SupportedAnnotationTypes} annotation.
	 */
	public BaseAnnotationProcessor(final Set> supportedAnnotationTypes) {
		this.supportedAnnotationTypeCanonicalNames = supportedAnnotationTypes.stream().map(Class::getCanonicalName).collect(toUnmodifiableSet());
	}

	/**
	 * {@inheritDoc}
	 * @implSpec This processor returns the union of any supported annotation types provided in the constructor, and any specified by the
	 *           {@link SupportedAnnotationTypes} annotation.
	 */
	@Override
	public Set getSupportedAnnotationTypes() {
		if(supportedAnnotationTypeCanonicalNames.isEmpty()) { //if we have no specific supported annotation types, return the default value
			return super.getSupportedAnnotationTypes(); //beyond checking for the `SupportedAnnotationTypes` annotation, the default version provides additional functionality such as appropriate warnings 
		}
		final SupportedAnnotationTypes supportedAnnotationTypesAnnotation = getClass().getAnnotation(SupportedAnnotationTypes.class);
		if(supportedAnnotationTypesAnnotation == null) {
			return supportedAnnotationTypeCanonicalNames;
		}
		assert supportedAnnotationTypesAnnotation != null;
		return toUnion(super.getSupportedAnnotationTypes(), supportedAnnotationTypeCanonicalNames);
	}

	/**
	 * {@inheritDoc}
	 * @implSpec This implementation indicates support for the latest supported source version.
	 * @see SourceVersion#latestSupported()
	 */
	@Override
	public SourceVersion getSupportedSourceVersion() {
		return SourceVersion.latestSupported();
	}

	/**
	 * Returns the processing environment providing by the tool framework.
	 * @apiNote This method provides access to the processing environment using a more modern approach, rather than accessing a protected variable directly from
	 *          {@link AbstractProcessor}.
	 * @return The processing environment providing by the tool framework.
	 */
	protected ProcessingEnvironment getProcessingEnvironment() {
		return processingEnv;
	}

	//## elements

	/**
	 * Finds a type element from a class if the type element is uniquely determinable in the environment.
	 * @implSpec This implementation delegates to {@link ModelElements#findTypeElementForClass(Elements, Class)}
	 * @param clazz The class for which a type element is to be found.
	 * @return The type element for the class, which will not be present if no type element can be uniquely determined.
	 * @see Class#getCanonicalName()
	 */
	public Optional findTypeElementForClass(@Nonnull final Class clazz) { //TODO create module-related variations as well
		return ModelElements.findTypeElementForClass(getProcessingEnvironment().getElementUtils(), clazz);
	}

	/**
	 * Finds a type element given its canonical name if the type element is uniquely determinable in the environment.
	 * @apiNote This method functions identically to {@link Elements#getTypeElement(CharSequence)} except that it returns an {@link Optional} and never
	 *          null.
	 * @implSpec This implementation delegates to {@link ModelElements#findTypeElementForCanonicalName(Elements, CharSequence)}.
	 * @param canonicalName The canonical name of the type element to return.
	 * @return The named type element, which will not be present if no type element can be uniquely determined.
	 */
	public Optional findTypeElementForCanonicalName(@Nonnull final CharSequence canonicalName) { //TODO create module-related variations as well
		return ModelElements.findTypeElementForCanonicalName(getProcessingEnvironment().getElementUtils(), canonicalName);
	}

	/**
	 * Returns all interfaces of a type element annotated with the given annotation.
	 * @implSpec This implementation delegates to {@link ModelElements#elementInterfacesAnnotatedWith(Types, TypeElement, Class)}.
	 * @param typeElement The type element representing the type potentially having an interface annotated with the specified annotation.
	 * @param annotationClass The type of annotation to look for.
	 * @return The interfaces of the type element which are in turn annotated with the given annotation.
	 */
	public Stream elementInterfacesAnnotatedWith(@Nonnull final TypeElement typeElement,
			@Nonnull final Class annotationClass) {
		return ModelElements.elementInterfacesAnnotatedWith(getProcessingEnvironment().getTypeUtils(), typeElement, annotationClass);
	}

	//## types

	/**
	 * Tests whether a type is assignable to the type corresponding to the given class (i.e. whether instances of each would have an instanceof
	 * relationship).
	 * @implSpec This implementation delegates to {@link ModelTypes#isTypeAssignableTo(Elements, Types, TypeMirror, Class)}.
	 * @param typeMirror The type to test.
	 * @param clazz The class representing the type against which to compare for assignability.
	 * @return true if the type is assignable to the type represented by the class.
	 * @throws IllegalArgumentException if no type could be found for the given class; or given a type for an executable, package, or module is invalid.
	 * @see Types#isAssignable(TypeMirror, TypeMirror)
	 */
	public boolean isTypeAssignableTo(@Nonnull final TypeMirror typeMirror, @Nonnull final Class clazz) {
		return ModelTypes.isTypeAssignableTo(getProcessingEnvironment().getElementUtils(), getProcessingEnvironment().getTypeUtils(), typeMirror, clazz);
	}

	/**
	 * Returns a predicate for testing whether a type is assignable to the type corresponding to the given class (i.e. whether instances of each would have an
	 * instanceof relationship).
	 * @apiNote This factory method is useful for creating the test once and using multiple times.
	 * @implSpec This implementation delegates to {@link ModelTypes#isTypeAssignableTo(Elements, Types, Class)}.
	 * @param clazz The class representing the type against which to compare for assignability.
	 * @return true if the type is assignable to the type represented by the class.
	 * @throws IllegalArgumentException if no type could be found for the given class; or given a type for an executable, package, or module is invalid.
	 * @see Types#isAssignable(TypeMirror, TypeMirror)
	 */
	public Predicate isTypeAssignableTo(@Nonnull final Class clazz) {
		return ModelTypes.isTypeAssignableTo(getProcessingEnvironment().getElementUtils(), getProcessingEnvironment().getTypeUtils(), clazz);
	}

	/**
	 * Finds a type corresponding to a class type element and actual type arguments, if the type element is uniquely determinable in the environment.
	 * @implSpec This implementation delegates {@link ModelTypes#findDeclaredType(Elements, Types, Class, TypeMirror...)}.
	 * @param clazz The class for which a type element is to be found.
	 * @param typeArgs The actual type arguments.
	 * @return The type element for the class, which will not be present if no type element can be uniquely determined.
	 * @see Class#getCanonicalName()
	 * @see Elements#getTypeElement(CharSequence)
	 * @see Types#getDeclaredType(TypeElement, TypeMirror...)
	 */
	public Optional findDeclaredTypeForClass(@Nonnull final Class clazz, @Nonnull final TypeMirror... typeArgs) {
		return ModelTypes.findDeclaredType(getProcessingEnvironment().getElementUtils(), getProcessingEnvironment().getTypeUtils(), clazz, typeArgs);
	}

	/**
	 * Finds a type corresponding to a class type element with an unbounded wildcard type parameter, if the type element is uniquely determinable in the
	 * environment.
	 * @implSpec This implementation delegates {@link ModelTypes#findDeclaredTypeWithUnboundedWildcardForClass(Elements, Types, Class)}.
	 * @param clazz The class for which a type element is to be found.
	 * @return The type element for the class, which will not be present if no type element can be uniquely determined.
	 * @see Class#getCanonicalName()
	 * @see Elements#getTypeElement(CharSequence)
	 * @see Types#getDeclaredType(TypeElement, TypeMirror...)
	 * @see #getUnboundedWildcardType()
	 */
	public Optional findDeclaredTypeWithUnboundedWildcardForClass(@Nonnull final Class clazz) {
		return ModelTypes.findDeclaredTypeWithUnboundedWildcardForClass(getProcessingEnvironment().getElementUtils(), getProcessingEnvironment().getTypeUtils(),
				clazz);
	}

	/**
	 * Returns a new unbounded wildcard type ({@code }).
	 * @implSpec This implementation delegates {@link ModelTypes#getUnboundedWildcardType(Types)}.
	 * @return The new unbounded wildcard type.
	 */
	public WildcardType getUnboundedWildcardType() {
		return ModelTypes.getUnboundedWildcardType(getProcessingEnvironment().getTypeUtils());
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy