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

org.springframework.test.context.TestContextAnnotationUtils Maven / Gradle / Ivy

There is a newer version: 6.1.13
Show newest version
/*
 * Copyright 2002-2023 the original author or 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 org.springframework.test.context;

import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotationCollectors;
import org.springframework.core.annotation.MergedAnnotationPredicates;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.core.annotation.RepeatableContainers;
import org.springframework.core.style.DefaultToStringStyler;
import org.springframework.core.style.SimpleValueStyler;
import org.springframework.core.style.ToStringCreator;
import org.springframework.lang.Nullable;
import org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentLruCache;
import org.springframework.util.ObjectUtils;

/**
 * {@code TestContextAnnotationUtils} is a collection of utility methods that
 * complements the standard support already available in {@link AnnotationUtils}
 * and {@link AnnotatedElementUtils}, while transparently honoring
 * {@link NestedTestConfiguration @NestedTestConfiguration} semantics.
 *
 * 

Mainly for internal use within the Spring TestContext Framework. * *

Whereas {@code AnnotationUtils} and {@code AnnotatedElementUtils} provide * utilities for getting or finding annotations, * {@code TestContextAnnotationUtils} goes a step further by providing support * for determining the root class on which an annotation is declared, * either directly or indirectly via a composed annotation. This * additional information is encapsulated in an {@link AnnotationDescriptor}. * *

The additional information provided by an {@code AnnotationDescriptor} is * required by the Spring TestContext Framework in order to be able to * support class inheritance and enclosing class hierarchy traversals for * annotations such as {@link ContextConfiguration @ContextConfiguration}, * {@link TestExecutionListeners @TestExecutionListeners}, and * {@link ActiveProfiles @ActiveProfiles} which offer support for merging and * overriding various inherited annotation attributes — for * example, {@link ContextConfiguration#inheritLocations}. * * @author Sam Brannen * @since 5.3, though originally since 4.0 as {@code org.springframework.test.util.MetaAnnotationUtils} * @see AnnotationUtils * @see AnnotatedElementUtils * @see AnnotationDescriptor */ public abstract class TestContextAnnotationUtils { private static final ConcurrentLruCache, EnclosingConfiguration> cachedEnclosingConfigurationModes = new ConcurrentLruCache<>(32, TestContextAnnotationUtils::lookUpEnclosingConfiguration); @Nullable private static volatile EnclosingConfiguration defaultEnclosingConfigurationMode; /** * Determine if an annotation of the specified {@code annotationType} is * present or meta-present on the supplied {@link Class} according to the * search algorithm used in {@link #findMergedAnnotation(Class, Class)}. *

If this method returns {@code true}, then {@code findMergedAnnotation(...)} * will return a non-null value. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @return {@code true} if a matching annotation is present * @since 5.3.3 * @see #findMergedAnnotation(Class, Class) */ public static boolean hasAnnotation(Class clazz, Class annotationType) { return MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY) .withEnclosingClasses(TestContextAnnotationUtils::searchEnclosingClass) .from(clazz) .isPresent(annotationType); } /** * Find the first annotation of the specified {@code annotationType} within * the annotation hierarchy above the supplied class, merge that * annotation's attributes with matching attributes from annotations * in lower levels of the annotation hierarchy, and synthesize the result back * into an annotation of the specified {@code annotationType}. *

In the context of this method, the term "above" means within the * {@linkplain Class#getSuperclass() superclass} hierarchy or within the * {@linkplain Class#getEnclosingClass() enclosing class} hierarchy of the * supplied class. The enclosing class hierarchy will only be searched * according to {@link NestedTestConfiguration @NestedTestConfiguration} * semantics. *

{@link org.springframework.core.annotation.AliasFor @AliasFor} semantics * are fully supported, both within a single annotation and within annotation * hierarchies. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @return the merged, synthesized {@code Annotation}, or {@code null} if not found * @see AnnotatedElementUtils#findMergedAnnotation(java.lang.reflect.AnnotatedElement, Class) * @see #findAnnotationDescriptor(Class, Class) * @see #searchEnclosingClass(Class) */ @Nullable public static T findMergedAnnotation(Class clazz, Class annotationType) { return findMergedAnnotation(clazz, annotationType, TestContextAnnotationUtils::searchEnclosingClass); } @Nullable private static T findMergedAnnotation(Class clazz, Class annotationType, Predicate> searchEnclosingClass) { return MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY) .withEnclosingClasses(searchEnclosingClass) .from(clazz) .get(annotationType) .synthesize(MergedAnnotation::isPresent).orElse(null); } /** * Get all repeatable annotations of the specified {@code annotationType} * within the annotation hierarchy above the supplied class; and for * each annotation found, merge that annotation's attributes with matching * attributes from annotations in lower levels of the annotation hierarchy and * synthesize the results back into an annotation of the specified {@code annotationType}. *

This method will find {@link java.lang.annotation.Inherited @Inherited} * annotations declared on superclasses if the supplied class does not have * any local declarations of the repeatable annotation. If no inherited * annotations are found, this method will search within the * {@linkplain Class#getEnclosingClass() enclosing class} hierarchy of the * supplied class. The enclosing class hierarchy will only be searched * according to {@link NestedTestConfiguration @NestedTestConfiguration} * semantics. *

The container type that holds the repeatable annotations will be looked up * via {@link java.lang.annotation.Repeatable}. *

{@link org.springframework.core.annotation.AliasFor @AliasFor} semantics * are fully supported, both within a single annotation and within annotation * hierarchies. * @param clazz the class on which to search for annotations (never {@code null}) * @param annotationType the annotation type to find (never {@code null}) * @return the set of all merged repeatable annotations found, or an empty set * if none were found * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(java.lang.reflect.AnnotatedElement, Class) * @see #searchEnclosingClass(Class) */ public static Set getMergedRepeatableAnnotations( Class clazz, Class annotationType) { // Present (via @Inherited semantics), directly present, or meta-present? Set mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS) .stream(annotationType) .collect(MergedAnnotationCollectors.toAnnotationSet()); if (!mergedAnnotations.isEmpty()) { return mergedAnnotations; } // Declared on an enclosing class of an inner class? if (searchEnclosingClass(clazz)) { // Then mimic @Inherited semantics within the enclosing class hierarchy. return getMergedRepeatableAnnotations(clazz.getEnclosingClass(), annotationType); } return Collections.emptySet(); } /** * Find the {@link AnnotationDescriptor} for the supplied {@code annotationType} * on the supplied {@link Class}, traversing its annotations, interfaces, * superclasses, and enclosing classes if no annotation can be found on the * given class itself. *

This method explicitly handles class-level annotations which are not * declared as {@linkplain java.lang.annotation.Inherited inherited} as * well as meta-annotations. *

The algorithm operates as follows: *

    *
  1. Search for the annotation on the given class and return a corresponding * {@code AnnotationDescriptor} if found. *
  2. Recursively search through all annotations that the given class declares.
  3. *
  4. Recursively search through all interfaces implemented by the given class.
  5. *
  6. Recursively search through the superclass hierarchy of the given class.
  7. *
  8. Recursively search through the enclosing class hierarchy of the given class * if appropriate according to {@link NestedTestConfiguration @NestedTestConfiguration} * semantics.
  9. *
*

In this context, the term recursively means that the search * process continues by returning to step #1 with the current annotation, * interface, superclass, or enclosing class as the class to look for * annotations on. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @return the corresponding annotation descriptor if the annotation was found; * otherwise {@code null} * @see #findAnnotationDescriptorForTypes(Class, Class...) */ @Nullable public static AnnotationDescriptor findAnnotationDescriptor( Class clazz, Class annotationType) { Assert.notNull(annotationType, "Annotation type must not be null"); return findAnnotationDescriptor(clazz, annotationType, TestContextAnnotationUtils::searchEnclosingClass, new HashSet<>()); } /** * Perform the search algorithm for {@link #findAnnotationDescriptor(Class, Class)}, * avoiding endless recursion by tracking which annotations have already been * visited. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @param searchEnclosingClass a predicate which evaluates to {@code true} * if a search should be performed on the enclosing class * @param visited the set of annotations that have already been visited * @return the corresponding annotation descriptor if the annotation was found; * otherwise {@code null} */ @Nullable private static AnnotationDescriptor findAnnotationDescriptor( @Nullable Class clazz, Class annotationType, Predicate> searchEnclosingClass, Set visited) { if (clazz == null || Object.class == clazz) { return null; } // Declared locally? if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) { return new AnnotationDescriptor<>(clazz, clazz.getAnnotation(annotationType)); } AnnotationDescriptor descriptor; // Declared on a composed annotation (i.e., as a meta-annotation)? for (Annotation composedAnn : clazz.getDeclaredAnnotations()) { Class composedType = composedAnn.annotationType(); if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedType.getName()) && visited.add(composedAnn)) { descriptor = findAnnotationDescriptor(composedType, annotationType, searchEnclosingClass, visited); if (descriptor != null) { return new AnnotationDescriptor<>(clazz, descriptor.getDeclaringClass(), descriptor.getAnnotation()); } } } // Declared on an interface? for (Class ifc : clazz.getInterfaces()) { descriptor = findAnnotationDescriptor(ifc, annotationType, searchEnclosingClass, visited); if (descriptor != null) { return new AnnotationDescriptor<>(clazz, descriptor.getDeclaringClass(), descriptor.getAnnotation()); } } // Declared on a superclass? descriptor = findAnnotationDescriptor(clazz.getSuperclass(), annotationType, searchEnclosingClass, visited); if (descriptor != null) { return descriptor; } // Declared on an enclosing class of an inner class? if (searchEnclosingClass.test(clazz)) { descriptor = findAnnotationDescriptor(clazz.getEnclosingClass(), annotationType, searchEnclosingClass, visited); if (descriptor != null) { return descriptor; } } return null; } /** * Find the {@link UntypedAnnotationDescriptor} for the first {@link Class} * in the inheritance hierarchy of the specified {@code clazz} (including * the specified {@code clazz} itself) which declares at least one of the * specified {@code annotationTypes}. *

This method traverses the annotations, interfaces, superclasses, and * enclosing classes of the specified {@code clazz} if no annotation can be * found on the given class itself. *

This method explicitly handles class-level annotations which are not * declared as {@linkplain java.lang.annotation.Inherited inherited} as * well as meta-annotations. *

The algorithm operates as follows: *

    *
  1. Search for a local declaration of one of the annotation types on the * given class and return a corresponding {@code UntypedAnnotationDescriptor} * if found. *
  2. Recursively search through all annotations that the given class declares.
  3. *
  4. Recursively search through all interfaces implemented by the given class.
  5. *
  6. Recursively search through the superclass hierarchy of the given class.
  7. *
  8. Recursively search through the enclosing class hierarchy of the given class * if appropriate according to {@link NestedTestConfiguration @NestedTestConfiguration} * semantics.
  9. *
*

In this context, the term recursively means that the search * process continues by returning to step #1 with the current annotation, * interface, superclass, or enclosing class as the class to look for * annotations on. * @param clazz the class to look for annotations on * @param annotationTypes the types of annotations to look for * @return the corresponding annotation descriptor if one of the annotations * was found; otherwise {@code null} * @see #findAnnotationDescriptor(Class, Class) */ @SuppressWarnings("unchecked") @Nullable public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes( Class clazz, Class... annotationTypes) { assertNonEmptyAnnotationTypeArray(annotationTypes, "The list of annotation types must not be empty"); return findAnnotationDescriptorForTypes(clazz, annotationTypes, new HashSet<>()); } /** * Perform the search algorithm for {@link #findAnnotationDescriptorForTypes(Class, Class...)}, * avoiding endless recursion by tracking which annotations have already been * visited. * @param clazz the class to look for annotations on * @param annotationTypes the types of annotations to look for * @param visited the set of annotations that have already been visited * @return the corresponding annotation descriptor if one of the annotations * was found; otherwise {@code null} */ @Nullable private static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(@Nullable Class clazz, Class[] annotationTypes, Set visited) { if (clazz == null || Object.class == clazz) { return null; } // Declared locally? for (Class annotationType : annotationTypes) { if (AnnotationUtils.isAnnotationDeclaredLocally(annotationType, clazz)) { return new UntypedAnnotationDescriptor(clazz, clazz.getAnnotation(annotationType), annotationTypes); } } // Declared on a composed annotation (i.e., as a meta-annotation)? for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) { UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes( composedAnnotation.annotationType(), annotationTypes, visited); if (descriptor != null) { return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), descriptor.getAnnotation(), annotationTypes); } } } // Declared on an interface? for (Class ifc : clazz.getInterfaces()) { UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(ifc, annotationTypes, visited); if (descriptor != null) { return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), descriptor.getAnnotation(), annotationTypes); } } // Declared on a superclass? UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(clazz.getSuperclass(), annotationTypes, visited); if (descriptor != null) { return descriptor; } // Declared on an enclosing class of an inner class? if (searchEnclosingClass(clazz)) { descriptor = findAnnotationDescriptorForTypes(clazz.getEnclosingClass(), annotationTypes, visited); if (descriptor != null) { return descriptor; } } return null; } /** * Determine if annotations on the enclosing class of the supplied class * should be searched by annotation search algorithms within the Spring * TestContext Framework. * @param clazz the class whose enclosing class should potentially be searched * @return {@code true} if the supplied class is an inner class whose enclosing * class should be searched * @see ClassUtils#isInnerClass(Class) * @see NestedTestConfiguration @NestedTestConfiguration */ public static boolean searchEnclosingClass(Class clazz) { return (ClassUtils.isInnerClass(clazz) && getEnclosingConfiguration(clazz) == EnclosingConfiguration.INHERIT); } static void clearCaches() { cachedEnclosingConfigurationModes.clear(); defaultEnclosingConfigurationMode = null; } /** * Get the {@link EnclosingConfiguration} mode for the supplied class. * @param clazz the class for which the enclosing configuration mode should * be resolved * @return the resolved enclosing configuration mode */ private static EnclosingConfiguration getEnclosingConfiguration(Class clazz) { return cachedEnclosingConfigurationModes.get(clazz); } private static EnclosingConfiguration lookUpEnclosingConfiguration(Class clazz) { // @NestedTestConfiguration should not be discovered on an enclosing class // for a nested interface (which is always static), so our predicate simply // ensures that the candidate class is an inner class. Predicate> searchEnclosingClass = ClassUtils::isInnerClass; NestedTestConfiguration nestedTestConfiguration = findMergedAnnotation(clazz, NestedTestConfiguration.class, searchEnclosingClass); return (nestedTestConfiguration != null ? nestedTestConfiguration.value() : getDefaultEnclosingConfigurationMode()); } private static EnclosingConfiguration getDefaultEnclosingConfigurationMode() { EnclosingConfiguration defaultConfigurationMode = defaultEnclosingConfigurationMode; if (defaultConfigurationMode == null) { String value = SpringProperties.getProperty(NestedTestConfiguration.ENCLOSING_CONFIGURATION_PROPERTY_NAME); EnclosingConfiguration enclosingConfigurationMode = EnclosingConfiguration.from(value); defaultConfigurationMode = (enclosingConfigurationMode != null ? enclosingConfigurationMode : EnclosingConfiguration.INHERIT); defaultEnclosingConfigurationMode = defaultConfigurationMode; } return defaultConfigurationMode; } private static void assertNonEmptyAnnotationTypeArray(Class[] annotationTypes, String message) { if (ObjectUtils.isEmpty(annotationTypes)) { throw new IllegalArgumentException(message); } for (Class clazz : annotationTypes) { if (!Annotation.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException("Array elements must be of type Annotation"); } } } /** * Descriptor for an {@link Annotation}, including the {@linkplain * #getDeclaringClass() class} on which the annotation is declared * as well as the {@linkplain #getAnnotation() merged annotation} instance. *

If the annotation is used as a meta-annotation, the root declaring * class is not directly annotated with the annotation but rather * indirectly via a composed annotation. *

Given the following example, if we are searching for the {@code @Transactional} * annotation on the {@code TransactionalTests} class, then the * properties of the {@code AnnotationDescriptor} would be as follows. *

    *
  • rootDeclaringClass: {@code TransactionalTests} class object
  • *
  • declaringClass: {@code TransactionalTests} class object
  • *
  • annotation: instance of the {@code Transactional} annotation
  • *
*

	 * @Transactional
	 * @ContextConfiguration({"/test-datasource.xml", "/repository-config.xml"})
	 * public class TransactionalTests { }
	 * 
*

Given the following example, if we are searching for the {@code @Transactional} * annotation on the {@code UserRepositoryTests} class, then the * properties of the {@code AnnotationDescriptor} would be as follows. *

    *
  • rootDeclaringClass: {@code UserRepositoryTests} class object
  • *
  • declaringClass: {@code RepositoryTests} class object
  • *
  • annotation: instance of the {@code Transactional} annotation
  • *
*

	 * @Transactional
	 * @ContextConfiguration({"/test-datasource.xml", "/repository-config.xml"})
	 * @Retention(RetentionPolicy.RUNTIME)
	 * public @interface RepositoryTests { }
	 *
	 * @RepositoryTests
	 * public class UserRepositoryTests { }
	 * 
* * @param the annotation type */ public static class AnnotationDescriptor { private final Class rootDeclaringClass; private final Class declaringClass; private final T annotation; AnnotationDescriptor(Class rootDeclaringClass, T annotation) { this(rootDeclaringClass, rootDeclaringClass, annotation); } @SuppressWarnings("unchecked") AnnotationDescriptor(Class rootDeclaringClass, Class declaringClass, T annotation) { Assert.notNull(rootDeclaringClass, "'rootDeclaringClass' must not be null"); Assert.notNull(declaringClass, "'declaringClass' must not be null"); Assert.notNull(annotation, "Annotation must not be null"); this.rootDeclaringClass = rootDeclaringClass; this.declaringClass = declaringClass; T mergedAnnotation = (T) AnnotatedElementUtils.findMergedAnnotation( rootDeclaringClass, annotation.annotationType()); Assert.state(mergedAnnotation != null, () -> "Failed to find merged annotation for " + annotation); this.annotation = mergedAnnotation; } public Class getRootDeclaringClass() { return this.rootDeclaringClass; } public Class getDeclaringClass() { return this.declaringClass; } /** * Get the merged annotation for this descriptor. */ public T getAnnotation() { return this.annotation; } @SuppressWarnings("unchecked") Class getAnnotationType() { return (Class) this.annotation.annotationType(); } /** * Find the next {@link AnnotationDescriptor} for the specified annotation * type in the hierarchy above the {@linkplain #getRootDeclaringClass() * root declaring class} of this descriptor. *

If a corresponding annotation is found in the superclass hierarchy * of the root declaring class, that will be returned. Otherwise, an * attempt will be made to find a corresponding annotation in the * {@linkplain Class#getEnclosingClass() enclosing class} hierarchy of * the root declaring class if {@linkplain #searchEnclosingClass appropriate}. * @return the next corresponding annotation descriptor if the annotation * was found; otherwise {@code null} */ @Nullable public AnnotationDescriptor next() { // Declared on a superclass? AnnotationDescriptor descriptor = findAnnotationDescriptor(getRootDeclaringClass().getSuperclass(), getAnnotationType()); // Declared on an enclosing class of an inner class? if (descriptor == null && searchEnclosingClass(getRootDeclaringClass())) { descriptor = findAnnotationDescriptor(getRootDeclaringClass().getEnclosingClass(), getAnnotationType()); } return descriptor; } /** * Find all annotations of the specified annotation type * that are present or meta-present on the {@linkplain #getRootDeclaringClass() * root declaring class} of this descriptor or on any interfaces that the * root declaring class implements. * @return the set of all merged, synthesized {@code Annotations} found, * or an empty set if none were found */ public Set findAllLocalMergedAnnotations() { SearchStrategy searchStrategy = SearchStrategy.TYPE_HIERARCHY; return MergedAnnotations.from(getRootDeclaringClass(), searchStrategy, RepeatableContainers.none()) .stream(getAnnotationType()) .filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex)) .collect(MergedAnnotationCollectors.toAnnotationSet()); } /** * Provide a textual representation of this {@code AnnotationDescriptor}. */ @Override public String toString() { return new ToStringCreator(this, new DefaultToStringStyler(new SimpleValueStyler())) .append("rootDeclaringClass", this.rootDeclaringClass) .append("declaringClass", this.declaringClass) .append("annotation", this.annotation) .toString(); } } /** * Untyped extension of {@link AnnotationDescriptor} that is used * to describe the declaration of one of several candidate annotation types * where the actual annotation type cannot be predetermined. */ public static class UntypedAnnotationDescriptor extends AnnotationDescriptor { private final Class[] annotationTypes; UntypedAnnotationDescriptor(Class rootDeclaringClass, Annotation annotation, Class[] annotationTypes) { this(rootDeclaringClass, rootDeclaringClass, annotation, annotationTypes); } UntypedAnnotationDescriptor(Class rootDeclaringClass, Class declaringClass, Annotation annotation, Class[] annotationTypes) { super(rootDeclaringClass, declaringClass, annotation); this.annotationTypes = annotationTypes; } /** * Find the next {@link UntypedAnnotationDescriptor} for the specified * annotation types in the hierarchy above the * {@linkplain #getRootDeclaringClass() root declaring class} of this * descriptor. *

If one of the corresponding annotations is found in the superclass * hierarchy of the root declaring class, that will be returned. Otherwise, * an attempt will be made to find a corresponding annotation in the * {@linkplain Class#getEnclosingClass() enclosing class} hierarchy of * the root declaring class if {@linkplain #searchEnclosingClass appropriate}. * @return the next corresponding annotation descriptor if one of the * annotations was found; otherwise {@code null} * @see AnnotationDescriptor#next() */ @Override @Nullable public UntypedAnnotationDescriptor next() { // Declared on a superclass? UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(getRootDeclaringClass().getSuperclass(), this.annotationTypes); // Declared on an enclosing class of an inner class? if (descriptor == null && searchEnclosingClass(getRootDeclaringClass())) { descriptor = findAnnotationDescriptorForTypes(getRootDeclaringClass().getEnclosingClass(), this.annotationTypes); } return descriptor; } /** * Throws an {@link UnsupportedOperationException} since the type of annotation * represented by an {@code UntypedAnnotationDescriptor} is unknown. */ @Override public Set findAllLocalMergedAnnotations() { throw new UnsupportedOperationException( "findAllLocalMergedAnnotations() is unsupported in UntypedAnnotationDescriptor"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy