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 02eee99554 Utility class modernization
02eee99554 is described below

commit 02eee995543c967ff6a899ab30ab21d7a6cde812
Author: James Bognar <[email protected]>
AuthorDate: Wed Nov 5 07:54:23 2025 -0500

    Utility class modernization
---
 .../juneau/common/reflect/AnnotationInfo.java      |  30 ++++--
 .../juneau/common/reflect/ConstructorInfo.java     |  24 -----
 .../juneau/common/reflect/ExecutableInfo.java      |  18 ----
 .../apache/juneau/common/reflect/FieldInfo.java    |  92 ++++++++--------
 .../juneau/common/reflect/ParameterInfo.java       |   7 +-
 .../juneau/common/reflect/ExecutableInfo_Test.java |  14 +--
 .../reflect/FieldInfo_AnnotationInfos_Test.java    | 117 +++++++++++++++++++++
 .../common/reflect/FieldInfo_FullName_Test.java    |  74 +++++++++++++
 8 files changed, 269 insertions(+), 107 deletions(-)

diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
index 8af5663e01..283ae8dd2e 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationInfo.java
@@ -524,6 +524,16 @@ public class AnnotationInfo<T extends Annotation> {
                return eq(value, a.annotationType().getName());
        }
 
+       /**
+        * Returns the method with the specified name on this annotation.
+        *
+        * @param methodName The method name.
+        * @return An optional containing the method, or empty if method not 
found.
+        */
+       public Optional<MethodInfo> getMethod(String methodName) {
+               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName())).findFirst();
+       }
+
        /**
         * Returns the value of the <c>value()</c> method on this annotation as 
a string.
         *
@@ -540,7 +550,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not a string.
         */
        public Optional<String> getString(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(String.class)).map(x -> 
s(x.invoke(a))).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(String.class)).map(x -> s(x.invoke(a)));
        }
 
        /**
@@ -550,7 +560,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not an integer.
         */
        public Optional<Integer> getInt(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(int.class)).map(x -> 
(Integer)x.invoke(a)).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(int.class)).map(x -> (Integer)x.invoke(a));
        }
 
        /**
@@ -560,7 +570,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not a boolean.
         */
        public Optional<Boolean> getBoolean(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(boolean.class)).map(x -> 
(Boolean)x.invoke(a)).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(boolean.class)).map(x -> (Boolean)x.invoke(a));
        }
 
        /**
@@ -570,7 +580,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not a long.
         */
        public Optional<Long> getLong(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(long.class)).map(x -> 
(Long)x.invoke(a)).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(long.class)).map(x -> (Long)x.invoke(a));
        }
 
        /**
@@ -580,7 +590,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not a double.
         */
        public Optional<Double> getDouble(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(double.class)).map(x -> 
(Double)x.invoke(a)).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(double.class)).map(x -> (Double)x.invoke(a));
        }
 
        /**
@@ -590,7 +600,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not a float.
         */
        public Optional<Float> getFloat(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(float.class)).map(x -> 
(Float)x.invoke(a)).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(float.class)).map(x -> (Float)x.invoke(a));
        }
 
        /**
@@ -601,7 +611,7 @@ public class AnnotationInfo<T extends Annotation> {
         */
        @SuppressWarnings("unchecked")
        public Optional<Class<?>> getClassValue(String methodName) {
-               return 
(Optional<Class<?>>)(Optional<?>)methods.get().stream().filter(x -> 
eq(methodName, x.getSimpleName()) && x.hasReturnType(Class.class)).map(x -> 
x.invoke(a)).findFirst();
+               return 
(Optional<Class<?>>)(Optional<?>)getMethod(methodName).filter(x -> 
x.hasReturnType(Class.class)).map(x -> x.invoke(a));
        }
 
        /**
@@ -611,7 +621,7 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the value of the specified method, or 
empty if not found or not a string array.
         */
        public Optional<String[]> getStringArray(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName()) && x.hasReturnType(String[].class)).map(x -> 
(String[])x.invoke(a)).findFirst();
+               return getMethod(methodName).filter(x -> 
x.hasReturnType(String[].class)).map(x -> (String[])x.invoke(a));
        }
 
        /**
@@ -622,7 +632,7 @@ public class AnnotationInfo<T extends Annotation> {
         */
        @SuppressWarnings("unchecked")
        public Optional<Class<?>[]> getClassArray(String methodName) {
-               return 
(Optional<Class<?>[]>)(Optional<?>)methods.get().stream().filter(x -> 
eq(methodName, x.getSimpleName()) && x.hasReturnType(Class[].class)).map(x -> 
x.invoke(a)).findFirst();
+               return 
(Optional<Class<?>[]>)(Optional<?>)getMethod(methodName).filter(x -> 
x.hasReturnType(Class[].class)).map(x -> x.invoke(a));
        }
 
        /**
@@ -637,6 +647,6 @@ public class AnnotationInfo<T extends Annotation> {
         * @return An optional containing the return type of the specified 
method, or empty if method not found.
         */
        public Optional<ClassInfo> getReturnType(String methodName) {
-               return methods.get().stream().filter(x -> eq(methodName, 
x.getSimpleName())).map(x -> x.getReturnType()).findFirst();
+               return getMethod(methodName).map(x -> x.getReturnType());
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
index bc9a7db918..27e48f8aab 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ConstructorInfo.java
@@ -71,19 +71,6 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
                this.c = c;
        }
 
-       /**
-        * Performs an action on this object if the specified predicate test 
passes.
-        *
-        * @param test A test to apply to determine if action should be 
executed.  Can be <jk>null</jk>.
-        * @param action An action to perform on this object.
-        * @return This object.
-        */
-       public ConstructorInfo accept(Predicate<ConstructorInfo> test, 
Consumer<ConstructorInfo> action) {
-               if (matches(test))
-                       action.accept(this);
-               return this;
-       }
-
        @Override /* Overridden from ExecutableInfo */
        public ConstructorInfo accessible() {
                super.accessible();
@@ -153,17 +140,6 @@ public class ConstructorInfo extends ExecutableInfo 
implements Comparable<Constr
                return t.orElse(null);
        }
 
-       /**
-        * Finds the annotation of the specified type defined on this 
constructor.
-        *
-        * @param <A> The annotation type to look for.
-        * @param type The annotation to look for.
-        * @return The annotation if found, or <jk>null</jk> if not.
-        */
-       public <A extends Annotation> A getAnnotation(Class<A> type) {
-               return getAnnotation(AnnotationProvider.DEFAULT, type);
-       }
-
        /**
         * Returns <jk>true</jk> if the specified annotation is present on this 
constructor.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
index 0892e03a0c..a3659dd61c 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
@@ -74,24 +74,6 @@ public abstract class ExecutableInfo extends AccessibleInfo {
                return this;
        }
 
-       /**
-        * Performs an action on all matching parameter annotations at the 
specified parameter index.
-        *
-        * @param <A> The annotation type.
-        * @param index The parameter index.
-        * @param type The annotation type.
-        * @param predicate The predicate.
-        * @param consumer The consumer.
-        * @return This object.
-        */
-       @Deprecated
-       public final <A extends Annotation> ExecutableInfo 
forEachParameterAnnotation(int index, Class<A> type, Predicate<A> predicate, 
Consumer<A> consumer) {
-               for (var ai : getParameter(index).getAnnotationInfos())
-                       if (type.isInstance(ai.inner()))
-                               consumeIf(predicate, consumer, 
type.cast(ai.inner()));
-               return this;
-       }
-
        /**
         * Returns how well this method matches the specified arg types.
         *
diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
index a67db4fdb1..2ccaccf821 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/FieldInfo.java
@@ -16,6 +16,8 @@
  */
 package org.apache.juneau.common.reflect;
 
+import static org.apache.juneau.common.reflect.ClassArrayFormat.*;
+import static org.apache.juneau.common.reflect.ClassNameFormat.*;
 import static org.apache.juneau.common.utils.CollectionUtils.*;
 import static org.apache.juneau.common.utils.PredicateUtils.*;
 import static org.apache.juneau.common.utils.Utils.*;
@@ -24,6 +26,7 @@ import java.lang.annotation.*;
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.function.*;
+import java.util.stream.*;
 
 import org.apache.juneau.common.collections.*;
 
