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 9c7f8b02b8 Utility class modernization
9c7f8b02b8 is described below
commit 9c7f8b02b83b7fbfe601758a1b36b9b87d201e36
Author: James Bognar <[email protected]>
AuthorDate: Wed Nov 5 13:34:06 2025 -0500
Utility class modernization
---
.../juneau/common/reflect/AnnotationProvider2.java | 268 ++++++++++-
.../apache/juneau/common/reflect/ClassInfo.java | 64 ---
.../juneau/common/reflect/ReflectionMap2.java | 523 +++++++++++++++++++++
.../java/org/apache/juneau/BeanPropertyMeta.java | 6 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 21 -
.../src/main/java/org/apache/juneau/Context.java | 129 +----
.../java/org/apache/juneau/ClassMeta_Test.java | 10 -
.../juneau/common/reflect/ClassInfo_Test.java | 150 +++---
8 files changed, 887 insertions(+), 284 deletions(-)
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider2.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider2.java
index d243e9c0c1..55c0ffab97 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider2.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/AnnotationProvider2.java
@@ -41,6 +41,18 @@ import org.apache.juneau.common.collections.*;
* <li>Caches results for performance
* </ul>
*
+ * <h5 class='section'>Usage:</h5>
+ * <p class='bjava'>
+ * <jc>// Create with default settings</jc>
+ * AnnotationProvider2 <jv>provider</jv> =
AnnotationProvider2.<jsm>create</jsm>().build();
+ *
+ * <jc>// Create with caching disabled</jc>
+ * AnnotationProvider2 <jv>provider</jv> = AnnotationProvider2
+ * .<jsm>create</jsm>()
+ * .disableCaching()
+ * .build();
+ * </p>
+ *
* <h5 class='section'>See Also:</h5>
* <ul>
* <li class='jc'>{@link AnnotationProvider}
@@ -57,12 +69,163 @@ public class AnnotationProvider2 {
/**
* Default instance.
*/
- public static final AnnotationProvider2 INSTANCE = new
AnnotationProvider2();
+ public static final AnnotationProvider2 INSTANCE = new
AnnotationProvider2(create());
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Builder
+
//-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Builder for creating configured {@link AnnotationProvider2}
instances.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * AnnotationProvider2 <jv>provider</jv> = AnnotationProvider2
+ * .<jsm>create</jsm>()
+ * .disableCaching()
+ * .build();
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li class='jm'>{@link AnnotationProvider2#create()}
+ * </ul>
+ */
+ public static class Builder {
+ boolean disableCaching;
+ ReflectionMap2.Builder<Annotation> runtimeAnnotations =
ReflectionMap2.create(Annotation.class);
+
+ Builder() {
+ disableCaching = DISABLE_ANNOTATION_CACHING;
+ }
+
+ /**
+ * Builds a new {@link AnnotationProvider2} instance with the
configured settings.
+ *
+ * @return A new immutable {@link AnnotationProvider2} instance.
+ */
+ public AnnotationProvider2 build() {
+ return new AnnotationProvider2(this);
+ }
+
+ /**
+ * Disables annotation caching entirely.
+ *
+ * <p>
+ * When disabled, annotation lookups will always perform fresh
searches without caching results.
+ *
+ * @return This object for method chaining.
+ */
+ public Builder disableCaching() {
+ disableCaching = true;
+ return this;
+ }
+
+ /**
+ * Conditionally disables or enables annotation caching.
+ *
+ * @param value Whether to disable caching.
+ * @return This object for method chaining.
+ */
+ public Builder disableCaching(boolean value) {
+ disableCaching = value;
+ return this;
+ }
+
+ /**
+ * Adds runtime annotations to be applied to classes and
methods.
+ *
+ * <p>
+ * Annotations must define either an {@code onClass()} method
that returns a {@code Class[]} array,
+ * or an {@code on()} method that returns a {@code String[]}
array to specify the targets.
+ *
+ * @param annotations The annotations to add.
+ * @return This object for method chaining.
+ * @throws BeanRuntimeException If the annotations are invalid.
+ */
+ public Builder addRuntimeAnnotations(List<Annotation>
annotations) {
+
+ for (var a : annotations) {
+ 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
BeanRuntimeException("Invalid annotation @{0} used in runtime annotations.
Annotation must define an onClass() method that returns a Class array.",
scn(a));
+ for (var c :
(Class<?>[])mi.accessible().invoke(a))
+
runtimeAnnotations.append(c.getName(), a);
+ }
+
+ mi = ci.getPublicMethod(x ->
x.hasName("on"));
+ if (nn(mi)) {
+ if (!
mi.getReturnType().is(String[].class))
+ throw new
BeanRuntimeException("Invalid annotation @{0} used in runtime annotations.
Annotation must define an on() method that returns a String array.", scn(a));
+ for (var s :
(String[])mi.accessible().invoke(a))
+
runtimeAnnotations.append(s, a);
+ }
+
+ } catch (BeanRuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new BeanRuntimeException(e, null,
"Invalid annotation @{0} used in runtime annotations.", cn(a));
+ }
+ }
+ return this;
+ }
+
+ public Builder addRuntimeAnnotations(Annotation...annotations) {
+ return addRuntimeAnnotations(l(annotations));
+ }
+ }
+
+ /**
+ * Creates a new {@link Builder} for constructing an annotation
provider.
+ *
+ * @return A new builder for configuring the annotation provider.
+ */
+ public static Builder create() {
+ return new Builder();
+ }
// @formatter:off
- private final Cache<Class<?>,List<AnnotationInfo<Annotation>>>
classAnnotationsInfo =
Cache.<Class<?>,List<AnnotationInfo<Annotation>>>create().supplier(this::findClassAnnotations).disableCaching(DISABLE_ANNOTATION_CACHING).build();
+ private final Cache<Class<?>,List<AnnotationInfo<Annotation>>>
classAnnotations;
+ private final Cache<Class<?>,List<AnnotationInfo<Annotation>>>
classDeclaredAnnotations;
+ private final Cache<Method,List<AnnotationInfo<Annotation>>>
methodAnnotations;
+ private final Cache<Field,List<AnnotationInfo<Annotation>>>
fieldAnnotations;
+ private final Cache<Constructor<?>,List<AnnotationInfo<Annotation>>>
constructorAnnotations;
+ private final ReflectionMap2<Annotation> runtimeAnnotations;
// @formatter:on
+ /**
+ * Constructor.
+ *
+ * @param builder The builder containing configuration settings.
+ */
+ protected AnnotationProvider2(Builder builder) {
+ this.classAnnotations =
Cache.<Class<?>,List<AnnotationInfo<Annotation>>>create()
+ .supplier(this::findClassAnnotations)
+ .disableCaching(builder.disableCaching)
+ .build();
+ this.classDeclaredAnnotations =
Cache.<Class<?>,List<AnnotationInfo<Annotation>>>create()
+ .supplier(this::findClassDeclaredAnnotations)
+ .disableCaching(builder.disableCaching)
+ .build();
+ this.methodAnnotations =
Cache.<Method,List<AnnotationInfo<Annotation>>>create()
+ .supplier(this::findMethodAnnotations)
+ .disableCaching(builder.disableCaching)
+ .build();
+ this.fieldAnnotations =
Cache.<Field,List<AnnotationInfo<Annotation>>>create()
+ .supplier(this::findFieldAnnotations)
+ .disableCaching(builder.disableCaching)
+ .build();
+ this.constructorAnnotations =
Cache.<Constructor<?>,List<AnnotationInfo<Annotation>>>create()
+ .supplier(this::findConstructorAnnotations)
+ .disableCaching(builder.disableCaching)
+ .build();
+ this.runtimeAnnotations = builder.runtimeAnnotations.build();
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// Public API
@@ -80,7 +243,7 @@ public class AnnotationProvider2 {
*/
public List<AnnotationInfo<Annotation>> find(Class<?> onClass) {
assertArgNotNull("onClass", onClass);
- return classAnnotationsInfo.get(onClass);
+ return classAnnotations.get(onClass);
}
/**
@@ -104,6 +267,61 @@ public class AnnotationProvider2 {
.map(a -> (AnnotationInfo<A>)a);
}
+ public List<AnnotationInfo<Annotation>> findDeclared(Class<?> onClass) {
+ assertArgNotNull("onClass", onClass);
+ return classDeclaredAnnotations.get(onClass);
+ }
+
+ public <A extends Annotation> Stream<AnnotationInfo<A>>
findDeclared(Class<A> type, Class<?> onClass) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("onClass", onClass);
+ return findDeclared(onClass).stream()
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+
+ public List<AnnotationInfo<Annotation>> find(Method onMethod) {
+ assertArgNotNull("onMethod", onMethod);
+ return methodAnnotations.get(onMethod);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <A extends Annotation> Stream<AnnotationInfo<A>> find(Class<A>
type, Method onMethod) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("onMethod", onMethod);
+ return find(onMethod).stream()
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+
+ public List<AnnotationInfo<Annotation>> find(Field onField) {
+ assertArgNotNull("onField", onField);
+ return fieldAnnotations.get(onField);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <A extends Annotation> Stream<AnnotationInfo<A>> find(Class<A>
type, Field onField) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("onField", onField);
+ return find(onField).stream()
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+
+ public List<AnnotationInfo<Annotation>> find(Constructor<?>
onConstructor) {
+ assertArgNotNull("onConstructor", onConstructor);
+ return constructorAnnotations.get(onConstructor);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <A extends Annotation> Stream<AnnotationInfo<A>> find(Class<A>
type, Constructor<?> onConstructor) {
+ assertArgNotNull("type", type);
+ assertArgNotNull("onConstructor", onConstructor);
+ return find(onConstructor).stream()
+ .filter(a -> a.isType(type))
+ .map(a -> (AnnotationInfo<A>)a);
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// Private implementation
//-----------------------------------------------------------------------------------------------------------------
@@ -148,6 +366,46 @@ public class AnnotationProvider2 {
return u(list);
}
+ private List<AnnotationInfo<Annotation>>
findClassDeclaredAnnotations(Class<?> forClass) {
+ var list = new ArrayList<AnnotationInfo<Annotation>>();
+
+ // On this class
+ findDeclaredAnnotations(list, forClass);
+
+ return u(list);
+ }
+
+ private List<AnnotationInfo<Annotation>> findMethodAnnotations(Method
forMethod) {
+ var list = new ArrayList<AnnotationInfo<Annotation>>();
+
+ MethodInfo.of(forMethod).getMatchingMethods().forEach(m -> {
+ runtimeAnnotations.findMatching(m.inner()).forEach(a ->
list.add(AnnotationInfo.of(m, a)));
+ list.addAll(m.getDeclaredAnnotationInfos());
+ });
+
+ return u(list);
+ }
+
+ private List<AnnotationInfo<Annotation>> findFieldAnnotations(Field
forField) {
+ var list = new ArrayList<AnnotationInfo<Annotation>>();
+
+ FieldInfo fi = FieldInfo.of(forField);
+ runtimeAnnotations.findMatching(forField).forEach(a ->
list.add(AnnotationInfo.of(fi, a)));
+ list.addAll(fi.getAnnotationInfos());
+
+ return u(list);
+ }
+
+ private List<AnnotationInfo<Annotation>>
findConstructorAnnotations(Constructor<?> forConstructor) {
+ var list = new ArrayList<AnnotationInfo<Annotation>>();
+
+ ConstructorInfo ci = ConstructorInfo.of(forConstructor);
+ runtimeAnnotations.findMatching(forConstructor).forEach(a ->
list.add(AnnotationInfo.of(ci, a)));
+ list.addAll(ci.getDeclaredAnnotationInfos());
+
+ return u(list);
+ }
+
/**
* Finds all declared annotations on the specified class and appends
them to the list.
*
@@ -156,6 +414,7 @@ public class AnnotationProvider2 {
*/
private void findDeclaredAnnotations(List<AnnotationInfo<Annotation>>
appendTo, Class<?> forClass) {
var ci = ClassInfo.of(forClass);
+ runtimeAnnotations.findMatching(forClass).forEach(x ->
appendTo.add(AnnotationInfo.of(ClassInfo.of(forClass), x)));
for (var a : forClass.getDeclaredAnnotations())
for (var a2 : splitRepeated(a))
appendTo.add(AnnotationInfo.of(ci, a2));
@@ -173,5 +432,4 @@ public class AnnotationProvider2 {
for (var a2 : splitRepeated(a))
appendTo.add(AnnotationInfo.of(pi, a2));
}
-}
-
+}
\ No newline at end of file
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 b885b98b92..d064767dcc 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
@@ -313,70 +313,6 @@ public class ClassInfo extends ElementInfo implements
Annotatable {
return (o instanceof ClassInfo o2) && eq(this, o2, (x, y) ->
eq(x.t, y.t));
}
- /**
- * Returns the first matching annotation on this class and
superclasses/interfaces.
- *
- * <p>
- * Annotations are searched in the following orders:
- * <ol>
- * <li>On the package of this class.
- * <li>On interfaces ordered parent-to-child.
- * <li>On parent classes ordered parent-to-child.
- * <li>On this class.
- * </ol>
- *
- * @param <A> The annotation type to look for.
- * @param annotationProvider The annotation provider.
- * @param type The annotation to look for.
- * @param filter A predicate to apply to the entries to determine if
annotation should be returned. Can be <jk>null</jk>.
- * @return This object.
- */
- public <A extends Annotation> A firstAnnotation(AnnotationProvider
annotationProvider, Class<A> type, Predicate<A> filter) {
- if (annotationProvider == null)
- annotationProvider = AnnotationProvider.DEFAULT;
- A x = null;
- x = getPackageAnnotation(type);
- if (nn(x) && test(filter, x))
- return x;
- var interfaces2 = interfaces.get();
- for (int i = interfaces2.size() - 1; i >= 0; i--) {
- x = annotationProvider.firstAnnotation(type,
interfaces2.get(i).inner(), filter);
- if (nn(x))
- return x;
- }
- var parents2 = parents.get();
- for (int i = parents2.size() - 1; i >= 0; i--) {
- x = annotationProvider.firstAnnotation(type,
parents2.get(i).inner(), filter);
- if (nn(x))
- return x;
- }
- x = annotationProvider.firstAnnotation(type, inner(), filter);
- if (nn(x) && test(filter, x))
- return x;
- return null;
- }
-
- /**
- * Returns the first matching annotation on this class and
superclasses/interfaces.
- *
- * <p>
- * Annotations are searched in the following orders:
- * <ol>
- * <li>On the package of this class.
- * <li>On interfaces ordered parent-to-child.
- * <li>On parent classes ordered parent-to-child.
- * <li>On this class.
- * </ol>
- *
- * @param <A> The annotation type to look for.
- * @param type The annotation to look for.
- * @param filter A predicate to apply to the entries to determine if
annotation should be returned. Can be <jk>null</jk>.
- * @return This object.
- */
- public <A extends Annotation> A firstAnnotation(Class<A> type,
Predicate<A> filter) {
- return firstAnnotation(null, type, filter);
- }
-
/**
* Performs an action on all matching fields on this class and all
parent classes.
*
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ReflectionMap2.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ReflectionMap2.java
new file mode 100644
index 0000000000..42bc351f17
--- /dev/null
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ReflectionMap2.java
@@ -0,0 +1,523 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.common.reflect;
+
+import static java.lang.Character.*;
+import static org.apache.juneau.common.utils.CollectionUtils.*;
+import static org.apache.juneau.common.utils.StringUtils.*;
+import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+
+import org.apache.juneau.common.utils.*;
+
+/**
+ * Allows arbitrary objects to be mapped to classes and methods base on
class/method name keys.
+ *
+ * <p>
+ * The valid pattern matches are:
+ * <ul class='spaced-list'>
+ * <li>Classes:
+ * <ul>
+ * <li>Fully qualified:
+ * <ul>
+ * <li><js>"com.foo.MyClass"</js>
+ * </ul>
+ * <li>Fully qualified inner class:
+ * <ul>
+ *
<li><js>"com.foo.MyClass$Inner1$Inner2"</js>
+ * </ul>
+ * <li>Simple:
+ * <ul>
+ * <li><js>"MyClass"</js>
+ * </ul>
+ * <li>Simple inner:
+ * <ul>
+ * <li><js>"MyClass$Inner1$Inner2"</js>
+ * <li><js>"Inner1$Inner2"</js>
+ * <li><js>"Inner2"</js>
+ * </ul>
+ * </ul>
+ * <li>Methods:
+ * <ul>
+ * <li>Fully qualified with args:
+ * <ul>
+ *
<li><js>"com.foo.MyClass.myMethod(String,int)"</js>
+ *
<li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js>
+ *
<li><js>"com.foo.MyClass.myMethod()"</js>
+ * </ul>
+ * <li>Fully qualified:
+ * <ul>
+ * <li><js>"com.foo.MyClass.myMethod"</js>
+ * </ul>
+ * <li>Simple with args:
+ * <ul>
+ *
<li><js>"MyClass.myMethod(String,int)"</js>
+ *
<li><js>"MyClass.myMethod(java.lang.String,int)"</js>
+ * <li><js>"MyClass.myMethod()"</js>
+ * </ul>
+ * <li>Simple:
+ * <ul>
+ * <li><js>"MyClass.myMethod"</js>
+ * </ul>
+ * <li>Simple inner class:
+ * <ul>
+ *
<li><js>"MyClass$Inner1$Inner2.myMethod"</js>
+ * <li><js>"Inner1$Inner2.myMethod"</js>
+ * <li><js>"Inner2.myMethod"</js>
+ * </ul>
+ * </ul>
+ * <li>Fields:
+ * <ul>
+ * <li>Fully qualified:
+ * <ul>
+ * <li><js>"com.foo.MyClass.myField"</js>
+ * </ul>
+ * <li>Simple:
+ * <ul>
+ * <li><js>"MyClass.myField"</js>
+ * </ul>
+ * <li>Simple inner class:
+ * <ul>
+ *
<li><js>"MyClass$Inner1$Inner2.myField"</js>
+ * <li><js>"Inner1$Inner2.myField"</js>
+ * <li><js>"Inner2.myField"</js>
+ * </ul>
+ * </ul>
+ * <li>Constructors:
+ * <ul>
+ * <li>Fully qualified with args:
+ * <ul>
+ *
<li><js>"com.foo.MyClass(String,int)"</js>
+ *
<li><js>"com.foo.MyClass(java.lang.String,int)"</js>
+ * <li><js>"com.foo.MyClass()"</js>
+ * </ul>
+ * <li>Simple with args:
+ * <ul>
+ * <li><js>"MyClass(String,int)"</js>
+ *
<li><js>"MyClass(java.lang.String,int)"</js>
+ * <li><js>"MyClass()"</js>
+ * </ul>
+ * <li>Simple inner class:
+ * <ul>
+ * <li><js>"MyClass$Inner1$Inner2()"</js>
+ * <li><js>"Inner1$Inner2()"</js>
+ * <li><js>"Inner2()"</js>
+ * </ul>
+ * </ul>
+ * <li>A comma-delimited list of anything on this list.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * </ul>
+ *
+ * @param <V> The type of object in this map.
+ */
+public class ReflectionMap2<V> {
+
+ /**
+ * Builder class.
+ * @param <V> The type of object in this map.
+ */
+ public static class Builder<V> {
+ final List<ClassEntry<V>> classEntries;
+ final List<MethodEntry<V>> methodEntries;
+ final List<FieldEntry<V>> fieldEntries;
+ final List<ConstructorEntry<V>> constructorEntries;
+
+ /**
+ * Constructor.
+ */
+ protected Builder() {
+ classEntries = list();
+ methodEntries = list();
+ fieldEntries = list();
+ constructorEntries = list();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyFrom The builder being copied.
+ */
+ protected Builder(Builder<V> copyFrom) {
+ classEntries = copyOf(copyFrom.classEntries);
+ methodEntries = copyOf(copyFrom.methodEntries);
+ fieldEntries = copyOf(copyFrom.fieldEntries);
+ constructorEntries =
copyOf(copyFrom.constructorEntries);
+ }
+
+ /**
+ * Adds a mapping to this builder.
+ *
+ * @param key
+ * The mapping key.
+ * <br>Can be any of the following:
+ * <ul>
+ * <li>Full class name (e.g.
<js>"com.foo.MyClass"</js>).
+ * <li>Simple class name (e.g. <js>"MyClass"</js>).
+ * <li>All classes (e.g. <js>"*"</js>).
+ * <li>Full method name (e.g.
<js>"com.foo.MyClass.myMethod"</js>).
+ * <li>Simple method name (e.g.
<js>"MyClass.myMethod"</js>).
+ * <li>A comma-delimited list of anything on this
list.
+ * </ul>
+ * @param value The value for this mapping.
+ * @return This object.
+ */
+ public Builder<V> append(String key, V value) {
+ if (StringUtils.isEmpty(key))
+ throw runtimeException("Invalid reflection
signature: [{0}]", key);
+ try {
+ splitNames(key, k -> {
+ if (k.endsWith(")")) {
+ int i = k.substring(0,
k.indexOf('(')).lastIndexOf('.');
+ if (i == -1 ||
isUpperCase(k.charAt(i + 1))) {
+
constructorEntries.add(new ConstructorEntry<>(k, value));
+ } else {
+ methodEntries.add(new
MethodEntry<>(k, value));
+ }
+ } else {
+ int i = k.lastIndexOf('.');
+ if (i == -1) {
+ classEntries.add(new
ClassEntry<>(k, value));
+ } else if
(isUpperCase(k.charAt(i + 1))) {
+ classEntries.add(new
ClassEntry<>(k, value));
+ fieldEntries.add(new
FieldEntry<>(k, value));
+ } else {
+ methodEntries.add(new
MethodEntry<>(k, value));
+ fieldEntries.add(new
FieldEntry<>(k, value));
+ }
+ }
+ });
+ } catch (@SuppressWarnings("unused")
IndexOutOfBoundsException e) {
+ throw runtimeException("Invalid reflection
signature: [{0}]", key);
+ }
+
+ return this;
+ }
+
+ /**
+ * Create new instance of {@link ReflectionMap2} based on the
contents of this builder.
+ *
+ * @return A new {@link ReflectionMap2} object.
+ */
+ public ReflectionMap2<V> build() {
+ return new ReflectionMap2<>(this);
+ }
+
+ /**
+ * Creates a copy of this builder.
+ *
+ * @return A copy of this builder.
+ */
+ public Builder<V> copy() {
+ return new Builder<>(this);
+ }
+ }
+
+ static class ClassEntry<V> {
+ final String simpleName, fullName;
+ final V value;
+
+ ClassEntry(String name, V value) {
+ this.simpleName = simpleClassName(name);
+ this.fullName = name;
+ this.value = value;
+ }
+
+ public boolean matches(Class<?> c) {
+ if (c == null)
+ return false;
+ return classMatches(simpleName, fullName, c);
+ }
+
+ @Override
+ public String toString() {
+ // @formatter:off
+ return mapb().filtered()
+ .add("simpleName", simpleName)
+ .add("fullName", fullName)
+ .add("value", value)
+ .toString();
+ // @formatter:on
+ }
+ }
+
+ static class ConstructorEntry<V> {
+ String simpleClassName, fullClassName, args[];
+ V value;
+
+ ConstructorEntry(String name, V value) {
+ int i = name.indexOf('(');
+ this.args = splita(name.substring(i + 1, name.length()
- 1));
+ name = name.substring(0, i).trim();
+ this.simpleClassName = simpleClassName(name);
+ this.fullClassName = name;
+ this.value = value;
+ }
+
+ public boolean matches(Constructor<?> m) {
+ if (m == null)
+ return false;
+ var c = m.getDeclaringClass();
+ return classMatches(simpleClassName, fullClassName, c)
&& (argsMatch(args, m.getParameterTypes()));
+ }
+
+ @Override
+ public String toString() {
+ // @formatter:off
+ return mapb().filtered()
+ .add("simpleClassName", simpleClassName)
+ .add("fullClassName", fullClassName)
+ .add("args", args)
+ .add("value", value)
+ .toString();
+ // @formatter:on
+ }
+ }
+
+ static class FieldEntry<V> {
+ String simpleClassName, fullClassName, fieldName;
+ V value;
+
+ FieldEntry(String name, V value) {
+ int i = name.lastIndexOf('.');
+ var s1 = name.substring(0, i);
+ var s2 = name.substring(i + 1);
+ this.simpleClassName = simpleClassName(s1);
+ this.fullClassName = s1;
+ this.fieldName = s2;
+ this.value = value;
+ }
+
+ public boolean matches(Field f) {
+ if (f == null)
+ return false;
+ var c = f.getDeclaringClass();
+ return classMatches(simpleClassName, fullClassName, c)
&& (eq(f.getName(), fieldName));
+ }
+
+ @Override
+ public String toString() {
+ // @formatter:off
+ return mapb().filtered()
+ .add("simpleClassName", simpleClassName)
+ .add("fullClassName", fullClassName)
+ .add("fieldName", fieldName)
+ .add("value", value)
+ .toString();
+ // @formatter:on
+ }
+ }
+
+ static class MethodEntry<V> {
+ String simpleClassName, fullClassName, methodName, args[];
+ V value;
+
+ MethodEntry(String name, V value) {
+ int i = name.indexOf('(');
+ this.args = i == -1 ? null :
splitMethodArgs(name.substring(i + 1, name.length() - 1));
+ if (nn(args)) {
+ for (int j = 0; j < args.length; j++) {
+
+ // Strip off generic parameters.
+ int k = args[j].indexOf('<');
+ if (k > 0)
+ args[j] = args[j].substring(0,
k);
+
+ // Convert from xxx[][] to [[Lxxx;
notation.
+ if (args[j].endsWith("[]")) {
+ int l = 0;
+ while (args[j].endsWith("[]")) {
+ l++;
+ args[j] =
args[j].substring(0, args[j].length() - 2);
+ }
+ var sb = new
StringBuilder(args[j].length() + l + 2);
+ for (int m = 0; m < l; m++)
+ sb.append('[');
+
sb.append('L').append(args[j]).append(';');
+ args[j] = sb.toString();
+ }
+ }
+ }
+ name = i == -1 ? name : name.substring(0, i);
+ i = name.lastIndexOf('.');
+ var s1 = name.substring(0, i).trim();
+ var s2 = name.substring(i + 1).trim();
+ this.simpleClassName = simpleClassName(s1);
+ this.fullClassName = s1;
+ this.methodName = s2;
+ this.value = value;
+ }
+
+ public boolean matches(Method m) {
+ if (m == null)
+ return false;
+ var c = m.getDeclaringClass();
+ // @formatter:off
+ return
+ classMatches(simpleClassName, fullClassName, c)
+ && (eq(m.getName(), methodName))
+ && (argsMatch(args, m.getParameterTypes()));
+ // @formatter:on
+ }
+
+ @Override
+ public String toString() {
+ // @formatter:off
+ return mapb().filtered()
+ .add("simpleClassName", simpleClassName)
+ .add("fullClassName", fullClassName)
+ .add("methodName", methodName)
+ .add("args", args)
+ .add("value", value)
+ .toString();
+ // @formatter:on
+ }
+ }
+
+ /**
+ * Static builder creator.
+ *
+ * @param <V> The type of object in this map.
+ * @param c The type of object in this map.
+ * @return A new instance of this object.
+ */
+ public static <V> Builder<V> create(Class<V> c) {
+ return new Builder<>();
+ }
+
+ static boolean argsMatch(String[] names, Class<?>[] args) {
+ if (names == null)
+ return true;
+ if (names.length != args.length)
+ return false;
+ for (int i = 0; i < args.length; i++) {
+ var n = names[i];
+ var a = args[i];
+ if (! (eq(n, a.getSimpleName()) || eq(n, a.getName())))
+ return false;
+ }
+ return true;
+ }
+
+ static boolean classMatches(String simpleName, String fullName,
Class<?> c) {
+ // For class
org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder
+ // c.getSimpleName() == "Builder"
+ // c.getFullName() ==
"org.apache.juneau.a.rttests.RountTripBeansWithBuilders$Ac$Builder"
+ // c.getPackage() == "org.apache.juneau.a.rttests"
+ var cSimple = scn(c);
+ var cFull = cn(c);
+ if (eq(simpleName, cSimple) || eq(fullName, cFull) ||
"*".equals(simpleName))
+ return true;
+ if (cFull.indexOf('$') != -1) {
+ var p = c.getPackage();
+ if (nn(p))
+ cFull = cFull.substring(p.getName().length() +
1);
+ if (eq(simpleName, cFull))
+ return true;
+ int i = cFull.indexOf('$');
+ while (i != -1) {
+ cFull = cFull.substring(i + 1);
+ if (eq(simpleName, cFull))
+ return true;
+ i = cFull.indexOf('$');
+ }
+ }
+ return false;
+ }
+
+ static String simpleClassName(String name) {
+ int i = name.indexOf('.');
+ if (i == -1)
+ return name;
+ return null;
+ }
+
+ static void splitNames(String key, Consumer<String> consumer) {
+ if (key.indexOf(',') == -1) {
+ consumer.accept(key);
+ } else {
+ int m = 0;
+ boolean escaped = false;
+ for (int i = 0; i < key.length(); i++) {
+ char c = key.charAt(i);
+ if (c == '(')
+ escaped = true;
+ else if (c == ')')
+ escaped = false;
+ else if (c == ',' && ! escaped) {
+ consumer.accept(key.substring(m,
i).trim());
+ m = i + 1;
+ }
+ }
+ consumer.accept(key.substring(m).trim());
+ }
+ }
+
+ final List<ClassEntry<V>> classEntries;
+
+ final List<MethodEntry<V>> methodEntries;
+
+ final List<FieldEntry<V>> fieldEntries;
+
+ final List<ConstructorEntry<V>> constructorEntries;
+
+ /**
+ * Constructor.
+ *
+ * @param b Initializer object.
+ */
+ protected ReflectionMap2(Builder<V> b) {
+ this.classEntries = u(copyOf(b.classEntries));
+ this.methodEntries = u(copyOf(b.methodEntries));
+ this.fieldEntries = u(copyOf(b.fieldEntries));
+ this.constructorEntries = u(copyOf(b.constructorEntries));
+ }
+
+ public Stream<V> findMatching(Class<?> c) {
+ return classEntries.stream().filter(x -> x.matches(c)).map(x ->
x.value);
+ }
+
+ public Stream<V> findMatching(Method m) {
+ return methodEntries.stream().filter(x -> x.matches(m)).map(x
-> x.value);
+ }
+
+ public Stream<V> findMatching(Field f) {
+ return fieldEntries.stream().filter(x -> x.matches(f)).map(x ->
x.value);
+ }
+
+ public Stream<V> findMatching(Constructor c) {
+ return constructorEntries.stream().filter(x ->
x.matches(c)).map(x -> x.value);
+ }
+
+ @Override /* Overridden from Object */
+ public String toString() {
+ // @formatter:off
+ return mapb().filtered()
+ .add("classEntries", classEntries)
+ .add("methodEntries", methodEntries)
+ .add("fieldEntries", fieldEntries)
+ .add("constructorEntries", constructorEntries)
+ .toString();
+ // @formatter:on
+ }
+}
\ No newline at end of file
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 0c1d9468e2..d6874cbf8d 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -635,9 +635,9 @@ public class BeanPropertyMeta implements
Comparable<BeanPropertyMeta> {
public <A extends Annotation> BeanPropertyMeta
forEachAnnotation(Class<A> a, Predicate<A> filter, Consumer<A> action) {
BeanContext bc = beanContext;
if (nn(a)) {
- bc.forEachAnnotation(a, field, filter, action);
- bc.forEachAnnotation(a, getter, filter, action);
- bc.forEachAnnotation(a, setter, filter, action);
+ if (nn(field)) bc.forEachAnnotation(a, field, filter,
action);
+ if (nn(getter)) bc.forEachAnnotation(a, getter, filter,
action);
+ if (nn(setter)) bc.forEachAnnotation(a, setter, filter,
action);
}
return this;
}
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 0e87ae4a69..8b0dec2190 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
@@ -851,27 +851,6 @@ public class ClassMeta<T> implements Type {
return (o instanceof ClassMeta<?> o2) && eq(this, o2, (x, y) ->
eq(x.innerClass, y.innerClass));
}
- /**
- * Returns the first matching annotation on this class or parent
classes/interfaces in parent-to-child order.
- *
- * @param <A> The annotation type to look for.
- * @param type The annotation to search for.
- * @param filter A predicate to apply to the entries to determine if
annotation should be used. Can be <jk>null</jk>.
- * @return This object.
- */
- public <A extends Annotation> Optional<A> firstAnnotation(Class<A>
type, Predicate<A> filter) {
- A[] array = annotationArray(type);
- if (array == null) {
- if (beanContext == null)
- return
Optional.ofNullable(info.firstAnnotation(BeanContext.DEFAULT, type, filter));
- return Optional.empty();
- }
- for (var a : array)
- if (test(filter, a))
- return Optional.of(a);
- return Optional.empty();
- }
-
/**
* Performs an action on all matching annotations of the specified type
defined on this class or parent classes/interfaces in parent-to-child order.
*
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 2011d50a82..7d8330f0e9 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
@@ -759,11 +759,8 @@ public abstract class Context implements
AnnotationProvider {
final boolean debug;
private final ReflectionMap<Annotation> annotationMap;
- private final Cache2<Class<?>,Class<? extends Annotation>,Annotation[]>
classAnnotationCache;
private final Cache2<Class<?>,Class<? extends Annotation>,Annotation[]>
declaredClassAnnotationCache;
- private final Cache2<Method,Class<? extends Annotation>,Annotation[]>
methodAnnotationCache;
- private final Cache2<Field,Class<? extends Annotation>,Annotation[]>
fieldAnnotationCache;
- private final Cache2<Constructor<?>,Class<? extends
Annotation>,Annotation[]> constructorAnnotationCache;
+ private final AnnotationProvider2 annotationProvider;
/**
* Constructor for this class.
@@ -804,11 +801,9 @@ public abstract class Context implements
AnnotationProvider {
});
this.annotationMap = rmb.build();
var disabled =
Boolean.getBoolean("juneau.disableAnnotationCaching");
- classAnnotationCache = (Cache2)Cache2.of(Class.class,
Class.class, Annotation[].class).disableCaching(disabled).supplier((k1, k2) ->
annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))).build();
declaredClassAnnotationCache = (Cache2)Cache2.of(Class.class,
Class.class, Annotation[].class).disableCaching(disabled).supplier((k1, k2) ->
annotationMap.appendAll(k1, k2, k1.getDeclaredAnnotationsByType(k2))).build();
- methodAnnotationCache = (Cache2)Cache2.of(Method.class,
Class.class, Annotation[].class).disableCaching(disabled).supplier((k1, k2) ->
annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))).build();
- fieldAnnotationCache = (Cache2)Cache2.of(Field.class,
Class.class, Annotation[].class).disableCaching(disabled).supplier((k1, k2) ->
annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))).build();
- constructorAnnotationCache =
(Cache2)Cache2.of(Constructor.class, Class.class,
Annotation[].class).disableCaching(disabled).supplier((k1, k2) ->
annotationMap.appendAll(k1, k2, k1.getAnnotationsByType(k2))).build();
+
+ annotationProvider =
AnnotationProvider2.create().addRuntimeAnnotations(annotations).build();
}
/**
@@ -820,11 +815,12 @@ public abstract class Context implements
AnnotationProvider {
annotationMap = copyFrom.annotationMap;
annotations = copyFrom.annotations;
debug = copyFrom.debug;
- classAnnotationCache = copyFrom.classAnnotationCache;
declaredClassAnnotationCache =
copyFrom.declaredClassAnnotationCache;
- methodAnnotationCache = copyFrom.methodAnnotationCache;
- fieldAnnotationCache = copyFrom.fieldAnnotationCache;
- constructorAnnotationCache =
copyFrom.constructorAnnotationCache;
+ annotationProvider = copyFrom.annotationProvider;
+ }
+
+ public AnnotationProvider2 getAnnotationProvider() {
+ return annotationProvider;
}
/**
@@ -854,75 +850,47 @@ public abstract class Context implements
AnnotationProvider {
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A firstAnnotation(Class<A> type, Class<?>
onClass, Predicate<A> filter) {
- if (nn(type) && nn(onClass))
- for (var a : annotations(type, onClass))
- if (test(filter, a))
- return a;
- return null;
+ return getAnnotationProvider().find(type, onClass).map(x ->
x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A firstAnnotation(Class<A> type,
Constructor<?> onConstructor, Predicate<A> filter) {
- if (nn(type) && nn(onConstructor))
- for (var a : annotations(type, onConstructor))
- if (test(filter, a))
- return a;
- return null;
+ return getAnnotationProvider().find(type, onConstructor).map(x
-> x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A firstAnnotation(Class<A> type, Field
onField, Predicate<A> filter) {
- if (nn(type) && nn(onField))
- for (var a : annotations(type, onField))
- if (test(filter, a))
- return a;
- return null;
+ return getAnnotationProvider().find(type, onField).map(x ->
x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A firstAnnotation(Class<A> type, Method
onMethod, Predicate<A> filter) {
- if (nn(type) && nn(onMethod))
- for (var a : annotations(type, onMethod))
- if (test(filter, a))
- return a;
- return null;
+ return getAnnotationProvider().find(type, onMethod).map(x ->
x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A firstDeclaredAnnotation(Class<A> type,
Class<?> onClass, Predicate<A> filter) {
- if (nn(type) && nn(onClass))
- for (var a : declaredAnnotations(type, onClass))
- if (test(filter, a))
- return a;
- return null;
+ return getAnnotationProvider().findDeclared(type,
onClass).map(x -> x.inner()).filter(x ->
filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> void forEachAnnotation(Class<A> type,
Class<?> onClass, Predicate<A> filter, Consumer<A> action) {
- if (nn(type) && nn(onClass))
- for (var a : annotations(type, onClass))
- consumeIf(filter, action, a);
+ getAnnotationProvider().find(type, onClass).map(x ->
x.inner()).filter(x -> filter.test(x)).forEach(x -> action.accept(x));
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> void forEachAnnotation(Class<A> type,
Constructor<?> onConstructor, Predicate<A> filter, Consumer<A> action) {
- if (nn(type) && nn(onConstructor))
- for (var a : annotations(type, onConstructor))
- consumeIf(filter, action, a);
+ getAnnotationProvider().find(type, onConstructor).map(x ->
x.inner()).filter(x -> filter.test(x)).forEach(x -> action.accept(x));
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> void forEachAnnotation(Class<A> type,
Field onField, Predicate<A> filter, Consumer<A> action) {
- if (nn(type) && nn(onField))
- for (var a : annotations(type, onField))
- consumeIf(filter, action, a);
+ getAnnotationProvider().find(type, onField).map(x ->
x.inner()).filter(x -> filter.test(x)).forEach(x -> action.accept(x));
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> void forEachAnnotation(Class<A> type,
Method onMethod, Predicate<A> filter, Consumer<A> action) {
- if (nn(type) && nn(onMethod))
- for (var a : annotations(type, onMethod))
- consumeIf(filter, action, a);
+ getAnnotationProvider().find(type, onMethod).map(x ->
x.inner()).filter(x -> filter.test(x)).forEach(x -> action.accept(x));
}
@Override /* Overridden from MetaProvider */
@@ -951,7 +919,7 @@ public abstract class Context implements AnnotationProvider
{
* @return <jk>true</jk> if the annotation exists on the specified
class.
*/
public <A extends Annotation> boolean hasAnnotation(Class<A> type,
Class<?> onClass) {
- return annotations(type, onClass).length > 0;
+ return getAnnotationProvider().find(type, onClass).map(x ->
x.inner()).findFirst().isPresent();
}
/**
@@ -963,7 +931,7 @@ public abstract class Context implements AnnotationProvider
{
* @return <jk>true</jk> if the annotation exists on the specified
field.
*/
public <A extends Annotation> boolean hasAnnotation(Class<A> type,
Constructor<?> onConstructor) {
- return annotations(type, onConstructor).length > 0;
+ return getAnnotationProvider().find(type, onConstructor).map(x
-> x.inner()).findFirst().isPresent();
}
/**
@@ -975,7 +943,7 @@ public abstract class Context implements AnnotationProvider
{
* @return <jk>true</jk> if the annotation exists on the specified
field.
*/
public <A extends Annotation> boolean hasAnnotation(Class<A> type,
Field onField) {
- return annotations(type, onField).length > 0;
+ return getAnnotationProvider().find(type, onField).map(x ->
x.inner()).findFirst().isPresent();
}
/**
@@ -987,7 +955,7 @@ public abstract class Context implements AnnotationProvider
{
* @return <jk>true</jk> if the annotation exists on the specified
method.
*/
public <A extends Annotation> boolean hasAnnotation(Class<A> type,
Method onMethod) {
- return annotations(type, onMethod).length > 0;
+ return getAnnotationProvider().find(type, onMethod).map(x ->
x.inner()).findFirst().isPresent();
}
/**
@@ -1001,52 +969,27 @@ public abstract class Context implements
AnnotationProvider {
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A lastAnnotation(Class<A> type, Class<?>
onClass, Predicate<A> filter) {
- A x = null;
- if (nn(type) && nn(onClass))
- for (var a : annotations(type, onClass))
- if (test(filter, a))
- x = a;
- return x;
+ return getAnnotationProvider().find(type, onClass).map(x ->
x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A lastAnnotation(Class<A> type,
Constructor<?> onConstructor, Predicate<A> filter) {
- A x = null;
- if (nn(type) && nn(onConstructor))
- for (var a : annotations(type, onConstructor))
- if (test(filter, a))
- x = a;
- return x;
+ return getAnnotationProvider().find(type, onConstructor).map(x
-> x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A lastAnnotation(Class<A> type, Field
onField, Predicate<A> filter) {
- A x = null;
- if (nn(type) && nn(onField))
- for (var a : annotations(type, onField))
- if (test(filter, a))
- x = a;
- return x;
+ return getAnnotationProvider().find(type, onField).map(x ->
x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A lastAnnotation(Class<A> type, Method
onMethod, Predicate<A> filter) {
- A x = null;
- if (nn(type) && nn(onMethod))
- for (var a : annotations(type, onMethod))
- if (test(filter, a))
- x = a;
- return x;
+ return getAnnotationProvider().find(type, onMethod).map(x ->
x.inner()).filter(x -> filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from MetaProvider */
public <A extends Annotation> A lastDeclaredAnnotation(Class<A> type,
Class<?> onClass, Predicate<A> filter) {
- A x = null;
- if (nn(type) && nn(onClass))
- for (var a : declaredAnnotations(type, onClass))
- if (test(filter, a))
- x = a;
- return x;
+ return getAnnotationProvider().findDeclared(type,
onClass).map(x -> x.inner()).filter(x ->
filter.test(x)).findFirst().orElse(null);
}
@Override /* Overridden from Object */
@@ -1054,26 +997,6 @@ public abstract class Context implements
AnnotationProvider {
return Utils2.toPropertyMap(this).asReadableString();
}
- @SuppressWarnings("unchecked")
- private <A extends Annotation> A[] annotations(Class<A> type, Class<?>
onClass) {
- return (A[])classAnnotationCache.get(onClass, type);
- }
-
- @SuppressWarnings("unchecked")
- private <A extends Annotation> A[] annotations(Class<A> type,
Constructor<?> onConstructor) {
- return (A[])constructorAnnotationCache.get(onConstructor, type);
- }
-
- @SuppressWarnings("unchecked")
- private <A extends Annotation> A[] annotations(Class<A> type, Field
onField) {
- return (A[])fieldAnnotationCache.get(onField, type);
- }
-
- @SuppressWarnings("unchecked")
- private <A extends Annotation> A[] annotations(Class<A> type, Method
onMethod) {
- return (A[])methodAnnotationCache.get(onMethod, type);
- }
-
@SuppressWarnings("unchecked")
private <A extends Annotation> A[] declaredAnnotations(Class<A> type,
Class<?> onClass) {
return (A[])declaredClassAnnotationCache.get(onClass, type);
diff --git a/juneau-utest/src/test/java/org/apache/juneau/ClassMeta_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/ClassMeta_Test.java
index a6ee7539ef..dbef394671 100755
--- a/juneau-utest/src/test/java/org/apache/juneau/ClassMeta_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ClassMeta_Test.java
@@ -307,16 +307,6 @@ class ClassMeta_Test extends TestBase {
assertList(l4, "5");
}
- @Test void firstAnnotation() {
- var c3 = bc.getClassMeta(C3.class);
- var c4 = bc.getClassMeta(C4.class);
- var c5 = bc.getClassMeta(C5.class);
- assertEquals(2, c3.firstAnnotation(A.class,
null).get().value());
- assertEquals(2, c4.firstAnnotation(A.class,
null).get().value());
- assertEquals(3, c5.firstAnnotation(A.class,
null).get().value());
- assertEquals(5, c3.firstAnnotation(A.class, x -> x.value() ==
5).get().value());
- }
-
@Test void lastAnnotation() {
var c3 = bc.getClassMeta(C3.class);
var c4 = bc.getClassMeta(C4.class);
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 a5ed60ae10..02a32663e9 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
@@ -595,12 +595,6 @@ public class ClassInfo_Test extends TestBase {
assertList(l4, "5");
}
- @Test void firstAnnotation() {
- assertEquals(2, g3.firstAnnotation(A.class, null).value());
- assertEquals(2, g4.firstAnnotation(A.class, null).value());
- assertEquals(3, g5.firstAnnotation(A.class, null).value());
- assertEquals(5, g3.firstAnnotation(A.class, x -> x.value() ==
5).value());
- }
@Test void lastAnnotation() {
assertEquals(7, g3.lastAnnotation(A.class, null).value());
assertEquals(7, g4.lastAnnotation(A.class, null).value());
@@ -1624,111 +1618,111 @@ public class ClassInfo_Test extends TestBase {
@Test
void a01_simple() {
ClassInfo ci = ClassInfo.of(String.class);
-
+
// SIMPLE format
- assertEquals("String",
+ assertEquals("String",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
- assertEquals("String",
+ assertEquals("String",
ci.getNameFormatted(SIMPLE, false, '.',
BRACKETS));
- assertEquals("String",
+ assertEquals("String",
ci.getNameFormatted(SIMPLE, true, '$',
BRACKETS));
}
@Test
void a02_short() {
ClassInfo ci = ClassInfo.of(String.class);
-
+
// SHORT format
- assertEquals("String",
+ assertEquals("String",
ci.getNameFormatted(SHORT, false, '$',
BRACKETS));
- assertEquals("String",
+ assertEquals("String",
ci.getNameFormatted(SHORT, true, '$',
BRACKETS));
}
@Test
void a03_full() {
ClassInfo ci = ClassInfo.of(String.class);
-
+
// FULL format
- assertEquals("java.lang.String",
+ assertEquals("java.lang.String",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
- assertEquals("java.lang.String",
+ assertEquals("java.lang.String",
ci.getNameFormatted(FULL, true, '$', BRACKETS));
}
@Test
void a04_innerClass() {
ClassInfo ci = ClassInfo.of(Map.Entry.class);
-
+
// SIMPLE - only innermost name
- assertEquals("Entry",
+ assertEquals("Entry",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
-
+
// SHORT - outer class + inner class
- assertEquals("Map$Entry",
+ assertEquals("Map$Entry",
ci.getNameFormatted(SHORT, false, '$',
BRACKETS));
- assertEquals("Map.Entry",
+ assertEquals("Map.Entry",
ci.getNameFormatted(SHORT, false, '.',
BRACKETS));
-
+
// FULL - package + outer + inner
- assertEquals("java.util.Map$Entry",
+ assertEquals("java.util.Map$Entry",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
- assertEquals("java.util.Map.Entry",
+ assertEquals("java.util.Map.Entry",
ci.getNameFormatted(FULL, false, '.',
BRACKETS));
}
@Test
void a05_arrays() {
ClassInfo ci = ClassInfo.of(String[].class);
-
+
// BRACKETS format
- assertEquals("String[]",
+ assertEquals("String[]",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
- assertEquals("String[]",
+ assertEquals("String[]",
ci.getNameFormatted(SHORT, false, '$',
BRACKETS));
- assertEquals("java.lang.String[]",
+ assertEquals("java.lang.String[]",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
-
+
// WORD format
- assertEquals("StringArray",
+ assertEquals("StringArray",
ci.getNameFormatted(SIMPLE, false, '$', WORD));
- assertEquals("StringArray",
+ assertEquals("StringArray",
ci.getNameFormatted(SHORT, false, '$', WORD));
- assertEquals("java.lang.StringArray",
+ assertEquals("java.lang.StringArray",
ci.getNameFormatted(FULL, false, '$', WORD));
}
@Test
void a06_multiDimensionalArrays() {
ClassInfo ci = ClassInfo.of(String[][].class);
-
+
// BRACKETS format
- assertEquals("String[][]",
+ assertEquals("String[][]",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
- assertEquals("java.lang.String[][]",
+ assertEquals("java.lang.String[][]",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
-
+
// WORD format
- assertEquals("StringArrayArray",
+ assertEquals("StringArrayArray",
ci.getNameFormatted(SIMPLE, false, '$', WORD));
- assertEquals("java.lang.StringArrayArray",
+ assertEquals("java.lang.StringArrayArray",
ci.getNameFormatted(FULL, false, '$', WORD));
}
@Test
void a07_primitiveArrays() {
ClassInfo ci = ClassInfo.of(int[].class);
-
+
// BRACKETS format
- assertEquals("int[]",
+ assertEquals("int[]",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
- assertEquals("int[]",
+ assertEquals("int[]",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
-
+
// WORD format
- assertEquals("intArray",
+ assertEquals("intArray",
ci.getNameFormatted(SIMPLE, false, '$', WORD));
- assertEquals("intArray",
+ assertEquals("intArray",
ci.getNameFormatted(FULL, false, '$', WORD));
}
@@ -1738,23 +1732,23 @@ public class ClassInfo_Test extends TestBase {
Field f =
GenericsTestClass.class.getDeclaredField("hashMap");
Type t = f.getGenericType();
ClassInfo ci = ClassInfo.of(t);
-
+
// Without type params
- assertEquals("HashMap",
+ assertEquals("HashMap",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
- assertEquals("java.util.HashMap",
+ assertEquals("java.util.HashMap",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
-
+
// With type params - SIMPLE
- assertEquals("HashMap<String,Integer>",
+ assertEquals("HashMap<String,Integer>",
ci.getNameFormatted(SIMPLE, true, '$',
BRACKETS));
-
+
// With type params - SHORT
- assertEquals("HashMap<String,Integer>",
+ assertEquals("HashMap<String,Integer>",
ci.getNameFormatted(SHORT, true, '$',
BRACKETS));
-
+
// With type params - FULL
-
assertEquals("java.util.HashMap<java.lang.String,java.lang.Integer>",
+
assertEquals("java.util.HashMap<java.lang.String,java.lang.Integer>",
ci.getNameFormatted(FULL, true, '$', BRACKETS));
}
@@ -1764,13 +1758,13 @@ public class ClassInfo_Test extends TestBase {
Field f =
GenericsTestClass.class.getDeclaredField("nestedMap");
Type t = f.getGenericType();
ClassInfo ci = ClassInfo.of(t);
-
+
// With type params - SIMPLE
- assertEquals("HashMap<String,ArrayList<Integer>>",
+ assertEquals("HashMap<String,ArrayList<Integer>>",
ci.getNameFormatted(SIMPLE, true, '$',
BRACKETS));
-
+
// With type params - FULL
-
assertEquals("java.util.HashMap<java.lang.String,java.util.ArrayList<java.lang.Integer>>",
+
assertEquals("java.util.HashMap<java.lang.String,java.util.ArrayList<java.lang.Integer>>",
ci.getNameFormatted(FULL, true, '$', BRACKETS));
}
@@ -1780,15 +1774,15 @@ public class ClassInfo_Test extends TestBase {
Field f =
GenericsTestClass.class.getDeclaredField("listArray");
Type t = f.getGenericType();
ClassInfo ci = ClassInfo.of(t);
-
+
// BRACKETS format
- assertEquals("ArrayList<String>[]",
+ assertEquals("ArrayList<String>[]",
ci.getNameFormatted(SIMPLE, true, '$',
BRACKETS));
- assertEquals("java.util.ArrayList<java.lang.String>[]",
+ assertEquals("java.util.ArrayList<java.lang.String>[]",
ci.getNameFormatted(FULL, true, '$', BRACKETS));
-
+
// WORD format
- assertEquals("ArrayList<String>Array",
+ assertEquals("ArrayList<String>Array",
ci.getNameFormatted(SIMPLE, true, '$', WORD));
}
@@ -1798,7 +1792,7 @@ public class ClassInfo_Test extends TestBase {
StringBuilder sb = new StringBuilder("Type: ");
ci.appendNameFormatted(sb, FULL, false, '$', BRACKETS);
assertEquals("Type: java.lang.String", sb.toString());
-
+
// Verify it returns the same StringBuilder for chaining
StringBuilder result = ci.appendNameFormatted(sb,
SIMPLE, false, '$', BRACKETS);
assertSame(sb, result);
@@ -1808,40 +1802,40 @@ public class ClassInfo_Test extends TestBase {
@Test
void a12_equivalentMethods() {
ClassInfo ci = ClassInfo.of(String.class);
-
+
// getName() equivalent
- assertEquals(ci.getName(),
+ assertEquals(ci.getName(),
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
-
+
// getSimpleName() equivalent
- assertEquals(ci.getNameSimple(),
+ assertEquals(ci.getNameSimple(),
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
}
@Test
void a13_innerClassArrays() {
ClassInfo ci = ClassInfo.of(Map.Entry[].class);
-
+
// SIMPLE
- assertEquals("Entry[]",
+ assertEquals("Entry[]",
ci.getNameFormatted(SIMPLE, false, '$',
BRACKETS));
- assertEquals("EntryArray",
+ assertEquals("EntryArray",
ci.getNameFormatted(SIMPLE, false, '$', WORD));
-
+
// SHORT
- assertEquals("Map$Entry[]",
+ assertEquals("Map$Entry[]",
ci.getNameFormatted(SHORT, false, '$',
BRACKETS));
- assertEquals("Map.Entry[]",
+ assertEquals("Map.Entry[]",
ci.getNameFormatted(SHORT, false, '.',
BRACKETS));
- assertEquals("Map$EntryArray",
+ assertEquals("Map$EntryArray",
ci.getNameFormatted(SHORT, false, '$', WORD));
-
+
// FULL
- assertEquals("java.util.Map$Entry[]",
+ assertEquals("java.util.Map$Entry[]",
ci.getNameFormatted(FULL, false, '$',
BRACKETS));
- assertEquals("java.util.Map.Entry[]",
+ assertEquals("java.util.Map.Entry[]",
ci.getNameFormatted(FULL, false, '.',
BRACKETS));
- assertEquals("java.util.Map$EntryArray",
+ assertEquals("java.util.Map$EntryArray",
ci.getNameFormatted(FULL, false, '$', WORD));
}