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 489757c8ea Utility class modernization
489757c8ea is described below

commit 489757c8eaaede6aea446de64b29470a50c482d5
Author: James Bognar <[email protected]>
AuthorDate: Wed Nov 5 16:26:37 2025 -0500

    Utility class modernization
---
 .../apache/juneau/common/reflect/ClassInfo.java    | 118 +++++++++++++++++----
 .../src/main/java/org/apache/juneau/ClassMeta.java |   4 +-
 .../src/main/java/org/apache/juneau/Context.java   |  42 --------
 .../java/org/apache/juneau/rest/RestContext.java   |  34 +++---
 .../juneau/common/reflect/ClassInfo_Test.java      |   2 +-
 5 files changed, 116 insertions(+), 84 deletions(-)

diff --git 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
index 6a91fc1c1b..2a94f54fa6 100644
--- 
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
+++ 
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ClassInfo.java
@@ -235,19 +235,19 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         */
        private List<ClassInfo> findParentsAndInterfaces() {
                var set = new LinkedHashSet<ClassInfo>();
-               
+
                // Process all parent classes (includes this class)
                var parents = getParents();
                for (int i = 0; i < parents.size(); i++) {
                        var parent = parents.get(i);
                        set.add(parent);
-                       
+
                        // Process interfaces declared on this parent (and 
their parent interfaces)
                        var declaredInterfaces = parent.getDeclaredInterfaces();
                        for (int j = 0; j < declaredInterfaces.size(); j++)
                                addInterfaceHierarchy(set, 
declaredInterfaces.get(j));
                }
-               
+
                return u(new ArrayList<>(set));
        }
 
@@ -260,13 +260,58 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
        private void addInterfaceHierarchy(LinkedHashSet<ClassInfo> set, 
ClassInfo iface) {
                if (!set.add(iface))
                        return;
-               
+
                // Process parent interfaces recursively
                var parentInterfaces = iface.getParents();
                for (int i = 0; i < parentInterfaces.size(); i++)
                        addInterfaceHierarchy(set, parentInterfaces.get(i));
        }
 
+       /**
+        * Finds all annotations on this class and parent classes/interfaces in 
child-to-parent order.
+        *
+        * <p>
+        * This is similar to {@link 
org.apache.juneau.common.reflect.AnnotationProvider2#find(Class)} but without 
runtime annotations.
+        *
+        * <p>
+        * Order of traversal:
+        * <ol>
+        *      <li>Annotations declared on this class
+        *      <li>Annotations declared on parent classes (child-to-parent 
order)
+        *      <li>For each parent class, annotations on interfaces declared 
on that class (child-to-parent interface hierarchy)
+        *      <li>Annotations on the package of this class
+        * </ol>
+        *
+        * @return A list of all annotation infos in child-to-parent order.
+        */
+       private List<AnnotationInfo<Annotation>> findAnnotationInfos() {
+               var list = new ArrayList<AnnotationInfo<Annotation>>();
+
+               // On all parent classes and interfaces (properly traversed to 
avoid duplicates)
+               var parentsAndInterfaces = getParentsAndInterfaces();
+               for (int i = 0; i < parentsAndInterfaces.size(); i++) {
+                       var ci = parentsAndInterfaces.get(i);
+                       // Add declared annotations from this class/interface
+                       for (var a : ci.inner().getDeclaredAnnotations())
+                               for (var a2 : splitRepeated(a))
+                                       list.add(AnnotationInfo.of(ci, a2));
+               }
+
+               // On the package of this class
+               var pkg = getPackage();
+               if (nn(pkg)) {
+                       var pi = PackageInfo.of(pkg.inner());
+                       for (var a : pkg.inner().getAnnotations())
+                               for (var a2 : splitRepeated(a))
+                                       list.add(AnnotationInfo.of(pi, a2));
+               }
+
+               return u(list);
+       }
+
+       // All annotations on this class and parent classes/interfaces in 
child-to-parent order.
+       private final Supplier<List<AnnotationInfo<Annotation>>> 
annotationInfos = memoize(this::findAnnotationInfos);
+
        // All record components if this is a record class (Java 14+).
        private final Supplier<List<RecordComponent>> recordComponents = 
memoize(() -> opt(c).filter(Class::isRecord).map(x -> 
u(l(x.getRecordComponents()))).orElse(liste()));
 
@@ -356,22 +401,6 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
                return (o instanceof ClassInfo o2) && eq(this, o2, (x, y) -> 
eq(x.t, y.t));
        }
 
-       /**
-        * Performs an action on all matching fields on this class and all 
parent classes.
-        *
-        * <p>
-        *      Results are ordered parent-to-child, and then alphabetical per 
class.
-        *
-        * @param filter A predicate to apply to the entries to determine if 
action should be performed.  Can be <jk>null</jk>.
-        * @param action An action to perform on the entry.
-        * @return This object.
-        */
-       public ClassInfo forEachAllField(Predicate<FieldInfo> filter, 
Consumer<FieldInfo> action) {
-               getAllFields().stream()
-                       .filter(fi -> test(filter, fi))
-                       .forEach(action);
-               return this;
-       }
 
        /**
         * Performs an action on all matching declared methods on this class 
and all parent classes.
@@ -978,6 +1007,53 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         */
        public List<ClassInfo> getParentsAndInterfaces() { return 
parentsAndInterfaces.get(); }
 
