This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new e41b63d0d5 Utility class modernization
e41b63d0d5 is described below

commit e41b63d0d5a4fe9825330eef7d631010778af6c7
Author: James Bognar <[email protected]>
AuthorDate: Tue Nov 4 09:49:45 2025 -0500

    Utility class modernization
---
 .../juneau/common/reflect/ParameterInfo.java       |  58 ++++++++
 .../juneau/common/reflect/ParamInfoTest.java       | 162 +++++++++++++++++++++
 2 files changed, 220 insertions(+)

diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
index 674a59a6c8..c32cd0f496 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ParameterInfo.java
@@ -48,6 +48,12 @@ public class ParameterInfo extends ElementInfo implements 
Annotatable {
                        .supplier(k -> opt(findAnnotation(k)))
                        .build();
 
+       @SuppressWarnings({"rawtypes"})
+       private final Cache foundAnnotations =
+               Cache.of(Class.class, List.class)
+                       .supplier(this::findAnnotationInfosInternal)
+                       .build();
+
        private final Supplier<List<AnnotationInfo<Annotation>>> annotations = 
memoize(this::_findAnnotations);
        private final Supplier<List<ParameterInfo>> matchingParameters = 
memoize(this::_findMatchingParameters);
 
@@ -248,6 +254,36 @@ public class ParameterInfo extends ElementInfo implements 
Annotatable {
                return 
(A)((Optional<Annotation>)annotationCache.get(type)).orElse(null);
        }
 
+       /**
+        * Finds all annotation infos of the specified type defined on this 
method parameter.
+        *
+        * <p>
+        * Searches through matching parameters in the hierarchy and the 
parameter type.
+        *
+        * @param <A> The annotation type to look for.
+        * @param type The annotation type to look for.
+        * @return A list of annotation infos found, or an empty list if none 
found.
+        */
+       @SuppressWarnings("unchecked")
+       public <A extends Annotation> List<AnnotationInfo<A>> 
findAnnotationInfos(Class<A> type) {
+               return (List<AnnotationInfo<A>>)foundAnnotations.get(type);
+       }
+
+       /**
+        * Finds the first annotation info of the specified type defined on 
this method parameter.
+        *
+        * <p>
+        * Searches through matching parameters in the hierarchy and the 
parameter type.
+        *
+        * @param <A> The annotation type to look for.
+        * @param type The annotation type to look for.
+        * @return The annotation info if found, or <jk>null</jk> if not.
+        */
+       public <A extends Annotation> AnnotationInfo<A> 
findAnnotationInfo(Class<A> type) {
+               var list = findAnnotationInfos(type);
+               return list.isEmpty() ? null : list.get(0);
+       }
+
        /**
         * Returns the first matching annotation on this method parameter.
         *
@@ -791,6 +827,28 @@ public class ParameterInfo extends ElementInfo implements 
Annotatable {
                return v.orElseGet(() -> 
executable.getParameter(index).getParameterType().unwrap(Value.class, 
Optional.class).getAnnotation(type));
        }
 
+       @SuppressWarnings("unchecked")
+       private <A extends Annotation> List<AnnotationInfo<A>> 
findAnnotationInfosInternal(Class<A> type) {
+               var list = new ArrayList<AnnotationInfo<A>>();
+
+               // Search through matching parameters in hierarchy
+               for (var mp : getMatchingParameters()) {
+                       mp.getAnnotationInfos().stream()
+                               .filter(x -> x.isType(type))
+                               .map(x -> (AnnotationInfo<A>)x)
+                               .forEach(list::add);
+               }
+
+               // Search on parameter type
+               var paramType = 
executable.getParameter(index).getParameterType().unwrap(Value.class, 
Optional.class);
+               paramType.getDeclaredAnnotationInfos().stream()
+                       .filter(x -> x.isType(type))
+                       .map(x -> (AnnotationInfo<A>)x)
+                       .forEach(list::add);
+
+               return list;
+       }
+
        private <A extends Annotation> ParameterInfo 
forEachAnnotation(AnnotationProvider ap, Class<A> a, Predicate<A> filter, 
Consumer<A> action) {
                if (executable.isConstructor) {
                        var ci = 
executable.getParameter(index).getParameterType().unwrap(Value.class, 
Optional.class);
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ParamInfoTest.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ParamInfoTest.java
index a55c2dcd19..890f7d69ba 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ParamInfoTest.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ParamInfoTest.java
@@ -614,6 +614,168 @@ class ParamInfoTest extends TestBase {
                }
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // findAnnotationInfos() / findAnnotationInfo()
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Nested
+       class FindAnnotationInfosTests {
+
+               // Annotations for testing
+               @Documented
+               @Target({PARAMETER, TYPE})
+               @Retention(RUNTIME)
+               public @interface FA1 {
+                       int value();
+               }
+
+               @Documented
+               @Target({PARAMETER, TYPE})
+               @Retention(RUNTIME)
+               public @interface FA2 {
+                       String value();
+               }
+
+               // Test finding annotation on parameter itself
+               public static class F1 {
+                       public void test(@FA1(1) String x) {}  // NOSONAR
+               }
+
+               @Test void findOnParameter() throws Exception {
+                       var mi = MethodInfo.of(F1.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(1, infos.size());
+                       assertEquals(1, infos.get(0).inner().value());
+               }
+
+               @Test void findOnParameter_single() throws Exception {
+                       var mi = MethodInfo.of(F1.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var info = pi.findAnnotationInfo(FA1.class);
+                       assertNotNull(info);
+                       assertEquals(1, info.inner().value());
+               }
+
+               // Test finding annotation from matching method parameters
+               public interface F2 {
+                       void test(@FA1(2) String x);
+               }
+
+               public static class F3 implements F2 {
+                       @Override public void test(String x) {}  // NOSONAR
+               }
+
+               @Test void findFromMatchingMethod() throws Exception {
+                       var mi = MethodInfo.of(F3.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(1, infos.size());
+                       assertEquals(2, infos.get(0).inner().value());
+               }
+
+               // Test finding annotation from parameter type
+               @FA1(3)
+               public static class F4Type {}
+
+               public static class F5 {
+                       public void test(F4Type x) {}  // NOSONAR
+               }
+
+               @Test void findFromParameterType() throws Exception {
+                       var mi = MethodInfo.of(F5.class.getMethod("test", 
F4Type.class));
+                       var pi = mi.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(1, infos.size());
+                       assertEquals(3, infos.get(0).inner().value());
+               }
+
+               // Test finding multiple annotations from hierarchy
+               public interface F6 {
+                       void test(@FA1(4) String x);
+               }
+
+               public static class F7 {
+                       public void test(@FA1(5) String x) {}  // NOSONAR
+               }
+
+               public static class F8 extends F7 implements F6 {
+                       @Override public void test(@FA1(6) String x) {}  // 
NOSONAR
+               }
+
+               @Test void findMultipleFromHierarchy() throws Exception {
+                       var mi = MethodInfo.of(F8.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(3, infos.size());
+                       assertEquals(6, infos.get(0).inner().value()); // F8
+                       assertEquals(4, infos.get(1).inner().value()); // F6
+                       assertEquals(5, infos.get(2).inner().value()); // F7
+               }
+
+               @Test void findMultipleFromHierarchy_single() throws Exception {
+                       var mi = MethodInfo.of(F8.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var info = pi.findAnnotationInfo(FA1.class);
+                       assertNotNull(info);
+                       assertEquals(6, info.inner().value()); // Returns first 
(F8)
+               }
+
+               // Test finding annotation from constructor parameters
+               public static class F9 {
+                       public F9(@FA1(7) String x) {}  // NOSONAR
+               }
+
+               public static class F10 extends F9 {
+                       public F10(@FA1(8) String x) { super(x); }  // NOSONAR
+               }
+
+               @Test void findFromMatchingConstructor() throws Exception {
+                       var ci = 
ConstructorInfo.of(F10.class.getConstructor(String.class));
+                       var pi = ci.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(2, infos.size());
+                       assertEquals(8, infos.get(0).inner().value()); // F10
+                       assertEquals(7, infos.get(1).inner().value()); // F9
+               }
+
+               // Test not found
+               public static class F11 {
+                       public void test(String x) {}  // NOSONAR
+               }
+
+               @Test void notFound() throws Exception {
+                       var mi = MethodInfo.of(F11.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(0, infos.size());
+               }
+
+               @Test void notFound_single() throws Exception {
+                       var mi = MethodInfo.of(F11.class.getMethod("test", 
String.class));
+                       var pi = mi.getParameter(0);
+                       var info = pi.findAnnotationInfo(FA1.class);
+                       assertNull(info);
+               }
+
+               // Test parameter annotation takes precedence over type 
annotation
+               @FA1(9)
+               public static class F12Type {}
+
+               public static class F13 {
+                       public void test(@FA1(10) F12Type x) {}  // NOSONAR
+               }
+
+               @Test void parameterAnnotationBeforeTypeAnnotation() throws 
Exception {
+                       var mi = MethodInfo.of(F13.class.getMethod("test", 
F12Type.class));
+                       var pi = mi.getParameter(0);
+                       var infos = pi.findAnnotationInfos(FA1.class);
+                       assertEquals(2, infos.size());
+                       assertEquals(10, infos.get(0).inner().value()); // 
Parameter annotation first
+                       assertEquals(9, infos.get(1).inner().value());  // Type 
annotation second
+               }
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Helpers
        
//-----------------------------------------------------------------------------------------------------------------

Reply via email to