@@ -65,6 +68,8 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
        Field f;  // Effectively final
        private final ClassInfo declaringClass;
        private final Supplier<ClassInfo> typeCache = memoize(() -> 
ClassInfo.of(f.getType()));
+       private final Supplier<List<AnnotationInfo<Annotation>>> annotations = 
memoize(this::_findAnnotations);
+       private final Supplier<String> fullName = memoize(this::findFullName);
 
        /**
         * Constructor.
@@ -78,17 +83,53 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
                this.f = f;
        }
 
+       private List<AnnotationInfo<Annotation>> _findAnnotations() {
+               return stream(f.getAnnotations()).map(a -> 
AnnotationInfo.of(this, a)).toList();
+       }
+
+       private String findFullName() {
+               var sb = new StringBuilder(128);
+               var dc = declaringClass;
+               var pi = dc.getPackage();
+               if (nn(pi))
+                       sb.append(pi.getName()).append('.');
+               dc.appendNameFormatted(sb, SHORT, true, '$', BRACKETS);
+               sb.append('.').append(getName());
+               return sb.toString();
+       }
+
        /**
-        * Performs an action on this object if the specified predicate test 
passes.
+        * Returns all annotations declared on this field.
         *
-        * @param test A test to apply to determine if action should be 
executed.  Can be <jk>null</jk>.
-        * @param action An action to perform on this object.
-        * @return This object.
+        * @return An unmodifiable list of all annotations declared on this 
field.
         */
