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 9ff4e3234a2b4274e065e4fabaa45ee13d41d940 Author: James Bognar <[email protected]> AuthorDate: Wed May 13 09:38:16 2026 -0400 refactor: route BeanPropertyMeta/BeanMap through commons SPI seams; lift @Uri to post-processor (TODO-5 Phase C Task 5 Steps A-E1) - Replace remaining MarshallingContext casts with BeanTypeResolver SPI calls - Retype readTransform/writeTransform BiFunctions to BeanSession - Route BeanProxyInvocationHandler.equals through commons-side BeanMap construction - Inline BeanMap.of(T) to bypass MarshallingContext.DEFAULT_SESSION - Lift @Uri annotation reads from BeanPropertyMeta.Builder.validate() to MarshalledPropertyPostProcessor Co-authored-by: Cursor <[email protected]> --- .../src/main/java/org/apache/juneau/BeanMap.java | 3 +- .../java/org/apache/juneau/BeanPropertyMeta.java | 37 ++++----- .../apache/juneau/BeanProxyInvocationHandler.java | 4 +- .../juneau/MarshalledPropertyPostProcessor.java | 17 +++- todo/TODO-5-bean-runtime-types-to-commons.md | 95 ++++++++++++++++++++++ 5 files changed, 131 insertions(+), 25 deletions(-) diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java index aee7aa8f68..4ba73a7f2d 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMap.java @@ -76,8 +76,9 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T * @param bean The bean being wrapped. * @return A new {@link BeanMap} instance wrapping the bean. */ + @SuppressWarnings("unchecked") public static <T> BeanMap<T> of(T bean) { - return MarshallingContext.DEFAULT_SESSION.toBeanMap(bean); + return new BeanMap<>(bean, BeanMeta.of((Class<T>) bean.getClass())); } /** 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 1eb2c56926..b2dda12be2 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 @@ -82,7 +82,7 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { */ public static class Builder { BeanMeta<?> beanMeta; // Package-private for BeanMeta access - Object bc; // Object-typed (was MarshallingContext) so the field can live in commons.bean; cast to MarshallingContext at marshalling-side use sites. Null when the owning BeanMeta was built via the commons-side path. + BeanTypeResolver bc; // The bean-modeling SPI seam to the marshalling-side type resolver. Null when the owning BeanMeta was built via the commons-side path. BeanConfigContext config; // Package-private for BeanMeta access. Always non-null — sourced from the owning BeanMeta. String name; // Package-private for BeanMeta access FieldInfo field; // Package-private for BeanMeta access @@ -92,11 +92,11 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { MethodInfo extraKeys; // Package-private for BeanMeta access BeanTypeInfo<?> rawTypeMeta; // Package-private for BeanMeta access (used to install swap-aware transforms). Null on commons-side path (no type resolution). Concrete instances are always {@link ClassMeta} since it's the only in-tree implementation; the field is typed against the bean-modeling SPI seam so the field can live in commons.bean. Object swap; // Object-typed so the field can live in commons.bean; cast to ObjectSwap by marshalling-side consumers. Set only via MarshalledPropertyPostProcessor (marshalling-side post-processor). - BiFunction<MarshallingSession,Object,Object> readTransform; // Package-private; defaults to identity if null. - BiFunction<MarshallingSession,Object,Object> writeTransform; // Package-private; defaults to identity if null. + BiFunction<BeanSession,Object,Object> readTransform; // Package-private; defaults to identity if null. Typed against the commons.bean SPI seam; marshalling-side installers cast the session argument back to {@link MarshallingSession} where needed (see {@link MarshalledPropertyPostProcessor#installSwapAwareTransforms}). + BiFunction<BeanSession,Object,Object> writeTransform; // Package-private; defaults to identity if null. Typed against the commons.bean SPI seam (see readTransform note). List<ClassInfo> dictionaryClasses; // Package-private for BeanMeta access; @MarshalledProp(dictionary={}) classes scanned during validate(). private boolean isConstructorArg; - private boolean isUri; + boolean isUri; // Package-private so MarshalledPropertyPostProcessor can set @Uri-derived flag. Mirrors rawTypeMeta.isUri() plus @Uri annotation reads on field/getter/setter. private boolean isDyna; private boolean isDynaGetterMap; BeanTypeInfo<?> typeMeta; // Package-private so the marshalling-side post-processor can override after @Swap/@MarshalledProp detection. Concrete instances are always {@link ClassMeta}; typed against the bean-modeling SPI seam. @@ -109,7 +109,7 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { Builder(BeanMeta<?> beanMeta, String name) { this.beanMeta = beanMeta; - this.bc = beanMeta.getMarshallingContext(); + this.bc = beanMeta.getMarshallingContext(); // MarshallingContext implements BeanTypeResolver; null on commons-side path. this.config = beanMeta.getConfig(); this.name = name; } @@ -147,7 +147,7 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { * @param value The transform function. Must not be <jk>null</jk>. * @return This object. */ - public Builder readTransform(BiFunction<MarshallingSession,Object,Object> value) { + public Builder readTransform(BiFunction<BeanSession,Object,Object> value) { readTransform = assertArgNotNull(ARG_value, value); return this; } @@ -167,7 +167,7 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { * @param value The transform function. Must not be <jk>null</jk>. * @return This object. */ - public Builder writeTransform(BiFunction<MarshallingSession,Object,Object> value) { + public Builder writeTransform(BiFunction<BeanSession,Object,Object> value) { writeTransform = assertArgNotNull(ARG_value, value); return this; } @@ -199,13 +199,13 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { * Sets the raw metadata type for this bean property from a {@link Class}. * * <p> - * Convenience overload that resolves the supplied class to a {@link ClassMeta} via the property's - * {@link MarshallingContext}, allowing callers from the bean-modeling layer to seed the type without - * holding a {@link ClassMeta} reference. + * Convenience overload that resolves the supplied class via the property's + * {@link BeanTypeResolver}, allowing callers from the bean-modeling layer to seed the type without + * holding a {@link BeanTypeInfo} reference. * * <p> * When the owning {@link BeanMeta} was built via the commons-side path - * ({@link BeanMeta#of(Class, BeanConfigContext)}), no marshalling context is available, so + * ({@link BeanMeta#of(Class, BeanConfigContext)}), no resolver is available, so * {@code rawTypeMeta}/{@code typeMeta} are left <jk>null</jk> and the property runs in raw-reflection mode. * * @param value The raw metadata type for this bean property. @@ -215,7 +215,9 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { assertArgNotNull(ARG_value, value); if (bc == null) return this; - return rawMetaType(((MarshallingContext) bc).getClassMeta(value)); + rawTypeMeta = bc.resolveType(null, info(value), null); + typeMeta = rawTypeMeta; + return this; } /** @@ -375,7 +377,6 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { if (ne(beanp.wo())) writeOnly = bool(beanp.wo()); }); - isUri |= ap.has(Uri.class, ifi); } if (nn(getter)) { @@ -384,7 +385,6 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { rawTypeMeta = bc.resolveType(opt(last(lbp)).orElse(null), getter.getReturnType(), typeVarImpls); if (nn(rawTypeMeta)) isUri |= rawTypeMeta.isUri(); - isUri |= ap.has(Uri.class, gi); lbp.forEach(x -> { var beanp = x.inner(); if (ne(beanp.ro())) @@ -400,7 +400,6 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { rawTypeMeta = bc.resolveType(opt(last(lbp)).orElse(null), setter.getParameterTypes().get(0), typeVarImpls); if (nn(rawTypeMeta)) isUri |= rawTypeMeta.isUri(); - isUri |= ap.has(Uri.class, si); lbp.forEach(x -> { var beanp = x.inner(); if (ne(beanp.ro())) @@ -498,7 +497,7 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { private final AnnotationProvider ap; // Annotation provider for finding annotations on this property. Sourced from bc (marshalling-side) or beanMeta.getConfig() (commons-side). private final Supplier<List<AnnotationInfo<?>>> annotations; // Memoized list of all annotations on this property. - private final Object bc; // MarshallingContext, but Object-typed so the field can live in commons.bean. Cast at marshalling-side use sites. Null when the owning BeanMeta was built via the commons-side path. + private final BeanTypeResolver bc; // The bean-modeling SPI seam to the marshalling-side type resolver. Null when the owning BeanMeta was built via the commons-side path. private final BeanConfigContext config; // Bean-modeling settings facade — always non-null. Mirrors the BeanMeta's config. private final BeanMeta<?> beanMeta; // The bean that this property belongs to. private final boolean canRead; // True if this property can be read. @@ -515,12 +514,12 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { private final String name; // The name of the property. private final Object overrideValue; // The bean property value (if it's an overridden delegate). private final BeanTypeInfo<?> rawTypeMeta; // The real class type of the bean property. Concrete instances are always {@link ClassMeta}; typed against the bean-modeling SPI seam for the eventual move to commons.bean. - private final BiFunction<MarshallingSession,Object,Object> readTransform; // Applied to raw getter result; identity by default. + private final BiFunction<BeanSession,Object,Object> readTransform; // Applied to raw getter result; identity by default. Typed against the commons.bean SPI seam. private final boolean readOnly; // True if this property is read-only. private final MethodInfo setter; // The bean property setter. private final Object swap; // ObjectSwap, but Object-typed so the field can live in commons.bean; cast at marshalling-side use sites. Defined only via @MarshalledProp(format=...) or @Swap. private final BeanTypeInfo<?> typeMeta; // The transformed class type of the bean property. Concrete instances are always {@link ClassMeta}; typed against the bean-modeling SPI seam. - private final BiFunction<MarshallingSession,Object,Object> writeTransform; // Applied to incoming value before raw setter; identity by default. + private final BiFunction<BeanSession,Object,Object> writeTransform; // Applied to incoming value before raw setter; identity by default. Typed against the commons.bean SPI seam. private final boolean writeOnly; // True if this property is write-only. /** @@ -554,7 +553,7 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { readTransform = b.readTransform != null ? b.readTransform : (session, o) -> o; writeTransform = b.writeTransform != null ? b.writeTransform : (session, o) -> o; - ap = nn(bc) ? ((MarshallingContext) bc).getAnnotationProvider() : b.config.getAnnotationProvider(); + ap = nn(bc) ? bc.getAnnotationProvider() : b.config.getAnnotationProvider(); hashCode = h(beanMeta, name); } diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java index be571d4962..56327347a8 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanProxyInvocationHandler.java @@ -131,7 +131,9 @@ public class BeanProxyInvocationHandler<T> implements InvocationHandler { return beanProps.equals(ih2.beanProps); } } - return eq(beanProps, meta.getMarshallingContext().toBeanMap(arg)); + @SuppressWarnings("unchecked") + var argMeta = (BeanMeta<Object>) BeanMeta.of(arg.getClass(), meta.getConfig()); + return eq(beanProps, BeanMap.of(arg, argMeta)); } if (mi.hasName("hashCode") && mi.getParameterCount() == 0) 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 d7638d1591..2265b8bd7e 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 @@ -80,6 +80,7 @@ final class MarshalledPropertyPostProcessor { bdClasses.addAll(l(mp.dictionary())); }); ap.find(Swap.class, b.innerField).stream().findFirst().ifPresent(x -> b.swap = swapSwap(x)); + b.isUri |= ap.has(Uri.class, b.innerField); } if (nn(b.getter)) { @@ -90,6 +91,7 @@ final class MarshalledPropertyPostProcessor { bdClasses.addAll(l(mp.dictionary())); }); ap.find(Swap.class, b.getter).stream().forEach(x -> b.swap = swapSwap(x)); + b.isUri |= ap.has(Uri.class, b.getter); } if (nn(b.setter)) { @@ -100,6 +102,7 @@ final class MarshalledPropertyPostProcessor { bdClasses.addAll(l(mp.dictionary())); }); ap.find(Swap.class, b.setter).stream().forEach(x -> b.swap = swapSwap(x)); + b.isUri |= ap.has(Uri.class, b.setter); } if (! bdClasses.isEmpty()) { @@ -152,14 +155,18 @@ final class MarshalledPropertyPostProcessor { if (p.readTransform == null) { p.readTransform = (session, o) -> { try { + // The transform is typed against BeanSession (commons.bean SPI) but ObjectSwap.swap requires a + // MarshallingSession. The marshalling-side BeanMap always wires a MarshallingSession into its + // transform call sites, so the narrowing cast is safe. + var ms = (MarshallingSession) session; if (nn(sw)) - return sw.swap(session, o); + return sw.swap(ms, o); if (o == null) return null; if (rtm.hasChildSwaps()) { ObjectSwap f = rtm.getChildObjectSwapForSwap(o.getClass()); if (nn(f)) - return f.swap(session, o); + return f.swap(ms, o); } return o; } catch (RuntimeException e) { @@ -172,14 +179,16 @@ final class MarshalledPropertyPostProcessor { if (p.writeTransform == null) { p.writeTransform = (session, o) -> { try { + // See readTransform note: BeanSession → MarshallingSession is safe on the marshalling-side path. + var ms = (MarshallingSession) session; if (nn(sw)) - return sw.unswap(session, o, rtm); + return sw.unswap(ms, 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 f.unswap(ms, o, rtm); } return o; } catch (RuntimeException e) { diff --git a/todo/TODO-5-bean-runtime-types-to-commons.md b/todo/TODO-5-bean-runtime-types-to-commons.md index eb62da62cb..f7eec1ef29 100644 --- a/todo/TODO-5-bean-runtime-types-to-commons.md +++ b/todo/TODO-5-bean-runtime-types-to-commons.md @@ -38,6 +38,101 @@ Build + full test green (`scripts/test.py --full`). --- +## Status (Phase C Task 5 — Steps A-E1 landed, uncommitted) + +**Phase C Task 5 Steps A-E1 LANDED in the working tree (uncommitted).** Build + targeted tests green. The bean-runtime cluster files are not yet moved (Steps G-J), but additional SPI decoupling is in place: + +### Steps completed in this checkpoint + +- **Step A — Two `((MarshallingContext) bc).X()` casts replaced with `BeanTypeResolver` SPI calls.** + - `BeanPropertyMeta.Builder.bc` and `BeanPropertyMeta.bc` instance fields retyped from `Object` to `BeanTypeResolver`. `MarshallingContext` implements `BeanTypeResolver`, so the marshalling-side construction path is unchanged. + - `Builder.rawMetaType(Class<?>)` now calls `bc.resolveType(null, info(value), null)` instead of `((MarshallingContext) bc).getClassMeta(value)`. + - `BeanPropertyMeta` constructor `ap` initialization now reads `bc.getAnnotationProvider()` directly without a `MarshallingContext` cast. + +- **Step B — `BiFunction<MarshallingSession,Object,Object>` retyped to `BiFunction<BeanSession,Object,Object>`.** + - `BeanPropertyMeta.Builder.readTransform` / `writeTransform` fields plus `BeanPropertyMeta.readTransform` / `writeTransform` instance fields and the corresponding Builder setter signatures all retyped to use the `BeanSession` SPI. + - Lambdas inside `MarshalledPropertyPostProcessor.installSwapAwareTransforms` now narrow `session` to `MarshallingSession` via a local `var ms = (MarshallingSession) session;` since `ObjectSwap.swap`/`ObjectSwap.unswap` require the marshalling-side session. Comment documents that the marshalling-side `BeanMap` always wires a `MarshallingSession`, so the narrowing cast is safe. + +- **Step C — `BeanProxyInvocationHandler.equals` routed off `meta.getMarshallingContext().toBeanMap(arg)`.** + - Replaced with `BeanMap.of(arg, (BeanMeta<Object>) BeanMeta.of(arg.getClass(), meta.getConfig()))` (the `BeanMap.of(T, BeanMeta<T>)` static factory shipped in Step 6). + - Equality semantics preserved: builds a fresh `BeanMeta` against the same `BeanConfigContext`, then compares property maps. No session is wired; equality only reads. + +- **Step D — `BeanMap.of(T)` static factory inlined.** + - Replaced `MarshallingContext.DEFAULT_SESSION.toBeanMap(bean)` with `new BeanMap<>(bean, BeanMeta.of((Class<T>) bean.getClass()))`. + - **Behavioral change:** The returned `BeanMap` no longer carries a `MarshallingSession`, so `ObjectSwap` transformations are not applied through `get`/`put`. The previous behavior was session-aware via `DEFAULT_SESSION`; the new behavior is bean-modeling-only. Verified by running `BeanMap_Test`, `BeanProxyInvocationHandler_Test`, `BeanMeta_Test` (all green) and the full `--build-only` check. + +- **Step E1 — `@Uri` annotation reads lifted from `BeanPropertyMeta.Builder.validate()` to `MarshalledPropertyPostProcessor.process()`.** + - The three `isUri |= ap.has(Uri.class, ifi/gi/si)` reads inside `validate()` are gone; the equivalent reads now live in the marshalling-side post-processor and update the same `b.isUri` flag (now package-private on the builder so the post-processor can write it). + - The `rawTypeMeta.isUri()` reads remain in `validate()` — they are bean-modeling concerns (`BeanTypeInfo.isUri()` is the commons SPI). + +### Build / test status + +- `python3 scripts/test.py --build-only` — **GREEN** after each step. +- Targeted tests run (`BeanProxyInvocationHandler_Test`, `BeanMap_Test`, `BeanMeta_Test`, `transforms/BeanMap_Test`, `UriAnnotation_Test`) — all 86 tests green. +- Full test suite not re-run (per "incomplete-but-documented over broken-build" rule below). + +### Step F audit — remaining juneau-marshall coupling in the 7 files + +A full audit reveals the cluster is **not yet move-ready**. The 7 files still reference these `juneau-marshall` types: + +**`BeanMap.java`** still depends on: +- `MarshallingSession` — held as the `private MarshallingSession session` field, returned from `getMarshallingSession()`, accepted by `setMarshallingSession(MarshallingSession)`. Used in `getBean(boolean)` for `session.convertToType(rawVal, cm)` (constructor-args path). +- `ClassMeta` — used as `var cm = pm.getClassMeta()` in `getBean(boolean)` and via `meta.getClassMeta()` in Javadoc/error formatting. +- `org.apache.juneau.annotation.*` (wildcard) — only used for Javadoc cross-references (`@Marshalled`, `@Swap`, `@MarshalledProp` etc.). +- `org.apache.juneau.internal.*` — used for `FilteredKeyMap` (instantiated in `keySet()`). FilteredKeyMap itself depends on `ClassMeta`. +- `org.apache.juneau.swap.*` — Javadoc-only references to `ObjectSwap`. + +**`BeanMapEntry.java`** still depends on: +- `org.apache.juneau.annotation.*` — wildcard import. Need to audit whether any non-Javadoc reference survives. +- `org.apache.juneau.swap.*` — Javadoc-only references. + +**`BeanMeta.java`** still depends on: +- `org.apache.juneau.annotation.*` — actively reads `@Marshalled` (lines 226, 232, 453, 1178) for bean detection, `typePropertyName`, and constructor-visibility relaxation. Reads `@Name` (~6 sites) for property-name resolution. +- `Marshalled` annotation reads still need lifting into `MarshalledBeanMetaInitializer` (helper methods that take the `AnnotationProvider` + `ClassInfo` and return: (1) `isBean(...)`, (2) `typePropertyName(...)`, (3) `allowsPrivateConstructor(...)`). +- `@Name` could either move to `commons.bean` (clean but ~26 files would need import updates) or stay in `juneau-marshall.annotation` and have its reads abstracted behind a marshalling-side helper. +- `MarshalledBeanMetaInitializer.findMarshalledFilter(cm)` and `.classInfoOf(cm)`, etc. — already in place; bean-side path works without them. +- `MarshalledPropertyPostProcessor.process((MarshallingContext) marshallingContext, p)` — called via cast; `marshallingContext` field is `Object`-typed already. The post-processor itself stays in juneau-marshall. +- `MarshallingContext` — referenced only via the cast above plus three Javadoc cross-references. +- `BeanRegistry` — narrowing cast inside `getBeanRegistry()` and `getPropertyBeanRegistry()`. Could stay marshalling-side-only since the bean-side type is the wider `BeanRegistryLookup`. +- `MarshalledFilter` — narrowing cast inside `getMarshalledFilter()`. Same pattern. +- `BeanProxyInvocationHandler` — instantiated in the `beanProxyInvocationHandler` supplier. Both types move together. + +**`BeanPropertyMeta.java`** still depends on: +- `MarshallingSession` — parameter type on the private `setPropertyValue(BeanMap<?>, String, Object, Object, boolean, boolean, MarshallingSession)`. Method is invoked from `set(...)` with `session = m.getMarshallingSession()`. Retyping to `BeanSession` is mechanically straightforward (the `session.parseToMap` / `session.parseToList` / `session.convertToType` / `session.convertToMemberType` methods are already on `BeanSession`), but **the method body still casts `swap` to `ObjectSwap` at [...] +- `ObjectSwap` — narrowing cast at line 1249 (`((ObjectSwap) swap).getSwapClass()`). Need to either: (a) extract this branch into a marshalling-side helper, (b) add a separate `Class<?> swapClass` field on the builder to avoid the cast, or (c) accept the cast lives at a narrow site that can stay `Object`-typed plus runtime reflection on a method reference. +- `BeanInstantiator` — used in two sites inside `setPropertyValue` to build empty collections/maps. Lives in `commons.inject`, so it's already commons-side. **Re-checked: `BeanInstantiator` is in `org.apache.juneau.commons.inject`, NOT marshalling-side. No action needed.** +- `ParseException` / `SerializeException` — caught/thrown inside `set`/`setPropertyValue`. Both live in `juneau-marshall.parser` / `juneau-marshall.serializer`. Move-blocker. +- `BeanRegistry` — narrowing cast inside `getBeanRegistry()`. Stays marshalling-side. +- `ClassMeta` — Javadoc cross-references only. +- `Uri` — fully lifted (Step E1). +- `Name` — Javadoc-only references after Step E1. Worth confirming with a grep before move. + +**`BeanPropertyValue.java`** — clean. Only `commons.*` imports. + +**`BeanPropertyConsumer.java`** — clean. Only `commons.*` imports. + +**`BeanProxyInvocationHandler.java`** — clean. Only commons static imports after Step C. + +### Why the move is not yet safe (summary) + +The two stubborn move-blockers are: + +1. **`BeanMap.session` typed as `MarshallingSession`** and the `getBean(boolean)` constructor-args path that calls `session.convertToType`. The field could be retyped to `BeanSession`, the public `getMarshallingSession()` removed/renamed to `getBeanSession()`, and the one external caller (`ParserSession` at line 902) adjusted to cast. That's still 20-30 lines of mechanical work but introduces a public-API breaking change to `BeanMap`. +2. **`BeanPropertyMeta.setPropertyValue(..., MarshallingSession session)`** retains an `(ObjectSwap) swap` cast inside the function body. Cleanest fix: capture the swap class as a side-data on the builder when `installSwapAwareTransforms` runs, replacing the cast with a `Class<?>` comparison. Mechanical but adds one more SPI field. + +Plus the secondary work: + +3. **`BeanMeta` `@Marshalled` reads** — three call sites need lift-out to `MarshalledBeanMetaInitializer`. Mechanical. +4. **`@Name` handling** — either move to `commons.bean` (clean but ~26 file import-fixups) or hide behind a marshalling-side helper (extra plumbing for property-name resolution). +5. **`FilteredKeyMap` relocation** — move to `commons.collections`, retype its `classMeta` field to `BeanTypeInfo`. +6. **`ParseException` / `SerializeException` references in `BeanPropertyMeta`** — replace with `RuntimeException` rethrows or a commons-side `BeanRuntimeException` wrapper. + +### Next step recommendation + +Pick up the remaining SPI cleanup as a fresh checkpoint focused on items 1-6 above. The cluster is much closer to move-ready than it was; the heavy lifts from Phase 5a/8a/8b are all in place. After the remaining items land, Steps G-J (the physical `git mv` + reference sweep + standalone compile verification) should be a couple of hours of mechanical work plus the import-fix wave. + +--- + ## Status (as of Phase C Tasks 1-2-3-4-4-deferred checkpoint, uncommitted) **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:
