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 34a1d3cfa4 Marshall module improvements
34a1d3cfa4 is described below
commit 34a1d3cfa484aa0756eedb5bb60650c2bebd8a84
Author: James Bognar <[email protected]>
AuthorDate: Fri Dec 12 13:10:32 2025 -0500
Marshall module improvements
---
.../juneau/commons/reflect/TypeVariables.java | 323 +++++++++++++++++++++
.../apache/juneau/commons/utils/ClassUtils.java | 67 -----
.../main/java/org/apache/juneau/BeanContext.java | 82 +-----
.../src/main/java/org/apache/juneau/BeanMeta.java | 2 +-
.../java/org/apache/juneau/BeanPropertyMeta.java | 2 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 2 +-
6 files changed, 332 insertions(+), 146 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/TypeVariables.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/TypeVariables.java
new file mode 100644
index 0000000000..04f8832280
--- /dev/null
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/TypeVariables.java
@@ -0,0 +1,323 @@
+/*
+ * 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.commons.reflect;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.commons.utils.*;
+
+/**
+ * Encapsulates type variable implementations for a class hierarchy.
+ *
+ * <p>
+ * This class provides a type-safe wrapper around the map returned by {@link
ClassUtils#findTypeVarImpls(Type)},
+ * using {@link List} instead of arrays for better API consistency.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Given class hierarchy:</jc>
+ * <jk>public static class</jk> BeanA<T> {
+ * <jk>public</jk> T <jf>x</jf>;
+ * }
+ * <jk>public static class</jk> BeanB <jk>extends</jk>
BeanA<Integer> {...}
+ *
+ * <jc>// Create TypeVariables from BeanB</jc>
+ * TypeVariables <jv>tv</jv> =
TypeVariables.<jsm>of</jsm>(BeanB.<jk>class</jk>);
+ *
+ * <jc>// Get the type variable implementation for BeanA</jc>
+ * Class<?> <jv>impl</jv> = <jv>tv</jv>.get(BeanA.<jk>class</jk>,
0); <jc>// Returns Integer.class</jc>
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5>
+ * <ul>
+ * <li>{@link ClassUtils#findTypeVarImpls(Type)}
+ * </ul>
+ */
+public class TypeVariables {
+
+ private final Map<Class<?>,List<Class<?>>> map;
+
+ /**
+ * Creates an empty TypeVariables instance.
+ *
+ * @return A new empty TypeVariables instance.
+ */
+ public static TypeVariables empty() {
+ return new TypeVariables(new LinkedHashMap<>());
+ }
+
+ /**
+ * Creates a TypeVariables instance from the specified type.
+ *
+ * <p>
+ * Recursively determines the classes represented by parameterized
types in the class hierarchy of the specified
+ * type.
+ *
+ * <p>
+ * For example, given the following classes...
+ * <p class='bjava'>
+ * <jk>public static class</jk> BeanA<T> {
+ * <jk>public</jk> T <jf>x</jf>;
+ * }
+ * <jk>public static class</jk> BeanB <jk>extends</jk>
BeanA<Integer> {...}
+ * </p>
+ * <p>
+ * ...calling this method on {@code BeanB.class} will return a
{@link TypeVariables} instance indicating
+ * that the {@code T} parameter on the BeanA class is implemented
with an {@code Integer}:
+ * <p class='bcode'>
+ * {BeanA.class:[Integer.class]}
+ * </p>
+ *
+ * <h5 class='section'>Known Limitations:</h5>
+ * <p>
+ * This code doesn't currently properly handle the following situation
with nested generic bounds:
+ * <p class='bjava'>
+ * <jk>public static class</jk> BeanB<T <jk>extends</jk>
Number> <jk>extends</jk> BeanA<>;
+ * <jk>public static class</jk> BeanC <jk>extends</jk>
BeanB<Integer>;
+ * </p>
+ *
+ * <p>
+ * When called on {@code BeanC}, the type variable will be resolved as
{@code Number}, not {@code Integer}.
+ * This limitation exists because the intermediate type parameter bound
information is lost during type resolution
+ *
+ * @param type The type to analyze.
+ * @return A new TypeVariables instance.
+ */
+ public static TypeVariables of(Type type) {
+ Map<Class<?>, List<Class<?>>> m = new LinkedHashMap<>();
+ findTypeVarImpls(type, m);
+ return fromMap(m);
+ }
+
+ /**
+ * Recursively determines the classes represented by parameterized
types in the class hierarchy.
+ *
+ * @param t The type we're recursing.
+ * @param m Where the results are loaded.
+ */
+ private static void findTypeVarImpls(Type t, Map<Class<?>,
List<Class<?>>> m) {
+ if (t instanceof Class<?> c) {
+ findTypeVarImpls(c.getGenericSuperclass(), m);
+ for (var ci : c.getGenericInterfaces())
+ findTypeVarImpls(ci, m);
+ } else if (t instanceof ParameterizedType t2) {
+ var rt = t2.getRawType();
+ if (rt instanceof Class<?> rt2) {
+ var gImpls = t2.getActualTypeArguments();
+ var gTypes = new
ArrayList<Class<?>>(gImpls.length);
+ for (var gt : gImpls) {
+ if (gt instanceof Class<?> c)
+ gTypes.add(c);
+ else if (gt instanceof TypeVariable<?>
tv) {
+ for (var upperBound :
tv.getBounds())
+ if (upperBound
instanceof Class upperBound2) {
+
gTypes.add(upperBound2);
+ break;
+ }
+ }
+ }
+ m.put(rt2, gTypes);
+ findTypeVarImpls(t2.getRawType(), m);
+ }
+ }
+ }
+
+ /**
+ * Creates a TypeVariables instance from a map of lists.
+ *
+ * <p>
+ * Package-private factory method for internal use.
+ *
+ * @param map The map of class to type variable implementations.
+ * @return A new TypeVariables instance.
+ */
+ static TypeVariables fromMap(Map<Class<?>, List<Class<?>>> map) {
+ return new TypeVariables(map);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param map The map of class to type variable implementations.
+ */
+ TypeVariables(Map<Class<?>, List<Class<?>>> map) {
+ this.map = map;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this instance contains type variable
implementations for the specified class.
+ *
+ * @param clazz The class to check.
+ * @return <jk>true</jk> if type variable implementations exist for the
class.
+ */
+ public boolean containsKey(Class<?> clazz) {
+ return map.containsKey(clazz);
+ }
+
+ /**
+ * Returns the list of type variable implementations for the specified
class.
+ *
+ * @param clazz The class to get type variable implementations for.
+ * @return The list of type variable implementations, or <jk>null</jk>
if not found.
+ */
+ public List<Class<?>> get(Class<?> clazz) {
+ return map.get(clazz);
+ }
+
+ /**
+ * Returns the type variable implementation at the specified index for
the given class.
+ *
+ * @param clazz The class to get the type variable implementation for.
+ * @param index The zero-based index of the type variable.
+ * @return The type variable implementation, or <jk>null</jk> if not
found or index is out of bounds.
+ */
+ public Class<?> get(Class<?> clazz, int index) {
+ var list = map.get(clazz);
+ if (list == null || index < 0 || index >= list.size())
+ return null;
+ return list.get(index);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this instance is empty (contains no type
variable implementations).
+ *
+ * @return <jk>true</jk> if this instance is empty.
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * Returns the number of classes with type variable implementations.
+ *
+ * @return The number of classes.
+ */
+ public int size() {
+ return map.size();
+ }
+
+ /**
+ * Converts a {@link Type} to a {@link Class} if possible.
+ *
+ * <p>
+ * This method resolves generic types, parameterized types, generic
arrays, and type variables
+ * to their concrete class representations.
+ *
+ * <h5 class='section'>Type Resolution:</h5>
+ * <ul>
+ * <li><b>Class</b> - Returns the class as-is
+ * <li><b>ParameterizedType</b> - Returns the raw type class
+ * <li><b>GenericArrayType</b> - Creates and returns the array
class
+ * <li><b>TypeVariable</b> - Resolves using type variable
implementations, or returns <jk>null</jk> if not found
+ * </ul>
+ *
+ * @param t The type to resolve.
+ * @return The resolved class, or <jk>null</jk> if the type cannot be
resolved to a class.
+ */
+ public Class<?> resolve(Type t) {
+ if (t instanceof Class<?> c)
+ return c;
+
+ if (t instanceof ParameterizedType pt)
+ // A parameter (e.g. <String>.
+ return (Class<?>)pt.getRawType();
+
+ if (t instanceof GenericArrayType gat) {
+ // An array parameter (e.g. <byte[]>).
+ var gatct = gat.getGenericComponentType();
+
+ if (gatct instanceof Class<?> gatct2)
+ return Array.newInstance(gatct2, 0).getClass();
+
+ if (gatct instanceof ParameterizedType gatct3)
+ return
Array.newInstance((Class<?>)gatct3.getRawType(), 0).getClass();
+
+ if (gatct instanceof GenericArrayType gatct4) {
+ var resolved = resolve(gatct4);
+ return resolved != null ?
Array.newInstance(resolved, 0).getClass() : null;
+ }
+
+ return null;
+
+ } else if (t instanceof TypeVariable<?> tv) {
+ String varName = tv.getName();
+ int varIndex = -1;
+ var gc = (Class<?>)tv.getGenericDeclaration();
+ TypeVariable<?>[] tvv = gc.getTypeParameters();
+ for (var i = 0; i < tvv.length; i++) {
+ if (tvv[i].getName().equals(varName)) {
+ varIndex = i;
+ }
+ }
+ if (varIndex != -1) {
+ // If we couldn't find a type variable
implementation, that means
+ // the type was defined at runtime (e.g. Bean b
= new Bean<Foo>();)
+ // in which case the type is lost through
erasure.
+ // Assume java.lang.Object as the type.
+ return get(gc, varIndex);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Static convenience method to resolve a type using type variable
implementations.
+ *
+ * <p>
+ * This method can resolve types even when {@code typeVars} is
<jk>null</jk> for non-type-variable types
+ * (e.g., {@link Class}, {@link ParameterizedType}, {@link
GenericArrayType}).
+ * For {@link TypeVariable} types, {@code typeVars} must not be
<jk>null</jk>.
+ *
+ * @param type The type to resolve.
+ * @param typeVars The type variable implementations, or <jk>null</jk>.
+ * @return The resolved class, or <jk>null</jk> if the type cannot be
resolved.
+ */
+ public static Class<?> resolve(Type type, TypeVariables typeVars) {
+ if (type instanceof Class<?> c)
+ return c;
+
+ if (type instanceof ParameterizedType pt)
+ return (Class<?>)pt.getRawType();
+
+ if (type instanceof GenericArrayType gat) {
+ var gatct = gat.getGenericComponentType();
+
+ if (gatct instanceof Class<?> gatct2)
+ return Array.newInstance(gatct2, 0).getClass();
+
+ if (gatct instanceof ParameterizedType gatct3)
+ return
Array.newInstance((Class<?>)gatct3.getRawType(), 0).getClass();
+
+ if (gatct instanceof GenericArrayType gatct4) {
+ var resolved = resolve(gatct4, typeVars);
+ return resolved != null ?
Array.newInstance(resolved, 0).getClass() : null;
+ }
+
+ return null;
+ }
+
+ if (type instanceof TypeVariable<?> tv) {
+ if (typeVars == null)
+ return null;
+ return typeVars.resolve(type);
+ }
+
+ return null;
+ }
+}
+
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
index fd06112a93..b1f09277b4 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
@@ -524,73 +524,6 @@ public class ClassUtils {
return null;
}
- /**
- * Recursively determines the classes represented by parameterized
types in the class hierarchy of the specified
- * type, and puts the results in the specified map.
- *
- * <p>
- * For example, given the following classes...
- * <p class='bjava'>
- * <jk>public static class</jk> BeanA<T> {
- * <jk>public</jk> T <jf>x</jf>;
- * }
- * <jk>public static class</jk> BeanB <jk>extends</jk>
BeanA<Integer> {...}
- * </p>
- * <p>
- * ...calling this method on {@code BeanB.class} will load the
following data into {@code m} indicating
- * that the {@code T} parameter on the BeanA class is implemented
with an {@code Integer}:
- * <p class='bcode'>
- * {BeanA.class:[Integer.class]}
- * </p>
- *
- * <h5 class='section'>Known Limitations:</h5>
- * <p>
- * This code doesn't currently properly handle the following situation
with nested generic bounds:
- * <p class='bjava'>
- * <jk>public static class</jk> BeanB<T <jk>extends</jk>
Number> <jk>extends</jk> BeanA<>;
- * <jk>public static class</jk> BeanC <jk>extends</jk>
BeanB<Integer>;
- * </p>
- *
- * <p>
- * When called on {@code BeanC}, the type variable will be resolved as
{@code Number}, not {@code Integer}.
- * This limitation exists because the intermediate type parameter bound
information is lost during type resolution
- *
- * @param t The type we're recursing.
- * @param m Where the results are loaded.
- * @return A map of class to type variable implementations.
- */
- public static Map<Class<?>,Class<?>[]> findTypeVarImpls(Type t) {
- Map<Class<?>,Class<?>[]> m = new LinkedHashMap<>();
- return findTypeVarImpls(t, m);
- }
-
- private static Map<Class<?>,Class<?>[]> findTypeVarImpls(Type t,
Map<Class<?>,Class<?>[]> m) {
- if (t instanceof Class<?> c) {
- findTypeVarImpls(c.getGenericSuperclass(), m);
- for (var ci : c.getGenericInterfaces())
- findTypeVarImpls(ci, m);
- } else if (t instanceof ParameterizedType t2) {
- var rt = t2.getRawType();
- if (rt instanceof Class) {
- Type[] gImpls = t2.getActualTypeArguments();
- Class<?>[] gTypes = new Class[gImpls.length];
- for (var i = 0; i < gImpls.length; i++) {
- Type gt = gImpls[i];
- if (gt instanceof Class<?> c)
- gTypes[i] = c;
- else if (gt instanceof TypeVariable<?>
tv) {
- for (var upperBound :
tv.getBounds())
- if (upperBound
instanceof Class upperBound2)
- gTypes[i] =
upperBound2;
- }
- }
- m.put((Class<?>)rt, gTypes);
- findTypeVarImpls(t2.getRawType(), m);
- }
- }
- return m;
- }
-
private static boolean canAddTo(Class<?> c) {
var b = MODIFIABLE_COLLECTION_TYPES.get(c);
if (b == null) {
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 7ce81e7015..d8c475b33d 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -3679,18 +3679,6 @@ public class BeanContext extends Context {
*/
public final String getBeanTypePropertyName() { return
typePropertyName; }
- /**
- * Construct a {@code ClassMeta} wrapper around a {@link Class} object.
- *
- * @param <T> The class type being wrapped.
- * @param type The class to resolve.
- * @return
- * If the class is not an array, returns a cached {@link
ClassMeta} object.
- * Otherwise, returns a new {@link ClassMeta} object every time.
- */
- public final <T> ClassMeta<T> getClassMeta(Class<T> type) {
- return getClassMeta(type, true);
- }
/**
* Used to resolve <c>ClassMetas</c> of type <c>Collection</c> and
<c>Map</c> that have
@@ -4216,7 +4204,7 @@ public class BeanContext extends Context {
* Can be <jk>null</jk> if the information is not known.
* @return The new {@code ClassMeta} object wrapped around the type.
*/
- protected final <T> ClassMeta<T> resolveClassMeta(AnnotationInfo<Beanp>
p, ClassInfo ci, Map<Class<?>,Class<?>[]> typeVarImpls) {
+ protected final <T> ClassMeta<T> resolveClassMeta(AnnotationInfo<Beanp>
p, ClassInfo ci, TypeVariables typeVarImpls) {
ClassMeta<T> cm = resolveClassMeta(ci, typeVarImpls);
ClassMeta<T> cm2 = cm;
@@ -4310,13 +4298,11 @@ public class BeanContext extends Context {
*
* @param <T> The class type being wrapped.
* @param type The class to resolve.
- * @param waitForInit
- * When enabled, wait for the ClassMeta constructor to finish
before returning.
* @return
* If the class is not an array, returns a cached {@link
ClassMeta} object.
* Otherwise, returns a new {@link ClassMeta} object every time.
*/
- final <T> ClassMeta<T> getClassMeta(Class<T> type, boolean waitForInit)
{
+ public final <T> ClassMeta<T> getClassMeta(Class<T> type) {
// This can happen if we have transforms defined against String
or Object.
if (cmCache == null)
@@ -4332,67 +4318,11 @@ public class BeanContext extends Context {
cm = new ClassMeta<>(type, this);
}
}
-// if (waitForInit)
-// cm.waitForInit();
return cm;
}
- /**
- * Convert a Type to a Class if possible.
- * Return null if not possible.
- */
- final Class resolve(Type t, Map<Class<?>,Class<?>[]> typeVarImpls) {
-
- if (t instanceof Class t2)
- return t2;
-
- if (t instanceof ParameterizedType t3)
- // A parameter (e.g. <String>.
- return (Class)t3.getRawType();
-
- if (t instanceof GenericArrayType t4) {
- // An array parameter (e.g. <byte[]>).
- var gatct = t4.getGenericComponentType();
-
- if (gatct instanceof Class<?> gatct2)
- return Array.newInstance(gatct2, 0).getClass();
-
- if (gatct instanceof ParameterizedType gatct3)
- return
Array.newInstance((Class)gatct3.getRawType(), 0).getClass();
-
- if (gatct instanceof GenericArrayType gatct4)
- return Array.newInstance(resolve(gatct4,
typeVarImpls), 0).getClass();
-
- return null;
-
- } else if (t instanceof TypeVariable t4) {
- if (nn(typeVarImpls)) {
- String varName = t4.getName();
- int varIndex = -1;
- var gc = (Class)t4.getGenericDeclaration();
- TypeVariable[] tvv = gc.getTypeParameters();
- for (var i = 0; i < tvv.length; i++) {
- if (tvv[i].getName().equals(varName)) {
- varIndex = i;
- }
- }
- if (varIndex != -1) {
-
- // If we couldn't find a type variable
implementation, that means
- // the type was defined at runtime
(e.g. Bean b = new Bean<Foo>();)
- // in which case the type is lost
through erasure.
- // Assume java.lang.Object as the type.
- if (! typeVarImpls.containsKey(gc))
- return null;
-
- return typeVarImpls.get(gc)[varIndex];
- }
- }
- }
- return null;
- }
- final ClassMeta resolveClassMeta(Type o, Map<Class<?>,Class<?>[]>
typeVarImpls) {
+ final ClassMeta resolveClassMeta(Type o, TypeVariables typeVars) {
if (o == null)
return null;
@@ -4411,10 +4341,10 @@ public class BeanContext extends Context {
// Handle ClassInfo by extracting the underlying Type
if (o instanceof ClassInfo ci) {
- return resolveClassMeta(ci.innerType(), typeVarImpls);
+ return resolveClassMeta(ci.innerType(), typeVars);
}
- Class c = resolve(o, typeVarImpls);
+ Class<?> c = TypeVariables.resolve(o, typeVars);
// This can happen when trying to resolve the "E getFirst()"
method on LinkedList, whose type is a TypeVariable
// These should just resolve to Object.
@@ -4444,7 +4374,7 @@ public class BeanContext extends Context {
if (rawType.isArray()) {
if (o instanceof GenericArrayType o2) {
- ClassMeta elementType =
resolveClassMeta(o2.getGenericComponentType(), typeVarImpls);
+ ClassMeta elementType =
resolveClassMeta(o2.getGenericComponentType(), typeVars);
return new ClassMeta(rawType, null, null,
elementType);
}
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
index 59c5a67d0d..e63daf772e 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java
@@ -490,7 +490,7 @@ public class BeanMeta<T> {
bms.stream().filter(x -> eq(x.methodType,
EXTRAKEYS)).forEach(x ->
normalProps.get(x.propertyName).setExtraKeys(info(x.method)));
}
- var typeVarImpls = ClassUtils.findTypeVarImpls(c);
+ var typeVarImpls = TypeVariables.of(c);
// Eliminate invalid properties, and set the contents
of getterProps and setterProps.
var readOnlyProps = bfo.map(x ->
x.getReadOnlyProperties()).orElse(sete());
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 bb0fc00d3c..fc70ab6898 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
@@ -280,7 +280,7 @@ public class BeanPropertyMeta implements
Comparable<BeanPropertyMeta> {
* @return <jk>true</jk> if this property is valid, <jk>false</jk>
otherwise.
* @throws Exception If validation fails.
*/
- public boolean validate(BeanContext bc, BeanRegistry
parentBeanRegistry, Map<Class<?>,Class<?>[]> typeVarImpls, Set<String> bpro,
Set<String> bpwo) throws Exception {
+ public boolean validate(BeanContext bc, BeanRegistry
parentBeanRegistry, TypeVariables typeVarImpls, Set<String> bpro, Set<String>
bpwo) throws Exception {
var bdClasses = list();
var ap = bc.getAnnotationProvider();
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 fac8de4f8d..e13a517c89 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
@@ -1332,7 +1332,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
if (beanContext == null)
return null;
if (cat.is(ARRAY)) {
- return
beanContext.getClassMeta(inner().getComponentType(), false);
+ return
beanContext.getClassMeta(inner().getComponentType());
} else if (cat.is(COLLECTION) || is(Optional.class)) {
// If this is a COLLECTION, see if it's parameterized
(e.g. AddressBook extends LinkedList<Person>)
var parameters = beanContext.findParameters(inner(),
inner());