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() {