+       /**
+        * Returns all annotations on this class and parent classes/interfaces 
in child-to-parent order.
+        *
+        * <p>
+        * This returns all declared annotations from:
+        * <ol>
+        *      <li>This class
+        *      <li>Parent classes in child-to-parent order
+        *      <li>For each class, interfaces declared on that class and their 
parent interfaces
+        *      <li>The package of this class
+        * </ol>
+        *
+        * <p>
+        * This does NOT include runtime annotations. For runtime annotation 
support, use
+        * {@link org.apache.juneau.common.reflect.AnnotationProvider2}.
+        *
+        * @return An unmodifiable list of all annotation infos.
+        */
+       public List<AnnotationInfo<Annotation>> getAnnotationInfos() { return 
annotationInfos.get(); }
+
+       /**
+        * Returns all annotations of the specified type on this class and 
parent classes/interfaces in child-to-parent order.
+        *
+        * <p>
+        * This returns all declared annotations from:
+        * <ol>
+        *      <li>This class
+        *      <li>Parent classes in child-to-parent order
+        *      <li>For each class, interfaces declared on that class and their 
parent interfaces
+        *      <li>The package of this class
+        * </ol>
+        *
+        * <p>
+        * This does NOT include runtime annotations. For runtime annotation 
support, use
+        * {@link org.apache.juneau.common.reflect.AnnotationProvider2}.
+        *
+        * @param <A> The annotation type.
+        * @param type The annotation type to filter by.
+        * @return A stream of annotation infos of the specified type.
+        */
+       public <A extends Annotation> Stream<AnnotationInfo<A>> 
getAnnotationInfos(Class<A> type) {
+               assertArgNotNull("type", type);
+               return getAnnotationInfos().stream()
+                       .filter(a -> a.isType(type))
+                       .map(a -> (AnnotationInfo<A>)a);
+       }
+
        /**
         * Returns the first matching method on this class.
         *
@@ -1803,7 +1879,7 @@ public class ClassInfo extends ElementInfo implements 
Annotatable {
         * @return The <jk>true</jk> if annotation if found.
         */
        public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
