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 340c7448f5 Marshall module improvements
340c7448f5 is described below

commit 340c7448f50df96dcd43792d7f082b244c912962
Author: James Bognar <[email protected]>
AuthorDate: Mon Dec 8 12:19:40 2025 -0500

    Marshall module improvements
---
 .../src/main/java/org/apache/juneau/ClassMeta.java | 58 ++++++++++++----------
 1 file changed, 32 insertions(+), 26 deletions(-)

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 d8e6fe8b36..f8f04078f7 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
@@ -155,7 +155,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
        private final OptionalSupplier<MethodInfo> fromStringMethod;            
   // Static fromString(String) or equivalent method
        private final OptionalSupplier<ClassInfoTyped<? extends T>> implClass;  
   // The implementation class to use if this is an interface.
        private final OptionalSupplier<InvocationHandler> 
proxyInvocationHandler;  // The invocation handler for this class (if it has 
one).
-       private final ClassMeta<?> keyType;                                     
   // If MAP, the key class type.
+       private final Supplier<Tuple2<ClassMeta<?>,ClassMeta<?>>> 
keyValueTypes;   // Key and value types for MAP types.
        private final SimpleReadWriteLock lock = new SimpleReadWriteLock(false);
        private final OptionalSupplier<MarshalledFilter> marshalledFilter;
        private final Supplier<Property<T,Object>> nameProperty;                
   // The method to set the name on an object (if it has one).
@@ -166,7 +166,6 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
        private final OptionalSupplier<ConstructorInfo> stringConstructor;      
   // The X(String) constructor (if it has one).
        private final ObjectSwap<T,?>[] swaps;                                  
   // The object POJO swaps associated with this bean (if it has any).
        private final Map<Class<?>,Mutater<T,?>> toMutaters = new 
ConcurrentHashMap<>();
-       private final ClassMeta<?> valueType;                                   
   // If MAP, the value class type.
        private final Supplier<Tuple2<BeanMeta<T>,String>> beanMeta;
 
        /**
@@ -260,21 +259,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                        implClass = memoize(()->findImplClass());
 
                        var _elementType = (ClassMeta<?>)null;
-                       var _keyType = (ClassMeta<?>)null;
-                       var _valueType = (ClassMeta<?>)null;
-
                        if (cat.is(ARRAY)) {
                                _elementType = 
beanContext.getClassMeta(inner().getComponentType(), false);
-                       } else if (cat.is(MAP) && ! cat.is(BEANMAP)) {
-                               // If this is a MAP, see if it's parameterized 
(e.g. AddressBook extends HashMap<String,Person>)
-                               var parameters = 
beanContext.findParameters(inner(), inner());
-                               if (nn(parameters) && parameters.length == 2) {
-                                       _keyType = parameters[0];
-                                       _valueType = parameters[1];
-                               } else {
-                                       _keyType = 
beanContext.getClassMeta(Object.class);
-                                       _valueType = 
beanContext.getClassMeta(Object.class);
-                               }
                        } 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());
@@ -284,11 +270,10 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                                        _elementType = 
beanContext.getClassMeta(Object.class);
                                }
                        }
+                       this.elementType = _elementType;
+                       this.keyValueTypes = memoize(()->findKeyValueTypes());
 
                        this.beanMeta = memoize(()->findBeanMeta());
-                       this.keyType = _keyType;
-                       this.valueType = _valueType;
-                       this.elementType = _elementType;
                        this.enumValues = memoize(()->findEnumValues());
                        this.dictionaryName = 
memoize(()->findBeanDictionaryName());
 
@@ -318,6 +303,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                        this.childSwaps = childSwaps == null ? null : 
Arrays.asList(childSwaps);
                        this.childUnswapMap = 
Cache.<Class<?>,ObjectSwap<?,?>>create().supplier(x -> findUnswap(x)).build();
                        this.childSwapMap = 
Cache.<Class<?>,ObjectSwap<?,?>>create().supplier(x -> findSwap(x)).build();
+
                        this.args = null;
                        this.stringMutater = Mutaters.get(String.class, 
inner());
                }
@@ -349,8 +335,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                this.cat = new Categories().set(ARGS);
                this.beanContext = null;
                this.elementType = null;
-               this.keyType = null;
-               this.valueType = null;
+               this.keyValueTypes = memoize(()->findKeyValueTypes());
                this.proxyInvocationHandler = null;
                this.beanMeta = memoize(()->findBeanMeta());
                this.swaps = null;
@@ -386,8 +371,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                this.fromStringMethod = mainType.fromStringMethod;
                this.beanContext = mainType.beanContext;
                this.elementType = elementType;
-               this.keyType = keyType;
-               this.valueType = valueType;
+               this.keyValueTypes = (keyType != null || valueType != null) ? 
memoize(()->Tuple2.of(keyType, valueType)) : mainType.keyValueTypes;
                this.proxyInvocationHandler = mainType.proxyInvocationHandler;
                this.beanMeta = mainType.beanMeta;
                this.swaps = mainType.swaps;
@@ -680,7 +664,9 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
         *
         * @return The key class type, or <jk>null</jk> if this class is not a 
Map.
         */
