From dd0d26b4ba395c964ef4ddc37906f41b5750bf95 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 6 Jan 2024 23:02:59 +0100 Subject: [PATCH] Refine exception handling for type not present versus access exception Includes TypeVariable bypass for reflection-free annotation retrieval. Includes info log message for annotation attribute retrieval failure. Closes gh-27182 (cherry picked from commit 70247c4a949b7e20cadd260b5fd46d880c183acb) --- .../core/SerializableTypeWrapper.java | 9 +- .../core/annotation/AnnotationUtils.java | 39 +- .../core/annotation/AnnotationsScanner.java | 4 +- .../core/annotation/AttributeMethods.java | 27 +- .../AnnotationIntrospectionFailureTests.java | 77 +- .../annotation/AttributeMethodsTests.java | 6 +- .../annotation/MergedAnnotationsTests.java | 727 ++++++++---------- 7 files changed, 417 insertions(+), 472 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java index 44f5719ad74..344805fa1b9 100644 --- a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java +++ b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -214,7 +214,12 @@ final class SerializableTypeWrapper { return result; } - return ReflectionUtils.invokeMethod(method, this.provider.getType(), args); + Type type = this.provider.getType(); + if (type instanceof TypeVariable tv && method.getName().equals("getName")) { + // Avoid reflection for common comparison of type variables + return tv.getName(); + } + return ReflectionUtils.invokeMethod(method, type, args); } } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 47baaae305e..565e5055b70 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -757,7 +757,7 @@ public abstract class AnnotationUtils { * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for * {@code Class} values (instead of early {@code Class.getAnnotations() failure}). *

This method not failing indicates that {@link #getAnnotationAttributes(Annotation)} - * won't failure either (when attempted later on). + * won't fail either (when attempted later on). * @param annotation the annotation to validate * @throws IllegalStateException if a declared {@code Class} attribute could not be read * @since 4.3.15 @@ -1056,8 +1056,7 @@ public abstract class AnnotationUtils { return null; } catch (Throwable ex) { - rethrowAnnotationConfigurationException(ex); - handleIntrospectionFailure(annotation.getClass(), ex); + handleValueRetrievalFailure(annotation, ex); return null; } } @@ -1073,14 +1072,18 @@ public abstract class AnnotationUtils { * @return the value returned from the method invocation * @since 5.3.24 */ - static Object invokeAnnotationMethod(Method method, Object annotation) { + @Nullable + static Object invokeAnnotationMethod(Method method, @Nullable Object annotation) { + if (annotation == null) { + return null; + } if (Proxy.isProxyClass(annotation.getClass())) { try { InvocationHandler handler = Proxy.getInvocationHandler(annotation); return handler.invoke(annotation, method, null); } catch (Throwable ex) { - // ignore and fall back to reflection below + // Ignore and fall back to reflection below } } return ReflectionUtils.invokeMethod(method, annotation); @@ -1114,20 +1117,32 @@ public abstract class AnnotationUtils { * @see #rethrowAnnotationConfigurationException * @see IntrospectionFailureLogger */ - static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) { + static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) { rethrowAnnotationConfigurationException(ex); IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO; boolean meta = false; if (element instanceof Class clazz && Annotation.class.isAssignableFrom(clazz)) { - // Meta-annotation or (default) value lookup on an annotation type + // Meta-annotation introspection failure logger = IntrospectionFailureLogger.DEBUG; meta = true; } if (logger.isEnabled()) { - String message = meta ? - "Failed to meta-introspect annotation " : - "Failed to introspect annotations on "; - logger.log(message + element + ": " + ex); + logger.log("Failed to " + (meta ? "meta-introspect annotation " : "introspect annotations on ") + + element + ": " + ex); + } + } + + /** + * Handle the supplied value retrieval exception. + * @param annotation the annotation instance from which to retrieve the value + * @param ex the exception that we encountered + * @see #handleIntrospectionFailure + */ + private static void handleValueRetrievalFailure(Annotation annotation, Throwable ex) { + rethrowAnnotationConfigurationException(ex); + IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO; + if (logger.isEnabled()) { + logger.log("Failed to retrieve value from " + annotation + ": " + ex); } } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java index e6a8626e05a..cb3ae366a90 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -453,7 +453,7 @@ abstract class AnnotationsScanner { for (int i = 0; i < annotations.length; i++) { Annotation annotation = annotations[i]; if (isIgnorable(annotation.annotationType()) || - !AttributeMethods.forAnnotationType(annotation.annotationType()).isValid(annotation)) { + !AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) { annotations[i] = null; } else { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java b/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java index a828ebe44b5..c56064b77a9 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -45,7 +45,7 @@ final class AttributeMethods { if (m1 != null && m2 != null) { return m1.getName().compareTo(m2.getName()); } - return m1 != null ? -1 : 1; + return (m1 != null ? -1 : 1); }; @@ -87,18 +87,26 @@ final class AttributeMethods { /** * Determine if values from the given annotation can be safely accessed without * causing any {@link TypeNotPresentException TypeNotPresentExceptions}. + *

This method is designed to cover Google App Engine's late arrival of such + * exceptions for {@code Class} values (instead of the more typical early + * {@code Class.getAnnotations() failure} on a regular JVM). * @param annotation the annotation to check * @return {@code true} if all values are present * @see #validate(Annotation) */ - boolean isValid(Annotation annotation) { + boolean canLoad(Annotation annotation) { assertAnnotation(annotation); for (int i = 0; i < size(); i++) { if (canThrowTypeNotPresentException(i)) { try { AnnotationUtils.invokeAnnotationMethod(get(i), annotation); } + catch (IllegalStateException ex) { + // Plain invocation failure to expose -> leave up to attribute retrieval + // (if any) where such invocation failure will be logged eventually. + } catch (Throwable ex) { + // TypeNotPresentException etc. -> annotation type not actually loadable. return false; } } @@ -108,13 +116,13 @@ final class AttributeMethods { /** * Check if values from the given annotation can be safely accessed without causing - * any {@link TypeNotPresentException TypeNotPresentExceptions}. In particular, - * this method is designed to cover Google App Engine's late arrival of such + * any {@link TypeNotPresentException TypeNotPresentExceptions}. + *

This method is designed to cover Google App Engine's late arrival of such * exceptions for {@code Class} values (instead of the more typical early - * {@code Class.getAnnotations() failure}). + * {@code Class.getAnnotations() failure} on a regular JVM). * @param annotation the annotation to validate * @throws IllegalStateException if a declared {@code Class} attribute could not be read - * @see #isValid(Annotation) + * @see #canLoad(Annotation) */ void validate(Annotation annotation) { assertAnnotation(annotation); @@ -123,6 +131,9 @@ final class AttributeMethods { try { AnnotationUtils.invokeAnnotationMethod(get(i), annotation); } + catch (IllegalStateException ex) { + throw ex; + } catch (Throwable ex) { throw new IllegalStateException("Could not obtain annotation attribute value for " + get(i).getName() + " declared on " + annotation.annotationType(), ex); @@ -147,7 +158,7 @@ final class AttributeMethods { @Nullable Method get(String name) { int index = indexOf(name); - return index != -1 ? this.attributeMethods[index] : null; + return (index != -1 ? this.attributeMethods[index] : null); } /** diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java index 0fa93c99e93..9750cd5ac2e 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationIntrospectionFailureTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2024 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. @@ -43,55 +43,45 @@ class AnnotationIntrospectionFailureTests { @Test void filteredTypeThrowsTypeNotPresentException() throws Exception { - FilteringClassLoader classLoader = new FilteringClassLoader( - getClass().getClassLoader()); - Class withExampleAnnotation = ClassUtils.forName( - WithExampleAnnotation.class.getName(), classLoader); - Annotation annotation = withExampleAnnotation.getAnnotations()[0]; + FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader()); + Class withAnnotation = ClassUtils.forName(WithExampleAnnotation.class.getName(), classLoader); + Annotation annotation = withAnnotation.getAnnotations()[0]; Method method = annotation.annotationType().getMethod("value"); method.setAccessible(true); - assertThatExceptionOfType(TypeNotPresentException.class).isThrownBy(() -> - ReflectionUtils.invokeMethod(method, annotation)) - .withCauseInstanceOf(ClassNotFoundException.class); + assertThatExceptionOfType(TypeNotPresentException.class) + .isThrownBy(() -> ReflectionUtils.invokeMethod(method, annotation)) + .withCauseInstanceOf(ClassNotFoundException.class); } @Test @SuppressWarnings("unchecked") void filteredTypeInMetaAnnotationWhenUsingAnnotatedElementUtilsHandlesException() throws Exception { - FilteringClassLoader classLoader = new FilteringClassLoader( - getClass().getClassLoader()); - Class withExampleMetaAnnotation = ClassUtils.forName( - WithExampleMetaAnnotation.class.getName(), classLoader); - Class exampleAnnotationClass = (Class) ClassUtils.forName( - ExampleAnnotation.class.getName(), classLoader); - Class exampleMetaAnnotationClass = (Class) ClassUtils.forName( - ExampleMetaAnnotation.class.getName(), classLoader); - assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes( - withExampleMetaAnnotation, exampleAnnotationClass)).isNull(); - assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes( - withExampleMetaAnnotation, exampleMetaAnnotationClass)).isNull(); - assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation, - exampleAnnotationClass)).isFalse(); - assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation, - exampleMetaAnnotationClass)).isFalse(); + FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader()); + Class withAnnotation = ClassUtils.forName(WithExampleMetaAnnotation.class.getName(), classLoader); + Class annotationClass = (Class) + ClassUtils.forName(ExampleAnnotation.class.getName(), classLoader); + Class metaAnnotationClass = (Class) + ClassUtils.forName(ExampleMetaAnnotation.class.getName(), classLoader); + assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(withAnnotation, annotationClass)).isNull(); + assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(withAnnotation, metaAnnotationClass)).isNull(); + assertThat(AnnotatedElementUtils.hasAnnotation(withAnnotation, annotationClass)).isFalse(); + assertThat(AnnotatedElementUtils.hasAnnotation(withAnnotation, metaAnnotationClass)).isFalse(); } @Test @SuppressWarnings("unchecked") void filteredTypeInMetaAnnotationWhenUsingMergedAnnotationsHandlesException() throws Exception { - FilteringClassLoader classLoader = new FilteringClassLoader( - getClass().getClassLoader()); - Class withExampleMetaAnnotation = ClassUtils.forName( - WithExampleMetaAnnotation.class.getName(), classLoader); - Class exampleAnnotationClass = (Class) ClassUtils.forName( - ExampleAnnotation.class.getName(), classLoader); - Class exampleMetaAnnotationClass = (Class) ClassUtils.forName( - ExampleMetaAnnotation.class.getName(), classLoader); - MergedAnnotations annotations = MergedAnnotations.from(withExampleMetaAnnotation); - assertThat(annotations.get(exampleAnnotationClass).isPresent()).isFalse(); - assertThat(annotations.get(exampleMetaAnnotationClass).isPresent()).isFalse(); - assertThat(annotations.isPresent(exampleMetaAnnotationClass)).isFalse(); - assertThat(annotations.isPresent(exampleAnnotationClass)).isFalse(); + FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader()); + Class withAnnotation = ClassUtils.forName(WithExampleMetaAnnotation.class.getName(), classLoader); + Class annotationClass = (Class) + ClassUtils.forName(ExampleAnnotation.class.getName(), classLoader); + Class metaAnnotationClass = (Class) + ClassUtils.forName(ExampleMetaAnnotation.class.getName(), classLoader); + MergedAnnotations annotations = MergedAnnotations.from(withAnnotation); + assertThat(annotations.get(annotationClass).isPresent()).isFalse(); + assertThat(annotations.get(metaAnnotationClass).isPresent()).isFalse(); + assertThat(annotations.isPresent(metaAnnotationClass)).isFalse(); + assertThat(annotations.isPresent(annotationClass)).isFalse(); } @@ -103,17 +93,16 @@ class AnnotationIntrospectionFailureTests { @Override protected boolean isEligibleForOverriding(String className) { - return className.startsWith( - AnnotationIntrospectionFailureTests.class.getName()); + return className.startsWith(AnnotationIntrospectionFailureTests.class.getName()) || + className.startsWith("jdk.internal"); } @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (name.startsWith(AnnotationIntrospectionFailureTests.class.getName()) && - name.contains("Filtered")) { + protected Class loadClassForOverriding(String name) throws ClassNotFoundException { + if (name.contains("Filtered") || name.startsWith("jdk.internal")) { throw new ClassNotFoundException(name); } - return super.loadClass(name, resolve); + return super.loadClassForOverriding(name); } } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AttributeMethodsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AttributeMethodsTests.java index 8a4acb7b797..a2b885601f7 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AttributeMethodsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AttributeMethodsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -112,7 +112,7 @@ class AttributeMethodsTests { ClassValue annotation = mockAnnotation(ClassValue.class); given(annotation.value()).willThrow(TypeNotPresentException.class); AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType()); - assertThat(attributes.isValid(annotation)).isFalse(); + assertThat(attributes.canLoad(annotation)).isFalse(); } @Test @@ -121,7 +121,7 @@ class AttributeMethodsTests { ClassValue annotation = mock(); given(annotation.value()).willReturn((Class) InputStream.class); AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType()); - assertThat(attributes.isValid(annotation)).isTrue(); + assertThat(attributes.canLoad(annotation)).isTrue(); } @Test diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index f73654666c9..6378c6f3a31 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -88,47 +88,47 @@ class MergedAnnotationsTests { @Test void preconditions() { assertThatIllegalArgumentException() - .isThrownBy(() -> MergedAnnotations.search(null)) - .withMessage("SearchStrategy must not be null"); + .isThrownBy(() -> MergedAnnotations.search(null)) + .withMessage("SearchStrategy must not be null"); Search search = MergedAnnotations.search(SearchStrategy.SUPERCLASS); assertThatIllegalArgumentException() - .isThrownBy(() -> search.withEnclosingClasses(null)) - .withMessage("Predicate must not be null"); + .isThrownBy(() -> search.withEnclosingClasses(null)) + .withMessage("Predicate must not be null"); assertThatIllegalStateException() - .isThrownBy(() -> search.withEnclosingClasses(Search.always)) - .withMessage("A custom 'searchEnclosingClass' predicate can only be combined with SearchStrategy.TYPE_HIERARCHY"); + .isThrownBy(() -> search.withEnclosingClasses(Search.always)) + .withMessage("A custom 'searchEnclosingClass' predicate can only be combined with SearchStrategy.TYPE_HIERARCHY"); assertThatIllegalArgumentException() - .isThrownBy(() -> search.withAnnotationFilter(null)) - .withMessage("AnnotationFilter must not be null"); + .isThrownBy(() -> search.withAnnotationFilter(null)) + .withMessage("AnnotationFilter must not be null"); assertThatIllegalArgumentException() - .isThrownBy(() -> search.withRepeatableContainers(null)) - .withMessage("RepeatableContainers must not be null"); + .isThrownBy(() -> search.withRepeatableContainers(null)) + .withMessage("RepeatableContainers must not be null"); assertThatIllegalArgumentException() - .isThrownBy(() -> search.from(null)) - .withMessage("AnnotatedElement must not be null"); + .isThrownBy(() -> search.from(null)) + .withMessage("AnnotatedElement must not be null"); } @Test void searchFromClassWithDefaultAnnotationFilterAndDefaultRepeatableContainers() { Stream> classes = MergedAnnotations.search(SearchStrategy.DIRECT) - .from(TransactionalComponent.class) - .stream() - .map(MergedAnnotation::getType); + .from(TransactionalComponent.class) + .stream() + .map(MergedAnnotation::getType); assertThat(classes).containsExactly(Transactional.class, Component.class, Indexed.class); } @Test void searchFromClassWithCustomAnnotationFilter() { Stream> classes = MergedAnnotations.search(SearchStrategy.DIRECT) - .withAnnotationFilter(annotationName -> annotationName.endsWith("Indexed")) - .from(TransactionalComponent.class) - .stream() - .map(MergedAnnotation::getType); + .withAnnotationFilter(annotationName -> annotationName.endsWith("Indexed")) + .from(TransactionalComponent.class) + .stream() + .map(MergedAnnotation::getType); assertThat(classes).containsExactly(Transactional.class, Component.class); } @@ -138,14 +138,14 @@ class MergedAnnotationsTests { RepeatableContainers containers = RepeatableContainers.of(TestConfiguration.class, Hierarchy.class); MergedAnnotations annotations = MergedAnnotations.search(SearchStrategy.DIRECT) - .withRepeatableContainers(containers) - .from(HierarchyClass.class); + .withRepeatableContainers(containers) + .from(HierarchyClass.class); assertThat(annotations.stream(TestConfiguration.class)) - .map(annotation -> annotation.getString("location")) - .containsExactly("A", "B"); + .map(annotation -> annotation.getString("location")) + .containsExactly("A", "B"); assertThat(annotations.stream(TestConfiguration.class)) - .map(annotation -> annotation.getString("value")) - .containsExactly("A", "B"); + .map(annotation -> annotation.getString("value")) + .containsExactly("A", "B"); } /** @@ -205,9 +205,9 @@ class MergedAnnotationsTests { .map(MergedAnnotation::getType); assertThat(classes).containsExactly(Component.class, Indexed.class); } - } + @Nested class ConventionBasedAnnotationAttributeOverrideTests { @@ -215,7 +215,7 @@ class MergedAnnotationsTests { void getWithInheritedAnnotationsAttributesWithConventionBasedComposedAnnotation() { MergedAnnotation annotation = MergedAnnotations.from(ConventionBasedComposedContextConfigurationClass.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); assertThat(annotation.isPresent()).isTrue(); assertThat(annotation.getStringArray("locations")).containsExactly("explicitDeclaration"); assertThat(annotation.getStringArray("value")).containsExactly("explicitDeclaration"); @@ -227,7 +227,7 @@ class MergedAnnotationsTests { // xmlConfigFiles can be used because it has an AliasFor annotation MergedAnnotation annotation = MergedAnnotations.from(HalfConventionBasedAndHalfAliasedComposedContextConfigurationClass1.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); assertThat(annotation.getStringArray("locations")).containsExactly("explicitDeclaration"); assertThat(annotation.getStringArray("value")).containsExactly("explicitDeclaration"); } @@ -238,7 +238,7 @@ class MergedAnnotationsTests { // locations doesn't apply because it has no AliasFor annotation MergedAnnotation annotation = MergedAnnotations.from(HalfConventionBasedAndHalfAliasedComposedContextConfigurationClass2.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); assertThat(annotation.getStringArray("locations")).isEmpty(); assertThat(annotation.getStringArray("value")).isEmpty(); } @@ -259,7 +259,7 @@ class MergedAnnotationsTests { void getWithTypeHierarchyWithLocalAliasesThatConflictWithAttributesInMetaAnnotationByConvention() { MergedAnnotation annotation = MergedAnnotations.from(SpringApplicationConfigurationClass.class, SearchStrategy.TYPE_HIERARCHY) - .get(ContextConfiguration.class); + .get(ContextConfiguration.class); assertThat(annotation.getStringArray("locations")).isEmpty(); assertThat(annotation.getStringArray("value")).isEmpty(); assertThat(annotation.getClassArray("classes")).containsExactly(Number.class); @@ -269,33 +269,32 @@ class MergedAnnotationsTests { void getWithTypeHierarchyOnMethodWithSingleElementOverridingAnArrayViaConvention() throws Exception { testGetWithTypeHierarchyWebMapping(WebController.class.getMethod("postMappedWithPathAttribute")); } - } + @Test void fromPreconditions() { SearchStrategy strategy = SearchStrategy.DIRECT; RepeatableContainers containers = RepeatableContainers.standardRepeatables(); assertThatIllegalArgumentException() - .isThrownBy(() -> MergedAnnotations.from(getClass(), strategy, null, AnnotationFilter.PLAIN)) - .withMessage("RepeatableContainers must not be null"); + .isThrownBy(() -> MergedAnnotations.from(getClass(), strategy, null, AnnotationFilter.PLAIN)) + .withMessage("RepeatableContainers must not be null"); assertThatIllegalArgumentException() - .isThrownBy(() -> MergedAnnotations.from(getClass(), strategy, containers, null)) - .withMessage("AnnotationFilter must not be null"); + .isThrownBy(() -> MergedAnnotations.from(getClass(), strategy, containers, null)) + .withMessage("AnnotationFilter must not be null"); assertThatIllegalArgumentException() - .isThrownBy(() -> MergedAnnotations.from(getClass(), new Annotation[0], null, AnnotationFilter.PLAIN)) - .withMessage("RepeatableContainers must not be null"); + .isThrownBy(() -> MergedAnnotations.from(getClass(), new Annotation[0], null, AnnotationFilter.PLAIN)) + .withMessage("RepeatableContainers must not be null"); assertThatIllegalArgumentException() - .isThrownBy(() -> MergedAnnotations.from(getClass(), new Annotation[0], containers, null)) - .withMessage("AnnotationFilter must not be null"); + .isThrownBy(() -> MergedAnnotations.from(getClass(), new Annotation[0], containers, null)) + .withMessage("AnnotationFilter must not be null"); } @Test void streamWhenFromNonAnnotatedClass() { - assertThat(MergedAnnotations.from(NonAnnotatedClass.class). - stream(TransactionalComponent.class)).isEmpty(); + assertThat(MergedAnnotations.from(NonAnnotatedClass.class).stream(TransactionalComponent.class)).isEmpty(); } @Test @@ -315,14 +314,12 @@ class MergedAnnotationsTests { @Test void isPresentWhenFromNonAnnotatedClass() { - assertThat(MergedAnnotations.from(NonAnnotatedClass.class). - isPresent(Transactional.class)).isFalse(); + assertThat(MergedAnnotations.from(NonAnnotatedClass.class).isPresent(Transactional.class)).isFalse(); } @Test void isPresentWhenFromAnnotationClassWithMetaDepth0() { - assertThat(MergedAnnotations.from(TransactionalComponent.class). - isPresent(TransactionalComponent.class)).isFalse(); + assertThat(MergedAnnotations.from(TransactionalComponent.class).isPresent(TransactionalComponent.class)).isFalse(); } @Test @@ -334,8 +331,7 @@ class MergedAnnotationsTests { @Test void isPresentWhenFromAnnotationClassWithMetaDepth2() { - MergedAnnotations annotations = MergedAnnotations.from( - ComposedTransactionalComponent.class); + MergedAnnotations annotations = MergedAnnotations.from(ComposedTransactionalComponent.class); assertThat(annotations.isPresent(Transactional.class)).isTrue(); assertThat(annotations.isPresent(Component.class)).isTrue(); assertThat(annotations.isPresent(ComposedTransactionalComponent.class)).isFalse(); @@ -343,28 +339,24 @@ class MergedAnnotationsTests { @Test void isPresentWhenFromClassWithMetaDepth0() { - assertThat(MergedAnnotations.from(TransactionalComponentClass.class).isPresent( - TransactionalComponent.class)).isTrue(); + assertThat(MergedAnnotations.from(TransactionalComponentClass.class).isPresent(TransactionalComponent.class)).isTrue(); } @Test void isPresentWhenFromSubclassWithMetaDepth0() { - assertThat(MergedAnnotations.from(SubTransactionalComponentClass.class).isPresent( - TransactionalComponent.class)).isFalse(); + assertThat(MergedAnnotations.from(SubTransactionalComponentClass.class).isPresent(TransactionalComponent.class)).isFalse(); } @Test void isPresentWhenFromClassWithMetaDepth1() { - MergedAnnotations annotations = MergedAnnotations.from( - TransactionalComponentClass.class); + MergedAnnotations annotations = MergedAnnotations.from(TransactionalComponentClass.class); assertThat(annotations.isPresent(Transactional.class)).isTrue(); assertThat(annotations.isPresent(Component.class)).isTrue(); } @Test void isPresentWhenFromClassWithMetaDepth2() { - MergedAnnotations annotations = MergedAnnotations.from( - ComposedTransactionalComponentClass.class); + MergedAnnotations annotations = MergedAnnotations.from(ComposedTransactionalComponentClass.class); assertThat(annotations.isPresent(Transactional.class)).isTrue(); assertThat(annotations.isPresent(Component.class)).isTrue(); assertThat(annotations.isPresent(ComposedTransactionalComponent.class)).isTrue(); @@ -395,35 +387,31 @@ class MergedAnnotationsTests { @Test void getMetaTypes() { - MergedAnnotation annotation = MergedAnnotations.from( - ComposedTransactionalComponentClass.class).get( - TransactionalComponent.class); + MergedAnnotation annotation = MergedAnnotations.from(ComposedTransactionalComponentClass.class) + .get(TransactionalComponent.class); assertThat(annotation.getMetaTypes()).containsExactly( ComposedTransactionalComponent.class, TransactionalComponent.class); } @Test void collectMultiValueMapFromNonAnnotatedClass() { - MultiValueMap map = MergedAnnotations.from( - NonAnnotatedClass.class).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); + MultiValueMap map = MergedAnnotations.from(NonAnnotatedClass.class) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); assertThat(map).isEmpty(); } @Test void collectMultiValueMapFromClassWithLocalAnnotation() { - MultiValueMap map = MergedAnnotations.from(TxConfig.class).stream( - Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); - assertThat(map).contains(entry("value", Arrays.asList("TxConfig"))); + MultiValueMap map = MergedAnnotations.from(TxConfig.class) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); + assertThat(map).contains(entry("value", List.of("TxConfig"))); } @Test void collectMultiValueMapFromClassWithLocalComposedAnnotationAndInheritedAnnotation() { MultiValueMap map = MergedAnnotations.from( - SubClassWithInheritedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); + SubClassWithInheritedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); assertThat(map).contains( entry("qualifier", Arrays.asList("composed2", "transactionManager"))); } @@ -431,19 +419,17 @@ class MergedAnnotationsTests { @Test void collectMultiValueMapFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() { MultiValueMap map = MergedAnnotations.from( - SubSubClassWithInheritedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); - assertThat(map).contains(entry("qualifier", Arrays.asList("transactionManager"))); + SubSubClassWithInheritedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); + assertThat(map).contains(entry("qualifier", List.of("transactionManager"))); } @Test void collectMultiValueMapFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() { MultiValueMap map = MergedAnnotations.from( - SubSubClassWithInheritedComposedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); - assertThat(map).contains(entry("qualifier", Arrays.asList("composed1"))); + SubSubClassWithInheritedComposedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); + assertThat(map).contains(entry("qualifier", List.of("composed1"))); } /** @@ -455,10 +441,10 @@ class MergedAnnotationsTests { */ @Test void collectMultiValueMapFromClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() { - MultiValueMap map = MergedAnnotations.from(DerivedTxConfig.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); - assertThat(map).contains(entry("value", Arrays.asList("DerivedTxConfig"))); + MultiValueMap map = MergedAnnotations.from( + DerivedTxConfig.class, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); + assertThat(map).contains(entry("value", List.of("DerivedTxConfig"))); } /** @@ -468,24 +454,23 @@ class MergedAnnotationsTests { @Test void collectMultiValueMapFromClassWithMultipleComposedAnnotations() { MultiValueMap map = MergedAnnotations.from( - TxFromMultipleComposedAnnotations.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); + TxFromMultipleComposedAnnotations.class, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); assertThat(map).contains( entry("value", Arrays.asList("TxInheritedComposed", "TxComposed"))); } @Test void getWithInheritedAnnotationsFromClassWithLocalAnnotation() { - MergedAnnotation annotation = MergedAnnotations.from(TxConfig.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + MergedAnnotation annotation = MergedAnnotations.from( + TxConfig.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.getString("value")).isEqualTo("TxConfig"); } @Test void getWithInheritedAnnotationsFromClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() { - MergedAnnotation annotation = MergedAnnotations.from(DerivedTxConfig.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + MergedAnnotation annotation = MergedAnnotations.from( + DerivedTxConfig.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.getString("value")).isEqualTo("DerivedTxConfig"); } @@ -499,53 +484,46 @@ class MergedAnnotationsTests { @Test void getWithInheritedAnnotationsFavorsLocalComposedAnnotationOverInheritedAnnotation() { MergedAnnotation annotation = MergedAnnotations.from( - SubClassWithInheritedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + SubClassWithInheritedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.getBoolean("readOnly")).isTrue(); } @Test void getWithInheritedAnnotationsFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() { MergedAnnotation annotation = MergedAnnotations.from( - SubSubClassWithInheritedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + SubSubClassWithInheritedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.getBoolean("readOnly")).isFalse(); } @Test void getWithInheritedAnnotationsFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() { MergedAnnotation annotation = MergedAnnotations.from( - SubSubClassWithInheritedComposedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + SubSubClassWithInheritedComposedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.getBoolean("readOnly")).isFalse(); } @Test void getWithInheritedAnnotationsFromInterfaceImplementedBySuperclass() { MergedAnnotation annotation = MergedAnnotations.from( - ConcreteClassWithInheritedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + ConcreteClassWithInheritedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.isPresent()).isFalse(); } @Test void getWithInheritedAnnotationsFromInheritedAnnotationInterface() { MergedAnnotation annotation = MergedAnnotations.from( - InheritedAnnotationInterface.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); + InheritedAnnotationInterface.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Transactional.class); assertThat(annotation.isPresent()).isTrue(); } @Test void getWithInheritedAnnotationsFromNonInheritedAnnotationInterface() { MergedAnnotation annotation = MergedAnnotations.from( - NonInheritedAnnotationInterface.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(Order.class); + NonInheritedAnnotationInterface.class, SearchStrategy.INHERITED_ANNOTATIONS).get(Order.class); assertThat(annotation.isPresent()).isTrue(); } - @Test void withInheritedAnnotationsFromAliasedComposedAnnotation() { MergedAnnotation annotation = MergedAnnotations.from( @@ -567,15 +545,11 @@ class MergedAnnotationsTests { @Test void getWithInheritedAnnotationsFromImplicitAliasesInMetaAnnotationOnComposedAnnotation() { MergedAnnotation annotation = MergedAnnotations.from( - ComposedImplicitAliasesContextConfigurationClass.class, - SearchStrategy.INHERITED_ANNOTATIONS).get( - ImplicitAliasesContextConfiguration.class); - assertThat(annotation.getStringArray("groovyScripts")).containsExactly("A.xml", - "B.xml"); - assertThat(annotation.getStringArray("xmlFiles")).containsExactly("A.xml", - "B.xml"); - assertThat(annotation.getStringArray("locations")).containsExactly("A.xml", - "B.xml"); + ComposedImplicitAliasesContextConfigurationClass.class, SearchStrategy.INHERITED_ANNOTATIONS) + .get(ImplicitAliasesContextConfiguration.class); + assertThat(annotation.getStringArray("groovyScripts")).containsExactly("A.xml", "B.xml"); + assertThat(annotation.getStringArray("xmlFiles")).containsExactly("A.xml", "B.xml"); + assertThat(annotation.getStringArray("locations")).containsExactly("A.xml", "B.xml"); assertThat(annotation.getStringArray("value")).containsExactly("A.xml", "B.xml"); } @@ -615,8 +589,8 @@ class MergedAnnotationsTests { } private void testGetWithInherited(Class element, String... expected) { - MergedAnnotation annotation = MergedAnnotations.from(element, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + MergedAnnotation annotation = MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS) + .get(ContextConfiguration.class); assertThat(annotation.getStringArray("locations")).isEqualTo(expected); assertThat(annotation.getStringArray("value")).isEqualTo(expected); assertThat(annotation.getClassArray("classes")).isEmpty(); @@ -625,8 +599,8 @@ class MergedAnnotationsTests { @Test void getWithInheritedAnnotationsFromShadowedAliasComposedAnnotation() { MergedAnnotation annotation = MergedAnnotations.from( - ShadowedAliasComposedContextConfigurationClass.class, - SearchStrategy.INHERITED_ANNOTATIONS).get(ContextConfiguration.class); + ShadowedAliasComposedContextConfigurationClass.class, SearchStrategy.INHERITED_ANNOTATIONS) + .get(ContextConfiguration.class); assertThat(annotation.getStringArray("locations")).containsExactly("test.xml"); assertThat(annotation.getStringArray("value")).containsExactly("test.xml"); } @@ -674,8 +648,7 @@ class MergedAnnotationsTests { @Test void getWithTypeHierarchyFromSubSubNonInheritedAnnotationInterface() { MergedAnnotation annotation = MergedAnnotations.from( - SubSubNonInheritedAnnotationInterface.class, - SearchStrategy.TYPE_HIERARCHY).get(Order.class); + SubSubNonInheritedAnnotationInterface.class, SearchStrategy.TYPE_HIERARCHY).get(Order.class); assertThat(annotation.isPresent()).isTrue(); assertThat(annotation.getAggregateIndex()).isEqualTo(2); } @@ -692,27 +665,22 @@ class MergedAnnotationsTests { void streamWithTypeHierarchyInheritedFromSuperInterfaceMethod() throws Exception { Method method = Hello2Impl.class.getMethod("method"); long count = MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY) - .from(method) - .stream(TestAnnotation1.class) - .count(); + .from(method).stream(TestAnnotation1.class).count(); assertThat(count).isEqualTo(1); } @Test void getWithTypeHierarchyInheritedFromAbstractMethod() throws NoSuchMethodException { Method method = ConcreteClassWithInheritedAnnotation.class.getMethod("handle"); - MergedAnnotation annotation = MergedAnnotations.from(method, - SearchStrategy.TYPE_HIERARCHY).get(Transactional.class); + MergedAnnotation annotation = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get(Transactional.class); assertThat(annotation.isPresent()).isTrue(); assertThat(annotation.getAggregateIndex()).isEqualTo(1); } @Test void getWithTypeHierarchyInheritedFromBridgedMethod() throws NoSuchMethodException { - Method method = ConcreteClassWithInheritedAnnotation.class.getMethod( - "handleParameterized", String.class); - MergedAnnotation annotation = MergedAnnotations.from(method, - SearchStrategy.TYPE_HIERARCHY).get(Transactional.class); + Method method = ConcreteClassWithInheritedAnnotation.class.getMethod("handleParameterized", String.class); + MergedAnnotation annotation = MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get(Transactional.class); assertThat(annotation.isPresent()).isTrue(); assertThat(annotation.getAggregateIndex()).isEqualTo(1); } @@ -740,16 +708,14 @@ class MergedAnnotationsTests { @Test void getWithTypeHierarchyFromClassWithMetaAndLocalTxConfig() { MergedAnnotation annotation = MergedAnnotations.from( - MetaAndLocalTxConfigClass.class, SearchStrategy.TYPE_HIERARCHY).get( - Transactional.class); + MetaAndLocalTxConfigClass.class, SearchStrategy.TYPE_HIERARCHY).get(Transactional.class); assertThat(annotation.getString("qualifier")).isEqualTo("localTxMgr"); } @Test void getWithTypeHierarchyFromClassWithAttributeAliasesInTargetAnnotation() { MergedAnnotation mergedAnnotation = MergedAnnotations.from( - AliasedTransactionalComponentClass.class, SearchStrategy.TYPE_HIERARCHY).get( - AliasedTransactional.class); + AliasedTransactionalComponentClass.class, SearchStrategy.TYPE_HIERARCHY).get(AliasedTransactional.class); AliasedTransactional synthesizedAnnotation = mergedAnnotation.synthesize(); String qualifier = "aliasForQualifier"; assertThat(mergedAnnotation.getString("value")).isEqualTo(qualifier); @@ -761,8 +727,7 @@ class MergedAnnotationsTests { @Test // gh-23767 void getWithTypeHierarchyFromClassWithComposedMetaTransactionalAnnotation() { MergedAnnotation mergedAnnotation = MergedAnnotations.from( - ComposedTransactionalClass.class, SearchStrategy.TYPE_HIERARCHY).get( - AliasedTransactional.class); + ComposedTransactionalClass.class, SearchStrategy.TYPE_HIERARCHY).get(AliasedTransactional.class); assertThat(mergedAnnotation.getString("value")).isEqualTo("anotherTransactionManager"); assertThat(mergedAnnotation.getString("qualifier")).isEqualTo("anotherTransactionManager"); } @@ -770,8 +735,7 @@ class MergedAnnotationsTests { @Test // gh-23767 void getWithTypeHierarchyFromClassWithMetaMetaAliasedTransactional() { MergedAnnotation mergedAnnotation = MergedAnnotations.from( - MetaMetaAliasedTransactionalClass.class, SearchStrategy.TYPE_HIERARCHY).get( - AliasedTransactional.class); + MetaMetaAliasedTransactionalClass.class, SearchStrategy.TYPE_HIERARCHY).get(AliasedTransactional.class); assertThat(mergedAnnotation.getString("value")).isEqualTo("meta"); assertThat(mergedAnnotation.getString("qualifier")).isEqualTo("meta"); } @@ -807,51 +771,46 @@ class MergedAnnotationsTests { @Test void getWithTypeHierarchyWhenMultipleMetaAnnotationsHaveClashingAttributeNames() { MergedAnnotations annotations = MergedAnnotations.from( - AliasedComposedContextConfigurationAndTestPropertySourceClass.class, - SearchStrategy.TYPE_HIERARCHY); + AliasedComposedContextConfigurationAndTestPropertySourceClass.class, SearchStrategy.TYPE_HIERARCHY); MergedAnnotation contextConfig = annotations.get(ContextConfiguration.class); assertThat(contextConfig.getStringArray("locations")).containsExactly("test.xml"); assertThat(contextConfig.getStringArray("value")).containsExactly("test.xml"); MergedAnnotation testPropSource = annotations.get(TestPropertySource.class); - assertThat(testPropSource.getStringArray("locations")).containsExactly( - "test.properties"); - assertThat(testPropSource.getStringArray("value")).containsExactly( - "test.properties"); + assertThat(testPropSource.getStringArray("locations")).containsExactly("test.properties"); + assertThat(testPropSource.getStringArray("value")).containsExactly("test.properties"); } @Test void getWithTypeHierarchyOnMethodWithSingleElementOverridingAnArrayViaAliasFor() throws Exception { - testGetWithTypeHierarchyWebMapping( - WebController.class.getMethod("getMappedWithValueAttribute")); - testGetWithTypeHierarchyWebMapping( - WebController.class.getMethod("getMappedWithPathAttribute")); + testGetWithTypeHierarchyWebMapping(WebController.class.getMethod("getMappedWithValueAttribute")); + testGetWithTypeHierarchyWebMapping(WebController.class.getMethod("getMappedWithPathAttribute")); } private void testGetWithTypeHierarchyWebMapping(AnnotatedElement element) { - MergedAnnotation annotation = MergedAnnotations.from(element, - SearchStrategy.TYPE_HIERARCHY).get(RequestMapping.class); + MergedAnnotation annotation = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY) + .get(RequestMapping.class); assertThat(annotation.getStringArray("value")).containsExactly("/test"); assertThat(annotation.getStringArray("path")).containsExactly("/test"); } @Test - void getDirectWithJavaxAnnotationType() throws Exception { - assertThat(MergedAnnotations.from(ResourceHolder.class).get( - Resource.class).getString("name")).isEqualTo("x"); + void getDirectWithJavaxAnnotationType() { + assertThat(MergedAnnotations.from(ResourceHolder.class).get(Resource.class) + .getString("name")).isEqualTo("x"); } @Test void streamInheritedFromClassWithInterface() throws Exception { Method method = TransactionalServiceImpl.class.getMethod("doIt"); - assertThat(MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS).stream( - Transactional.class)).isEmpty(); + assertThat(MergedAnnotations.from(method, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class)).isEmpty(); } @Test void streamTypeHierarchyFromClassWithInterface() throws Exception { Method method = TransactionalServiceImpl.class.getMethod("doIt"); - assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).stream( - Transactional.class)).hasSize(1); + assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY) + .stream(Transactional.class)).hasSize(1); } @Test @@ -860,8 +819,8 @@ class MergedAnnotationsTests { Method method = Leaf.class.getMethod("annotatedOnLeaf"); assertThat(method.getAnnotation(Order.class)).isNotNull(); assertThat(MergedAnnotations.from(method).get(Order.class).getDistance()).isEqualTo(0); - assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get( - Order.class).getDistance()).isEqualTo(0); + assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get(Order.class) + .getDistance()).isEqualTo(0); } @Test @@ -869,8 +828,8 @@ class MergedAnnotationsTests { Method method = Leaf.class.getMethod("fromInterfaceImplementedByRoot"); assertThat(method.getAnnotation(Order.class)).isNull(); assertThat(MergedAnnotations.from(method).get(Order.class).getDistance()).isEqualTo(-1); - assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get( - Order.class).getDistance()).isEqualTo(0); + assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get(Order.class) + .getDistance()).isEqualTo(0); } @Test @@ -878,8 +837,8 @@ class MergedAnnotationsTests { Method method = Leaf.class.getMethod("metaAnnotatedOnLeaf"); assertThat(method.getAnnotation(Order.class)).isNull(); assertThat(MergedAnnotations.from(method).get(Order.class).getDistance()).isEqualTo(1); - assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get( - Order.class).getDistance()).isEqualTo(1); + assertThat(MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY).get(Order.class) + .getDistance()).isEqualTo(1); } @Test @@ -1214,7 +1173,7 @@ class MergedAnnotationsTests { } @Test - void isDirectlyPresentForAllScenarios() throws Exception { + void isDirectlyPresentForAllScenarios() { // no class-level annotation assertThat(MergedAnnotations.from(NonAnnotatedInterface.class).get( Transactional.class).isDirectlyPresent()).isFalse(); @@ -1357,7 +1316,7 @@ class MergedAnnotationsTests { } @Test - void getValueFromNonPublicAnnotation() throws Exception { + void getValueFromNonPublicAnnotation() { Annotation[] declaredAnnotations = NonPublicAnnotatedClass.class.getDeclaredAnnotations(); assertThat(declaredAnnotations).hasSize(1); Annotation annotation = declaredAnnotations[0]; @@ -1499,7 +1458,7 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithoutAttributeAliases() throws Exception { + void synthesizeWithoutAttributeAliases() { Component component = WebController.class.getAnnotation(Component.class); assertThat(component).isNotNull(); Component synthesizedComponent = MergedAnnotation.from(component).synthesize(); @@ -1631,114 +1590,123 @@ class MergedAnnotationsTests { } @Test - void synthesizeWhenAliasForIsMissingAttributeDeclaration() throws Exception { + void synthesizeWhenAliasForIsMissingAttributeDeclaration() { AliasForWithMissingAttributeDeclaration annotation = AliasForWithMissingAttributeDeclarationClass.class.getAnnotation( AliasForWithMissingAttributeDeclaration.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("@AliasFor declaration on attribute 'foo' in annotation") - .withMessageContaining(AliasForWithMissingAttributeDeclaration.class.getName()) - .withMessageContaining("points to itself"); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("@AliasFor declaration on attribute 'foo' in annotation") + .withMessageContaining(AliasForWithMissingAttributeDeclaration.class.getName()) + .withMessageContaining("points to itself"); } @Test - void synthesizeWhenAliasForHasDuplicateAttributeDeclaration() throws Exception { - AliasForWithDuplicateAttributeDeclaration annotation = AliasForWithDuplicateAttributeDeclarationClass.class.getAnnotation( - AliasForWithDuplicateAttributeDeclaration.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("In @AliasFor declared on attribute 'foo' in annotation") - .withMessageContaining(AliasForWithDuplicateAttributeDeclaration.class.getName()) - .withMessageContaining("attribute 'attribute' and its alias 'value' are present with values of 'baz' and 'bar'"); + void synthesizeWhenAliasForHasDuplicateAttributeDeclaration() { + AliasForWithDuplicateAttributeDeclaration annotation = + AliasForWithDuplicateAttributeDeclarationClass.class.getAnnotation( + AliasForWithDuplicateAttributeDeclaration.class); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("In @AliasFor declared on attribute 'foo' in annotation") + .withMessageContaining(AliasForWithDuplicateAttributeDeclaration.class.getName()) + .withMessageContaining("attribute 'attribute' and its alias 'value' are present with values of 'baz' and 'bar'"); } @Test - void synthesizeWhenAttributeAliasForNonexistentAttribute() throws Exception { + void synthesizeWhenAttributeAliasForNonexistentAttribute() { AliasForNonexistentAttribute annotation = AliasForNonexistentAttributeClass.class.getAnnotation( AliasForNonexistentAttribute.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("@AliasFor declaration on attribute 'foo' in annotation") - .withMessageContaining(AliasForNonexistentAttribute.class.getName()) - .withMessageContaining("declares an alias for 'bar' which is not present"); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("@AliasFor declaration on attribute 'foo' in annotation") + .withMessageContaining(AliasForNonexistentAttribute.class.getName()) + .withMessageContaining("declares an alias for 'bar' which is not present"); } @Test - void synthesizeWhenAttributeAliasWithMirroredAliasForWrongAttribute() throws Exception { + void synthesizeWhenAttributeAliasWithMirroredAliasForWrongAttribute() { AliasForWithMirroredAliasForWrongAttribute annotation = AliasForWithMirroredAliasForWrongAttributeClass.class.getAnnotation( AliasForWithMirroredAliasForWrongAttribute.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessage("@AliasFor declaration on attribute 'bar' in annotation [" - + AliasForWithMirroredAliasForWrongAttribute.class.getName() - + "] declares an alias for 'quux' which is not present."); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(annotation)) + .withMessage("@AliasFor declaration on attribute 'bar' in annotation [" + + AliasForWithMirroredAliasForWrongAttribute.class.getName() + + "] declares an alias for 'quux' which is not present."); } @Test - void synthesizeWhenAttributeAliasForAttributeOfDifferentType() throws Exception { + void synthesizeWhenAttributeAliasForAttributeOfDifferentType() { AliasForAttributeOfDifferentType annotation = AliasForAttributeOfDifferentTypeClass.class.getAnnotation( AliasForAttributeOfDifferentType.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("Misconfigured aliases") - .withMessageContaining(AliasForAttributeOfDifferentType.class.getName()) - .withMessageContaining("attribute 'foo'") - .withMessageContaining("attribute 'bar'") - .withMessageContaining("same return type"); + + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("Misconfigured aliases") + .withMessageContaining(AliasForAttributeOfDifferentType.class.getName()) + .withMessageContaining("attribute 'foo'") + .withMessageContaining("attribute 'bar'") + .withMessageContaining("same return type"); } @Test - void synthesizeWhenAttributeAliasForWithMissingDefaultValues() throws Exception { + void synthesizeWhenAttributeAliasForWithMissingDefaultValues() { AliasForWithMissingDefaultValues annotation = AliasForWithMissingDefaultValuesClass.class.getAnnotation( AliasForWithMissingDefaultValues.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("Misconfigured aliases") - .withMessageContaining(AliasForWithMissingDefaultValues.class.getName()) - .withMessageContaining("attribute 'foo' in annotation") - .withMessageContaining("attribute 'bar' in annotation") - .withMessageContaining("default values"); + + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("Misconfigured aliases") + .withMessageContaining(AliasForWithMissingDefaultValues.class.getName()) + .withMessageContaining("attribute 'foo' in annotation") + .withMessageContaining("attribute 'bar' in annotation") + .withMessageContaining("default values"); } @Test - void synthesizeWhenAttributeAliasForAttributeWithDifferentDefaultValue() throws Exception { + void synthesizeWhenAttributeAliasForAttributeWithDifferentDefaultValue() { AliasForAttributeWithDifferentDefaultValue annotation = AliasForAttributeWithDifferentDefaultValueClass.class.getAnnotation( AliasForAttributeWithDifferentDefaultValue.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("Misconfigured aliases") - .withMessageContaining(AliasForAttributeWithDifferentDefaultValue.class.getName()) - .withMessageContaining("attribute 'foo' in annotation") - .withMessageContaining("attribute 'bar' in annotation") - .withMessageContaining("same default value"); + + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("Misconfigured aliases") + .withMessageContaining(AliasForAttributeWithDifferentDefaultValue.class.getName()) + .withMessageContaining("attribute 'foo' in annotation") + .withMessageContaining("attribute 'bar' in annotation") + .withMessageContaining("same default value"); } @Test - void synthesizeWhenAttributeAliasForMetaAnnotationThatIsNotMetaPresent() throws Exception { + void synthesizeWhenAttributeAliasForMetaAnnotationThatIsNotMetaPresent() { AliasedComposedTestConfigurationNotMetaPresent annotation = AliasedComposedTestConfigurationNotMetaPresentClass.class.getAnnotation( AliasedComposedTestConfigurationNotMetaPresent.class); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(annotation)) - .withMessageStartingWith("@AliasFor declaration on attribute 'xmlConfigFile' in annotation") - .withMessageContaining(AliasedComposedTestConfigurationNotMetaPresent.class.getName()) - .withMessageContaining("declares an alias for attribute 'location' in annotation") - .withMessageContaining(TestConfiguration.class.getName()) - .withMessageContaining("not meta-present"); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(annotation)) + .withMessageStartingWith("@AliasFor declaration on attribute 'xmlConfigFile' in annotation") + .withMessageContaining(AliasedComposedTestConfigurationNotMetaPresent.class.getName()) + .withMessageContaining("declares an alias for attribute 'location' in annotation") + .withMessageContaining(TestConfiguration.class.getName()) + .withMessageContaining("not meta-present"); } @Test - void synthesizeWithImplicitAliases() throws Exception { + void synthesizeWithImplicitAliases() { testSynthesisWithImplicitAliases(ValueImplicitAliasesTestConfigurationClass.class, "value"); testSynthesisWithImplicitAliases(Location1ImplicitAliasesTestConfigurationClass.class, "location1"); testSynthesisWithImplicitAliases(XmlImplicitAliasesTestConfigurationClass.class, "xmlFile"); testSynthesisWithImplicitAliases(GroovyImplicitAliasesSimpleTestConfigurationClass.class, "groovyScript"); } - private void testSynthesisWithImplicitAliases(Class clazz, String expected) throws Exception { + private void testSynthesisWithImplicitAliases(Class clazz, String expected) { ImplicitAliasesTestConfiguration config = clazz.getAnnotation(ImplicitAliasesTestConfiguration.class); assertThat(config).isNotNull(); ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(config).synthesize(); @@ -1750,8 +1718,7 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithImplicitAliasesWithImpliedAliasNamesOmitted() - throws Exception { + void synthesizeWithImplicitAliasesWithImpliedAliasNamesOmitted() { testSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted( ValueImplicitAliasesWithImpliedAliasNamesOmittedTestConfigurationClass.class, "value"); @@ -1763,8 +1730,7 @@ class MergedAnnotationsTests { "xmlFile"); } - private void testSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted( - Class clazz, String expected) { + private void testSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(Class clazz, String expected) { ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration config = clazz.getAnnotation( ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration.class); assertThat(config).isNotNull(); @@ -1777,7 +1743,7 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithImplicitAliasesForAliasPair() throws Exception { + void synthesizeWithImplicitAliasesForAliasPair() { ImplicitAliasesForAliasPairTestConfiguration config = ImplicitAliasesForAliasPairTestConfigurationClass.class.getAnnotation( ImplicitAliasesForAliasPairTestConfiguration.class); @@ -1788,7 +1754,7 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithTransitiveImplicitAliases() throws Exception { + void synthesizeWithTransitiveImplicitAliases() { TransitiveImplicitAliasesTestConfiguration config = TransitiveImplicitAliasesTestConfigurationClass.class.getAnnotation( TransitiveImplicitAliasesTestConfiguration.class); @@ -1799,70 +1765,69 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithTransitiveImplicitAliasesForAliasPair() throws Exception { + void synthesizeWithTransitiveImplicitAliasesForAliasPair() { TransitiveImplicitAliasesForAliasPairTestConfiguration config = TransitiveImplicitAliasesForAliasPairTestConfigurationClass.class.getAnnotation( TransitiveImplicitAliasesForAliasPairTestConfiguration.class); - TransitiveImplicitAliasesForAliasPairTestConfiguration synthesized = MergedAnnotation.from( - config).synthesize(); + TransitiveImplicitAliasesForAliasPairTestConfiguration synthesized = MergedAnnotation.from(config).synthesize(); assertSynthesized(synthesized); assertThat(synthesized.xml()).isEqualTo("test.xml"); assertThat(synthesized.groovy()).isEqualTo("test.xml"); } @Test - void synthesizeWithImplicitAliasesWithMissingDefaultValues() throws Exception { + void synthesizeWithImplicitAliasesWithMissingDefaultValues() { Class clazz = ImplicitAliasesWithMissingDefaultValuesTestConfigurationClass.class; Class annotationType = ImplicitAliasesWithMissingDefaultValuesTestConfiguration.class; - ImplicitAliasesWithMissingDefaultValuesTestConfiguration config = clazz.getAnnotation( - annotationType); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(clazz, config)) - .withMessageStartingWith("Misconfigured aliases:") - .withMessageContaining("attribute 'location1' in annotation [" + annotationType.getName() + "]") - .withMessageContaining("attribute 'location2' in annotation [" + annotationType.getName() + "]") - .withMessageContaining("default values"); + ImplicitAliasesWithMissingDefaultValuesTestConfiguration config = clazz.getAnnotation(annotationType); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(clazz, config)) + .withMessageStartingWith("Misconfigured aliases:") + .withMessageContaining("attribute 'location1' in annotation [" + annotationType.getName() + "]") + .withMessageContaining("attribute 'location2' in annotation [" + annotationType.getName() + "]") + .withMessageContaining("default values"); } @Test - void synthesizeWithImplicitAliasesWithDifferentDefaultValues() - throws Exception { + void synthesizeWithImplicitAliasesWithDifferentDefaultValues() { Class clazz = ImplicitAliasesWithDifferentDefaultValuesTestConfigurationClass.class; Class annotationType = ImplicitAliasesWithDifferentDefaultValuesTestConfiguration.class; - ImplicitAliasesWithDifferentDefaultValuesTestConfiguration config = clazz.getAnnotation( - annotationType); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(clazz, config)) - .withMessageStartingWith("Misconfigured aliases:") - .withMessageContaining("attribute 'location1' in annotation [" + annotationType.getName() + "]") - .withMessageContaining("attribute 'location2' in annotation [" + annotationType.getName() + "]") - .withMessageContaining("same default value"); + ImplicitAliasesWithDifferentDefaultValuesTestConfiguration config = clazz.getAnnotation(annotationType); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> MergedAnnotation.from(clazz, config)) + .withMessageStartingWith("Misconfigured aliases:") + .withMessageContaining("attribute 'location1' in annotation [" + annotationType.getName() + "]") + .withMessageContaining("attribute 'location2' in annotation [" + annotationType.getName() + "]") + .withMessageContaining("same default value"); } @Test - void synthesizeWithImplicitAliasesWithDuplicateValues() throws Exception { + void synthesizeWithImplicitAliasesWithDuplicateValues() { Class clazz = ImplicitAliasesWithDuplicateValuesTestConfigurationClass.class; Class annotationType = ImplicitAliasesWithDuplicateValuesTestConfiguration.class; - ImplicitAliasesWithDuplicateValuesTestConfiguration config = clazz.getAnnotation( - annotationType); - assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> - MergedAnnotation.from(clazz, config)) - .withMessageStartingWith("Different @AliasFor mirror values for annotation") - .withMessageContaining(annotationType.getName()) - .withMessageContaining("declared on class") - .withMessageContaining(clazz.getName()) - .withMessageContaining("are declared with values of"); + ImplicitAliasesWithDuplicateValuesTestConfiguration config = clazz.getAnnotation(annotationType); + + assertThatExceptionOfType(AnnotationConfigurationException.class) + .isThrownBy(() -> MergedAnnotation.from(clazz, config)) + .withMessageStartingWith("Different @AliasFor mirror values for annotation") + .withMessageContaining(annotationType.getName()) + .withMessageContaining("declared on class") + .withMessageContaining(clazz.getName()) + .withMessageContaining("are declared with values of"); } @Test - void synthesizeFromMapWithoutAttributeAliases() throws Exception { + void synthesizeFromMapWithoutAttributeAliases() { Component component = WebController.class.getAnnotation(Component.class); assertThat(component).isNotNull(); Map map = Collections.singletonMap("value", "webController"); MergedAnnotation annotation = MergedAnnotation.of(Component.class, map); + Component synthesizedComponent = annotation.synthesize(); assertSynthesized(synthesizedComponent); assertThat(synthesizedComponent.value()).isEqualTo("webController"); @@ -1870,14 +1835,13 @@ class MergedAnnotationsTests { @Test @SuppressWarnings("unchecked") - void synthesizeFromMapWithNestedMap() throws Exception { + void synthesizeFromMapWithNestedMap() { ComponentScanSingleFilter componentScan = ComponentScanSingleFilterClass.class.getAnnotation( ComponentScanSingleFilter.class); assertThat(componentScan).isNotNull(); assertThat(componentScan.value().pattern()).isEqualTo("*Foo"); Map map = MergedAnnotation.from(componentScan).asMap( - annotation -> new LinkedHashMap<>(), - Adapt.ANNOTATION_TO_MAP); + annotation -> new LinkedHashMap<>(), Adapt.ANNOTATION_TO_MAP); Map filterMap = (Map) map.get("value"); assertThat(filterMap.get("pattern")).isEqualTo("*Foo"); filterMap.put("pattern", "newFoo"); @@ -1891,13 +1855,11 @@ class MergedAnnotationsTests { @Test @SuppressWarnings("unchecked") - void synthesizeFromMapWithNestedArrayOfMaps() throws Exception { - ComponentScan componentScan = ComponentScanClass.class.getAnnotation( - ComponentScan.class); + void synthesizeFromMapWithNestedArrayOfMaps() { + ComponentScan componentScan = ComponentScanClass.class.getAnnotation(ComponentScan.class); assertThat(componentScan).isNotNull(); Map map = MergedAnnotation.from(componentScan).asMap( - annotation -> new LinkedHashMap<>(), - Adapt.ANNOTATION_TO_MAP); + annotation -> new LinkedHashMap<>(), Adapt.ANNOTATION_TO_MAP); Map[] filters = (Map[]) map.get("excludeFilters"); List patterns = Arrays.stream(filters).map( m -> (String) m.get("pattern")).toList(); @@ -1906,18 +1868,16 @@ class MergedAnnotationsTests { filters[0].put("enigma", 42); filters[1].put("pattern", "newBar"); filters[1].put("enigma", 42); - MergedAnnotation annotation = MergedAnnotation.of( - ComponentScan.class, map); + MergedAnnotation annotation = MergedAnnotation.of(ComponentScan.class, map); ComponentScan synthesizedComponentScan = annotation.synthesize(); assertSynthesized(synthesizedComponentScan); - assertThat(Arrays.stream(synthesizedComponentScan.excludeFilters()).map( - Filter::pattern)).containsExactly("newFoo", "newBar"); + assertThat(Arrays.stream(synthesizedComponentScan.excludeFilters()).map(Filter::pattern)) + .containsExactly("newFoo", "newBar"); } @Test - void synthesizeFromDefaultsWithoutAttributeAliases() throws Exception { - MergedAnnotation annotation = MergedAnnotation.of( - AnnotationWithDefaults.class); + void synthesizeFromDefaultsWithoutAttributeAliases() { + MergedAnnotation annotation = MergedAnnotation.of(AnnotationWithDefaults.class); AnnotationWithDefaults synthesized = annotation.synthesize(); assertThat(synthesized.text()).isEqualTo("enigma"); assertThat(synthesized.predicate()).isTrue(); @@ -1925,51 +1885,45 @@ class MergedAnnotationsTests { } @Test - void synthesizeFromDefaultsWithAttributeAliases() throws Exception { - MergedAnnotation annotation = MergedAnnotation.of( - TestConfiguration.class); + void synthesizeFromDefaultsWithAttributeAliases() { + MergedAnnotation annotation = MergedAnnotation.of(TestConfiguration.class); TestConfiguration synthesized = annotation.synthesize(); assertThat(synthesized.value()).isEmpty(); assertThat(synthesized.location()).isEmpty(); } @Test - void synthesizeWhenAttributeAliasesWithDifferentValues() throws Exception { + void synthesizeWhenAttributeAliasesWithDifferentValues() { assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() -> MergedAnnotation.from(TestConfigurationMismatch.class.getAnnotation(TestConfiguration.class)).synthesize()); } @Test - void synthesizeFromMapWithMinimalAttributesWithAttributeAliases() - throws Exception { + void synthesizeFromMapWithMinimalAttributesWithAttributeAliases() { Map map = Collections.singletonMap("location", "test.xml"); - MergedAnnotation annotation = MergedAnnotation.of( - TestConfiguration.class, map); + MergedAnnotation annotation = MergedAnnotation.of(TestConfiguration.class, map); TestConfiguration synthesized = annotation.synthesize(); assertThat(synthesized.value()).isEqualTo("test.xml"); assertThat(synthesized.location()).isEqualTo("test.xml"); } @Test - void synthesizeFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements() - throws Exception { + void synthesizeFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements() { synthesizeFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements( Collections.singletonMap("value", "/foo")); synthesizeFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements( Collections.singletonMap("path", "/foo")); } - private void synthesizeFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements( - Map map) { - MergedAnnotation annotation = MergedAnnotation.of(GetMapping.class, - map); + private void synthesizeFromMapWithAttributeAliasesThatOverrideArraysWithSingleElements(Map map) { + MergedAnnotation annotation = MergedAnnotation.of(GetMapping.class, map); GetMapping synthesized = annotation.synthesize(); assertThat(synthesized.value()).isEqualTo("/foo"); assertThat(synthesized.path()).isEqualTo("/foo"); } @Test - void synthesizeFromMapWithImplicitAttributeAliases() throws Exception { + void synthesizeFromMapWithImplicitAttributeAliases() { testSynthesisFromMapWithImplicitAliases("value"); testSynthesisFromMapWithImplicitAliases("location1"); testSynthesisFromMapWithImplicitAliases("location2"); @@ -1978,13 +1932,12 @@ class MergedAnnotationsTests { testSynthesisFromMapWithImplicitAliases("groovyScript"); } - private void testSynthesisFromMapWithImplicitAliases(String attributeNameAndValue) - throws Exception { - Map map = Collections.singletonMap(attributeNameAndValue, - attributeNameAndValue); + private void testSynthesisFromMapWithImplicitAliases(String attributeNameAndValue) { + Map map = Collections.singletonMap(attributeNameAndValue, attributeNameAndValue); MergedAnnotation annotation = MergedAnnotation.of( ImplicitAliasesTestConfiguration.class, map); ImplicitAliasesTestConfiguration synthesized = annotation.synthesize(); + assertThat(synthesized.value()).isEqualTo(attributeNameAndValue); assertThat(synthesized.location1()).isEqualTo(attributeNameAndValue); assertThat(synthesized.location2()).isEqualTo(attributeNameAndValue); @@ -1994,12 +1947,12 @@ class MergedAnnotationsTests { } @Test - void synthesizeFromMapWithMissingAttributeValue() throws Exception { + void synthesizeFromMapWithMissingAttributeValue() { testMissingTextAttribute(Collections.emptyMap()); } @Test - void synthesizeFromMapWithNullAttributeValue() throws Exception { + void synthesizeFromMapWithNullAttributeValue() { Map map = Collections.singletonMap("text", null); assertThat(map).containsKey("text"); testMissingTextAttribute(map); @@ -2008,12 +1961,12 @@ class MergedAnnotationsTests { private void testMissingTextAttribute(Map attributes) { assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(() -> MergedAnnotation.of(AnnotationWithoutDefaults.class, attributes).synthesize().text()) - .withMessage("No value found for attribute named 'text' in merged annotation " + - AnnotationWithoutDefaults.class.getName()); + .withMessage("No value found for attribute named 'text' in merged annotation " + + AnnotationWithoutDefaults.class.getName()); } @Test - void synthesizeFromMapWithAttributeOfIncorrectType() throws Exception { + void synthesizeFromMapWithAttributeOfIncorrectType() { Map map = Collections.singletonMap("value", 42L); MergedAnnotation annotation = MergedAnnotation.of(Component.class, map); assertThatIllegalStateException().isThrownBy(() -> annotation.synthesize().value()) @@ -2023,10 +1976,11 @@ class MergedAnnotationsTests { } @Test - void synthesizeFromAnnotationAttributesWithoutAttributeAliases() throws Exception { + void synthesizeFromAnnotationAttributesWithoutAttributeAliases() { Component component = WebController.class.getAnnotation(Component.class); assertThat(component).isNotNull(); Map attributes = MergedAnnotation.from(component).asMap(); + Component synthesized = MergedAnnotation.of(Component.class, attributes).synthesize(); assertSynthesized(synthesized); assertThat(synthesized).isEqualTo(component); @@ -2060,47 +2014,41 @@ class MergedAnnotationsTests { private void assertToStringForWebMappingWithPathAndValue(RequestMapping webMapping) { assertThat(webMapping.toString()) - .startsWith("@org.springframework.core.annotation.MergedAnnotationsTests.RequestMapping(") - .contains( - // Strings - "value={\"/test\"}", "path={\"/test\"}", "name=\"bar\"", - // Characters - "ch='X'", "chars={'X'}", - // Enums - "method={GET, POST}", - // Classes - "clazz=org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod.class", - "classes={int[][].class, org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod[].class}", - // Bytes - "byteValue=(byte) 0xFF", "bytes={(byte) 0xFF}", - // Shorts - "shortValue=9876", "shorts={9876}", - // Longs - "longValue=42L", "longs={42L}", - // Floats - "floatValue=3.14f", "floats={3.14f}", - // Doubles - "doubleValue=99.999d", "doubles={99.999d}" - ) - .endsWith(")"); + .startsWith("@org.springframework.core.annotation.MergedAnnotationsTests.RequestMapping(") + .contains( + // Strings + "value={\"/test\"}", "path={\"/test\"}", "name=\"bar\"", + // Characters + "ch='X'", "chars={'X'}", + // Enums + "method={GET, POST}", + // Classes + "clazz=org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod.class", + "classes={int[][].class, org.springframework.core.annotation.MergedAnnotationsTests.RequestMethod[].class}", + // Bytes + "byteValue=(byte) 0xFF", "bytes={(byte) 0xFF}", + // Shorts + "shortValue=9876", "shorts={9876}", + // Longs + "longValue=42L", "longs={42L}", + // Floats + "floatValue=3.14f", "floats={3.14f}", + // Doubles + "doubleValue=99.999d", "doubles={99.999d}" + ) + .endsWith(")"); } @Test void equalsForSynthesizedAnnotations() throws Exception { - Method methodWithPath = WebController.class.getMethod( - "handleMappedWithPathAttribute"); - RequestMapping webMappingWithAliases = methodWithPath.getAnnotation( - RequestMapping.class); + Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute"); + RequestMapping webMappingWithAliases = methodWithPath.getAnnotation(RequestMapping.class); assertThat(webMappingWithAliases).isNotNull(); - Method methodWithPathAndValue = WebController.class.getMethod( - "handleMappedWithSamePathAndValueAttributes"); - RequestMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation( - RequestMapping.class); + Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes"); + RequestMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(RequestMapping.class); assertThat(webMappingWithPathAndValue).isNotNull(); - RequestMapping synthesizedWebMapping1 = MergedAnnotation.from( - webMappingWithAliases).synthesize(); - RequestMapping synthesizedWebMapping2 = MergedAnnotation.from( - webMappingWithPathAndValue).synthesize(); + RequestMapping synthesizedWebMapping1 = MergedAnnotation.from(webMappingWithAliases).synthesize(); + RequestMapping synthesizedWebMapping2 = MergedAnnotation.from(webMappingWithPathAndValue).synthesize(); // Equality amongst standard annotations assertThat(webMappingWithAliases).isEqualTo(webMappingWithAliases); assertThat(webMappingWithPathAndValue).isEqualTo(webMappingWithPathAndValue); @@ -2122,51 +2070,33 @@ class MergedAnnotationsTests { @Test void hashCodeForSynthesizedAnnotations() throws Exception { - Method methodWithPath = WebController.class.getMethod( - "handleMappedWithPathAttribute"); - RequestMapping webMappingWithAliases = methodWithPath.getAnnotation( - RequestMapping.class); + Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute"); + RequestMapping webMappingWithAliases = methodWithPath.getAnnotation(RequestMapping.class); assertThat(webMappingWithAliases).isNotNull(); - Method methodWithPathAndValue = WebController.class.getMethod( - "handleMappedWithSamePathAndValueAttributes"); - RequestMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation( - RequestMapping.class); + Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes"); + RequestMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(RequestMapping.class); assertThat(webMappingWithPathAndValue).isNotNull(); - RequestMapping synthesizedWebMapping1 = MergedAnnotation.from( - webMappingWithAliases).synthesize(); + RequestMapping synthesizedWebMapping1 = MergedAnnotation.from(webMappingWithAliases).synthesize(); assertThat(synthesizedWebMapping1).isNotNull(); - RequestMapping synthesizedWebMapping2 = MergedAnnotation.from( - webMappingWithPathAndValue).synthesize(); + RequestMapping synthesizedWebMapping2 = MergedAnnotation.from(webMappingWithPathAndValue).synthesize(); assertThat(synthesizedWebMapping2).isNotNull(); // Equality amongst standard annotations - assertThat(webMappingWithAliases.hashCode()).isEqualTo( - webMappingWithAliases.hashCode()); - assertThat(webMappingWithPathAndValue.hashCode()).isEqualTo( - webMappingWithPathAndValue.hashCode()); + assertThat(webMappingWithAliases.hashCode()).isEqualTo(webMappingWithAliases.hashCode()); + assertThat(webMappingWithPathAndValue.hashCode()).isEqualTo(webMappingWithPathAndValue.hashCode()); // Inequality amongst standard annotations - assertThat(webMappingWithAliases.hashCode()).isNotEqualTo( - webMappingWithPathAndValue.hashCode()); - assertThat(webMappingWithPathAndValue.hashCode()).isNotEqualTo( - webMappingWithAliases.hashCode()); + assertThat(webMappingWithAliases.hashCode()).isNotEqualTo(webMappingWithPathAndValue.hashCode()); + assertThat(webMappingWithPathAndValue.hashCode()).isNotEqualTo(webMappingWithAliases.hashCode()); // Equality amongst synthesized annotations - assertThat(synthesizedWebMapping1.hashCode()).isEqualTo( - synthesizedWebMapping1.hashCode()); - assertThat(synthesizedWebMapping2.hashCode()).isEqualTo( - synthesizedWebMapping2.hashCode()); - assertThat(synthesizedWebMapping1.hashCode()).isEqualTo( - synthesizedWebMapping2.hashCode()); - assertThat(synthesizedWebMapping2.hashCode()).isEqualTo( - synthesizedWebMapping1.hashCode()); + assertThat(synthesizedWebMapping1.hashCode()).isEqualTo(synthesizedWebMapping1.hashCode()); + assertThat(synthesizedWebMapping2.hashCode()).isEqualTo(synthesizedWebMapping2.hashCode()); + assertThat(synthesizedWebMapping1.hashCode()).isEqualTo(synthesizedWebMapping2.hashCode()); + assertThat(synthesizedWebMapping2.hashCode()).isEqualTo(synthesizedWebMapping1.hashCode()); // Equality between standard and synthesized annotations - assertThat(synthesizedWebMapping1.hashCode()).isEqualTo( - webMappingWithPathAndValue.hashCode()); - assertThat(webMappingWithPathAndValue.hashCode()).isEqualTo( - synthesizedWebMapping1.hashCode()); + assertThat(synthesizedWebMapping1.hashCode()).isEqualTo(webMappingWithPathAndValue.hashCode()); + assertThat(webMappingWithPathAndValue.hashCode()).isEqualTo(synthesizedWebMapping1.hashCode()); // Inequality between standard and synthesized annotations - assertThat(synthesizedWebMapping1.hashCode()).isNotEqualTo( - webMappingWithAliases.hashCode()); - assertThat(webMappingWithAliases.hashCode()).isNotEqualTo( - synthesizedWebMapping1.hashCode()); + assertThat(synthesizedWebMapping1.hashCode()).isNotEqualTo(webMappingWithAliases.hashCode()); + assertThat(webMappingWithAliases.hashCode()).isNotEqualTo(synthesizedWebMapping1.hashCode()); } /** @@ -2194,7 +2124,7 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithArrayOfAnnotations() throws Exception { + void synthesizeWithArrayOfAnnotations() { Hierarchy hierarchy = HierarchyClass.class.getAnnotation(Hierarchy.class); assertThat(hierarchy).isNotNull(); Hierarchy synthesizedHierarchy = MergedAnnotation.from(hierarchy).synthesize(); @@ -2216,12 +2146,10 @@ class MergedAnnotationsTests { } @Test - void synthesizeWithArrayOfChars() throws Exception { - CharsContainer charsContainer = GroupOfCharsClass.class.getAnnotation( - CharsContainer.class); + void synthesizeWithArrayOfChars() { + CharsContainer charsContainer = GroupOfCharsClass.class.getAnnotation(CharsContainer.class); assertThat(charsContainer).isNotNull(); - CharsContainer synthesizedCharsContainer = MergedAnnotation.from( - charsContainer).synthesize(); + CharsContainer synthesizedCharsContainer = MergedAnnotation.from(charsContainer).synthesize(); assertSynthesized(synthesizedCharsContainer); char[] chars = synthesizedCharsContainer.chars(); assertThat(chars).containsExactly('x', 'y', 'z'); @@ -2234,53 +2162,50 @@ class MergedAnnotationsTests { @Test void getValueWhenHasDefaultOverride() { - MergedAnnotation annotation = MergedAnnotations.from( - DefaultOverrideClass.class).get(DefaultOverrideRoot.class); + MergedAnnotation annotation = MergedAnnotations.from(DefaultOverrideClass.class) + .get(DefaultOverrideRoot.class); assertThat(annotation.getString("text")).isEqualTo("metameta"); } @Test // gh-22654 void getValueWhenHasDefaultOverrideWithImplicitAlias() { - MergedAnnotation annotation1 = MergedAnnotations.from( - DefaultOverrideImplicitAliasMetaClass1.class).get(DefaultOverrideRoot.class); + MergedAnnotation annotation1 = MergedAnnotations.from(DefaultOverrideImplicitAliasMetaClass1.class) + .get(DefaultOverrideRoot.class); assertThat(annotation1.getString("text")).isEqualTo("alias-meta-1"); - MergedAnnotation annotation2 = MergedAnnotations.from( - DefaultOverrideImplicitAliasMetaClass2.class).get(DefaultOverrideRoot.class); + MergedAnnotation annotation2 = MergedAnnotations.from(DefaultOverrideImplicitAliasMetaClass2.class) + .get(DefaultOverrideRoot.class); assertThat(annotation2.getString("text")).isEqualTo("alias-meta-2"); } @Test // gh-22654 void getValueWhenHasDefaultOverrideWithExplicitAlias() { - MergedAnnotation annotation = MergedAnnotations.from( - DefaultOverrideExplicitAliasRootMetaMetaClass.class).get( - DefaultOverrideExplicitAliasRoot.class); + MergedAnnotation annotation = MergedAnnotations.from(DefaultOverrideExplicitAliasRootMetaMetaClass.class) + .get(DefaultOverrideExplicitAliasRoot.class); assertThat(annotation.getString("text")).isEqualTo("meta"); assertThat(annotation.getString("value")).isEqualTo("meta"); } @Test // gh-22703 void getValueWhenThreeDeepMetaWithValue() { - MergedAnnotation annotation = MergedAnnotations.from( - ValueAttributeMetaMetaClass.class).get(ValueAttribute.class); - assertThat(annotation.getStringArray(MergedAnnotation.VALUE)).containsExactly( - "FromValueAttributeMeta"); + MergedAnnotation annotation = MergedAnnotations.from(ValueAttributeMetaMetaClass.class) + .get(ValueAttribute.class); + assertThat(annotation.getStringArray(MergedAnnotation.VALUE)).containsExactly("FromValueAttributeMeta"); } @Test void asAnnotationAttributesReturnsPopulatedAnnotationAttributes() { - MergedAnnotation annotation = MergedAnnotations.from( - SpringApplicationConfigurationClass.class).get( - SpringApplicationConfiguration.class); - AnnotationAttributes attributes = annotation.asAnnotationAttributes( - Adapt.CLASS_TO_STRING); - assertThat(attributes).containsEntry("classes", new String[] { Number.class.getName() }); + MergedAnnotation annotation = MergedAnnotations.from(SpringApplicationConfigurationClass.class) + .get(SpringApplicationConfiguration.class); + AnnotationAttributes attributes = annotation.asAnnotationAttributes(Adapt.CLASS_TO_STRING); + assertThat(attributes).containsEntry("classes", new String[] {Number.class.getName()}); assertThat(attributes.annotationType()).isEqualTo(SpringApplicationConfiguration.class); } + // @formatter:off + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE, ElementType.METHOD }) @Inherited @interface Transactional { @@ -2333,8 +2258,8 @@ class MergedAnnotationsTests { static class AliasedTransactionalComponentClass { } + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE, ElementType.METHOD }) @Inherited @interface AliasedTransactional { @@ -2701,7 +2626,7 @@ class MergedAnnotationsTests { void handleFromInterface(); } - static abstract class AbstractClassWithInheritedAnnotation + abstract static class AbstractClassWithInheritedAnnotation implements InterfaceWithInheritedAnnotation { @Transactional @@ -3005,7 +2930,7 @@ class MergedAnnotationsTests { } } - public static abstract class SimpleGeneric { + public abstract static class SimpleGeneric { @Order(1) public abstract void something(T arg); @@ -3095,7 +3020,7 @@ class MergedAnnotationsTests { } } - public static abstract class BaseClassWithGenericAnnotatedMethod { + public abstract static class BaseClassWithGenericAnnotatedMethod { @Order abstract void foo(T t);