From 70247c4a949b7e20cadd260b5fd46d880c183acb 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 --- .../core/SerializableTypeWrapper.java | 9 +- .../core/annotation/AnnotationUtils.java | 39 +- .../core/annotation/AnnotationsScanner.java | 4 +- .../core/annotation/AttributeMethods.java | 23 +- .../AnnotationIntrospectionFailureTests.java | 77 +-- .../annotation/AttributeMethodsTests.java | 6 +- .../annotation/MergedAnnotationsTests.java | 628 ++++++++---------- 7 files changed, 368 insertions(+), 418 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 d2dbd05bad8b..76bf2678d071 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. @@ -217,7 +217,12 @@ else if (Type[].class == method.getReturnType() && ObjectUtils.isEmpty(args)) { 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 5744e9a900de..89741e14151b 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 static boolean isInJavaLangAnnotationPackage(@Nullable String annotationT * 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 static Object getValue(@Nullable Annotation annotation, @Nullable String return null; } catch (Throwable ex) { - rethrowAnnotationConfigurationException(ex); - handleIntrospectionFailure(annotation.getClass(), ex); + handleValueRetrievalFailure(annotation, ex); return null; } } @@ -1073,14 +1072,18 @@ public static Object getValue(@Nullable Annotation annotation, @Nullable String * @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 @@ static void rethrowAnnotationConfigurationException(Throwable ex) { * @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 c3f44cc897f4..6a913b250df9 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. @@ -451,7 +451,7 @@ static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defe 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 4756a0da1017..c24f51b6aba9 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. @@ -87,18 +87,26 @@ private AttributeMethods(@Nullable Class annotationType, M /** * 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 @@ boolean isValid(Annotation annotation) { /** * 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 @@ void validate(Annotation annotation) { 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); 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 0fa93c99e938..9750cd5ac2e8 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 @@ static class FilteringClassLoader extends OverridingClassLoader { @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 8a4acb7b7978..a2b885601f72 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 @@ void isValidWhenHasTypeNotPresentExceptionReturnsFalse() { 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 @@ void isValidWhenDoesNotHaveTypeNotPresentExceptionReturnsTrue() { 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 d4ad78965aff..96c91e13da47 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 @@ -88,47 +88,47 @@ class FluentSearchApiTests { @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 @@ void searchFromClassWithCustomRepeatableContainers() { 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 @@ void searchFromNonAnnotatedStaticNestedClassWithAnnotatedEnclosingClassWithEnclo .map(MergedAnnotation::getType); assertThat(classes).containsExactly(Component.class, Indexed.class); } - } + @Nested class ConventionBasedAnnotationAttributeOverrideTests { @@ -215,7 +215,7 @@ class ConventionBasedAnnotationAttributeOverrideTests { 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 @@ void getWithInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnn // 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 @@ void getWithInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnn // 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 @@ void getWithTypeHierarchyWithSingleElementOverridingAnArrayViaConvention() { 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 @@ void getWithTypeHierarchyWithLocalAliasesThatConflictWithAttributesInMetaAnnotat 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 @@ void streamWhenFromClassWithMetaDepth2() { @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 @@ void isPresentWhenFromAnnotationClassWithMetaDepth1() { @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 @@ void isPresentWhenFromAnnotationClassWithMetaDepth2() { @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 @@ void getRootWhenDirect() { @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()); + 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,18 +419,16 @@ void collectMultiValueMapFromClassWithLocalComposedAnnotationAndInheritedAnnotat @Test void collectMultiValueMapFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() { MultiValueMap map = MergedAnnotations.from( - SubSubClassWithInheritedAnnotation.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); + 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()); + SubSubClassWithInheritedComposedAnnotation.class, SearchStrategy.INHERITED_ANNOTATIONS) + .stream(Transactional.class).collect(MergedAnnotationCollectors.toMultiValueMap()); assertThat(map).contains(entry("qualifier", List.of("composed1"))); } @@ -455,9 +441,9 @@ void collectMultiValueMapFavorsInheritedComposedAnnotationsOverMoreLocallyDeclar */ @Test void collectMultiValueMapFromClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() { - MultiValueMap map = MergedAnnotations.from(DerivedTxConfig.class, - SearchStrategy.INHERITED_ANNOTATIONS).stream(Transactional.class).collect( - MergedAnnotationCollectors.toMultiValueMap()); + 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 @@ void collectMultiValueMapFromClassWithLocalAnnotationThatShadowsAnnotationFromSu @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 @@ void getWithInheritedAnnotationsFromMetaCycleAnnotatedClassWithMissingTargetMeta @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 @@ void withInheritedAnnotationsFromAliasedValueComposedAnnotation() { @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 @@ void getWithInheritedAnnotationsFromTransitiveImplicitAliasesWithSkippedLevelWit } 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 @@ private void testGetWithInherited(Class element, String... expected) { @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 @@ void getWithTypeHierarchyFromSubNonInheritedAnnotationInterface() { @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 @@ void getWithTypeHierarchyInheritedFromInterfaceMethod() throws Exception { 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 @@ void getWithTypeHierarchyFromBridgeMethod() { @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 @@ void getWithTypeHierarchyFromClassWithAttributeAliasesInTargetAnnotation() { @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 @@ void getWithTypeHierarchyFromClassWithComposedMetaTransactionalAnnotation() { @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 @@ private MergedAnnotation testGetWithTypeHierarchy(Class element, String... @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() { - assertThat(MergedAnnotations.from(ResourceHolder.class).get( - Resource.class).getString("name")).isEqualTo("x"); + 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 @@ -859,8 +818,8 @@ void getFromMethodWithMethodAnnotationOnLeaf() throws Exception { 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 @@ -868,8 +827,8 @@ void getFromMethodWithAnnotationOnMethodInInterface() throws Exception { 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 @@ -877,8 +836,8 @@ void getFromMethodWithMetaAnnotationOnLeaf() throws Exception { 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 @@ -1634,33 +1593,37 @@ 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() { - 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'"); + 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() { 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 @@ -1668,37 +1631,40 @@ 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() { 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() { 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 @@ -1706,13 +1672,14 @@ 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 @@ -1720,13 +1687,14 @@ 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 @@ -1761,8 +1729,7 @@ void synthesizeWithImplicitAliasesWithImpliedAliasNamesOmitted() { "xmlFile"); } - private void testSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted( - Class clazz, String expected) { + private void testSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(Class clazz, String expected) { ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration config = clazz.getAnnotation( ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration.class); assertThat(config).isNotNull(); @@ -1801,8 +1768,7 @@ 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"); @@ -1813,14 +1779,14 @@ 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 @@ -1828,14 +1794,14 @@ 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 @@ -1843,15 +1809,15 @@ 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 @@ -1860,6 +1826,7 @@ void synthesizeFromMapWithoutAttributeAliases() { 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"); @@ -1873,8 +1840,7 @@ void synthesizeFromMapWithNestedMap() { 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"); @@ -1889,12 +1855,10 @@ void synthesizeFromMapWithNestedMap() { @Test @SuppressWarnings("unchecked") void synthesizeFromMapWithNestedArrayOfMaps() { - ComponentScan componentScan = ComponentScanClass.class.getAnnotation( - ComponentScan.class); + 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(); @@ -1903,18 +1867,16 @@ void synthesizeFromMapWithNestedArrayOfMaps() { 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() { - MergedAnnotation annotation = MergedAnnotation.of( - AnnotationWithDefaults.class); + MergedAnnotation annotation = MergedAnnotation.of(AnnotationWithDefaults.class); AnnotationWithDefaults synthesized = annotation.synthesize(); assertThat(synthesized.text()).isEqualTo("enigma"); assertThat(synthesized.predicate()).isTrue(); @@ -1923,8 +1885,7 @@ void synthesizeFromDefaultsWithoutAttributeAliases() { @Test void synthesizeFromDefaultsWithAttributeAliases() { - MergedAnnotation annotation = MergedAnnotation.of( - TestConfiguration.class); + MergedAnnotation annotation = MergedAnnotation.of(TestConfiguration.class); TestConfiguration synthesized = annotation.synthesize(); assertThat(synthesized.value()).isEmpty(); assertThat(synthesized.location()).isEmpty(); @@ -1939,8 +1900,7 @@ void synthesizeWhenAttributeAliasesWithDifferentValues() { @Test 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"); @@ -1954,10 +1914,8 @@ void 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"); @@ -1974,11 +1932,11 @@ void synthesizeFromMapWithImplicitAttributeAliases() { } private void testSynthesisFromMapWithImplicitAliases(String attributeNameAndValue) { - Map map = Collections.singletonMap(attributeNameAndValue, - 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); @@ -2002,8 +1960,8 @@ void synthesizeFromMapWithNullAttributeValue() { 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 @@ -2021,6 +1979,7 @@ 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); @@ -2054,47 +2013,41 @@ void toStringForSynthesizedAnnotations() throws Exception { 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); @@ -2116,51 +2069,33 @@ void equalsForSynthesizedAnnotations() throws Exception { @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()); } /** @@ -2211,11 +2146,9 @@ void synthesizeWithArrayOfAnnotations() { @Test void synthesizeWithArrayOfChars() { - CharsContainer charsContainer = GroupOfCharsClass.class.getAnnotation( - CharsContainer.class); + 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'); @@ -2228,49 +2161,46 @@ void synthesizeWithArrayOfChars() { @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})