-       public ClassMeta<?> getKeyType() { return keyType; }
+       public ClassMeta<?> getKeyType() {
+               return keyValueTypes.get().getA();
+       }
 
        /**
         * Returns the method or field annotated with {@link NameProperty 
@NameProperty}.
@@ -861,7 +847,9 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
         *
         * @return The value class type, or <jk>null</jk> if this class is not 
a Map.
         */
-       public ClassMeta<?> getValueType() { return valueType; }
+       public ClassMeta<?> getValueType() {
+               return keyValueTypes.get().getB();
+       }
 
        /**
         * Returns <jk>true</jk> if this class has a transform associated with 
it that allows it to be created from an InputStream.
@@ -1388,6 +1376,18 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                return BeanMeta.create(this, beanFilter.get(), null, 
implClass.map(x -> x.getPublicConstructor(x2 -> 
x2.hasNumParameters(0)).orElse(null)).orElse(null));
        }
 
+       private Tuple2<ClassMeta<?>,ClassMeta<?>> findKeyValueTypes() {
+               if (cat.is(MAP) && ! cat.is(BEANMAP)) {
+                       // If this is a MAP, see if it's parameterized (e.g. 
AddressBook extends HashMap<String,Person>)
+                       var parameters = beanContext.findParameters(inner(), 
inner());
+                       if (nn(parameters) && parameters.length == 2) {
+                               return Tuple2.of(parameters[0], parameters[1]);
+                       }
+                       return 
Tuple2.of(beanContext.getClassMeta(Object.class), 
beanContext.getClassMeta(Object.class));
+               }
+               return Tuple2.of(null,null);
+       }
+
        @SuppressWarnings("unchecked")
        private BuilderSwap<T,?> findBuilderSwap() {
                var bc = beanContext;
@@ -1746,8 +1746,14 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
                        return elementType.toString(sb, 
simple).append('[').append(']');
                if (cat.is(BEANMAP))
                        return 
sb.append(cn(BeanMap.class)).append('<').append(n).append('>');
-               if (cat.is(MAP))
-                       return sb.append(n).append(keyType.isObject() && 
valueType.isObject() ? "" : "<" + keyType.toString(simple) + "," + 
valueType.toString(simple) + ">");
+               if (cat.is(MAP)) {
+                       var kvTypes = keyValueTypes.get();
+                       var kt = kvTypes.getA();
+                       var vt = kvTypes.getB();
+                       if (kt != null && vt != null && kt.isObject() && 
vt.isObject())
+                               return sb.append(n);
+                       return sb.append(n).append('<').append(kt == null ? "?" 
: kt.toString(simple)).append(',').append(vt == null ? "?" : 
vt.toString(simple)).append('>');
+               }
                if (cat.is(COLLECTION) || is(Optional.class))
                        return sb.append(n).append(elementType.isObject() ? "" 
: "<" + elementType.toString(simple) + ">");
                return sb.append(n);

Reply via email to