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 86c6da075a org.apache.juneau.common.reflect API improvements
86c6da075a is described below
commit 86c6da075a4dc5a626da4e25af4354f48e074231
Author: James Bognar <[email protected]>
AuthorDate: Mon Nov 17 11:25:36 2025 -0500
org.apache.juneau.common.reflect API improvements
---
.../juneau/common/reflect/AnnotationInfo.java | 336 ++++++++++++++++++---
1 file changed, 288 insertions(+), 48 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 64dc47512f..5e937bb127 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
@@ -29,9 +29,38 @@ import java.util.function.*;
import org.apache.juneau.common.annotation.*;
/**
- * Represents an annotation instance on a class and the class it was found on.
+ * Encapsulates information about an annotation instance and the element it's
declared on.
+ *
+ * <p>
+ * This class provides a convenient wrapper around Java annotations that
allows you to:
+ * <ul>
+ * <li>Access annotation values in a type-safe manner
+ * <li>Query annotation properties without reflection boilerplate
+ * <li>Track where the annotation was found (class, method, field, etc.)
+ * <li>Sort annotations by precedence using ranks
+ * </ul>
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Get annotation info from a class</jc>
+ * ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
+ * Optional<AnnotationInfo<MyAnnotation>> <jv>ai</jv> =
+ *
<jv>ci</jv>.getAnnotationInfos(MyAnnotation.<jk>class</jk>).findFirst();
+ *
+ * <jc>// Access annotation values</jc>
+ * <jv>ai</jv>.ifPresent(<jv>x</jv> -> {
+ * String <jv>value</jv> =
<jv>x</jv>.getValue(String.<jk>class</jk>,
<js>"value"</js>).orElse(<js>"default"</js>);
+ * <jk>int</jk> <jv>priority</jv> =
<jv>x</jv>.getInt(<js>"priority"</js>).orElse(0);
+ * });
+ * </p>
*
* <h5 class='section'>See Also:</h5><ul>
+ * <li class='jc'>{@link ClassInfo}
+ * <li class='jc'>{@link MethodInfo}
+ * <li class='jc'>{@link FieldInfo}
+ * <li class='jc'>{@link ConstructorInfo}
+ * <li class='jc'>{@link ParameterInfo}
+ * <li class='jc'>{@link PackageInfo}
* </ul>
*
* @param <T> The annotation type.
@@ -39,12 +68,20 @@ import org.apache.juneau.common.annotation.*;
public class AnnotationInfo<T extends Annotation> {
/**
- * Convenience constructor for creating an annotation info object.
+ * Creates a new annotation info object.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Create annotation info for a class annotation</jc>
+ * ClassInfo <jv>ci</jv> =
ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
+ * MyAnnotation <jv>annotation</jv> =
<jv>ci</jv>.inner().getAnnotation(MyAnnotation.<jk>class</jk>);
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> =
AnnotationInfo.<jsm>of</jsm>(<jv>ci</jv>, <jv>annotation</jv>);
+ * </p>
*
- * @param <A> The annotation class.
+ * @param <A> The annotation type.
* @param on The annotatable object where the annotation was found
(class, method, field, constructor, parameter, or package).
- * @param value The annotation found.
- * @return A new {@link AnnotationInfo} object.
+ * @param value The annotation instance. Must not be <jk>null</jk>.
+ * @return A new {@link AnnotationInfo} object wrapping the annotation.
*/
public static <A extends Annotation> AnnotationInfo<A> of(Annotatable
on, A value) {
return new AnnotationInfo<>(on, value);
@@ -58,10 +95,10 @@ public class AnnotationInfo<T extends Annotation> {
private final Supplier<List<MethodInfo>> methods = memoize(() ->
stream(a.annotationType().getMethods()).map(m ->
MethodInfo.of(ClassInfo.of(a.annotationType()), m)).toList());
/**
- * Constructor for class annotations.
+ * Constructor.
*
- * @param c The class where the annotation was found.
- * @param a The annotation found.
+ * @param on The annotatable object where the annotation was found.
+ * @param a The annotation instance.
*/
AnnotationInfo(Annotatable on, T a) {
this.annotatable = on; // TODO - Shouldn't allow null.
@@ -70,31 +107,69 @@ public class AnnotationInfo<T extends Annotation> {
}
/**
- * Returns the rank of this annotation.
+ * Returns the rank of this annotation for sorting by precedence.
*
* <p>
- * The rank is used for sorting annotations in order of precedence.
+ * The rank is determined by checking if the annotation has a {@code
rank()} method that returns an {@code int}.
+ * If found, that value is used; otherwise the rank defaults to {@code
0}.
*
- * @return The rank of this annotation.
+ * <p>
+ * Higher rank values indicate higher precedence when multiple
annotations of the same type are present.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Annotation with rank method</jc>
+ * <ja>@interface</ja> MyAnnotation {
+ * <jk>int</jk> rank() <jk>default</jk> 0;
+ * }
+ *
+ * <jc>// Get rank from annotation info</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>int</jk> <jv>rank</jv> = <jv>ai</jv>.getRank(); <jc>//
Returns value from rank() method</jc>
+ * </p>
+ *
+ * @return The rank of this annotation, or {@code 0} if no rank method
exists.
*/
public int getRank() {
return rank;
}
/**
- * Returns the class name of the annotation.
+ * Returns the simple class name of this annotation.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * String <jv>name</jv> = <jv>ai</jv>.getName(); <jc>// Returns
"MyAnnotation"</jc>
+ * </p>
*
- * @return The simple class name of the annotation.
+ * @return The simple class name of the annotation (e.g., {@code
"Override"} for {@code @Override}).
*/
public String getName() { return scn(a.annotationType()); }
/**
- * Returns a matching value on this annotation.
+ * Returns the value of a specific annotation method.
+ *
+ * <p>
+ * This method provides type-safe access to annotation field values
without requiring
+ * explicit reflection calls or casting.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @interface MyAnnotation { String
value(); int priority(); }</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ *
+ * <jc>// Get string value</jc>
+ * Optional<String> <jv>value</jv> =
<jv>ai</jv>.getValue(String.<jk>class</jk>, <js>"value"</js>);
+ *
+ * <jc>// Get int value</jc>
+ * Optional<Integer> <jv>priority</jv> =
<jv>ai</jv>.getValue(Integer.<jk>class</jk>, <js>"priority"</js>);
+ * </p>
*
- * @param <V> The annotation field type.
- * @param type The annotation field type.
- * @param name The annotation field name.
- * @return An optional containing the value, or empty if not found.
+ * @param <V> The expected type of the annotation field value.
+ * @param type The expected class of the annotation field value.
+ * @param name The name of the annotation method (field).
+ * @return An {@link Optional} containing the value if found and type
matches, empty otherwise.
*/
@SuppressWarnings("unchecked")
public <V> Optional<V> getValue(Class<V> type, String name) {
@@ -107,9 +182,23 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Casts this annotation info to a specific annotation type.
*
+ * <p>
+ * This is useful when you have an {@code AnnotationInfo<?>} and need
to narrow it to a specific type.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationInfo<?> <jv>ai</jv> = ...;
+ *
+ * <jc>// Safe cast</jc>
+ * AnnotationInfo<MyAnnotation> <jv>myAi</jv> =
<jv>ai</jv>.cast(MyAnnotation.<jk>class</jk>);
+ * <jk>if</jk> (<jv>myAi</jv> != <jk>null</jk>) {
+ * <jc>// Use strongly-typed annotation info</jc>
+ * }
+ * </p>
+ *
* @param <A> The annotation type to cast to.
* @param type The annotation type to cast to.
- * @return This annotation info cast to the specified type, or
<jk>null</jk> if the cast is not valid.
+ * @return This annotation info cast to the specified type, or
<jk>null</jk> if the annotation is not of the specified type.
*/
@SuppressWarnings("unchecked")
public <A extends Annotation> AnnotationInfo<A> cast(Class<A> type) {
@@ -117,31 +206,70 @@ public class AnnotationInfo<T extends Annotation> {
}
/**
- * Returns <jk>true</jk> if this annotation has the specified
annotation defined on it.
+ * Returns <jk>true</jk> if this annotation is itself annotated with
the specified annotation.
+ *
+ * <p>
+ * This checks for meta-annotations on the annotation type.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Check if @MyAnnotation is annotated with @Documented</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>boolean</jk> <jv>isDocumented</jv> =
<jv>ai</jv>.hasAnnotation(Documented.<jk>class</jk>);
+ * </p>
*
- * @param <A> The annotation class.
- * @param type The annotation to test for.
- * @return <jk>true</jk> if this annotation has the specified
annotation defined on it.
+ * @param <A> The meta-annotation type.
+ * @param type The meta-annotation to test for.
+ * @return <jk>true</jk> if this annotation is annotated with the
specified annotation.
*/
public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
return nn(this.a.annotationType().getAnnotation(type));
}
/**
- * Returns the annotation found.
+ * Returns the wrapped annotation instance.
*
- * @return The annotation found.
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * MyAnnotation <jv>annotation</jv> = <jv>ai</jv>.inner();
+ *
+ * <jc>// Access annotation methods directly</jc>
+ * String <jv>value</jv> = <jv>annotation</jv>.value();
+ * </p>
+ *
+ * @return The wrapped annotation instance.
*/
public T inner() {
return a;
}
/**
- * Returns <jk>true</jk> if this annotation is in the specified {@link
AnnotationGroup group}.
+ * Returns <jk>true</jk> if this annotation is in the specified {@link
AnnotationGroup}.
*
- * @param <A> The annotation class.
- * @param group The group annotation.
- * @return <jk>true</jk> if this annotation is in the specified {@link
AnnotationGroup group}.
+ * <p>
+ * Annotation groups are used to logically group related annotations
together.
+ * This checks if the annotation is annotated with {@link
AnnotationGroup} and if
+ * the group value matches the specified type.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Define an annotation group</jc>
+ * <ja>@interface</ja> MyGroup {}
+ *
+ * <jc>// Annotation in the group</jc>
+ * <ja>@AnnotationGroup</ja>(MyGroup.<jk>class</jk>)
+ * <ja>@interface</ja> MyAnnotation {}
+ *
+ * <jc>// Check if annotation is in group</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>boolean</jk> <jv>inGroup</jv> =
<jv>ai</jv>.isInGroup(MyGroup.<jk>class</jk>); <jc>// Returns true</jc>
+ * </p>
+ *
+ * @param <A> The group annotation type.
+ * @param group The group annotation class to test for.
+ * @return <jk>true</jk> if this annotation is in the specified group.
+ * @see AnnotationGroup
*/
public <A extends Annotation> boolean isInGroup(Class<A> group) {
var x = a.annotationType().getAnnotation(AnnotationGroup.class);
@@ -149,20 +277,46 @@ public class AnnotationInfo<T extends Annotation> {
}
/**
- * Returns <jk>true</jk> if this annotation is the specified type.
+ * Returns <jk>true</jk> if this annotation is of the specified type.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationInfo<?> <jv>ai</jv> = ...;
+ *
+ * <jk>if</jk> (<jv>ai</jv>.isType(MyAnnotation.<jk>class</jk>)) {
+ * <jc>// Handle MyAnnotation specifically</jc>
+ * }
+ * </p>
*
- * @param <A> The annotation class.
- * @param type The type to test against.
- * @return <jk>true</jk> if this annotation is the specified type.
+ * @param <A> The annotation type to test for.
+ * @param type The annotation type to test against.
+ * @return <jk>true</jk> if this annotation's type is exactly the
specified type.
*/
public <A extends Annotation> boolean isType(Class<A> type) {
return this.a.annotationType() == type;
}
/**
- * Converts this object to a readable map for debugging purposes.
+ * Converts this annotation info to a map representation for debugging
purposes.
+ *
+ * <p>
+ * The returned map contains:
+ * <ul>
+ * <li>The annotatable element's type and label (e.g., {@code
"CLASS_TYPE" -> "com.example.MyClass"})
+ * <li>A nested map with the annotation's simple name as key and
its non-default values
+ * </ul>
+ *
+ * <p>
+ * Only annotation values that differ from their default values are
included.
*
- * @return A new map showing the attributes of this object.
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * LinkedHashMap<String,Object> <jv>map</jv> =
<jv>ai</jv>.toMap();
+ * <jc>// Returns: {"CLASS_TYPE": "MyClass", "@MyAnnotation":
{"value": "foo", "priority": 5}}</jc>
+ * </p>
+ *
+ * @return A new map showing the attributes of this annotation info.
*/
public LinkedHashMap<String, Object> toMap() {
var jm = new LinkedHashMap<String, Object>();
@@ -328,17 +482,34 @@ public class AnnotationInfo<T extends Annotation> {
/**
* 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.
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * Optional<MethodInfo> <jv>method</jv> =
<jv>ai</jv>.getMethod(<js>"value"</js>);
+ * <jv>method</jv>.ifPresent(<jv>m</jv> ->
System.<jsf>out</jsf>.println(<jv>m</jv>.getReturnType()));
+ * </p>
+ *
+ * @param methodName The method name to look for.
+ * @return An {@link Optional} containing the method info, 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.
+ * Returns the value of the {@code value()} method on this annotation
as a string.
+ *
+ * <p>
+ * This is a convenience method equivalent to calling {@link
#getString(String) getString("value")}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation("foo")</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * String <jv>value</jv> =
<jv>ai</jv>.getValue().orElse(<js>"default"</js>); <jc>// Returns "foo"</jc>
+ * </p>
*
- * @return An optional containing the value of the <c>value()</c>
method, or empty if not found or not a string.
+ * @return An {@link Optional} containing the value of the {@code
value()} method, or empty if not found or not a string.
*/
public Optional<String> getValue() {
return getString("value");
@@ -347,8 +518,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
string.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(name="John", age=30)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * String <jv>name</jv> =
<jv>ai</jv>.getString(<js>"name"</js>).orElse(<js>"unknown"</js>); <jc>//
Returns "John"</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a string.
+ * @return An {@link Optional} containing the value as a string, or
empty if not found or not a string type.
*/
public Optional<String> getString(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(String.class)).map(x -> s(x.invoke(a)));
@@ -357,8 +535,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as an
integer.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(priority=5)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>int</jk> <jv>priority</jv> =
<jv>ai</jv>.getInt(<js>"priority"</js>).orElse(0); <jc>// Returns 5</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not an integer.
+ * @return An {@link Optional} containing the value as an integer, or
empty if not found or not an {@code int} type.
*/
public Optional<Integer> getInt(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(int.class)).map(x -> (Integer)x.invoke(a));
@@ -367,8 +552,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
boolean.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(enabled=true)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>boolean</jk> <jv>enabled</jv> =
<jv>ai</jv>.getBoolean(<js>"enabled"</js>).orElse(<jk>false</jk>); <jc>//
Returns true</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a boolean.
+ * @return An {@link Optional} containing the value as a boolean, or
empty if not found or not a {@code boolean} type.
*/
public Optional<Boolean> getBoolean(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(boolean.class)).map(x -> (Boolean)x.invoke(a));
@@ -377,8 +569,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
long.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(timestamp=1234567890L)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>long</jk> <jv>timestamp</jv> =
<jv>ai</jv>.getLong(<js>"timestamp"</js>).orElse(0L); <jc>// Returns
1234567890L</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a long.
+ * @return An {@link Optional} containing the value as a long, or empty
if not found or not a {@code long} type.
*/
public Optional<Long> getLong(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(long.class)).map(x -> (Long)x.invoke(a));
@@ -387,8 +586,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
double.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(threshold=0.95)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>double</jk> <jv>threshold</jv> =
<jv>ai</jv>.getDouble(<js>"threshold"</js>).orElse(0.0); <jc>// Returns
0.95</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a double.
+ * @return An {@link Optional} containing the value as a double, or
empty if not found or not a {@code double} type.
*/
public Optional<Double> getDouble(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(double.class)).map(x -> (Double)x.invoke(a));
@@ -397,8 +603,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
float.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(weight=0.5f)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * <jk>float</jk> <jv>weight</jv> =
<jv>ai</jv>.getFloat(<js>"weight"</js>).orElse(0.0f); <jc>// Returns 0.5f</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a float.
+ * @return An {@link Optional} containing the value as a float, or
empty if not found or not a {@code float} type.
*/
public Optional<Float> getFloat(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(float.class)).map(x -> (Float)x.invoke(a));
@@ -407,8 +620,18 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
class.
*
+ * <p>
+ * For type-safe access to a class of a specific supertype, use {@link
#getClassValue(String, Class)}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(type=String.class)</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * Class<?> <jv>type</jv> =
<jv>ai</jv>.getClassValue(<js>"type"</js>).orElse(<jk>null</jk>); <jc>//
Returns String.class</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a class.
+ * @return An {@link Optional} containing the class value, or empty if
not found or not a {@link Class} type.
*/
@SuppressWarnings("unchecked")
public Optional<Class<?>> getClassValue(String methodName) {
@@ -443,8 +666,15 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
string array.
*
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(tags={"foo", "bar"})</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * String[] <jv>tags</jv> =
<jv>ai</jv>.getStringArray(<js>"tags"</js>).orElse(<jk>new</jk> String[0]);
<jc>// Returns ["foo", "bar"]</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a string array.
+ * @return An {@link Optional} containing the string array value, or
empty if not found or not a {@code String[]} type.
*/
public Optional<String[]> getStringArray(String methodName) {
return getMethod(methodName).filter(x ->
x.hasReturnType(String[].class)).map(x -> (String[])x.invoke(a));
@@ -453,8 +683,18 @@ public class AnnotationInfo<T extends Annotation> {
/**
* Returns the value of the specified method on this annotation as a
class array.
*
+ * <p>
+ * For type-safe access to an array of classes of a specific supertype,
use {@link #getClassArray(String, Class)}.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// For annotation: @MyAnnotation(types={String.class,
Integer.class})</jc>
+ * AnnotationInfo<MyAnnotation> <jv>ai</jv> = ...;
+ * Class<?>[] <jv>types</jv> =
<jv>ai</jv>.getClassArray(<js>"types"</js>).orElse(<jk>new</jk> Class[0]);
<jc>// Returns [String.class, Integer.class]</jc>
+ * </p>
+ *
* @param methodName The method name.
- * @return An optional containing the value of the specified method, or
empty if not found or not a class array.
+ * @return An {@link Optional} containing the class array value, or
empty if not found or not a {@code Class[]} type.
*/
@SuppressWarnings("unchecked")
public Optional<Class<?>[]> getClassArray(String methodName) {