-       public FieldInfo accept(Predicate<FieldInfo> test, Consumer<FieldInfo> 
action) {
-               if (matches(test))
-                       action.accept(this);
-               return this;
+       public List<AnnotationInfo<Annotation>> getAnnotationInfos() {
+               return annotations.get();
+       }
+
+       /**
+        * Returns all annotations of the specified type declared on this field.
+        *
+        * @param <A> The annotation type.
+        * @param type The annotation type.
+        * @return A stream of all matching annotations.
+        */
+       @SuppressWarnings("unchecked")
+       public <A extends Annotation> Stream<AnnotationInfo<A>> 
getAnnotationInfos(Class<A> type) {
+               return annotations.get().stream()
+                       .filter(x -> type.isInstance(x.inner()))
+                       .map(x -> (AnnotationInfo<A>)x);
+       }
+
+       /**
+        * Returns the first annotation of the specified type declared on this 
field.
+        *
+        * @param <A> The annotation type.
+        * @param type The annotation type.
+        * @return An optional containing the first matching annotation, or 
empty if not found.
+        */
+       public <A extends Annotation> Optional<AnnotationInfo<A>> 
getAnnotationInfo(Class<A> type) {
+               return getAnnotationInfos(type).findFirst();
        }
 
        /**
@@ -146,7 +187,7 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
         * @return The annotation, or <jk>null</jk> if not found.
         */
        public <A extends Annotation> A getAnnotation(Class<A> type) {
-               return getAnnotation(AnnotationProvider.DEFAULT, type);
+               return 
getAnnotationInfo(type).map(AnnotationInfo::inner).orElse(null);
        }
 
        /**
@@ -167,14 +208,7 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
         * @return The underlying executable name.
         */
        public String getFullName() {
-               var sb = new StringBuilder(128);
-               ClassInfo dc = declaringClass;
-               PackageInfo pi = dc.getPackage();
-               if (nn(pi))
-                       sb.append(pi.getName()).append('.');
-               dc.appendNameFormatted(sb, ClassNameFormat.SHORT, true, '$', 
ClassArrayFormat.BRACKETS);
-               sb.append(".").append(getName());
-               return sb.toString();
+               return fullName.get();
        }
 
        /**
@@ -406,30 +440,6 @@ public class FieldInfo extends AccessibleInfo implements 
Comparable<FieldInfo>,
                return f.isEnumConstant();
        }
 
-       /**
-        * Returns a {@link Type} object that represents the declared type for 
the field represented by this object.
-        *
-        * <p>
-        * Same as calling {@link Field#getGenericType()}.
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bjava'>
-        *      <jc>// Get generic type information for field: 
List&lt;String&gt; values</jc>
-        *      FieldInfo <jv>fi</jv> = 
ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getField(<js>"values"</js>);
-        *      Type <jv>type</jv> = <jv>fi</jv>.getGenericType();
-        *      <jk>if</jk> (<jv>type</jv> <jk>instanceof</jk> 
ParameterizedType) {
-        *              ParameterizedType <jv>pType</jv> = 
(ParameterizedType)<jv>type</jv>;
-        *              <jc>// pType.getActualTypeArguments()[0] is 
String.class</jc>
-        *      }
-        * </p>
-        *
-        * @return A {@link Type} object representing the declared type.
-        * @see Field#getGenericType()
-        */
-       public Type getGenericType() {
-               return f.getGenericType();
-       }
-
        /**
         * Returns an {@link AnnotatedType} object that represents the use of a 
type to specify the declared type of the field.
         *
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 110489cbb6..e2c6b5e808 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
@@ -777,7 +777,12 @@ public class ParameterInfo extends ElementInfo implements 
Annotatable {
                        var mi = (MethodInfo)executable;
                        var ci = 
executable.getParameter(index).getParameterType().unwrap(Value.class, 
Optional.class);
                        ci.forEachAnnotation(ap, a, filter, action);
-                       mi.forEachMatchingParentFirst(x -> true, x -> 
x.forEachParameterAnnotation(index, a, filter, action));
+                       mi.forEachMatchingParentFirst(x -> true, x -> {
+                               
x.getParameter(index).getAnnotationInfos().stream()
+                                       .filter(ai -> a.isInstance(ai.inner()))
+                                       .map(ai -> a.cast(ai.inner()))
+                                       .forEach(ann -> consumeIf(filter, 
action, ann));
+                       });
                }
                return this;
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ExecutableInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ExecutableInfo_Test.java
index 88b46d5e6e..03cac9d33c 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ExecutableInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ExecutableInfo_Test.java
@@ -281,18 +281,6 @@ class ExecutableInfo_Test extends TestBase {
                assertTrue(c_m3.hasAnnotation(CA.class));
        }
 
-       @Test void getAnnotation() {
-               check(null, c_c2.getAnnotation(CA.class));
-               check("@CA()", c_c3.getAnnotation(CA.class));
-               check(null, c_m1.getAnnotation(CA.class));
-               check(null, c_m2.getAnnotation(CA.class));
-               check("@CA()", c_m3.getAnnotation(CA.class));
-       }
-
-       @Test void getAnnotation_nullArg() {
-               check(null, c_c3.getAnnotation(null));
-       }
-
        
//-----------------------------------------------------------------------------------------------------------------
        // Exceptions
        
//-----------------------------------------------------------------------------------------------------------------
@@ -378,7 +366,7 @@ class ExecutableInfo_Test extends TestBase {
                // TRANSIENT is a valid modifier flag but doesn't apply to 
executables
                // Should return false (executables can't be transient) but not 
throw exception
                assertFalse(e_deprecated.is(TRANSIENT));
-               
+
                // CLASS is not a modifier flag and doesn't apply to 
executables, should throw exception
                assertThrowsWithMessage(RuntimeException.class, "Invalid flag 
for element: CLASS", () -> e_deprecated.is(ElementFlag.CLASS));
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_AnnotationInfos_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_AnnotationInfos_Test.java
new file mode 100644
index 0000000000..9ca8755a2b
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_AnnotationInfos_Test.java
@@ -0,0 +1,117 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.common.reflect;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.lang.annotation.*;
+
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for {@link FieldInfo#getAnnotationInfos()} methods.
+ */
+public class FieldInfo_AnnotationInfos_Test {
+
+       @Retention(RetentionPolicy.RUNTIME)
+       @Target(ElementType.FIELD)
+       public @interface TestAnnotation1 {
+               String value() default "";
+       }
+
+       @Retention(RetentionPolicy.RUNTIME)
+       @Target(ElementType.FIELD)
+       public @interface TestAnnotation2 {
+               int value() default 0;
+       }
+
+       @Retention(RetentionPolicy.RUNTIME)
+       @Target(ElementType.FIELD)
+       public @interface TestAnnotation3 {
+               String value() default "";
+       }
+
+       public static class TestClass {
+               @TestAnnotation1("test1")
+               @TestAnnotation2(42)
+               public String field1;
+
+               @TestAnnotation1("test2")
+               public String field2;
+
+               public String field3;
+       }
+
+       @Test
+       public void testGetAnnotationInfos() {
+               var ci = ClassInfo.of(TestClass.class);
+               var field1 = ci.getPublicField(x -> 
x.getName().equals("field1"));
+               var field2 = ci.getPublicField(x -> 
x.getName().equals("field2"));
+               var field3 = ci.getPublicField(x -> 
x.getName().equals("field3"));
+
+               // field1 has 2 annotations
+               var annotations1 = field1.getAnnotationInfos();
+               assertEquals(2, annotations1.size());
+               assertTrue(annotations1.stream().anyMatch(a -> 
a.hasSimpleName("TestAnnotation1")));
+               assertTrue(annotations1.stream().anyMatch(a -> 
a.hasSimpleName("TestAnnotation2")));
+
+               // field2 has 1 annotation
+               var annotations2 = field2.getAnnotationInfos();
+               assertEquals(1, annotations2.size());
+               assertTrue(annotations2.stream().anyMatch(a -> 
a.hasSimpleName("TestAnnotation1")));
+
+               // field3 has no annotations
+               var annotations3 = field3.getAnnotationInfos();
+               assertEquals(0, annotations3.size());
+       }
+
+       @Test
+       public void testGetAnnotationInfosTyped() {
+               var ci = ClassInfo.of(TestClass.class);
+               var field1 = ci.getPublicField(x -> 
x.getName().equals("field1"));
+               var field2 = ci.getPublicField(x -> 
x.getName().equals("field2"));
+
+               // Test filtering by type for field1
+               var ann1_type1 = 
field1.getAnnotationInfos(TestAnnotation1.class).toList();
+               assertEquals(1, ann1_type1.size());
+               assertEquals("test1", ann1_type1.get(0).getValue().get());
+
+               var ann1_type2 = 
field1.getAnnotationInfos(TestAnnotation2.class).toList();
+               assertEquals(1, ann1_type2.size());
+               assertEquals(42, ann1_type2.get(0).getInt("value").get());
+
+               // Test filtering by type that doesn't exist
+               var ann1_type3 = 
field1.getAnnotationInfos(TestAnnotation3.class).toList();
+               assertEquals(0, ann1_type3.size());
+
+               // Test filtering for field2
+               var ann2_type1 = 
field2.getAnnotationInfos(TestAnnotation1.class).toList();
+               assertEquals(1, ann2_type1.size());
+               assertEquals("test2", ann2_type1.get(0).getValue().get());
+
+               var ann2_type2 = 
field2.getAnnotationInfos(TestAnnotation2.class).toList();
+               assertEquals(0, ann2_type2.size());
+       }
+
+       @Test
+       public void testGetAnnotationInfosMemoization() {
+               var ci = ClassInfo.of(TestClass.class);
+               var field1 = ci.getPublicField(x -> 
x.getName().equals("field1"));
+
+               // Calling getAnnotationInfos() multiple times should return 
the same list instance
+               var annotations1 = field1.getAnnotationInfos();
+               var annotations2 = field1.getAnnotationInfos();
+               assertSame(annotations1, annotations2);
+       }
+}
+
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_FullName_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_FullName_Test.java
new file mode 100644
index 0000000000..94dc3d803a
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/FieldInfo_FullName_Test.java
@@ -0,0 +1,74 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.common.reflect;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for {@link FieldInfo#getFullName()} method.
+ */
+public class FieldInfo_FullName_Test {
+
+       public static class TestClass {
+               public String field1;
+               public int field2;
+       }
+
+       @Test
+       public void testGetFullName() {
+               var ci = ClassInfo.of(TestClass.class);
+               var field1 = ci.getPublicField(x -> 
x.getName().equals("field1"));
+               var field2 = ci.getPublicField(x -> 
x.getName().equals("field2"));
+
+               // Verify full names are correct
+               String fullName1 = field1.getFullName();
+               String fullName2 = field2.getFullName();
+
+               
assertTrue(fullName1.endsWith("FieldInfo_FullName_Test$TestClass.field1"));
+               
assertTrue(fullName2.endsWith("FieldInfo_FullName_Test$TestClass.field2"));
+               
+               // Verify package is included
+               
assertTrue(fullName1.startsWith("org.apache.juneau.common.reflect."));
+               
assertTrue(fullName2.startsWith("org.apache.juneau.common.reflect."));
+       }
+
+       @Test
+       public void testGetFullNameMemoization() {
+               var ci = ClassInfo.of(TestClass.class);
+               var field1 = ci.getPublicField(x -> 
x.getName().equals("field1"));
+
+               // Calling getFullName() multiple times should return the same 
String instance (memoized)
+               String name1 = field1.getFullName();
+               String name2 = field1.getFullName();
+               assertSame(name1, name2);
+       }
+
+       public static class InnerClass {
+               public String innerField;
+       }
+
+       @Test
+       public void testGetFullNameWithInnerClass() {
+               var ci = ClassInfo.of(InnerClass.class);
+               var field = ci.getPublicField(x -> 
x.getName().equals("innerField"));
+
+               String fullName = field.getFullName();
+               
+               // Verify $ separator is used for inner class
+               
assertTrue(fullName.contains("FieldInfo_FullName_Test$InnerClass"));
+               assertTrue(fullName.endsWith(".innerField"));
+       }
+}
+

Reply via email to