-               return hasAnnotation(null, type);
+               return getAnnotationInfos(type).findFirst().isPresent();
        }
 
        @Override
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index 4c4226d274..8b29d4ece4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -206,13 +206,13 @@ public class ClassMeta<T> implements Type {
                        .orElse(null);
                        // @formatter:on
 
-                       ci.forEachAllField(x -> x.hasAnnotation(bc, 
ParentProperty.class), x -> {
+                       ci.getAllFields().stream().filter(x -> 
x.hasAnnotation(bc, ParentProperty.class)).forEach(x -> {
                                if (x.isStatic())
                                        throw new ClassMetaRuntimeException(c, 
"@ParentProperty used on invalid field ''{0}''.  Must be static.", x);
                                parentPropertyMethod = new 
Setter.FieldSetter(x.accessible().inner());
                        });
 
-                       ci.forEachAllField(x -> x.hasAnnotation(bc, 
NameProperty.class), x -> {
+                       ci.getAllFields().stream().filter(x -> 
x.hasAnnotation(bc, NameProperty.class)).forEach(x -> {
                                if (x.isStatic())
                                        throw new ClassMetaRuntimeException(c, 
"@NameProperty used on invalid field ''{0}''.  Must be static.", x);
                                namePropertyMethod = new 
Setter.FieldSetter(x.accessible().inner());
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
index 2f4fc7f394..830ef992c5 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
@@ -18,7 +18,6 @@ package org.apache.juneau;
 
 import static org.apache.juneau.collections.JsonMap.*;
 import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.apache.juneau.common.utils.PredicateUtils.*;
 import static org.apache.juneau.common.utils.ThrowableUtils.*;
 import static org.apache.juneau.common.utils.Utils.*;
 
@@ -758,8 +757,6 @@ public abstract class Context implements AnnotationProvider 
{
        final List<Annotation> annotations;
        final boolean debug;
 
-       private final ReflectionMap<Annotation> annotationMap;
-       private final Cache2<Class<?>,Class<? extends Annotation>,Annotation[]> 
declaredClassAnnotationCache;
        private final AnnotationProvider2 annotationProvider;
 
        /**
@@ -767,42 +764,10 @@ public abstract class Context implements 
AnnotationProvider {
         *
         * @param builder The builder for this class.
         */
-       @SuppressWarnings({ "unchecked", "rawtypes" })
        protected Context(Builder builder) {
                init(builder);
                debug = builder.debug;
                annotations = 
opt(builder.annotations).orElseGet(Collections::emptyList);
-
-               ReflectionMap.Builder<Annotation> rmb = 
ReflectionMap.create(Annotation.class);
-
-               annotations.forEach(a -> {
-                       try {
-                               var ci = ClassInfo.of(a.getClass());
-
-                               MethodInfo mi = ci.getPublicMethod(x -> 
x.hasName("onClass"));
-                               if (nn(mi)) {
-                                       if (! 
mi.getReturnType().is(Class[].class))
-                                               throw new 
ConfigException("Invalid annotation @{0} used in BEAN_annotations property.  
Annotation must define an onClass() method that returns a Class array.", 
scn(a));
-                                       for (var c : 
(Class<?>[])mi.accessible().invoke(a))
-                                               rmb.append(c.getName(), a);
-                               }
-
-                               mi = ci.getPublicMethod(x -> x.hasName("on"));
-                               if (nn(mi)) {
-                                       if (! 
mi.getReturnType().is(String[].class))
-                                               throw new 
ConfigException("Invalid annotation @{0} used in BEAN_annotations property.  
Annotation must define an on() method that returns a String array.", scn(a));
-                                       for (var s : 
(String[])mi.accessible().invoke(a))
-                                               rmb.append(s, a);
-                               }
-
-                       } catch (Exception e) {
-                               throw new ConfigException(e, "Invalid 
annotation @{0} used in BEAN_annotations property.", cn(a));
-                       }
-               });
-               this.annotationMap = rmb.build();
-               var disabled = 
Boolean.getBoolean("juneau.disableAnnotationCaching");
-               declaredClassAnnotationCache = (Cache2)Cache2.of(Class.class, 
Class.class, Annotation[].class).disableCaching(disabled).supplier((k1, k2) -> 
annotationMap.appendAll(k1, k2, k1.getDeclaredAnnotationsByType(k2))).build();
-
                annotationProvider = 
AnnotationProvider2.create().addRuntimeAnnotations(annotations).build();
        }
 
@@ -812,10 +777,8 @@ public abstract class Context implements 
AnnotationProvider {
         * @param copyFrom The context to copy from.
         */
        protected Context(Context copyFrom) {
-               annotationMap = copyFrom.annotationMap;
                annotations = copyFrom.annotations;
                debug = copyFrom.debug;
-               declaredClassAnnotationCache = 
copyFrom.declaredClassAnnotationCache;
                annotationProvider = copyFrom.annotationProvider;
        }
 
@@ -995,11 +958,6 @@ public abstract class Context implements 
AnnotationProvider {
                return Utils2.toPropertyMap(this).asReadableString();
        }
 
-       @SuppressWarnings("unchecked")
-       private <A extends Annotation> A[] declaredAnnotations(Class<A> type, 
Class<?> onClass) {
-               return (A[])declaredClassAnnotationCache.get(onClass, type);
-       }
-
        /**
         * Perform optional initialization on builder before it is used.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 866c54ab99..8151383fe2 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -1758,16 +1758,15 @@ public class RestContext extends Context {
 
                        // Get @RestInject fields initialized with values.
                        // @formatter:off
-                       rci.forEachAllField(
-                               x -> x.hasAnnotation(RestInject.class),
-                               x -> x.getOptional(resource.get()).ifPresent(
+                       rci.getAllFields().stream()
+                               .filter(x -> x.hasAnnotation(RestInject.class))
+                               .forEach(x -> 
x.getOptional(resource.get()).ifPresent(
                                        y -> beanStore.add(
                                                x.getType().inner(),
                                                y,
                                                
RestInjectAnnotation.name(x.getAnnotation(RestInject.class))
-                                       )
                                )
-                       );
+                       ));
                        // @formatter:on
 
                        rci.forEachMethod(x -> 
x.hasAnnotation(RestInject.class), x -> {
@@ -1794,19 +1793,18 @@ public class RestContext extends Context {
 
                        runInitHooks(bs, resource());
 
-                       // Set @RestInject fields not initialized with values.
-                       // @formatter:off
-                       rci.forEachAllField(
-                               x -> x.hasAnnotation(RestInject.class),
-                               x -> x.setIfNull(
-                                       resource.get(),
-                                       beanStore.getBean(
-                                               x.getType().inner(),
-                                               
RestInjectAnnotation.name(x.getAnnotation(RestInject.class))
-                                       ).orElse(null)
-                               )
-                       );
-                       // @formatter:on
+               // Set @RestInject fields not initialized with values.
+               // @formatter:off
+               rci.getAllFields().stream()
+                       .filter(x -> x.hasAnnotation(RestInject.class))
+                       .forEach(x -> x.setIfNull(
+                               resource.get(),
+                               beanStore.getBean(
+                                       x.getType().inner(),
+                                       
RestInjectAnnotation.name(x.getAnnotation(RestInject.class))
+                               ).orElse(null)
+                       ));
+               // @formatter:on
 
                        return this;
                }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
index 02a32663e9..9f655faafd 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/common/reflect/ClassInfo_Test.java
@@ -568,7 +568,7 @@ public class ClassInfo_Test extends TestBase {
        @Test void hasAnnotation() {
                assertTrue(g3.hasAnnotation(A.class));
                assertFalse(g3.hasAnnotation(B.class));
-               assertFalse(g3.hasAnnotation(null));
+               assertThrows(IllegalArgumentException.class, 
()->g3.hasAnnotation(null));
        }
 
        @Test void getAnnotations() {

Reply via email to