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

commit becca0e0799761c2ed2e831b50fc0291580d4f1d
Author: James Bognar <[email protected]>
AuthorDate: Fri Dec 12 19:41:01 2025 -0500

    Marshall module improvements
---
 .../main/java/org/apache/juneau/BeanContext.java   |   1 +
 .../src/main/java/org/apache/juneau/Context.java   | 170 ++++++++++++++-------
 .../org/apache/juneau/ComboRoundTrip_Tester.java   |   4 +-
 .../org/apache/juneau/ComboSerialize_Tester.java   |   2 +-
 4 files changed, 120 insertions(+), 57 deletions(-)

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 4d7c4e450b..664fb21c94 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
@@ -165,6 +165,7 @@ import org.apache.juneau.utils.*;
  */
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public class BeanContext extends Context {
+
        /**
         * Builder class.
         */
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 a213283717..1f11fa9150 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
@@ -25,7 +25,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
 import java.lang.annotation.*;
 import java.lang.reflect.*;
 import java.util.*;
-import java.util.concurrent.*;
 import java.util.function.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.collections.*;
@@ -74,19 +73,104 @@ import org.apache.juneau.xml.annotation.*;
  *
  */
 public abstract class Context {
+
+       /*
+        * Cache of static <c>create</c> methods that return builder instances 
for context classes.
+        *
+        * <p>
+        * This cache stores {@link MethodInfo} objects for public static 
methods named <c>create</c> that return
+        * builder objects. The methods are discovered by:
+        * <ol>
+        *      <li>Finding public constructors that take a single parameter 
(the builder type)
+        *      <li>Looking for a matching static <c>create</c> method that 
returns the builder type
+        *      <li>Caching the result for future lookups
+        * </ol>
+        *
+        * <p>
+        * Used by {@link #createBuilder(Class)} to efficiently locate and 
invoke builder creation methods.
+        *
+        * @see #createBuilder(Class)
+        */
+       private static final Cache<Class<?>,MethodInfo> BUILDER_CREATE_METHODS 
= Cache.<Class<?>,MethodInfo>create()
+               .supplier(type -> {
+                       var c = info(type);
+                       // @formatter:off
+                       return c.getPublicConstructors().stream()
+                               .filter(ci -> ci.hasNumParameters(1) && ! 
ci.getParameter(0).getParameterType().is(type))
+                               .map(ci -> c.getPublicMethod(
+                                       x -> x.isStatic()
+                                       && x.isNotDeprecated()
+                                       && x.hasName("create")
+                                       && 
x.hasReturnType(ci.getParameter(0).getParameterType())
+                                       ).orElse(null))
+                               .filter(Objects::nonNull)
+                               .findFirst()
+                               .orElseThrow(() -> rex("Could not find builder 
create method on class {0}", cn(type)));
+                       // @formatter:on
+               })
+               .build();
+
+       /*
+        * Cache of public constructors on context classes that accept builder 
instances.
+        *
+        * <p>
+        * This cache stores {@link ConstructorInfo} objects for public 
constructors on context classes that take
+        * a single parameter of the builder type. The constructor is 
discovered by:
+        * <ol>
+        *      <li>Finding public constructors on the context type that take 
exactly one parameter
+        *      <li>Matching constructors where the parameter type is a parent 
of (or equal to) the builder type
+        *      <li>Caching the result for future lookups
+        * </ol>
+        *
+        * <p>
+        * Used by {@link Builder#getContextConstructor()} to efficiently 
locate and invoke context constructors
+        * when building context instances from builders.
+        *
+        * @see Builder#getContextConstructor()
+        * @see Builder#innerBuild()
+        */
+       private static final Cache2<Class<? extends Context>,Class<? extends 
Builder>,ConstructorInfo> CONTEXT_CONSTRUCTORS = Cache2.<Class<? extends 
Context>,Class<? extends Builder>,ConstructorInfo>create()
+               .supplier((cacheType, builderType) -> {
+                       var ct = info(cacheType);
+                       var bt = info(builderType);
+                       return ct
+                               .getPublicConstructor(x -> 
x.hasNumParameters(1) && 
x.getParameter(0).getParameterType().isParentOf(builderType))
+                               .orElseThrow(() -> rex("Public constructor not 
found: {0}({1})", ct.getName(), bt.getName()));
+               })
+               .build();
+
+
+       /*
+        * Default annotation provider instance for finding annotations on 
classes, methods, fields, and constructors.
+        *
+        * <p>
+        * This is a static reference to {@link AnnotationProvider#INSTANCE}, 
used by the {@link Builder#traverse(AnnotationWorkList, Object)}
+        * method to discover annotations that can be applied to context 
builders.
+        *
+        * <p>
+        * The annotation provider supports:
+        * <ul>
+        *      <li>Finding annotations on classes, methods, fields, and 
constructors
+        *      <li>Traversing class hierarchies (parent-to-child or 
child-to-parent order)
+        *      <li>Supporting runtime annotations (annotations added 
programmatically)
+        *      <li>Caching results for performance
+        * </ul>
+        *
+        * @see AnnotationProvider
+        * @see Builder#traverse(AnnotationWorkList, Object)
+        */
+       private static final AnnotationProvider AP = 
AnnotationProvider.INSTANCE;
+
        /**
         * Builder class.
         */
        public abstract static class Builder {
 
-               private static final AnnotationProvider AP = 
AnnotationProvider.INSTANCE;
-               private static final Map<Class<?>,ConstructorInfo> 
CONTEXT_CONSTRUCTORS = new ConcurrentHashMap<>();
-
-               boolean debug;
-               Class<? extends Context> type;
-               Context impl;
-               List<Annotation> annotations;
-               Cache<HashKey,? extends Context> cache;
+               private boolean debug;
+               private Class<? extends Context> type;
+               private Context impl;
+               private List<Annotation> annotations;
+               private Cache<HashKey,? extends Context> cache;
 
                private final List<Object> builders = list();
                private final AnnotationWorkList applied = 
AnnotationWorkList.create();
@@ -354,17 +438,29 @@ public abstract class Context {
                }
 
                /**
-                * Apply a consumer to this builder.
+                * Returns this builder cast to the specified subtype if it is 
an instance of that type.
                 *
-                * @param <T> The builder subtype that this consumer can be 
applied to.
-                * @param subtype The builder subtype that this consumer can be 
applied to.
-                * @param consumer The consumer.
-                * @return This object.
+                * <p>
+                * This is a type-safe way to check if this builder is an 
instance of a specific builder subtype
+                * and cast it accordingly. Returns an empty {@link Optional} 
if this builder is not an instance
+                * of the specified subtype.
+                *
+                * <h5 class='section'>Example:</h5>
+                * <p class='bjava'>
+                *      Builder <jv>b</jv> = JsonSerializer.<jsm>create</jsm>();
+                *      Optional&lt;JsonSerializer.Builder&gt; 
<jv>jsonBuilder</jv> = 
<jv>b</jv>.asSubtype(JsonSerializer.Builder.<jk>class</jk>);
+                *      <jk>if</jk> (<jv>jsonBuilder</jv>.isPresent()) {
+                *              <jc>// Use JsonSerializer.Builder-specific 
methods</jc>
+                *              <jv>jsonBuilder</jv>.get().pretty();
+                *      }
+                * </p>
+                *
+                * @param <T> The builder subtype.
+                * @param subtype The builder subtype class to cast to.
+                * @return An {@link Optional} containing this builder cast to 
the subtype, or empty if not an instance.
                 */
-               public <T extends Builder> Builder apply(Class<T> subtype, 
Consumer<T> consumer) {
-                       if (subtype.isInstance(this))
-                               consumer.accept(subtype.cast(this));
-                       return this;
+               public <T extends Builder> Optional<T> asSubtype(Class<T> 
subtype) {
+                       return opt(subtype.isInstance(this) ? 
subtype.cast(this) : null);
                }
 
                /**
@@ -506,9 +602,7 @@ public abstract class Context {
                 * @return <jk>true</jk> if any of the annotations/appliers can 
be applied to this builder.
                 */
                public boolean canApply(AnnotationWorkList work) {
-                       var f = Flag.create();
-                       work.forEach(x -> builders.forEach(b -> 
f.setIf(x.canApply(b))));
-                       return f.isSet();
+                       return work.stream().anyMatch(x -> 
builders.stream().anyMatch(b -> x.canApply(b)));
                }
 
                /**
@@ -644,17 +738,7 @@ public abstract class Context {
                }
 
                private ConstructorInfo getContextConstructor() {
-                       var cci = CONTEXT_CONSTRUCTORS.get(type);
-                       if (cci == null) {
-                               // @formatter:off
-                               cci = info(type).getPublicConstructor(
-                                       x -> x.hasNumParameters(1)
-                                       && x.getParameter(0).canAccept(this)
-                                       ).orElseThrow(() -> rex("Public 
constructor not found: {0}({1})", cn(type), cn(this)));
-                               // @formatter:on
-                               CONTEXT_CONSTRUCTORS.put(type, cci);
-                       }
-                       return cci;
+                       return CONTEXT_CONSTRUCTORS.get(type, getClass());
                }
 
                private Context innerBuild() {
@@ -704,8 +788,6 @@ public abstract class Context {
                }
        }
 
-       private static final Map<Class<?>,MethodInfo> BUILDER_CREATE_METHODS = 
new ConcurrentHashMap<>();
-
        /**
         * Predicate for annotations that themselves are annotated with {@link 
ContextApply}.
         */
@@ -724,26 +806,6 @@ public abstract class Context {
        public static Builder createBuilder(Class<? extends Context> type) {
                try {
                        MethodInfo mi = BUILDER_CREATE_METHODS.get(type);
-                       if (mi == null) {
-                               var c = info(type);
-                               for (var ci : c.getPublicConstructors()) {
-                                       if (ci.hasNumParameters(1) && ! 
ci.getParameter(0).getParameterType().is(type)) {
-                                               // @formatter:off
-                                               mi = c.getPublicMethod(
-                                                       x -> x.isStatic()
-                                                       && x.isNotDeprecated()
-                                                       && x.hasName("create")
-                                                       && 
x.hasReturnType(ci.getParameter(0).getParameterType())
-                                                       ).orElse(null);
-                                               // @formatter:on
-                                               if (nn(mi))
-                                                       break;
-                                       }
-                               }
-                               if (mi == null)
-                                       throw rex("Could not find builder 
create method on class {0}", cn(type));
-                               BUILDER_CREATE_METHODS.put(type, mi);
-                       }
                        var b = (Builder)mi.invoke(null);
                        b.type(type);
                        return b;
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java 
b/juneau-utest/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
index 9098845bd9..dff4db37ee 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
@@ -188,7 +188,7 @@ public class ComboRoundTrip_Tester<T> {
                        if (x.getA().equals(BeanContext.Builder.class))
                                sb.beanContext((Consumer<BeanContext.Builder>) 
x.getB());
                        else if (x.getA().isInstance(sb))
-                               sb.apply(Serializer.Builder.class, 
(Consumer<Serializer.Builder>) x.getB());
+                               
sb.asSubtype(Serializer.Builder.class).ifPresent((Consumer<Serializer.Builder>) 
x.getB());
                });
                return sb.build();
        }
@@ -200,7 +200,7 @@ public class ComboRoundTrip_Tester<T> {
                        if (x.getA().equals(BeanContext.Builder.class))
                                pb.beanContext((Consumer<BeanContext.Builder>) 
x.getB());
                        else if (x.getA().isInstance(pb))
-                               pb.apply(Parser.Builder.class, 
(Consumer<Parser.Builder>) x.getB());
+                               
pb.asSubtype(Parser.Builder.class).ifPresent((Consumer<Parser.Builder>) 
x.getB());
                });
                return pb.build();
        }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/ComboSerialize_Tester.java 
b/juneau-utest/src/test/java/org/apache/juneau/ComboSerialize_Tester.java
index 230b4715bb..3b97425ca7 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/ComboSerialize_Tester.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ComboSerialize_Tester.java
@@ -147,7 +147,7 @@ public class ComboSerialize_Tester<T> {
                        if (x.getA().equals(BeanContext.Builder.class))
                                sb.beanContext((Consumer<BeanContext.Builder>) 
x.getB());
                        else if (x.getA().isInstance(sb))
-                               sb.apply(Serializer.Builder.class, 
(Consumer<Serializer.Builder>) x.getB());
+                               
sb.asSubtype(Serializer.Builder.class).ifPresent((Consumer<Serializer.Builder>) 
x.getB());
                });
                return sb.build();
        }

Reply via email to