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

commit 4f2f61c4cb677ed52c016ed61348a24c3dc7af32
Author: James Bognar <[email protected]>
AuthorDate: Wed May 13 08:36:32 2026 -0400

    refactor: lift BeanMeta constructor body + CharSequence parsing to commons 
SPI (TODO-5 Step 8b-ii Phase C tasks 3 + 4-deferred)
    
    Co-authored-by: Cursor <[email protected]>
---
 .../apache/juneau/commons/bean/BeanSession.java    |  32 +++
 .../src/main/java/org/apache/juneau/BeanMeta.java  | 210 +++---------------
 .../java/org/apache/juneau/BeanPropertyMeta.java   |   5 +-
 .../juneau/MarshalledBeanMetaInitializer.java      | 242 +++++++++++++++++++++
 .../juneau/MarshalledPropertyPostProcessor.java    |  76 +++++++
 .../java/org/apache/juneau/MarshallingSession.java |  34 +++
 todo/TODO-5-bean-runtime-types-to-commons.md       |  72 ++++--
 7 files changed, 478 insertions(+), 193 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanSession.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanSession.java
index 8b4b511079..5823355100 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanSession.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanSession.java
@@ -70,6 +70,38 @@ public interface BeanSession {
         */
        Object convertToMemberType(Object outer, Object value, Object 
targetType);
 
+       /**
+        * Parses the specified JSON-formatted character sequence into a {@link 
java.util.Map}.
+        *
+        * <p>
+        * Used by {@code BeanPropertyMeta.setPropertyValue} when a {@link 
CharSequence} value is supplied for a
+        * {@code Map}-typed property — the bean-modeling layer cannot 
reference the marshalling-side JSON parser
+        * directly, so the parse is delegated to the session via this SPI.
+        *
+        * <p>
+        * Implementations typically route through {@code 
JsonMap.ofJson(value).session(this)}.
+        *
+        * @param value The JSON-formatted character sequence to parse.  Must 
not be <jk>null</jk>.
+        * @return The parsed map.
+        */
+       java.util.Map<?,?> parseToMap(CharSequence value);
+
+       /**
+        * Parses the specified JSON-formatted character sequence into a {@link 
java.util.Collection}.
+        *
+        * <p>
+        * Used by {@code BeanPropertyMeta.setPropertyValue} when a {@link 
CharSequence} value is supplied for a
+        * {@code Collection}-typed property — the bean-modeling layer cannot 
reference the marshalling-side JSON
+        * parser directly, so the parse is delegated to the session via this 
SPI.
+        *
+        * <p>
+        * Implementations typically route through {@code new 
JsonList(value).setBeanSession(this)}.
+        *
+        * @param value The JSON-formatted character sequence to parse.  Must 
not be <jk>null</jk>.
+        * @return The parsed collection.
+        */
+       java.util.Collection<?> parseToList(CharSequence value);
+
        /**
         * Wraps the specified bean in a {@code BeanMap}.
         *
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 ae900b20f8..1136b1e611 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
@@ -40,9 +40,6 @@ import org.apache.juneau.commons.reflect.Visibility;
 import org.apache.juneau.commons.utils.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.commons.bean.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.swap.*;
 
 /**
  * Encapsulates all access to the properties of a bean class (like a souped-up 
{@link java.beans.BeanInfo}).
@@ -226,16 +223,16 @@ public class BeanMeta<T> {
                        if (bc.isNotABean(cm))
                                return notABean("Class matches exclude-class 
list");
 
-                       if (bc.isBeansRequireSerializable() && ! 
cm.isAssignableTo(Serializable.class) && ! ap.has(Marshalled.class, cm) && ! 
ap.has(org.apache.juneau.commons.bean.BeanType.class, cm))
+                       if (bc.isBeansRequireSerializable() && ! 
cm.isAssignableTo(Serializable.class) && ! ap.has(Marshalled.class, cm) && ! 
ap.has(BeanType.class, cm))
                                return notABean("Class is not serializable");
 
                        if (ap.has(BeanIgnore.class, cm))
                                return notABean("Class is annotated with 
@BeanIgnore");
 
-               if ((! bc.getBeanClassVisibility().isVisible(cm.getModifiers()) 
|| cm.isAnonymousClass()) && ! ap.has(Marshalled.class, cm) && ! 
ap.has(org.apache.juneau.commons.bean.BeanType.class, cm))
+               if ((! bc.getBeanClassVisibility().isVisible(cm.getModifiers()) 
|| cm.isAnonymousClass()) && ! ap.has(Marshalled.class, cm) && ! 
ap.has(BeanType.class, cm))
                        return notABean("Class is not public");
 
-                       var bm = new BeanMeta<>(cm, findMarshalledFilter(cm), 
null, implClass);
+                       var bm = new BeanMeta<>(cm, 
MarshalledBeanMetaInitializer.findMarshalledFilter(cm), null, implClass);
 
                        if (nn(bm.notABeanReason))
                                return notABean(bm.notABeanReason);
@@ -275,46 +272,6 @@ public class BeanMeta<T> {
                return name.orElse(null);
        }
 
-       /*
-        * Finds and creates the bean filter for the specified class metadata.
-        *
-        * <p>
-        * Searches for {@link Marshalled @Marshalled} annotations on the class 
and its parent classes/interfaces. If found, creates a
-        * {@link MarshalledFilter} that applies the configuration from those 
annotations.
-        *
-        * <p>
-        * When multiple {@link Marshalled @Marshalled} annotations are found 
(e.g., on a parent class and a child class), they are
-        * applied in reverse order (parent classes first, then child classes). 
This ensures that child class annotations
-        * override parent class annotations, allowing child classes to 
customize or extend the bean configuration.
-        *
-        * <p>
-        * The bean filter controls various aspects of bean serialization and 
parsing, such as:
-        * <ul>
-        *      <li>Property inclusion/exclusion lists
-        *      <li>Property ordering and sorting
-        *      <li>Type name mapping for dictionary lookups
-        *      <li>Fluent setter detection
-        *      <li>Read-only and write-only property definitions
-        * </ul>
-        *
-        * @param <T> The class type.
-        * @param cm The class metadata to find the filter for.
-        * @return The bean filter, or <jk>null</jk> if no {@link Marshalled 
@Marshalled} annotations are found on the class or its hierarchy.
-        * @see Marshalled
-        * @see MarshalledFilter
-        */
-       private static <T> MarshalledFilter findMarshalledFilter(ClassMeta<T> 
cm) {
-               var ap = cm.getMarshallingContext().getAnnotationProvider();
-               var l = ap.find(Marshalled.class, cm);
-               var bt = ap.find(org.apache.juneau.commons.bean.BeanType.class, 
cm);
-               if (l.isEmpty() && bt.isEmpty())
-                       return null;
-               return MarshalledFilter.create(cm)
-                       
.applyAnnotations(reverse(l.stream().map(AnnotationInfo::inner).toList()))
-                       
.applyBeanTypeAnnotations(reverse(bt.stream().map(AnnotationInfo::inner).toList()))
-                       .build();
-       }
-
        /*
         * Extracts the property name from a single {@link BeanProp @BeanProp} 
or {@link Name @Name} annotation.
         *
@@ -359,7 +316,7 @@ public class BeanMeta<T> {
        }
 
        private BeanConstructor beanConstructor;                                
   // The constructor for this bean.
-       private final MarshallingContext marshallingContext;                    
   // The bean context that created this metadata object.  Null when 
constructed via {@link #of(Class, BeanConfigContext)}.
+       private final Object marshallingContext;                                
   // MarshallingContext, but Object-typed so the field can live in 
commons.bean.  Cast to MarshallingContext at marshalling-side use sites.  Null 
when constructed via {@link #of(Class, BeanConfigContext)}.
        private final BeanConfigContext config;                                 
   // Bean-modeling settings facade — always non-null.  Sources: 
marshallingContext.getBeanConfigContext() (marshalling-side) or the explicit 
BeanConfigContext (commons-side).
        private final BeanFilter beanFilter;                                    
   // Optional bean filter associated with the target class.  Typed as the 
bean-modeling-side SPI seam; marshalling-side callers cast back to {@link 
MarshalledFilter} via {@link #getMarshalledFilter()}.
        private final NullableSupplier<InvocationHandler> 
beanProxyInvocationHandler;  // The invocation handler for this bean (if it's 
an interface).
@@ -445,7 +402,7 @@ public class BeanMeta<T> {
         * @param implClass Optional implementation class constructor to use if 
one cannot be found. Can be <jk>null</jk>.
         */
        protected BeanMeta(BeanTypeInfo<T> cm, BeanFilter bf, String[] pNames, 
ClassInfo implClass) {
-               this((ClassMeta<T>) cm, (ClassMeta<T>) cm, ((ClassMeta<T>) 
cm).getMarshallingContext().getBeanConfigContext(), ((ClassMeta<T>) 
cm).getMarshallingContext(), bf, pNames, implClass);
+               this(cm, MarshalledBeanMetaInitializer.classInfoOf(cm), 
MarshalledBeanMetaInitializer.configOf(cm), 
MarshalledBeanMetaInitializer.contextOf(cm), bf, pNames, implClass);
        }
 
        /**
@@ -468,7 +425,7 @@ public class BeanMeta<T> {
                "java:S3776", // Cognitive complexity acceptable for bean 
metadata initialization
                "java:S107"   // 7 parameters needed to support both 
construction paths
        })
-       private BeanMeta(ClassMeta<T> cm, ClassInfo ci0, BeanConfigContext 
config, MarshallingContext mc, BeanFilter bf, String[] pNames, ClassInfo 
implClass) {
+       private BeanMeta(BeanTypeInfo<T> cm, ClassInfo ci0, BeanConfigContext 
config, Object mc, BeanFilter bf, String[] pNames, ClassInfo implClass) {
                classMeta = cm;
                classInfo = ci0;
                this.config = config;
@@ -621,18 +578,18 @@ public class BeanMeta<T> {
 
                        propertiesValue.set(unsortedPropertiesTemp ? map() : 
sortedMap());
 
-                       normalProps.forEach((k, v) -> {
-                               var pMeta = v.build();
-                               if (pMeta.isDyna())
-                                       dynaPropertyValue.set(pMeta);
-                               propertiesValue.get().put(k, pMeta);
-                               // Build the property-level BeanRegistry 
side-map entry from the builder's accumulated
-                               // dictionary classes.  Parents to the 
bean-level registry so @MarshalledProp(dictionary={})
-                               // entries chain on top of bean and global 
dictionaries.  Skipped on the commons-side path
-                               // (no marshallingContext means no BeanRegistry 
construction).
-                               if (nn(marshallingContext) && 
nn(v.dictionaryClasses))
-                                       propertyBeanRegistriesTemp.put(pMeta, 
new BeanRegistry(marshallingContext, (BeanRegistry) beanRegistry.get(), 
v.dictionaryClasses));
-                       });
+               normalProps.forEach((k, v) -> {
+                       var pMeta = v.build();
+                       if (pMeta.isDyna())
+                               dynaPropertyValue.set(pMeta);
+                       propertiesValue.get().put(k, pMeta);
+                       // Build the property-level BeanRegistry side-map entry 
from the builder's accumulated
+                       // dictionary classes.  Parents to the bean-level 
registry so @MarshalledProp(dictionary={})
+                       // entries chain on top of bean and global 
dictionaries.  Skipped on the commons-side path
+                       // (no marshallingContext means no BeanRegistry 
construction).
+                       if (nn(marshallingContext) && nn(v.dictionaryClasses))
+                               propertyBeanRegistriesTemp.put(pMeta, 
MarshalledBeanMetaInitializer.buildPropertyBeanRegistry(marshallingContext, 
beanRegistry.get(), v.dictionaryClasses));
+               });
 
                        // If a beanFilter is defined, look for inclusion and 
exclusion lists.
                        if (bf != null) {
@@ -702,16 +659,15 @@ public class BeanMeta<T> {
 
                        // When constructed via the commons-side path, 
marshallingContext is null and validate() runs in
                        // raw-reflection mode (no 
ClassMeta/ObjectSwap/BeanRegistry resolution).
-                       if (p.validate(marshallingContext, typeVarImpls, 
readOnlyProps, writeOnlyProps)) {
+                       if (p.validate((BeanTypeResolver) marshallingContext, 
typeVarImpls, readOnlyProps, writeOnlyProps)) {
 
                                // Marshalling-side post-processor — applies 
@MarshalledProp / @Swap annotation effects
-                               // (swap detection, properties override, 
dictionary classes) after validate() succeeds.
-                               // Skipped on the commons-side path: those 
annotations require a MarshallingContext to
-                               // resolve 
ObjectSwap/StringFormatSwap/Surrogate types and the swap class meta.
+                               // (swap detection, properties override, 
dictionary classes) and installs swap-aware
+                               // read/write transforms.  Skipped on the 
commons-side path: those annotations require a
+                               // MarshallingContext to resolve 
ObjectSwap/StringFormatSwap/Surrogate types and the swap
+                               // class meta.
                                if (nn(marshallingContext))
-                                       
MarshalledPropertyPostProcessor.process(marshallingContext, p);
-
-                               installSwapAwareTransforms(p);
+                                       
MarshalledPropertyPostProcessor.process((MarshallingContext) 
marshallingContext, p);
 
                                if (nn(p.getter))
                                        getterProps.put(p.getter.inner(), 
p.name);
@@ -727,75 +683,6 @@ public class BeanMeta<T> {
                }
        }
 
-       /**
-        * Installs swap-aware read/write transforms on a {@link 
BeanPropertyMeta.Builder} after validation.
-        *
-        * <p>
-        * After {@link BeanPropertyMeta.Builder#validate validate()} succeeds, 
the builder's {@code swap} and
-        * {@code rawTypeMeta} fields describe whether the property has a 
configured {@link ObjectSwap} (via
-        * {@link org.apache.juneau.annotation.MarshalledProp 
@MarshalledProp(format=...)} or
-        * {@link org.apache.juneau.annotation.Swap @Swap}) and whether the 
property's raw type has child swaps registered
-        * on it.  This method packages those concerns into install-time 
closures so the marshalling-side swap behavior is
-        * established as data on the {@link BeanPropertyMeta} rather than 
executed by the bean-modeling
-        * {@link BeanPropertyMeta#get get}/{@link BeanPropertyMeta#set set} 
methods themselves.
-        *
-        * <p>
-        * If neither a property-level swap nor a child swap on the raw type 
meta is present, no transforms are installed
-        * and the property's {@code get}/{@code set} fall through to identity 
(raw access).
-        *
-        * @param p The builder to attach swap-aware transforms to.
-        */
-       @SuppressWarnings({
-               "rawtypes",   // ObjectSwap used raw to mirror 
BeanPropertyMeta's field declaration.
-               "unchecked"   // Wildcard ObjectSwap captured by raw alias to 
allow runtime polymorphic dispatch.
-       })
-       private static void installSwapAwareTransforms(BeanPropertyMeta.Builder 
p) {
-               ObjectSwap sw = (ObjectSwap) p.swap;
-               ClassMeta<?> rtm = (ClassMeta<?>) p.rawTypeMeta;
-               if (sw == null && (rtm == null || ! rtm.hasChildSwaps()))
-                       return;
-               if (p.readTransform == null) {
-                       p.readTransform = (session, o) -> {
-                               try {
-                                       if (nn(sw))
-                                               return sw.swap(session, o);
-                                       if (o == null)
-                                               return null;
-                                       if (rtm.hasChildSwaps()) {
-                                               ObjectSwap f = 
rtm.getChildObjectSwapForSwap(o.getClass());
-                                               if (nn(f))
-                                                       return f.swap(session, 
o);
-                                       }
-                                       return o;
-                               } catch (RuntimeException e) {
-                                       throw e;
-                               } catch (Exception e) {
-                                       throw new SerializeException(e);
-                               }
-                       };
-               }
-               if (p.writeTransform == null) {
-                       p.writeTransform = (session, o) -> {
-                               try {
-                                       if (nn(sw))
-                                               return sw.unswap(session, o, 
rtm);
-                                       if (o == null)
-                                               return null;
-                                       if (rtm.hasChildSwaps()) {
-                                               ObjectSwap f = 
rtm.getChildObjectSwapForUnswap(o.getClass());
-                                               if (nn(f))
-                                                       return 
f.unswap(session, o, rtm);
-                                       }
-                                       return o;
-                               } catch (RuntimeException e) {
-                                       throw e;
-                               } catch (Exception e) {
-                                       throw new ParseException(e);
-                               }
-                       };
-               }
-       }
-
        @Override /* Overridden from Object */
        public boolean equals(Object o) {
                return (o instanceof BeanMeta<?> o2) && eq(this, o2, (x, y) -> 
eq(x.classInfo, y.classInfo));
@@ -1042,7 +929,7 @@ public class BeanMeta<T> {
         *
         * @return The bean context, or <jk>null</jk> for bean-modeling-only 
construction.
         */
-       protected MarshallingContext getMarshallingContext() { return 
marshallingContext; }
+       protected MarshallingContext getMarshallingContext() { return 
(MarshallingContext) marshallingContext; }
 
        /**
         * Returns the constructor for this bean, if one was found.
@@ -1541,19 +1428,9 @@ public class BeanMeta<T> {
         */
        private BeanRegistryLookup findBeanRegistry() {
                // BeanRegistry is a marshalling-side concern (polymorphic 
dispatch via @Marshalled(typeName)/dictionary).
-               // On the commons-side construction path there is no 
MarshallingContext to drive it — return null and let
-               // callers route through getPropertyBeanRegistry(...) (which is 
also empty on this path).
-               if (marshallingContext == null)
-                       return null;
-
-               // Bean dictionary on bean filter.
-               var beanDictionaryClasses = opt(beanFilter).map(x -> new 
ArrayList<>(x.getBeanDictionary())).orElse(new ArrayList<>());
-
-               // Bean dictionary from @Marshalled(typeName) annotation.
-               var ba = config.getAnnotationProvider().find(Marshalled.class, 
classInfo);
-               ba.stream().map(x -> 
x.inner().typeName()).filter(Utils::ne).findFirst().ifPresent(x -> 
beanDictionaryClasses.add(classInfo));
-
-               return new BeanRegistry(marshallingContext, null, 
beanDictionaryClasses);
+               // Lifted out to {@link MarshalledBeanMetaInitializer} so this 
class no longer references
+               // {@link BeanRegistry} directly.  Returns null on the 
commons-side path (no marshallingContext).
+               return 
MarshalledBeanMetaInitializer.buildBeanRegistry(marshallingContext, beanFilter, 
classInfo, config);
        }
 
        /*
@@ -1664,32 +1541,13 @@ public class BeanMeta<T> {
                }
 
                // Parent-class BeanRegistry lookup is a marshalling-side 
concern: it walks parents and interfaces to
-               // see if any of THEIR ClassMeta-backed BeanRegistries declares 
a typeName for our raw class.  On the
-               // commons-side path (no marshallingContext) we skip this 
entirely.
-               if (nn(marshallingContext)) {
-                       var n = classInfo
-                               .getParentsAndInterfaces()
-                               .stream()
-                               .skip(1)
-                               .map(marshallingContext::getClassMeta)
-                               .map(ClassMeta::getBeanRegistry)
-                               .filter(Objects::nonNull)
-                               .map(x -> x.getTypeName(rawClass))
-                               .filter(Objects::nonNull)
-                               .findFirst()
-                               .orElse(null);
-
-                       if (n != null)
-                               return n;
-               }
+               // see if any of THEIR ClassMeta-backed BeanRegistries declares 
a typeName for our raw class.  Lifted
+               // out to {@link MarshalledBeanMetaInitializer}.  Returns null 
on the commons-side path.
+               var n = 
MarshalledBeanMetaInitializer.findTypeNameInParents(marshallingContext, 
classInfo, rawClass);
+               if (n != null)
+                       return n;
 
-               return config.getAnnotationProvider().find(Marshalled.class, 
classInfo)
-                       .stream()
-                       .map(AnnotationInfo::inner)
-                       .filter(x -> ! x.typeName().isEmpty())
-                       .map(Marshalled::typeName)
-                       .findFirst()
-                       .orElse(null);
+               return 
MarshalledBeanMetaInitializer.findMarshalledTypeName(config, classInfo);
        }
 
        /*
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 fe365128a6..81cda3d4ad 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
@@ -33,7 +33,6 @@ import java.util.function.*;
 import java.util.stream.*;
 
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.collections.*;
 import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.collections.*;
 import org.apache.juneau.commons.lang.*;
@@ -1144,7 +1143,7 @@ public class BeanPropertyMeta implements 
Comparable<BeanPropertyMeta> {
 
                                if (! (value1 instanceof Map)) {
                                        if (value1 instanceof CharSequence 
value21)
-                                               value1 = 
JsonMap.ofJson(value21).session(session);
+                                               value1 = 
session.parseToMap(value21);
                                        else
                                                throw 
bex(beanMeta.getClassMeta(), "Cannot set property ''{0}'' of type ''{1}'' to 
object of type ''{2}''", name, propertyClass.getName(), cn(value1));
                                }
@@ -1202,7 +1201,7 @@ public class BeanPropertyMeta implements 
Comparable<BeanPropertyMeta> {
 
                                if (! (value1 instanceof Collection)) {
                                        if (value1 instanceof CharSequence 
value2)
-                                               value1 = new 
JsonList(value2).setBeanSession(session);
+                                               value1 = 
session.parseToList(value2);
                                        else
                                                throw 
bex(beanMeta.getClassMeta(), "Cannot set property ''{0}'' of type ''{1}'' to 
object of type ''{2}''", name, propertyClass.getName(), cn(value1));
                                }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledBeanMetaInitializer.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledBeanMetaInitializer.java
new file mode 100644
index 0000000000..3b0c49c0a1
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledBeanMetaInitializer.java
@@ -0,0 +1,242 @@
+/*
+ * 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;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.commons.utils.StringUtils.*;
+import static org.apache.juneau.commons.utils.Utils.*;
+
+import java.util.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.commons.bean.*;
+import org.apache.juneau.commons.reflect.*;
+import org.apache.juneau.commons.utils.*;
+
+/**
+ * Marshalling-side bridge for {@link BeanMeta} construction.
+ *
+ * <p>
+ * Encapsulates marshalling-aware operations that were lifted out of {@link 
BeanMeta} as part of TODO-5
+ * Step 8b-ii Phase C Task 3, so that {@link BeanMeta} can physically move 
into the
+ * {@code org.apache.juneau.commons.bean} package without directly referencing
+ * {@link BeanRegistry}, {@link MarshallingContext}, or marshalling-side 
annotations like
+ * {@link Marshalled @Marshalled} from inside its constructor body.
+ *
+ * <p>
+ * Each helper takes the marshalling-side {@link MarshallingContext} as an 
{@link Object} so the
+ * call sites inside {@link BeanMeta} do not need to import {@link 
MarshallingContext}.  The helper
+ * casts back internally.
+ */
+final class MarshalledBeanMetaInitializer {
+
+       private MarshalledBeanMetaInitializer() {}
+
+       /**
+        * Builds the bean-level {@link BeanRegistry} for a given bean class.
+        *
+        * <p>
+        * Replaces the body of {@link BeanMeta}'s former private {@code 
findBeanRegistry()} method.  Returns
+        * <jk>null</jk> when {@code marshallingContext} is <jk>null</jk> 
(commons-side construction path).
+        *
+        * @param marshallingContext The marshalling context.  May be 
<jk>null</jk>.
+        * @param beanFilter The bean filter applied to this bean meta.  May be 
<jk>null</jk>.
+        * @param classInfo The class info of the bean.  Must not be 
<jk>null</jk>.
+        * @param config The bean-modeling configuration.  Must not be 
<jk>null</jk>.
+        * @return The bean-level {@link BeanRegistryLookup}, or <jk>null</jk> 
on the commons-side path.
+        */
+       static BeanRegistryLookup buildBeanRegistry(Object marshallingContext, 
BeanFilter beanFilter, ClassInfo classInfo, BeanConfigContext config) {
+               if (marshallingContext == null)
+                       return null;
+               MarshallingContext mc = (MarshallingContext) marshallingContext;
+
+               // Bean dictionary on bean filter.
+               var beanDictionaryClasses = opt(beanFilter).map(x -> new 
ArrayList<>(x.getBeanDictionary())).orElse(new ArrayList<>());
+
+               // Bean dictionary from @Marshalled(typeName) annotation.
+               var ba = config.getAnnotationProvider().find(Marshalled.class, 
classInfo);
+               ba.stream().map(x -> 
x.inner().typeName()).filter(Utils::ne).findFirst().ifPresent(x -> 
beanDictionaryClasses.add(classInfo));
+
+               return new BeanRegistry(mc, null, beanDictionaryClasses);
+       }
+
+       /**
+        * Constructs a per-property {@link BeanRegistry} chained on top of the 
bean-level registry.
+        *
+        * <p>
+        * Replaces the inline {@code new BeanRegistry(marshallingContext, 
beanRegistry, dictionaryClasses)} call that
+        * lived inside {@link BeanMeta}'s property-iteration loop.
+        *
+        * @param marshallingContext The marshalling context.  Must not be 
<jk>null</jk>.
+        * @param parent The bean-level registry (chained behind the 
per-property one).  May be <jk>null</jk>.
+        * @param dictionaryClasses The per-property {@link 
MarshalledProp#dictionary() @MarshalledProp(dictionary)} classes.  Must not be 
<jk>null</jk>.
+        * @return The per-property {@link BeanRegistryLookup}.
+        */
+       static BeanRegistryLookup buildPropertyBeanRegistry(Object 
marshallingContext, BeanRegistryLookup parent, List<ClassInfo> 
dictionaryClasses) {
+               return new BeanRegistry((MarshallingContext) 
marshallingContext, (BeanRegistry) parent, dictionaryClasses);
+       }
+
+       /**
+        * Walks the parent classes/interfaces of {@code classInfo} and returns 
the first
+        * {@link Marshalled#typeName() @Marshalled(typeName)} mapping for 
{@code rawClass} found in any parent's
+        * {@link BeanRegistry}.
+        *
+        * <p>
+        * Replaces the parent-walk stream that previously used {@code 
marshallingContext::getClassMeta} directly
+        * inside {@link BeanMeta#findDictionaryName()}.
+        *
+        * @param marshallingContext The marshalling context.  May be 
<jk>null</jk>.
+        * @param classInfo The bean's class info (used to derive the 
parents/interfaces walk).
+        * @param rawClass The raw class to look up a type name for.
+        * @return The dictionary name found in a parent's registry, or 
<jk>null</jk>.
+        */
+       static String findTypeNameInParents(Object marshallingContext, 
ClassInfo classInfo, Class<?> rawClass) {
+               if (marshallingContext == null)
+                       return null;
+               MarshallingContext mc = (MarshallingContext) marshallingContext;
+               return classInfo
+                       .getParentsAndInterfaces()
+                       .stream()
+                       .skip(1)
+                       .map(mc::getClassMeta)
+                       .map(ClassMeta::getBeanRegistry)
+                       .filter(Objects::nonNull)
+                       .map(x -> x.getTypeName(rawClass))
+                       .filter(Objects::nonNull)
+                       .findFirst()
+                       .orElse(null);
+       }
+
+       /**
+        * Extracts the {@link BeanConfigContext} embedded in a 
marshalling-side {@link BeanTypeInfo} (which is always
+        * a {@link ClassMeta}).
+        *
+        * <p>
+        * Used by the protected {@code BeanMeta(BeanTypeInfo, BeanFilter, 
String[], ClassInfo)} bridging constructor —
+        * lifts the {@code ((ClassMeta<T>) 
cm).getMarshallingContext().getBeanConfigContext()} cast chain out of the
+        * commons-side {@link BeanMeta} body.
+        *
+        * @param cm The bean type info.  Must be a marshalling-side {@link 
ClassMeta} instance.
+        * @return The bean-modeling configuration of the marshalling context.
+        */
+       static BeanConfigContext configOf(BeanTypeInfo<?> cm) {
+               return ((ClassMeta<?>) 
cm).getMarshallingContext().getBeanConfigContext();
+       }
+
+       /**
+        * Extracts the {@link MarshallingContext} embedded in a 
marshalling-side {@link BeanTypeInfo} (which is always
+        * a {@link ClassMeta}) and returns it as an {@link Object} so the 
caller does not need to import
+        * {@link MarshallingContext}.
+        *
+        * @param cm The bean type info.  Must be a marshalling-side {@link 
ClassMeta} instance.
+        * @return The marshalling context, as an {@link Object}.
+        */
+       static Object contextOf(BeanTypeInfo<?> cm) {
+               return ((ClassMeta<?>) cm).getMarshallingContext();
+       }
+
+       /**
+        * Narrows a marshalling-side {@link BeanTypeInfo} (which is always a 
{@link ClassMeta}) to {@link ClassInfo}.
+        *
+        * <p>
+        * Since {@link ClassMeta} extends {@link ClassInfo}, the cast is 
direct.  Provided as a helper so the commons-side
+        * caller does not have to import {@link ClassMeta}.
+        *
+        * @param cm The bean type info.  Must be a marshalling-side {@link 
ClassMeta} instance.
+        * @return The class info for the underlying bean class.
+        */
+       static ClassInfo classInfoOf(BeanTypeInfo<?> cm) {
+               return (ClassMeta<?>) cm;
+       }
+
+       /**
+        * Looks up the {@link Marshalled#typePropertyName() 
@Marshalled(typePropertyName)} value for a bean class.
+        *
+        * <p>
+        * Returns the configured value, or an empty string if no {@link 
Marshalled @Marshalled} annotation specifies one.
+        * The caller (typically {@link BeanMeta}'s constructor) falls back to 
a config-supplied default when this method
+        * returns an empty string.
+        *
+        * @param config The bean-modeling configuration (used to access the 
annotation provider).
+        * @param classInfo The bean's class info.
+        * @return The configured type property name, or empty if not set.
+        */
+       static String findTypePropertyName(BeanConfigContext config, ClassInfo 
classInfo) {
+               var ba = config.getAnnotationProvider().find(Marshalled.class, 
classInfo);
+               return ba.stream().map(x -> 
x.inner().typePropertyName()).filter(Utils::ne).findFirst().orElse("");
+       }
+
+       /**
+        * Looks up the {@link Marshalled#typeName() @Marshalled(typeName)} 
value for a bean class.
+        *
+        * <p>
+        * Used by {@link BeanMeta#findDictionaryName()} as the last fallback 
when no other dictionary name is found.
+        *
+        * @param config The bean-modeling configuration (used to access the 
annotation provider).
+        * @param classInfo The bean's class info.
+        * @return The configured type name, or <jk>null</jk> if not set.
+        */
+       static String findMarshalledTypeName(BeanConfigContext config, 
ClassInfo classInfo) {
+               return config.getAnnotationProvider().find(Marshalled.class, 
classInfo)
+                       .stream()
+                       .map(AnnotationInfo::inner)
+                       .filter(x -> ! x.typeName().isEmpty())
+                       .map(Marshalled::typeName)
+                       .findFirst()
+                       .orElse(null);
+       }
+
+       /**
+        * Determines whether the class has any "registered as bean" annotation
+        * ({@link Marshalled @Marshalled} or {@link BeanType @BeanType}).
+        *
+        * <p>
+        * Used by {@link BeanMeta#findBeanConstructor()} to decide whether 
private constructors are permissible.
+        *
+        * @param config The bean-modeling configuration (used to access the 
annotation provider).
+        * @param classInfo The bean's class info.
+        * @return <jk>true</jk> if the class has either annotation.
+        */
+       static boolean hasBeanRegistrationAnnotation(BeanConfigContext config, 
ClassInfo classInfo) {
+               var ap = config.getAnnotationProvider();
+               return ! ap.find(Marshalled.class, classInfo).isEmpty()
+                       || ! ap.find(BeanType.class, classInfo).isEmpty();
+       }
+
+       /**
+        * Resolves the bean filter for a marshalling-side {@link BeanMeta}.
+        *
+        * <p>
+        * Lifted out of {@link BeanMeta}'s former private {@code 
findMarshalledFilter(ClassMeta)} static helper.  Returns
+        * a {@link MarshalledFilter} (the only concrete {@link BeanFilter} 
implementation in-tree) built from the
+        * {@link Marshalled @Marshalled} and {@link BeanType @BeanType} 
annotations on the class.
+        *
+        * @param cm The class meta for the bean class.
+        * @return The bean filter, or <jk>null</jk> if no relevant annotations 
are present.
+        */
+       static <T> MarshalledFilter findMarshalledFilter(ClassMeta<T> cm) {
+               var ap = cm.getMarshallingContext().getAnnotationProvider();
+               var l = ap.find(Marshalled.class, cm);
+               var bt = ap.find(BeanType.class, cm);
+               if (l.isEmpty() && bt.isEmpty())
+                       return null;
+               return MarshalledFilter.create(cm)
+                       
.applyAnnotations(reverse(l.stream().map(AnnotationInfo::inner).toList()))
+                       
.applyBeanTypeAnnotations(reverse(bt.stream().map(AnnotationInfo::inner).toList()))
+                       .build();
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledPropertyPostProcessor.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledPropertyPostProcessor.java
index c0f1332bc0..7f6defe51b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledPropertyPostProcessor.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledPropertyPostProcessor.java
@@ -28,6 +28,8 @@ import java.util.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.commons.reflect.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.swap.*;
 import org.apache.juneau.swaps.*;
 
@@ -121,6 +123,80 @@ final class MarshalledPropertyPostProcessor {
                // layer and cannot reference ObjectSwap directly).
                if (nn(b.swap) && nn(b.rawTypeMeta))
                        b.typeMeta = bc.getClassMeta(((ObjectSwap) 
b.swap).getSwapClass());
+
+               // Install swap-aware read/write transforms on the builder.
+               // Previously lived as a private static helper on {@link 
BeanMeta}; moved here as part of TODO-5 Step 8b-ii
+               // Phase C Task 3 so {@link BeanMeta} no longer references 
{@link ObjectSwap}/{@link ParseException}/{@link SerializeException}.
+               installSwapAwareTransforms(b);
+       }
+
+       /**
+        * Installs swap-aware read/write transforms on a {@link 
BeanPropertyMeta.Builder} after validation.
+        *
+        * <p>
+        * After {@link BeanPropertyMeta.Builder#validate validate()} succeeds, 
the builder's {@code swap} and
+        * {@code rawTypeMeta} fields describe whether the property has a 
configured {@link ObjectSwap} (via
+        * {@link org.apache.juneau.annotation.MarshalledProp 
@MarshalledProp(format=...)} or
+        * {@link org.apache.juneau.annotation.Swap @Swap}) and whether the 
property's raw type has child swaps registered
+        * on it.  This method packages those concerns into install-time 
closures so the marshalling-side swap behavior is
+        * established as data on the {@link BeanPropertyMeta} rather than 
executed by the bean-modeling
+        * {@link BeanPropertyMeta#get get}/{@link BeanPropertyMeta#set set} 
methods themselves.
+        *
+        * <p>
+        * If neither a property-level swap nor a child swap on the raw type 
meta is present, no transforms are installed
+        * and the property's {@code get}/{@code set} fall through to identity 
(raw access).
+        *
+        * @param p The builder to attach swap-aware transforms to.
+        */
+       @SuppressWarnings({
+               "rawtypes",   // ObjectSwap used raw to mirror 
BeanPropertyMeta's field declaration.
+               "unchecked"   // Wildcard ObjectSwap captured by raw alias to 
allow runtime polymorphic dispatch.
+       })
+       static void installSwapAwareTransforms(BeanPropertyMeta.Builder p) {
+               ObjectSwap sw = (ObjectSwap) p.swap;
+               ClassMeta<?> rtm = (ClassMeta<?>) p.rawTypeMeta;
+               if (sw == null && (rtm == null || ! rtm.hasChildSwaps()))
+                       return;
+               if (p.readTransform == null) {
+                       p.readTransform = (session, o) -> {
+                               try {
+                                       if (nn(sw))
+                                               return sw.swap(session, o);
+                                       if (o == null)
+                                               return null;
+                                       if (rtm.hasChildSwaps()) {
+                                               ObjectSwap f = 
rtm.getChildObjectSwapForSwap(o.getClass());
+                                               if (nn(f))
+                                                       return f.swap(session, 
o);
+                                       }
+                                       return o;
+                               } catch (RuntimeException e) {
+                                       throw e;
+                               } catch (Exception e) {
+                                       throw new SerializeException(e);
+                               }
+                       };
+               }
+               if (p.writeTransform == null) {
+                       p.writeTransform = (session, o) -> {
+                               try {
+                                       if (nn(sw))
+                                               return sw.unswap(session, o, 
rtm);
+                                       if (o == null)
+                                               return null;
+                                       if (rtm.hasChildSwaps()) {
+                                               ObjectSwap f = 
rtm.getChildObjectSwapForUnswap(o.getClass());
+                                               if (nn(f))
+                                                       return 
f.unswap(session, o, rtm);
+                                       }
+                                       return o;
+                               } catch (RuntimeException e) {
+                                       throw e;
+                               } catch (Exception e) {
+                                       throw new ParseException(e);
+                               }
+                       };
+               }
        }
 
        private static ObjectSwap 
marshalledPropSwap(AnnotationInfo<MarshalledProp> ai) {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshallingSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshallingSession.java
index 66e50ee20d..f4f84bb73c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshallingSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshallingSession.java
@@ -1321,4 +1321,38 @@ public class MarshallingSession extends ContextSession 
implements ConverterSessi
                throw illegalArg("Unsupported targetType for 
convertToMemberType: {0}", targetType.getClass().getName());
        }
 
+       /**
+        * Bridge implementation of {@link 
BeanSession#parseToMap(CharSequence)} that delegates to
+        * {@link org.apache.juneau.collections.JsonMap#ofJson(CharSequence)} 
paired with this session.
+        *
+        * <p>
+        * Used by {@link BeanPropertyMeta#setPropertyValue} to parse a {@link 
CharSequence} into a {@link Map} when
+        * the property is map-typed.  Lifted out of {@link BeanPropertyMeta} 
so the bean-modeling layer no longer
+        * references the marshalling-side JSON parser.
+        *
+        * @param value The JSON-formatted character sequence to parse.  Must 
not be <jk>null</jk>.
+        * @return The parsed {@link org.apache.juneau.collections.JsonMap}.
+        */
+       @Override /* BeanSession */
+       public final java.util.Map<?,?> parseToMap(CharSequence value) {
+               return 
org.apache.juneau.collections.JsonMap.ofJson(value).session(this);
+       }
+
+       /**
+        * Bridge implementation of {@link 
BeanSession#parseToList(CharSequence)} that delegates to
+        * {@link 
org.apache.juneau.collections.JsonList#JsonList(CharSequence)} paired with this 
session.
+        *
+        * <p>
+        * Used by {@link BeanPropertyMeta#setPropertyValue} to parse a {@link 
CharSequence} into a {@link java.util.Collection}
+        * when the property is collection-typed.  Lifted out of {@link 
BeanPropertyMeta} so the bean-modeling layer
+        * no longer references the marshalling-side JSON parser.
+        *
+        * @param value The JSON-formatted character sequence to parse.  Must 
not be <jk>null</jk>.
+        * @return The parsed {@link org.apache.juneau.collections.JsonList}.
+        */
+       @Override /* BeanSession */
+       public final java.util.Collection<?> parseToList(CharSequence value) {
+               return new 
org.apache.juneau.collections.JsonList(value).setBeanSession(this);
+       }
+
 }
\ No newline at end of file
diff --git a/todo/TODO-5-bean-runtime-types-to-commons.md 
b/todo/TODO-5-bean-runtime-types-to-commons.md
index 1be86c3f7c..b7c19d95ed 100644
--- a/todo/TODO-5-bean-runtime-types-to-commons.md
+++ b/todo/TODO-5-bean-runtime-types-to-commons.md
@@ -4,9 +4,16 @@ This is the remaining work from **Phase 5 of the bean-layer 
split**. Phase 5a (t
 
 ---
 
-## Status (as of Phase 5h checkpoint + Step 8b-i)
+## Status (as of Phase C Tasks 1-2-3-4-4-deferred checkpoint, uncommitted)
 
-**Step 8b-i complete (additional SPI-seam extraction, uncommitted).** Three 
new SPIs landed in the working tree (no commit yet — left for user review). 
Build and full test suite green. See "Step 8b-i" entry in the step list below 
for full detail. Summary: `BeanTypeInfo<T>` (abstract class — ClassMeta 
extends), `BeanFilter` interface (MarshalledFilter implements), 
`BeanRegistryLookup` interface (BeanRegistry implements), and `Delegate<T>` 
physically moved to `commons.bean`. The remaining  [...]
+**Phase C Tasks 1, 2, 3, 4, 4-deferred LANDED (working tree, uncommitted).** 
Build + full test green. See "Phase C status" block under Step 8b-ii for full 
detail. Summary:
+- **Task 1** — Public getter widening (`getClassMeta()` → `BeanTypeInfo<?>`, 
`getBeanRegistry()` → `BeanRegistryLookup`) committed locally at `48621576d7`.
+- **Task 2** — `validate(...)` body lift-out via new `BeanTypeResolver` SPI 
committed locally.
+- **Task 3** — `BeanMeta` constructor body lift-out via new 
`MarshalledBeanMetaInitializer` (this checkpoint). Includes relocation of 
`installSwapAwareTransforms` from `BeanMeta` into 
`MarshalledPropertyPostProcessor`, retype of `BeanMeta.marshallingContext` 
field to `Object`, and removal of `parser.*`/`serializer.*`/`swap.*` imports 
from `BeanMeta`.
+- **Task 4** — `setPropertyValue` Collection-branch JsonList wrap → ArrayList 
wrap (committed earlier).
+- **Task 4-deferred** — CharSequence-parsing sites now route through new 
`BeanSession.parseToMap(CharSequence)` / 
`BeanSession.parseToList(CharSequence)` SPI methods (this checkpoint). 
`BeanPropertyMeta` dropped its `import org.apache.juneau.collections.*`.
+
+**Phase C Task 5 (physical `git mv` + reference sweep) — NOT STARTED.** Survey 
revealed the remaining marshalling-side coupling on the 8 files is deeper than 
the original prep scope estimated. The bulk of remaining cleanup is lifting ~20 
marshalling-side annotation reads (`@Marshalled`, `@BeanProp`, etc.) from 
`BeanMeta`/`BeanPropertyMeta` to `MarshalledBeanMetaInitializer`, plus retyping 
`BiFunction<MarshallingSession,...>` field types to use the `BeanSession` SPI 
seam, plus relocating  [...]
 
 **Step 8a complete (SPI-seam extraction).** Commit `3a74fcd50a`. The minimum 
SPI surface that the 8 target types need from the marshalling layer is now in 
place:
 
@@ -110,17 +117,54 @@ Known limitations of the commons-side path (acceptable 
for Step 6, scoped for la
   - **Sub-item 10 (`JsonList` / `JsonMap` → JDK collections)** — partial. The 
`propertyCache` allocations inside 
`BeanPropertyMeta.add(BeanMap,String,Object)` and 
`add(BeanMap,String,String,Object)` now use `new ArrayList<>()` / `new 
LinkedHashMap<>()` (test-invisible — they're internal caches for read-only 
beans). The `c = new JsonList(session)` / `map = new JsonMap(session)` fallback 
constructions in `add(...)` likewise flipped to `new ArrayList<>()` / `new 
LinkedHashMap<>()`. **Deferr [...]
   - **Sub-item 8 (`BeanMap.load(...)` → `BeanMapLoader`)** — created 
`juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMapLoader.java`
 with `static <T> BeanMap<T> load(BeanMap<T> m, String input) throws 
ParseException` and `static <T> BeanMap<T> load(BeanMap<T> m, Reader r, 
ReaderParser p) throws ParseException, IOException`. Removed 
`BeanMap.load(Reader, ReaderParser)` and `BeanMap.load(String)` from 
`BeanMap.java`. Updated 2 call sites in `MarshallingContext.java` (`bs.n [...]
 
-  **Phase C status (uncommitted, working tree) — IN PROGRESS. Tasks 1, 2, 4 
LANDED, build + full test green. Tasks 3, 5 NOT STARTED.**
+  **Phase C status (uncommitted, working tree) — Tasks 1, 2, 3, 4, 4-deferred 
LANDED, build + full test green. Task 5 NOT STARTED (deeper coupling 
discovered).**
 
-  Phase C-tasks-1-2-and-4 landed:
+  Phase C-tasks-1-2-3-4-and-4-deferred landed:
   - **Task 1 (public getter widening) — COMPLETE.** 
`BeanPropertyMeta.getClassMeta()` now returns `BeanTypeInfo<?>` (was 
`ClassMeta<?>`). `BeanMeta.getClassMeta()` now returns `BeanTypeInfo<T>` (was 
`ClassMeta<T>`). `BeanMeta.getBeanRegistry()` and 
`BeanMeta.getPropertyBeanRegistry(BeanPropertyMeta)` now return 
`BeanRegistryLookup` (was `BeanRegistry`). `BeanMap.getClassMeta()` now returns 
`BeanTypeInfo<T>` (was `ClassMeta<T>`). The cascade across ~30 marshalling-side 
files needed explic [...]
-  - **Task 2 (`validate(...)` body lift-out) — COMPLETE (signature seam).** 
Introduced new SPI `BeanTypeResolver` in `org.apache.juneau.commons.bean` 
(3-method interface: `resolveType(AnnotationInfo<BeanProp>, ClassInfo, 
TypeVariables)`, `objectType()`, `getAnnotationProvider()`). 
`MarshallingContext` now `implements BeanTypeResolver` and exposes two new 
public bridge methods (`resolveType` delegating to the existing protected 
`resolveClassMeta`, and `objectType` returning `cmObject`). ` [...]
-  - **Task 4 (`new JsonList(valueList)` → `new ArrayList<>(valueList)`) — 
COMPLETE.** `BeanPropertyMeta.setPropertyValue`'s abstract-Collection branch 
(the path that builds a typed collection by copying then converting elements) 
now constructs `new ArrayList<>(valueList)` instead of `new 
JsonList(valueList)`. Three `BeanMap_Test` assertions (`a05_arrayProperties`, 
`a06_arrayProperties_usingConfig`, `a09_beanPropertyAnnotation`) updated to 
expect `ArrayList` for the corresponding `lb1`/`l [...]
-
-  Phase C-tasks-3-and-5 NOT started:
-  - **Task 3 — `BeanMeta` constructor body lift-out** — the 
`BeanMeta(BeanTypeInfo<T>, BeanFilter, String[], ClassInfo)` constructor still 
contains marshalling-layer-aware logic (cast back to `ClassMeta<T>` to read 
`getMarshallingContext()` / `getBeanConfigContext()`, builds swap-aware 
transforms, registers per-property `BeanRegistry`). Moving that body to a 
marshalling-side helper (`MarshalledBeanMetaInitializer`) so the commons-side 
constructor body is commons-clean is still pending.
-  - **Task 4 (deferred CharSequence-parsing sites)** — two 
`CharSequence`-handling sites inside `setPropertyValue` are still in place: 
`value1 = JsonMap.ofJson(value21).session(session)` (line 1147, Map branch) and 
`value1 = new JsonList(value2).setBeanSession(session)` (line 1205, Collection 
branch). These invoke the marshalling-side JSON parser and should move to a 
marshalling-side conversion helper before `BeanPropertyMeta` physically moves 
(or be lifted into a pre-`set(...)` conversi [...]
-  - **Task 5 — Physical `git mv` + reference sweep** — the 8 target files 
(`BeanMap`, `BeanMapEntry`, `BeanMeta`, `BeanMetaFiltered`, `BeanPropertyMeta`, 
`BeanPropertyValue`, `BeanPropertyConsumer`, `BeanProxyInvocationHandler`) are 
still in `juneau-core/juneau-marshall/src/main/java/org/apache/juneau/`. Before 
the move can succeed cleanly, the marshalling-side imports in the 8 files still 
need cleanup: `BeanMap` imports `annotation.*`, `internal.*`, `swap.*`; 
`BeanMapEntry` imports `ann [...]
+  - **Task 2 (`validate(...)` body lift-out) — COMPLETE (signature seam).** 
Introduced new SPI `BeanTypeResolver` in `org.apache.juneau.commons.bean` 
(3-method interface: `resolveType(AnnotationInfo<BeanProp>, ClassInfo, 
TypeVariables)`, `objectType()`, `getAnnotationProvider()`). 
`MarshallingContext` now `implements BeanTypeResolver` and exposes two new 
public bridge methods (`resolveType` delegating to the existing protected 
`resolveClassMeta`, and `objectType` returning `cmObject`). ` [...]
+  - **Task 3 (`BeanMeta` constructor body lift-out) — COMPLETE.** New 
marshalling-side helper `MarshalledBeanMetaInitializer` in 
`juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledBeanMetaInitializer.java`.
 It owns the marshalling-aware helpers that used to live as private code paths 
inside `BeanMeta`'s constructor and helpers: `buildBeanRegistry(Object 
marshallingContext, BeanFilter, ClassInfo, BeanConfigContext)` (replaces 
`findBeanRegistry()` body), `buildPropertyB [...]
+  - **Task 4 (`new JsonList(valueList)` → `new ArrayList<>(valueList)`) — 
COMPLETE.** `BeanPropertyMeta.setPropertyValue`'s abstract-Collection branch 
(the path that builds a typed collection by copying then converting elements) 
now constructs `new ArrayList<>(valueList)` instead of `new 
JsonList(valueList)`. Three `BeanMap_Test` assertions (`a05_arrayProperties`, 
`a06_arrayProperties_usingConfig`, `a09_beanPropertyAnnotation`) updated to 
expect `ArrayList` for the corresponding `lb1`/`l [...]
+  - **Task 4-deferred (CharSequence-parsing sites in `setPropertyValue`) — 
COMPLETE.** Added two new methods to the `BeanSession` SPI in `commons.bean`: 
`Map<?,?> parseToMap(CharSequence)` and `Collection<?> 
parseToList(CharSequence)`. `MarshallingSession` implements both as bridge 
methods that delegate to `JsonMap.ofJson(value).session(this)` / `new 
JsonList(value).setBeanSession(this)` respectively (the original inline calls). 
`BeanPropertyMeta.setPropertyValue`'s two `CharSequence` br [...]
+
+  **Phase C Task 5 status — NOT STARTED.** Surveying the 8 target files in 
light of the lifts from Tasks 1-4-deferred revealed that the remaining 
marshalling-side coupling is deeper than the original Task 5 prep scope. To be 
done in a follow-up pass; this checkpoint stops at "all SPI extractions + 
helper lifts done; physical move blocked on deeper coupling cleanup".
+
+  Inventory of what's still blocking the physical `git mv` (remaining 
marshalling-side surface area on the 8 target files):
+
+  - **`BeanPropertyMeta`** — still imports `annotation.*`, `internal.*`, 
`parser.*`, `serializer.*`, `swap.*`.
+    - `org.apache.juneau.annotation.*` — Javadoc references to 
`@MarshalledProp`, `@Marshalled`, plus `AnnotationInfo`/`AnnotationProvider` 
(those live in `commons.reflect` though — confirm).
+    - `org.apache.juneau.internal.*` — uses `TypeVariables`/`ClassInfo` 
helpers (`TypeVariables` is in `internal`, check it's not commons-reachable).
+    - `org.apache.juneau.parser.ParseException` (catch on line 1120 of 
`set(...)`).
+    - `org.apache.juneau.swap.ObjectSwap` (cast on line 1259 of 
`setPropertyValue`'s defensive double-unswap check).
+    - `MarshallingSession`-typed parameter on `setPropertyValue`, 
`applyChildPropertiesFilter(MarshallingSession, ClassMeta, Object)`, 
`swapAndFilterProperty(MarshallingSession, Object)`, plus 
`BiFunction<MarshallingSession,Object,Object>` field types on 
`readTransform`/`writeTransform` and 
`Builder.readTransform`/`Builder.writeTransform` setter parameters.
+    - Three remaining `((MarshallingContext) bc).X()` casts: 
`Builder.rawMetaType(Class<?>)` (`getClassMeta(value)`), the constructor's `ap` 
initialization (`getAnnotationProvider()`), and `applyChildPropertiesFilter` 
(`getBeanMeta(o.getClass())`).
+    - `applyChildPropertiesFilter` signature: `(MarshallingSession session, 
ClassMeta cm, Object o)` — both `MarshallingSession` and `ClassMeta` are 
marshalling-side; lifting this method to a marshalling-side post-processor (or 
accepting an `Object`-typed signature with marshalling-side narrowing) is the 
cleanest move.
+    - `newBeanMap(MarshallingSession session, Object o, BeanMetaFiltered 
meta)` private static helper — `BeanMetaFiltered` is one of the 8 (moves with 
cluster); `MarshallingSession` is the blocker.
+    - `BeanMeta#installSwapAwareTransforms` — already moved to 
`MarshalledPropertyPostProcessor`, but `BeanPropertyMeta.Builder` still has 
package-private `swap` / `rawTypeMeta` fields read from 
`MarshalledPropertyPostProcessor` (cross-package access becomes a problem after 
the move).
+  - **`BeanMeta`** — still imports `annotation.*`.
+    - `@Marshalled`, `@BeanType`, `@BeanProp`, `@BeanCtor`, `@BeanIgnore`, 
`@Transient`, `@Name`, `@Uri` annotation reads scattered across `create(...)`, 
the constructor body (line ~453: `ap.find(Marshalled.class, classInfo)`), 
`findBeanConstructor` (`ap.has(BeanType.class, cm)` / `ap.has(Marshalled.class, 
cm)`), `findFluentSetters` (uses `@Marshalled`), `findClassFieldMeta` (uses 
`@BeanProp`, `@Name`, `@BeanIgnore`, `@Transient`), `findGetters`/`findSetters` 
(uses `@BeanProp`, `@Name`,  [...]
+    - `MarshalledFilter` (`beanFilter` is typed `BeanFilter` per Step 8b-i but 
`MarshalledFilter` cast surfaces in `getMarshalledFilter()`).
+    - `create(ClassMeta<T>, ClassInfo)` static factory — uses 
`ClassMeta`/`MarshallingContext.getAnnotationProvider()`; could relocate to 
`MarshalledBeanMetaInitializer` as `MarshalledBeanMetaInitializer.create(...)` 
returning `BeanMetaValue<T>`.
+  - **`BeanMap`** — still imports `annotation.*`, `internal.*`, `swap.*`.
+    - `@Marshalled` Javadoc reference and the `BeanMap.of(T)` factory which 
routes through `MarshallingContext.DEFAULT_SESSION.toBeanMap(bean)` — easiest 
cleanup is to delete `BeanMap.of(T)` and update callers to use 
`MarshallingContext.DEFAULT_SESSION.toBeanMap(bean)` directly (callers in 
`juneau-assertions` need updating; `juneau-assertions` already depends on 
`juneau-marshall`).
+    - `ObjectSwap` cast inside `BeanMap.getBean()` Optional handling.
+    - `org.apache.juneau.internal.*` — likely `ClassInfo` and 
`TypeVariables`-like helpers; check if commons-reachable.
+  - **`BeanMapEntry`** — imports `annotation.*` (Javadoc references to 
`@Marshalled` / `@MarshalledProp`) + `swap.*` (Javadoc `ObjectSwap`). Body 
references: none that survive Phase B/C. Should be a trivial cleanup: replace 
Javadoc fully-qualified names with `{@link org.apache.juneau.swap.ObjectSwap}` 
form so the imports can be dropped.
+  - **`BeanMetaFiltered`** — imports `annotation.*` (just `MarshalledFilter` 
and `@Marshalled` Javadoc). Single line `super(innerMeta.getClassMeta(), 
(MarshalledFilter) innerMeta.getMarshalledFilter(), pNames, null);` — body is 
marshalling-aware. After the cluster moves, this becomes a marshalling-side 
type that lives in `commons.bean` purely because `BeanMeta` does; the 
constructor stays unchanged because it threads marshalling-side data through 
`BeanMeta`'s protected constructor.
+  - **`BeanPropertyValue`** — clean as of Phase C Task 1 (no marshalling-side 
imports survive). Ready to move.
+  - **`BeanPropertyConsumer`** — clean (functional interface, no 
marshalling-side references). Ready to move.
+  - **`BeanProxyInvocationHandler`** — still uses 
`meta.getMarshallingContext().toBeanMap(arg)` inside `equals(Object)`. Route 
this through `BeanSession.toBeanMap(arg)` using the SPI seam; the field is 
already typed against an SPI that `MarshallingContext` exposes.
+
+  **Phase C Task 5 prerequisites (estimated effort):**
+  - Lift the ~20 
`@Marshalled`/`@BeanProp`/`@BeanCtor`/`@BeanIgnore`/`@Transient`/`@Name`/`@Uri` 
annotation reads from `BeanMeta` / `BeanPropertyMeta` to 
`MarshalledBeanMetaInitializer` (or move `@BeanType`/`@Name`/`@Uri` to 
`commons.bean.annotation` since they're bean-modeling concerns). Estimated: 
~150-200 lines of helper code, ~30 call-site changes in 
`BeanMeta`/`BeanPropertyMeta`.
+  - Replace the three `((MarshallingContext) bc).X()` casts in 
`BeanPropertyMeta` with SPI calls (`Builder.rawMetaType(Class<?>)`, constructor 
`ap`, `applyChildPropertiesFilter`). Either extend `BeanTypeResolver` with 
`getClassMeta(Class<?>)` and `getBeanMeta(Class<?>)` methods, or move the call 
sites to a marshalling-side helper.
+  - Move `applyChildPropertiesFilter(MarshallingSession, ClassMeta, Object)` 
to a marshalling-side helper (it's not callable from the commons-side path 
anyway — it throws `UnsupportedOperationException` when `bc == null`).
+  - Retype `BiFunction<MarshallingSession,Object,Object>` fields on 
`BeanPropertyMeta`/`BeanPropertyMeta.Builder` to 
`BiFunction<BeanSession,Object,Object>` (or 
`BiFunction<Object,Object,Object>`), updating the marshalling-side 
`MarshalledPropertyPostProcessor.installSwapAwareTransforms` to cast back.
+  - Route `BeanProxyInvocationHandler`'s 
`meta.getMarshallingContext().toBeanMap(arg)` through 
`BeanSession.toBeanMap(arg)` (the SPI already supports this — just need to use 
a `BeanSession`-typed handle).
+  - Delete `BeanMap.of(T)` (and update `juneau-assertions` callers to use 
`MarshallingContext.DEFAULT_SESSION.toBeanMap` directly) to remove the 
`MarshallingContext.DEFAULT_SESSION` reference from `BeanMap`.
+  - Clean up Javadoc references in `BeanMapEntry`, `BeanMap`, `BeanMeta`, 
`BeanPropertyMeta` to fully-qualify marshalling-side links so the wildcard 
imports can be dropped.
+  - **Then** the physical `git mv` + reference sweep across ~80-120 files + 
verify `juneau-commons` standalone compile + full test suite.
+
+  Total estimated effort for Task 5 completion: ~600-1000 lines of helper code 
lifts + careful import surgery on the 8 files + the actual physical move + 
reference sweep. Recommend a fresh checkpoint pass focused exclusively on Task 
5 prep + Task 5.
 
   **Phase B status (uncommitted, working tree) — COMPLETE, build + full test 
green (49,912 tests pass).**
 
@@ -138,9 +182,9 @@ Known limitations of the commons-side path (acceptable for 
Step 6, scoped for la
 
   - **Phase C Task 1 — Public API widen.** — DONE. See Phase C status block 
above. `BeanPropertyMeta.getClassMeta()` → `BeanTypeInfo<?>`, 
`BeanMeta.getClassMeta()` → `BeanTypeInfo<T>`, `BeanMeta.getBeanRegistry()` / 
`getPropertyBeanRegistry(...)` → `BeanRegistryLookup`, `BeanMap.getClassMeta()` 
→ `BeanTypeInfo<T>`. ~26 marshalling-side callers updated with explicit 
`(ClassMeta<?>)` / `(BeanRegistry)` casts at the call sites that invoke 
`ClassMeta`-only / `BeanRegistry`-only methods. Buil [...]
   - **Phase C Task 2 — `validate(...)` body lift-out.** — DONE (signature 
seam). See Phase C status block above. New `BeanTypeResolver` SPI in 
`commons.bean`. `MarshallingContext` implements it. 
`Builder.validate(BeanTypeResolver bc, ...)` parameter retyped. 
`bc.resolveClassMeta(...)` → `bc.resolveType(...)`, `bc.object()` → 
`bc.objectType()`. Build + full test green. Note: three other marshalling-only 
seams on `BeanPropertyMeta` remain (Builder `rawMetaType(Class<?>)`, 
constructor `ap`  [...]
-  - **Phase C Task 3 — Marshalling-side `BeanMeta` factory helper.** — NOT 
STARTED. The `BeanMeta(BeanTypeInfo<T>, BeanFilter, String[], ClassInfo)` 
protected constructor still contains marshalling-layer-aware logic (cast back 
to `ClassMeta<T>` to read `getMarshallingContext()` / `getBeanConfigContext()`, 
builds swap-aware transforms, registers per-property `BeanRegistry`). Move the 
body of that constructor out to a marshalling-side helper that sets `classMeta` 
and `marshallingContext` v [...]
-  - **Phase C Task 4 — `JsonList`/`JsonMap` final cleanup.** — PARTIAL. Done: 
`setPropertyValue`'s `new JsonList(valueList)` flipped to `new 
ArrayList<>(valueList)` (typed-element abstract-Collection branch). Three test 
assertions updated. Pass-through behavior preserved for raw-`Object`-element 
collections (a03/a04 tests still green). Deferred (test-invisible at the moment 
but still marshalling-layer leaks): two `CharSequence`-handling sites inside 
`setPropertyValue` (`value1 = JsonMap. [...]
-  - **Phase C Task 5 — Physical `git mv` + reference sweep.** — NOT STARTED. 
Prerequisites: Phase C Tasks 2, 3, and the deferred Task 4 CharSequence-parsing 
sites. Before the move, the 8 files still need to drop their marshalling-side 
imports (`annotation.*`, `internal.*`, `collections.*`, `parser.*`, 
`serializer.*`, `swap.*`). After those are cleaned up, `git mv` the 8 files 
into 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/`, 
update `package` declarations, r [...]
+  - **Phase C Task 3 — Marshalling-side `BeanMeta` factory helper.** — DONE. 
See Phase C status block above. New `MarshalledBeanMetaInitializer` in 
`juneau-marshall` owns the marshalling-aware helpers (`buildBeanRegistry`, 
`buildPropertyBeanRegistry`, `findTypeNameInParents`, `findMarshalledTypeName`, 
plus extractors `configOf`/`contextOf`/`classInfoOf` and the relocated 
`findMarshalledFilter`). `installSwapAwareTransforms` relocated from `BeanMeta` 
to `MarshalledPropertyPostProcessor` ( [...]
+  - **Phase C Task 4 — `JsonList`/`JsonMap` final cleanup.** — DONE. The 
remaining CharSequence-handling sites 
(`JsonMap.ofJson(value21).session(session)` and `new 
JsonList(value2).setBeanSession(session)`) now route through new `BeanSession` 
SPI methods `parseToMap(CharSequence)` / `parseToList(CharSequence)`. 
`MarshallingSession` implements both as bridge methods delegating to the 
original `JsonMap.ofJson` / `new JsonList` calls. `BeanPropertyMeta` dropped 
its `import org.apache.juneau [...]
+  - **Phase C Task 5 — Physical `git mv` + reference sweep.** — NOT STARTED. 
Prerequisites partially met (Tasks 1-4-deferred done). The remaining 
marshalling-side coupling in the 8 files is deeper than the original Task 5 
prep scope (see "Phase C Task 5 status" block above for full inventory). 
Recommend a fresh checkpoint pass focused exclusively on: (a) lifting ~20 
`@Marshalled`/`@BeanProp`/`@BeanCtor`/`@BeanIgnore`/`@Transient`/`@Name`/`@Uri` 
annotation reads from `BeanMeta`/`BeanPrope [...]
 - [ ] **Step 8c** — (optional) Cleanup pass for anything that comes up during 
8b-ii: deprecated bridges, stale imports, package-info docs, etc.
 - [ ] **Step 9** — Reference sweep: 80–120 unique files (mostly inside 
`juneau-marshall`). Update imports, Javadoc `{@link …}` references, 
package-info docs.
 - [ ] **Step 10** — Update `juneau-docs` release notes / migration guide 
(`docs/pages/release-notes/9.5.0.md`, `## Package Moves` section) with the 
bean-runtime relocations.

Reply via email to