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 81b1eee387 Improvements to ClassInfo APIs
81b1eee387 is described below
commit 81b1eee387e98226c0cad616917997a309eaf081
Author: James Bognar <[email protected]>
AuthorDate: Thu Nov 13 08:51:03 2025 -0500
Improvements to ClassInfo APIs
---
.../juneau/common/reflect/AnnotationProvider.java | 286 +++++++++++++++++++++
.../juneau/common/reflect/AnnotationTraversal.java | 250 ++++++++++++++++++
.../apache/juneau/common/reflect/ClassInfo.java | 73 ++++++
.../juneau/common/reflect/ConstructorInfo.java | 62 +++++
.../apache/juneau/common/reflect/FieldInfo.java | 59 ++++-
.../apache/juneau/common/reflect/MethodInfo.java | 74 ++++++
.../juneau/common/reflect/ParameterInfo.java | 65 +++++
7 files changed, 868 insertions(+), 1 deletion(-)
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
index eb09b86a4a..5021f6f062 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider.java
@@ -20,7 +20,9 @@ import static org.apache.juneau.common.utils.AssertionUtils.*;
import static org.apache.juneau.common.utils.ClassUtils.*;
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.*;
+import static org.apache.juneau.common.reflect.AnnotationTraversal.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
@@ -912,4 +914,288 @@ public class AnnotationProvider {
for (int i = parents2.size() - 1; i >= 0; i--)
findDeclaredParentFirst(type,
parents2.get(i).inner()).map(x -> x.inner()).filter(x -> filter == null ||
filter.test(x)).forEach(x -> action.accept(x));
}
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Stream-based traversal methods
+
//-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Streams annotations from a class using configurable traversal
options.
+ *
+ * <p>
+ * This method provides a flexible, stream-based API for traversing
annotations without creating intermediate lists.
+ * It uses {@link AnnotationTraversal} enums to configure what elements
to search and in what order.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search class only</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ * search(MyAnnotation.<jk>class</jk>, <jv>ci</jv>, SELF);
+ *
+ * <jc>// Search class and parents</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ * search(MyAnnotation.<jk>class</jk>, <jv>ci</jv>, SELF,
PARENTS);
+ *
+ * <jc>// Search class, parents, and package (parent-first order
using rstream)</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s3</jv> =
+ * rstream(search(MyAnnotation.<jk>class</jk>,
<jv>ci</jv>, SELF, PARENTS, PACKAGE).toList());
+ * </p>
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param clazz The class to search.
+ * @param traversals The traversal options (what to search and order).
+ * @return A stream of {@link AnnotationInfo} objects. Never
<jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, ClassInfo clazz, AnnotationTraversal...
traversals) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("clazz", clazz);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == SELF) {
+ return findDeclared(type,
clazz.inner());
+ } else if (traversal == PARENTS) {
+ return
clazz.getParentsAndInterfaces().stream().flatMap(x -> findDeclared(type,
x.inner()));
+ } else if (traversal == PACKAGE) {
+ A packageAnn =
clazz.getPackageAnnotation(type);
+ return nn(packageAnn) ?
Stream.of(AnnotationInfo.of(clazz, packageAnn)) : Stream.empty();
+ }
+ throw illegalArg("Invalid traversal type for
class annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Streams annotations from a class using configurable traversal
options in parent-first order.
+ *
+ * <p>
+ * This is equivalent to calling {@link #findAnnotations(Class,
ClassInfo, AnnotationTraversal...)}
+ * and reversing the result.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param clazz The class to search.
+ * @param traversals The traversal options (what to search and order).
+ * @return A stream of {@link AnnotationInfo} objects in parent-first
order. Never <jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, ClassInfo clazz,
AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, clazz,
traversals).toList());
+ }
+
+ /**
+ * Streams annotations from a method using configurable traversal
options.
+ *
+ * <p>
+ * This method provides a flexible, stream-based API for traversing
method annotations without creating intermediate lists.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search method and matching parent methods</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ * findAnnotations(MyAnnotation.<jk>class</jk>,
<jv>mi</jv>, SELF, MATCHING_METHODS);
+ *
+ * <jc>// Search method, matching methods, and return type
(parent-first using findAnnotationsParentFirst)</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ * findAnnotationsParentFirst(MyAnnotation.<jk>class</jk>,
<jv>mi</jv>, SELF, MATCHING_METHODS, RETURN_TYPE);
+ * </p>
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param method The method to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects. Never
<jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, MethodInfo method, AnnotationTraversal...
traversals) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("method", method);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == SELF) {
+ return find(type, method.inner());
+ } else if (traversal == MATCHING_METHODS) {
+ return
method.getMatchingMethods().stream().skip(1).flatMap(x -> find(type,
x.inner()));
+ } else if (traversal == RETURN_TYPE) {
+ return findAnnotations(type,
method.getReturnType().unwrap(Value.class, Optional.class), PARENTS);
+ }
+ throw illegalArg("Invalid traversal type for
method annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Streams annotations from a method using configurable traversal
options in parent-first order.
+ *
+ * <p>
+ * This is equivalent to calling {@link #findAnnotations(Class,
MethodInfo, AnnotationTraversal...)}
+ * and reversing the result.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param method The method to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects in parent-first
order. Never <jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, MethodInfo method,
AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, method,
traversals).toList());
+ }
+
+ /**
+ * Streams annotations from a parameter using configurable traversal
options.
+ *
+ * <p>
+ * This method provides a flexible, stream-based API for traversing
parameter annotations without creating intermediate lists.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search parameter, matching parameters, and parameter
type</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ * findAnnotations(MyAnnotation.<jk>class</jk>,
<jv>pi</jv>, SELF, MATCHING_PARAMETERS, PARAMETER_TYPE);
+ *
+ * <jc>// Search in parent-first order using
findAnnotationsParentFirst</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ * findAnnotationsParentFirst(MyAnnotation.<jk>class</jk>,
<jv>pi</jv>, SELF, MATCHING_PARAMETERS, PARAMETER_TYPE);
+ * </p>
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param parameter The parameter to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects. Never
<jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, ParameterInfo parameter, AnnotationTraversal...
traversals) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("parameter", parameter);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == SELF) {
+ return
parameter.getAnnotationInfos(type);
+ } else if (traversal == MATCHING_PARAMETERS) {
+ return
parameter.getMatchingParameters().stream().skip(1).flatMap(x ->
x.getAnnotationInfos(type));
+ } else if (traversal == PARAMETER_TYPE) {
+ return findAnnotations(type,
parameter.getParameterType().unwrap(Value.class, Optional.class), PARENTS,
PACKAGE);
+ }
+ throw illegalArg("Invalid traversal type for
parameter annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Streams annotations from a parameter using configurable traversal
options in parent-first order.
+ *
+ * <p>
+ * This is equivalent to calling {@link #findAnnotations(Class,
ParameterInfo, AnnotationTraversal...)}
+ * and reversing the result.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param parameter The parameter to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects in parent-first
order. Never <jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, ParameterInfo parameter,
AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, parameter,
traversals).toList());
+ }
+
+ /**
+ * Streams annotations from a field using configurable traversal
options.
+ *
+ * <p>
+ * This method provides a flexible, stream-based API for traversing
field annotations without creating intermediate lists.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search field annotations</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ * findAnnotations(MyAnnotation.<jk>class</jk>,
<jv>fi</jv>, SELF);
+ * </p>
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param field The field to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects. Never
<jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, FieldInfo field, AnnotationTraversal...
traversals) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("field", field);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == SELF) {
+ return find(type, field.inner());
+ }
+ throw illegalArg("Invalid traversal type for
field annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Streams annotations from a field using configurable traversal
options in parent-first order.
+ *
+ * <p>
+ * This is equivalent to calling {@link #findAnnotations(Class,
FieldInfo, AnnotationTraversal...)}
+ * and reversing the result.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param field The field to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects in parent-first
order. Never <jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, FieldInfo field,
AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, field,
traversals).toList());
+ }
+
+ /**
+ * Streams annotations from a constructor using configurable traversal
options.
+ *
+ * <p>
+ * This method provides a flexible, stream-based API for traversing
constructor annotations without creating intermediate lists.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search constructor annotations</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ * findAnnotations(MyAnnotation.<jk>class</jk>,
<jv>ci</jv>, SELF);
+ * </p>
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param constructor The constructor to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects. Never
<jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, ConstructorInfo constructor,
AnnotationTraversal... traversals) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("constructor", constructor);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == SELF) {
+ return find(type, constructor.inner());
+ }
+ throw illegalArg("Invalid traversal type for
constructor annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Streams annotations from a constructor using configurable traversal
options in parent-first order.
+ *
+ * <p>
+ * This is equivalent to calling {@link #findAnnotations(Class,
ConstructorInfo, AnnotationTraversal...)}
+ * and reversing the result.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param constructor The constructor to search.
+ * @param traversals The traversal options.
+ * @return A stream of {@link AnnotationInfo} objects in parent-first
order. Never <jk>null</jk>.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, ConstructorInfo constructor,
AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, constructor,
traversals).toList());
+ }
}
\ No newline at end of file
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationTraversal.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationTraversal.java
new file mode 100644
index 0000000000..a9a9afb4ab
--- /dev/null
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationTraversal.java
@@ -0,0 +1,250 @@
+/*
+ * 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;
+
+/**
+ * Defines traversal options for annotation searches.
+ *
+ * <p>
+ * These enums configure what elements to traverse and in what order when
searching for annotations.
+ * They are used with {@link AnnotationProvider#search(Class, ClassInfo,
AnnotationTraversal...)}
+ * and related methods.
+ *
+ * <p>
+ * Each traversal type has an order of precedence that determines the search
order.
+ * When multiple traversal types are specified, they are automatically sorted
by their precedence
+ * to ensure consistent behavior regardless of the order they are specified.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// These produce the same result (automatically sorted by
precedence):</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ *
annotationProvider.streamClassAnnotations(MyAnnotation.<jk>class</jk>,
<jv>ci</jv>, PACKAGE, PARENTS, SELF);
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ *
annotationProvider.streamClassAnnotations(MyAnnotation.<jk>class</jk>,
<jv>ci</jv>, SELF, PARENTS, PACKAGE);
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * <li class='jc'>{@link AnnotationProvider}
+ * <li class='jm'>{@link AnnotationProvider#search(Class, ClassInfo,
AnnotationTraversal...)}
+ * <li class='jm'>{@link AnnotationProvider#search(Class, MethodInfo,
AnnotationTraversal...)}
+ * <li class='jm'>{@link AnnotationProvider#search(Class, ParameterInfo,
AnnotationTraversal...)}
+ * </ul>
+ */
+public enum AnnotationTraversal {
+
+ /**
+ * Include the element itself (class, method, field, constructor,
parameter).
+ *
+ * <p>
+ * This searches for annotations directly declared on the target
element.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * All element types (classes, methods, fields, constructors,
parameters).
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 10 (highest - searched first)
+ */
+ SELF(10),
+
+ /**
+ * Include parent classes and interfaces in the traversal.
+ *
+ * <p>
+ * For classes: Traverses the superclass hierarchy and all implemented
interfaces,
+ * interleaved in child-to-parent order (using {@link
ClassInfo#getParentsAndInterfaces()}).
+ * Default order is child-to-parent unless {@link #REVERSE} is
specified.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * Classes.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Given: class Child extends Parent implements
Interface</jc>
+ * <jc>// Traverses parents and interfaces interleaved: Parent →
Interface → GrandParent → ...</jc>
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 20
+ */
+ PARENTS(20),
+
+ /**
+ * Include matching methods in the traversal.
+ *
+ * <p>
+ * For methods: Searches annotations on methods with the same signature
in parent classes and interfaces.
+ * This finds annotations on overridden methods.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * Methods.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Given:</jc>
+ * <jk>class</jk> Parent {
+ * <ja>@MyAnnotation</ja>
+ * <jk>public void</jk> method() {}
+ * }
+ * <jk>class</jk> Child <jk>extends</jk> Parent {
+ * <ja>@Override</ja>
+ * <jk>public void</jk> method() {} <jc>// Will find
@MyAnnotation from Parent</jc>
+ * }
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 20
+ */
+ MATCHING_METHODS(20),
+
+ /**
+ * Include matching parameters in the traversal.
+ *
+ * <p>
+ * For parameters: Searches annotations on parameters in matching
parent methods or constructors.
+ * This finds annotations on parameters in overridden methods or parent
constructors.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * Parameters.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Given:</jc>
+ * <jk>class</jk> Parent {
+ * <jk>public void</jk> method(<ja>@MyAnnotation</ja>
String <jv>param</jv>) {}
+ * }
+ * <jk>class</jk> Child <jk>extends</jk> Parent {
+ * <ja>@Override</ja>
+ * <jk>public void</jk> method(String <jv>param</jv>) {}
<jc>// Will find @MyAnnotation from Parent</jc>
+ * }
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 20
+ */
+ MATCHING_PARAMETERS(20),
+
+ /**
+ * Include the return type in the traversal.
+ *
+ * <p>
+ * For methods: Searches annotations on the method's return type and
its hierarchy.
+ * Automatically includes {@link #PARENTS} of the return type.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * Methods.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Given: public MyClass myMethod() {...}</jc>
+ * <jc>// Searches: MyClass hierarchy and its interfaces</jc>
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 30
+ */
+ RETURN_TYPE(30),
+
+ /**
+ * Include the parameter type in the traversal.
+ *
+ * <p>
+ * For parameters: Searches annotations on the parameter's type and its
hierarchy.
+ * Automatically includes {@link #PARENTS} and {@link #PACKAGE} of the
parameter type.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * Parameters.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Given: void method(MyClass param) {...}</jc>
+ * <jc>// Searches: MyClass hierarchy, its interfaces, and
package</jc>
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 30
+ */
+ PARAMETER_TYPE(30),
+
+ /**
+ * Include package annotations in the traversal.
+ *
+ * <p>
+ * Searches for annotations on the package-info class.
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * Classes and parameters (via {@link #PARAMETER_TYPE}).
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Searches annotations in package-info.java</jc>
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 40 (lowest - searched last)
+ */
+ PACKAGE(40),
+
+ /**
+ * Reverse the order of the resulting stream.
+ *
+ * <p>
+ * When this flag is present, the final stream is wrapped in {@code
rstream()} to reverse the order.
+ * This allows parent-first ordering (parent annotations before child
annotations).
+ *
+ * <p>
+ * By default, traversals return results in child-to-parent order
(child annotations first).
+ * Using {@code REVERSE} changes this to parent-to-child order (parent
annotations first).
+ *
+ * <h5 class='section'>Applicable to:</h5>
+ * All stream-based traversal methods.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Default (child-first): Child → Parent → GrandParent</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ * streamClassAnnotations(MyAnnotation.<jk>class</jk>,
<jv>ci</jv>, PARENTS);
+ *
+ * <jc>// With REVERSE (parent-first): GrandParent → Parent →
Child</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ * streamClassAnnotations(MyAnnotation.<jk>class</jk>,
<jv>ci</jv>, PARENTS, REVERSE);
+ * </p>
+ *
+ * <h5 class='section'>Order:</h5>
+ * Precedence: 999 (modifier - does not affect traversal order)
+ */
+ REVERSE(999);
+
+ private final int order;
+
+ AnnotationTraversal(int order) {
+ this.order = order;
+ }
+
+ /**
+ * Returns the precedence order of this traversal type.
+ *
+ * <p>
+ * Lower values have higher precedence and are processed first.
+ *
+ * @return The order value.
+ */
+ public int getOrder() {
+ return order;
+ }
+}
+
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 393d4b1077..11767a3651 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
@@ -752,6 +752,79 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
.map(a -> (AnnotationInfo<A>)a);
}
+ /**
+ * Finds annotations on this class using the specified traversal
settings.
+ *
+ * <p>
+ * This method allows flexible annotation traversal across different
scopes using {@link AnnotationTraversal} enums.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search class only</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ *
<jv>ci</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF);
+ *
+ * <jc>// Search class and parents</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ *
<jv>ci</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF, PARENTS);
+ *
+ * <jc>// Search class, parents, and package</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s3</jv> =
+ *
<jv>ci</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF, PARENTS,
PACKAGE);
+ * </p>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider}.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(e.g., SELF, PARENTS, PACKAGE).
+ * @return A stream of annotation infos matching the specified type and
traversal settings.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, AnnotationTraversal... traversals) {
+ assertArgNotNull("type", type);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == AnnotationTraversal.SELF) {
+ return
Arrays.stream(inner.getDeclaredAnnotations())
+ .flatMap(a ->
Arrays.stream(splitRepeated(a)))
+ .map(a ->
AnnotationInfo.of(this, a))
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ } else if (traversal ==
AnnotationTraversal.PARENTS) {
+ return
getParentsAndInterfaces().stream()
+ .flatMap(x ->
Arrays.stream(x.inner().getDeclaredAnnotations())
+ .flatMap(a ->
Arrays.stream(splitRepeated(a)))
+ .map(a ->
AnnotationInfo.of(x, a))
+ .filter(a ->
a.isType(type))
+ .map(a ->
(AnnotationInfo<A>)a));
+ } else if (traversal ==
AnnotationTraversal.PACKAGE) {
+ A packageAnn =
getPackageAnnotation(type);
+ return nn(packageAnn) ?
Stream.of(AnnotationInfo.of(this, packageAnn)) : Stream.empty();
+ }
+ throw illegalArg("Invalid traversal type for
class annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Finds annotations on this class using the specified traversal
settings in parent-first order.
+ *
+ * <p>
+ * This method is identical to {@link #findAnnotations(Class,
AnnotationTraversal...)} but returns
+ * results in parent-to-child order.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(e.g., SELF, PARENTS, PACKAGE).
+ * @return A stream of annotation infos matching the specified type and
traversal settings in parent-first order.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, traversals).toList());
+ }
+
/**
* Returns the first matching method on this class.
*
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 6b3b874af9..c14b510661 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
@@ -17,8 +17,15 @@
package org.apache.juneau.common.reflect;
import static org.apache.juneau.common.utils.AssertionUtils.*;
+import static org.apache.juneau.common.utils.ClassUtils.*;
+import static org.apache.juneau.common.utils.CollectionUtils.*;
+import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
+import java.lang.annotation.*;
import java.lang.reflect.*;
+import java.util.*;
+import java.util.stream.*;
import org.apache.juneau.common.utils.*;
@@ -88,6 +95,61 @@ public class ConstructorInfo extends ExecutableInfo
implements Comparable<Constr
return this;
}
+ /**
+ * Finds annotations on this constructor using the specified traversal
settings.
+ *
+ * <p>
+ * This method allows flexible annotation traversal across different
scopes using {@link AnnotationTraversal} enums.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search constructor only</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ *
<jv>ci</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF);
+ * </p>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider}.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(currently only SELF is supported for constructors).
+ * @return A stream of annotation infos matching the specified type and
traversal settings.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, AnnotationTraversal... traversals) {
+ assertArgNotNull("type", type);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == AnnotationTraversal.SELF) {
+ return
Arrays.stream(inner.getDeclaredAnnotations())
+ .flatMap(a ->
Arrays.stream(splitRepeated(a)))
+ .map(a ->
AnnotationInfo.of(this, a))
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+ throw illegalArg("Invalid traversal type for
constructor annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Finds annotations on this constructor using the specified traversal
settings in parent-first order.
+ *
+ * <p>
+ * This method is identical to {@link #findAnnotations(Class,
AnnotationTraversal...)} but returns
+ * results in parent-to-child order.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(currently only SELF is supported for constructors).
+ * @return A stream of annotation infos matching the specified type and
traversal settings in parent-first order.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, traversals).toList());
+ }
+
@Override
public int compareTo(ConstructorInfo o) {
int i = getSimpleName().compareTo(o.getSimpleName());
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 827c0f486b..2d6dcf1961 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
@@ -19,8 +19,10 @@ 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.AssertionUtils.*;
+import static org.apache.juneau.common.utils.ClassUtils.*;
+import static org.apache.juneau.common.utils.CollectionUtils.*;
+import static org.apache.juneau.common.utils.ThrowableUtils.*;
import static org.apache.juneau.common.utils.Utils.*;
-import static java.util.Arrays.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
@@ -117,6 +119,61 @@ public class FieldInfo extends AccessibleInfo implements
Comparable<FieldInfo>,
.map(x -> (AnnotationInfo<A>)x);
}
+ /**
+ * Finds annotations on this field using the specified traversal
settings.
+ *
+ * <p>
+ * This method allows flexible annotation traversal across different
scopes using {@link AnnotationTraversal} enums.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search field only</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ *
<jv>fi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF);
+ * </p>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider}.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(currently only SELF is supported for fields).
+ * @return A stream of annotation infos matching the specified type and
traversal settings.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, AnnotationTraversal... traversals) {
+ assertArgNotNull("type", type);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == AnnotationTraversal.SELF) {
+ return
Arrays.stream(inner.getDeclaredAnnotations())
+ .flatMap(a ->
Arrays.stream(splitRepeated(a)))
+ .map(a ->
AnnotationInfo.of(this, a))
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+ throw illegalArg("Invalid traversal type for
field annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Finds annotations on this field using the specified traversal
settings in parent-first order.
+ *
+ * <p>
+ * This method is identical to {@link #findAnnotations(Class,
AnnotationTraversal...)} but returns
+ * results in parent-to-child order.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(currently only SELF is supported for fields).
+ * @return A stream of annotation infos matching the specified type and
traversal settings in parent-first order.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, traversals).toList());
+ }
+
/**
* Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and
quietly ignores security exceptions.
*
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
index 5addb984f3..fa319ca42d 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/MethodInfo.java
@@ -17,8 +17,10 @@
package org.apache.juneau.common.reflect;
import static org.apache.juneau.common.utils.AssertionUtils.*;
+import static org.apache.juneau.common.utils.ClassUtils.*;
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.*;
import java.beans.*;
@@ -169,6 +171,78 @@ public class MethodInfo extends ExecutableInfo implements
Comparable<MethodInfo>
.map(a -> (AnnotationInfo<A>)a);
}
+ /**
+ * Finds annotations on this method using the specified traversal
settings.
+ *
+ * <p>
+ * This method allows flexible annotation traversal across different
scopes using {@link AnnotationTraversal} enums.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search method only</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ *
<jv>mi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF);
+ *
+ * <jc>// Search method and matching methods in parent classes</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ *
<jv>mi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF,
MATCHING_METHODS);
+ *
+ * <jc>// Search method, matching methods, and return type</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s3</jv> =
+ *
<jv>mi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF,
MATCHING_METHODS, RETURN_TYPE);
+ * </p>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider}.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(e.g., SELF, MATCHING_METHODS, RETURN_TYPE).
+ * @return A stream of annotation infos matching the specified type and
traversal settings.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, AnnotationTraversal... traversals) {
+ assertArgNotNull("type", type);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == AnnotationTraversal.SELF) {
+ return
Arrays.stream(inner.getDeclaredAnnotations())
+ .flatMap(a ->
Arrays.stream(splitRepeated(a)))
+ .map(a ->
AnnotationInfo.of(this, a))
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ } else if (traversal ==
AnnotationTraversal.MATCHING_METHODS) {
+ return
getMatchingMethods().stream().skip(1)
+ .flatMap(x ->
Arrays.stream(x.inner().getDeclaredAnnotations())
+ .flatMap(a ->
Arrays.stream(splitRepeated(a)))
+ .map(a ->
AnnotationInfo.of(x, a))
+ .filter(a ->
a.isType(type))
+ .map(a ->
(AnnotationInfo<A>)a));
+ } else if (traversal ==
AnnotationTraversal.RETURN_TYPE) {
+ return
getReturnType().unwrap(Value.class, Optional.class).findAnnotations(type,
AnnotationTraversal.PARENTS);
+ }
+ throw illegalArg("Invalid traversal type for
method annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Finds annotations on this method using the specified traversal
settings in parent-first order.
+ *
+ * <p>
+ * This method is identical to {@link #findAnnotations(Class,
AnnotationTraversal...)} but returns
+ * results in parent-to-child order.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(e.g., SELF, MATCHING_METHODS, RETURN_TYPE).
+ * @return A stream of annotation infos matching the specified type and
traversal settings in parent-first order.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, traversals).toList());
+ }
+
/**
* Returns all annotations on the declaring class, this method and
parent overridden methods, return type, and package in child-to-parent order.
*
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 caacb793bd..29b0bee81d 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
@@ -17,8 +17,10 @@
package org.apache.juneau.common.reflect;
+import static org.apache.juneau.common.utils.AssertionUtils.*;
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.*;
import java.lang.annotation.*;
@@ -128,6 +130,69 @@ public class ParameterInfo extends ElementInfo implements
Annotatable {
return getAnnotationInfos().stream().filter(x ->
x.isType(type)).map(x -> (AnnotationInfo<A>)x);
}
+ /**
+ * Finds annotations on this parameter using the specified traversal
settings.
+ *
+ * <p>
+ * This method allows flexible annotation traversal across different
scopes using {@link AnnotationTraversal} enums.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bjava'>
+ * <jc>// Search parameter only</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s1</jv> =
+ *
<jv>pi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF);
+ *
+ * <jc>// Search parameter and matching parameters in parent
methods</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s2</jv> =
+ *
<jv>pi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF,
MATCHING_PARAMETERS);
+ *
+ * <jc>// Search parameter, matching parameters, and parameter
type</jc>
+ * Stream<AnnotationInfo<MyAnnotation>> <jv>s3</jv> =
+ *
<jv>pi</jv>.findAnnotations(MyAnnotation.<jk>class</jk>, SELF,
MATCHING_PARAMETERS, PARAMETER_TYPE);
+ * </p>
+ *
+ * <p>
+ * This does NOT include runtime annotations. For runtime annotation
support, use
+ * {@link org.apache.juneau.common.reflect.AnnotationProvider}.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(e.g., SELF, MATCHING_PARAMETERS, PARAMETER_TYPE).
+ * @return A stream of annotation infos matching the specified type and
traversal settings.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotations(Class<A> type, AnnotationTraversal... traversals) {
+ assertArgNotNull("type", type);
+
+ return Arrays.stream(traversals)
+
.sorted(Comparator.comparingInt(AnnotationTraversal::getOrder))
+ .flatMap(traversal -> {
+ if (traversal == AnnotationTraversal.SELF) {
+ return getAnnotationInfos(type);
+ } else if (traversal ==
AnnotationTraversal.MATCHING_PARAMETERS) {
+ return
getMatchingParameters().stream().skip(1).flatMap(x ->
x.getAnnotationInfos(type));
+ } else if (traversal ==
AnnotationTraversal.PARAMETER_TYPE) {
+ return
getParameterType().unwrap(Value.class, Optional.class).findAnnotations(type,
AnnotationTraversal.PARENTS, AnnotationTraversal.PACKAGE);
+ }
+ throw illegalArg("Invalid traversal type for
parameter annotations: {0}", traversal);
+ });
+ }
+
+ /**
+ * Finds annotations on this parameter using the specified traversal
settings in parent-first order.
+ *
+ * <p>
+ * This method is identical to {@link #findAnnotations(Class,
AnnotationTraversal...)} but returns
+ * results in parent-to-child order.
+ *
+ * @param <A> The annotation type.
+ * @param type The annotation type to search for.
+ * @param traversals The traversal settings defining what to search
(e.g., SELF, MATCHING_PARAMETERS, PARAMETER_TYPE).
+ * @return A stream of annotation infos matching the specified type and
traversal settings in parent-first order.
+ */
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findAnnotationsParentFirst(Class<A> type, AnnotationTraversal... traversals) {
+ return rstream(findAnnotations(type, traversals).toList());
+ }
+
/**
* Returns this parameter and all matching parameters in parent classes.
*