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 a61bede3b5c988f7de5c260284d9ac8d9fe6a9cb
Author: James Bognar <[email protected]>
AuthorDate: Tue May 12 15:37:27 2026 -0400

    refactor: extract BeanTypeInfo/BeanFilter/BeanRegistryLookup SPI seams in 
commons.bean (TODO-5 Step 8b-i)
    
    Co-authored-by: Cursor <[email protected]>
---
 .../org/apache/juneau/commons/bean/BeanFilter.java | 131 ++++++++++++++++++
 .../juneau/commons/bean/BeanRegistryLookup.java    |  51 +++++++
 .../apache/juneau/commons/bean/BeanTypeInfo.java   | 147 +++++++++++++++++++++
 .../org/apache/juneau/commons/bean}/Delegate.java  |  14 +-
 .../apache/juneau/jena/RdfSerializerSession.java   |   3 +-
 .../juneau/jena/RdfStreamSerializerSession.java    |   3 +-
 .../src/main/java/org/apache/juneau/BeanMeta.java  |  10 +-
 .../main/java/org/apache/juneau/BeanRegistry.java  |   3 +-
 .../src/main/java/org/apache/juneau/ClassMeta.java |   4 +-
 .../java/org/apache/juneau/MarshalledFilter.java   |   2 +-
 .../apache/juneau/html/HtmlSerializerSession.java  |   5 +-
 .../org/apache/juneau/internal/DelegateList.java   |   1 +
 .../org/apache/juneau/internal/DelegateMap.java    |   1 +
 .../org/apache/juneau/internal/FilteredKeyMap.java |   1 +
 .../apache/juneau/xml/XmlSerializerSession.java    |   5 +-
 todo/TODO-5-bean-runtime-types-to-commons.md       |  30 ++++-
 16 files changed, 389 insertions(+), 22 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanFilter.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanFilter.java
new file mode 100644
index 0000000000..740e506ad8
--- /dev/null
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanFilter.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.commons.bean;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.juneau.commons.reflect.ClassInfo;
+import org.apache.juneau.commons.reflect.ClassInfoTyped;
+
+/**
+ * Bean-modeling SPI seam that exposes the per-class filter surface the 
bean-runtime types
+ * ({@link BeanMeta}-equivalent) need without coupling the bean-modeling layer 
to the marshalling-side
+ * {@code MarshalledFilter}.
+ *
+ * <p>
+ * Marshalling-side {@code MarshalledFilter} implements this interface; 
bean-modeling-side code only
+ * sees {@link BeanFilter}.  This interface is restricted to 
commons-compatible return types
+ * (collections, {@link ClassInfo}, {@link PropertyNamer}, plain values).
+ */
+public interface BeanFilter {
+
+       /**
+        * @return The bean class that this filter applies to, or <jk>null</jk> 
if this is a non-bean filter.
+        */
+       ClassInfoTyped<?> getBeanClass();
+
+       /**
+        * @return The class that this filter applies to.
+        */
+       Class<?> getMarshalledClass();
+
+       /**
+        * @return The dictionary name associated with this bean, or 
<jk>null</jk> if no name is defined.
+        */
+       String getTypeName();
+
+       /**
+        * @return The example associated with this class, or <jk>null</jk>.
+        */
+       String getExample();
+
+       /**
+        * @return The implementation class associated with this class, or 
<jk>null</jk>.
+        */
+       ClassInfo getImplClass();
+
+       /**
+        * @return The interface class associated with this class, or 
<jk>null</jk>.
+        */
+       ClassInfo getInterfaceClass();
+
+       /**
+        * @return The stop class associated with this class, or <jk>null</jk>.
+        */
+       ClassInfo getStopClass();
+
+       /**
+        * @return The names of the properties associated with the bean class 
(ordered), or an empty set if all properties.
+        */
+       Set<String> getProperties();
+
+       /**
+        * @return The names of the properties to ignore on a bean, or an empty 
set.
+        */
+       Set<String> getExcludeProperties();
+
+       /**
+        * @return The names of the read-only properties on a bean, or an empty 
set.
+        */
+       Set<String> getReadOnlyProperties();
+
+       /**
+        * @return The names of the write-only properties on a bean, or an 
empty set.
+        */
+       Set<String> getWriteOnlyProperties();
+
+       /**
+        * @return The property namer for this filter, or <jk>null</jk>.
+        */
+       PropertyNamer getPropertyNamer();
+
+       /**
+        * @return The list of bean dictionary classes, or an empty list.
+        */
+       List<ClassInfo> getBeanDictionary();
+
+       /**
+        * @return <jk>true</jk> if fluent setters should be found.
+        */
+       boolean isFluentSetters();
+
+       /**
+        * @return <jk>true</jk> if this bean opts out of alphabetical property 
sorting.
+        */
+       boolean isUnsortedProperties();
+
+       /**
+        * Calls the {@link BeanInterceptor#readProperty(Object, String, 
Object)} on the registered interceptor.
+        *
+        * @param bean The bean from which the property was read.
+        * @param name The property name.
+        * @param value The value just extracted from calling the bean getter.
+        * @return The value to serialize.  Default is just to return the 
existing value.
+        */
+       Object readProperty(Object bean, String name, Object value);
+
+       /**
+        * Calls the {@link BeanInterceptor#writeProperty(Object, String, 
Object)} on the registered interceptor.
+        *
+        * @param bean The bean to which the property is being written.
+        * @param name The property name.
+        * @param value The value just parsed.
+        * @return The value to assign.  Default is just to return the existing 
value.
+        */
+       Object writeProperty(Object bean, String name, Object value);
+}
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanRegistryLookup.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanRegistryLookup.java
new file mode 100644
index 0000000000..af2963c58d
--- /dev/null
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanRegistryLookup.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.commons.bean;
+
+/**
+ * Bean-modeling SPI seam that exposes the dictionary-lookup surface the 
bean-runtime types need without
+ * coupling the bean-modeling layer to the marshalling-side {@code 
BeanRegistry}.
+ *
+ * <p>
+ * Marshalling-side {@code BeanRegistry} implements this interface.  The 
bean-modeling-side
+ * only sees {@link BeanRegistryLookup} when it needs to translate between a 
raw {@link Class}
+ * and a polymorphic type name.  All operations involving {@code ClassMeta} 
(e.g.
+ * {@code getClassMeta(String)}) remain on the marshalling-side {@code 
BeanRegistry} itself
+ * and are reached only via narrowing casts from marshalling-side call sites.
+ *
+ * <p>
+ * The bean-modeling layer never instantiates a registry directly — instances 
are always supplied
+ * by the marshalling layer via marshalling-side hooks.
+ */
+public interface BeanRegistryLookup {
+
+       /**
+        * Given the specified raw class, return the dictionary name for it, or 
<jk>null</jk> if not found.
+        *
+        * @param c The class to lookup in this registry.
+        * @return The dictionary name for the specified class, or 
<jk>null</jk> if not found.
+        */
+       String getTypeName(Class<?> c);
+
+       /**
+        * Returns <jk>true</jk> if this dictionary has an entry for the 
specified type name.
+        *
+        * @param typeName The bean type name.
+        * @return <jk>true</jk> if this dictionary has an entry for the 
specified type name.
+        */
+       boolean hasName(String typeName);
+}
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeInfo.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeInfo.java
new file mode 100644
index 0000000000..ac96183c0c
--- /dev/null
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeInfo.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.commons.bean;
+
+import java.lang.reflect.Type;
+
+import org.apache.juneau.commons.reflect.ClassInfoTyped;
+import org.apache.juneau.commons.reflect.ExecutableException;
+
+/**
+ * Bean-modeling SPI seam that exposes the type-classification surface the 
bean-runtime types
+ * ({@link BeanMeta}-equivalents, {@link BeanPropertyMeta}-equivalents, etc.) 
need without coupling
+ * the bean-modeling layer to the marshalling-side {@code ClassMeta}.
+ *
+ * <p>
+ * Marshalling-side {@code ClassMeta&lt;T&gt;} extends this class; 
bean-modeling-side code only sees
+ * {@link BeanTypeInfo}.  The bean-modeling-side never instantiates this class 
directly — it always
+ * gets instances handed in by the marshalling layer (e.g. via builder calls 
like
+ * {@code BeanPropertyMeta.Builder#rawMetaType(ClassMeta)}).
+ *
+ * <p>
+ * All extra abstract methods declared here mirror methods that {@code 
ClassMeta} already implements,
+ * so {@code ClassMeta} satisfies this contract by virtue of its existing 
implementation.
+ *
+ * @param <T> The raw class type this instance represents.
+ */
+public abstract class BeanTypeInfo<T> extends ClassInfoTyped<T> {
+
+       /**
+        * Constructor.
+        *
+        * @param inner The class type.
+        */
+       protected BeanTypeInfo(Class<T> inner) {
+               super(inner);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param inner The class type.
+        * @param innerType The generic type (if parameterized type).
+        */
+       protected BeanTypeInfo(Class<T> inner, Type innerType) {
+               super(inner, innerType);
+       }
+
+       /**
+        * Returns <jk>true</jk> if this class is a URI/URL or annotated with a 
URI marker.
+        *
+        * @return <jk>true</jk> if this class is a URI.
+        */
+       public abstract boolean isUri();
+
+       /**
+        * Returns <jk>true</jk> if this class is a {@link java.util.Optional}.
+        *
+        * @return <jk>true</jk> if this class is an Optional.
+        */
+       public abstract boolean isOptional();
+
+       /**
+        * Returns <jk>true</jk> if this class is classified as a bean.
+        *
+        * @return <jk>true</jk> if this class is a bean.
+        */
+       public abstract boolean isBean();
+
+       /**
+        * Returns <jk>true</jk> if this class is {@link Object}.
+        *
+        * @return <jk>true</jk> if this class is {@code Object}.
+        */
+       public abstract boolean isObject();
+
+       /**
+        * For array and {@code Collection} types, returns the type info of the 
element type, or <jk>null</jk>
+        * if this is not an array/collection.
+        *
+        * @return The element type info, or <jk>null</jk>.
+        */
+       public abstract BeanTypeInfo<?> getElementType();
+
+       /**
+        * For {@code Map} types, returns the type info of the key type, or 
<jk>null</jk> if this is not a map.
+        *
+        * @return The key type info, or <jk>null</jk>.
+        */
+       public abstract BeanTypeInfo<?> getKeyType();
+
+       /**
+        * For {@code Map} types, returns the type info of the value type, or 
<jk>null</jk> if this is not a map.
+        *
+        * @return The value type info, or <jk>null</jk>.
+        */
+       public abstract BeanTypeInfo<?> getValueType();
+
+       /**
+        * Returns <jk>true</jk> if this class can be instantiated using a 
no-arg constructor.
+        *
+        * @return <jk>true</jk> if a new instance can be created.
+        */
+       public abstract boolean canCreateNewInstance();
+
+       /**
+        * Returns <jk>true</jk> if this class can be instantiated, optionally 
with the specified outer object
+        * for non-static inner classes.
+        *
+        * @param outer The outer object instance, or <jk>null</jk> if not 
applicable.
+        * @return <jk>true</jk> if a new instance can be created.
+        */
+       public abstract boolean canCreateNewInstance(Object outer);
+
+       /**
+        * Creates a new instance of this class.
+        *
+        * @return A new instance of the class.
+        * @throws ExecutableException If the class could not be instantiated.
+        */
+       public abstract T newInstance() throws ExecutableException;
+
+       /**
+        * For {@link java.util.Optional} types, returns an empty optional 
default value, or <jk>null</jk>
+        * if this isn't an Optional.
+        *
+        * <p>
+        * Returns {@link Object} rather than {@code T} because the 
marshalling-side {@code ClassMeta} returns
+        * a wildcard {@code Optional<?>} which is not assignable to {@code T} 
under Java's type system.
+        *
+        * @return The default empty Optional value, or <jk>null</jk>.
+        */
+       public abstract Object getOptionalDefault();
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Delegate.java 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/Delegate.java
similarity index 69%
rename from 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Delegate.java
rename to 
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/Delegate.java
index 9f3b58ec6a..605003c830 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Delegate.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/Delegate.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau;
+package org.apache.juneau.commons.bean;
 
 /**
  * An object that represents another object, often wrapping that object.
@@ -23,17 +23,21 @@ package org.apache.juneau;
  * <b>*** Internal Interface - Not intended for external use ***</b>
  *
  * <p>
- * For example, {@link BeanMap} is a map representation of a bean.
+ * For example, {@code BeanMap} is a map representation of a bean.
  *
+ * <p>
+ * The returned type info is a {@link BeanTypeInfo} so the bean-modeling layer 
does not depend
+ * on the marshalling-side {@code ClassMeta}.  Marshalling-side 
implementations narrow the return
+ * type via Java covariant returns (e.g. {@code ClassMeta<T> getClassMeta()}).
  *
  * @param <T> The represented class type.
  */
 public interface Delegate<T> {
 
        /**
-        * The {@link ClassMeta} of the class of the represented object.
+        * The {@link BeanTypeInfo} of the class of the represented object.
         *
         * @return The class type of the represented object.
         */
-       ClassMeta<T> getClassMeta();
-}
\ No newline at end of file
+       BeanTypeInfo<T> getClassMeta();
+}
diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
index 84f964b21d..b0e5a6ae93 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfSerializerSession.java
@@ -32,6 +32,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.utils.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.jena.annotation.*;
@@ -340,7 +341,7 @@ public class RdfSerializerSession extends 
WriterSerializerSession {
 
                        if (aType.isDelegate()) {
                                wType = aType;
-                               aType = ((Delegate)o).getClassMeta();
+                               aType = (ClassMeta)((Delegate)o).getClassMeta();
                        }
 
                        sType = aType;
diff --git 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfStreamSerializerSession.java
 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfStreamSerializerSession.java
index f8461aa88b..e6ad4d09a1 100644
--- 
a/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfStreamSerializerSession.java
+++ 
b/juneau-core/juneau-marshall-rdf/src/main/java/org/apache/juneau/jena/RdfStreamSerializerSession.java
@@ -29,6 +29,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.utils.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.serializer.*;
@@ -284,7 +285,7 @@ public class RdfStreamSerializerSession extends 
OutputStreamSerializerSession {
                if (nn(o)) {
                        if (aType.isDelegate()) {
                                wType = aType;
-                               aType = ((Delegate)o).getClassMeta();
+                               aType = (ClassMeta)((Delegate)o).getClassMeta();
                        }
                        sType = aType;
                        var swap = aType.getSwap(this);
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 eb0cc118cd..8795a98aba 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
@@ -361,7 +361,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 BeanConfigContext config;                                 
   // Bean-modeling settings facade — always non-null.  Sources: 
marshallingContext.getBeanConfigContext() (marshalling-side) or the explicit 
BeanConfigContext (commons-side).
-       private final MarshalledFilter beanFilter;                              
         // Optional bean filter associated with the target class.
+       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).
        private final Supplier<BeanRegistry> beanRegistry;                      
   // The bean registry for this bean.
        private final Supplier<List<ClassInfo>> classHierarchy;                 
   // List of all classes traversed in the class hierarchy.
@@ -444,7 +444,7 @@ public class BeanMeta<T> {
         * @param pNames Explicit list of property names and order. If 
<jk>null</jk>, properties are determined automatically.
         * @param implClass Optional implementation class constructor to use if 
one cannot be found. Can be <jk>null</jk>.
         */
-       protected BeanMeta(ClassMeta<T> cm, MarshalledFilter bf, String[] 
pNames, ClassInfo implClass) {
+       protected BeanMeta(ClassMeta<T> cm, BeanFilter bf, String[] pNames, 
ClassInfo implClass) {
                this(cm, cm, cm.getMarshallingContext().getBeanConfigContext(), 
cm.getMarshallingContext(), bf, pNames, implClass);
        }
 
@@ -468,7 +468,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, MarshalledFilter bf, String[] pNames, ClassInfo 
implClass) {
+       private BeanMeta(ClassMeta<T> cm, ClassInfo ci0, BeanConfigContext 
config, MarshallingContext mc, BeanFilter bf, String[] pNames, ClassInfo 
implClass) {
                classMeta = cm;
                classInfo = ci0;
                this.config = config;
@@ -809,7 +809,9 @@ public class BeanMeta<T> {
         * @see Bean
         */
        public MarshalledFilter getMarshalledFilter() {
-               return beanFilter;
+               // The field is typed as the bean-modeling-side BeanFilter SPI 
but the only concrete
+               // implementation in-tree is MarshalledFilter, so the narrowing 
cast is safe.
+               return (MarshalledFilter) beanFilter;
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanRegistry.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanRegistry.java
index a1197c11bd..831d2f5d54 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanRegistry.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanRegistry.java
@@ -27,6 +27,7 @@ import java.util.*;
 import java.util.concurrent.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.collections.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.commons.reflect.*;
@@ -55,7 +56,7 @@ import org.apache.juneau.commons.utils.*;
        "java:S115", // Constants use UPPER_snakeCase naming convention
        "java:S1452"  // Wildcard required - Class<?> for bean dictionary types
 })
-public class BeanRegistry {
+public class BeanRegistry implements BeanRegistryLookup {
 
        // Argument name constants for assertArgNotNull
        private static final String ARG_bc = "bc";
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index cf99d27494..f05140c218 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -36,7 +36,7 @@ import java.util.function.*;
 import java.util.stream.*;
 
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.commons.bean.BeanType;
+import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.collections.*;
 import org.apache.juneau.commons.conversion.*;
 import org.apache.juneau.commons.function.*;
@@ -73,7 +73,7 @@ import org.apache.juneau.swap.*;
        "java:S1452",  // Wildcard required - ClassMeta<?>, ObjectSwap<T,?>, 
etc. for element/component types
        "java:S6539"   // Monster Class: ClassMeta is a focused 
reflection-metadata cache; splitting would increase coupling
 })
-public class ClassMeta<T> extends ClassInfoTyped<T> {
+public class ClassMeta<T> extends BeanTypeInfo<T> {
 
        private static class Categories {
                int bits;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledFilter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledFilter.java
index 8df0ec7415..bf7dc0a076 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledFilter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/MarshalledFilter.java
@@ -82,7 +82,7 @@ import org.apache.juneau.swap.*;
        "rawtypes",   // Raw types necessary for generic type handling
        "java:S1452" // Wildcard required - ClassInfoTyped<?>, ClassMeta<?> for 
filter metadata
 })
-public class MarshalledFilter {
+public class MarshalledFilter implements BeanFilter {
 
        /**
         * Builder class.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
index 336ff0294e..36eabf0cac 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
@@ -33,6 +33,7 @@ import java.util.function.*;
 import java.util.regex.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.lang.*;
 import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.httppart.*;
@@ -981,7 +982,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
 
                        if (aType.isDelegate()) {
                                wType = aType;
-                               aType = ((Delegate)o).getClassMeta();
+                               aType = (ClassMeta)((Delegate)o).getClassMeta();
                        }
 
                        sType = aType;
@@ -1144,7 +1145,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                if (type == null)
                        type = object();
                else if (type.isDelegate())
-                       type = ((Delegate)o).getClassMeta();
+                       type = (ClassMeta)((Delegate)o).getClassMeta();
                ObjectSwap swap = type.getSwap(this);
                if (nn(swap)) {
                        o = swap(swap, o);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateList.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateList.java
index f574ba96ba..5cc51b917a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateList.java
@@ -20,6 +20,7 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
+import org.apache.juneau.commons.bean.*;
 
 /**
  * Represents a wrapped {@link Collection} where entries in the list can be 
removed or reordered without affecting the
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
index 5035a39337..d179fa1b4c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
@@ -22,6 +22,7 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
+import org.apache.juneau.commons.bean.*;
 
 /**
  * Represents a wrapped {@link Map} where entries in the map can be removed 
without affecting the underlying map.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FilteredKeyMap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FilteredKeyMap.java
index dc72676195..8a6b02bc20 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FilteredKeyMap.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FilteredKeyMap.java
@@ -22,6 +22,7 @@ import static org.apache.juneau.commons.utils.Utils.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
 
 /**
  * Wrapper around a map where the key names are overridden.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index 82481eb423..13b46ad88c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -32,6 +32,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
 import org.apache.juneau.commons.lang.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.serializer.*;
@@ -761,7 +762,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
                        } else if (aType.isBean()) {
                                bm = toBeanMap(o);
                        } else if (aType.isDelegate()) {
-                               var innerType = ((Delegate<?>)o).getClassMeta();
+                               var innerType = 
(ClassMeta<?>)((Delegate<?>)o).getClassMeta();
                                var ns = 
Value.of(getXmlClassMeta(innerType).getNamespace());
                                if (ns.isPresent()) {
                                        if (nn(ns.get().uri))
@@ -934,7 +935,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession {
 
                        if (aType.isDelegate()) {
                                wType = aType;
-                               eType = aType = ((Delegate<?>)o).getClassMeta();
+                               eType = aType = 
(ClassMeta<?>)((Delegate<?>)o).getClassMeta();
                        }
 
                        sType = aType;
diff --git a/todo/TODO-5-bean-runtime-types-to-commons.md 
b/todo/TODO-5-bean-runtime-types-to-commons.md
index 56d740ecbd..fd6b4ee8e1 100644
--- a/todo/TODO-5-bean-runtime-types-to-commons.md
+++ b/todo/TODO-5-bean-runtime-types-to-commons.md
@@ -4,7 +4,9 @@ This is the remaining work from **Phase 5 of the bean-layer 
split**. Phase 5a (t
 
 ---
 
-## Status (as of Phase 5h checkpoint)
+## Status (as of Phase 5h checkpoint + Step 8b-i)
+
+**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  [...]
 
 **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:
 
@@ -90,8 +92,30 @@ Known limitations of the commons-side path (acceptable for 
Step 6, scoped for la
 - [x] **Step 6** — `BeanMeta.of(Class<T>, BeanConfigContext)` factory + 
`protected BeanMeta(Class<T>, BeanConfigContext)` constructor wired up. 
`BeanMeta` now carries a non-null `BeanConfigContext config` facade for all 
settings reads; the `marshallingContext` and `classMeta` fields are 
documented-nullable and stay null on the commons-side path. 
`BeanPropertyMeta.Builder.bc` and `BeanPropertyMeta.bc` similarly nullable; new 
mirrored `config` field on both. `Builder.validate(...)` accepts [...]
 - [x] **Step 7** — Per-format extension survey + pre-Step-8 hardening. (a) 
Survey result: `ExtendedBeanMeta` (composes `BeanMeta<?>`), `XmlBeanMeta` 
(extends `ExtendedBeanMeta`), `RdfBeanMeta` (extends `ExtendedBeanMeta`); no 
`HtmlBeanMeta` exists. All marshalling-side, all stay in `juneau-marshall`. (b) 
Hardened `BeanPropertyMeta.add(BeanMap,String,Object)` / 
`add(BeanMap,String,String,Object)` / `setArray` / `applyChildPropertiesFilter` 
(all throw `UnsupportedOperationException` with a [...]
 - [x] **Step 8a** — SPI seams in `commons.bean` (commit `3a74fcd50a`). 
`BeanSession` interface created, `MarshallingSession` implements it, four 
marshalling-only-historically settings migrated to `BeanConfigContext` so 
`BeanPropertyMeta`/`BeanMap` can stop reading them through 
`MarshallingContext`. See "Status (as of Phase 5h checkpoint)" above for full 
detail.
-- [ ] **Step 8b** — Round out the remaining SPI seams (`BeanTypeInfo` for 
`ClassMeta`, `BeanRegistryLookup` for `BeanRegistry`, optional 
`BeanPropertySwap` for `ObjectSwap`), retype the 8 types' fields to use the 
seams (or `Object`) on the bean-modeling side, then `git mv` all 8 files into 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/`. 
Verify `juneau-commons` still compiles standalone (`cd 
juneau-core/juneau-commons && mvn clean compile`).
-- [ ] **Step 8c** — (optional) Cleanup pass for anything that comes up during 
8b: deprecated bridges, stale imports, package-info docs, etc.
+- [x] **Step 8b-i** — Foundational SPI extraction (uncommitted, working tree). 
Three new SPI seams and one type relocation, all build- and test-green:
+  - **`BeanTypeInfo<T>`** in `commons.bean` (abstract class extending 
`ClassInfoTyped<T>`). Declares the bean-modeling-side type-classification 
surface needed by the 8 target types: `isUri()`, `isOptional()`, `isBean()`, 
`isObject()`, `getElementType()`, `getKeyType()`, `getValueType()`, 
`canCreateNewInstance()`, `canCreateNewInstance(Object)`, `newInstance()` 
(throws `ExecutableException` from commons), `getOptionalDefault()` (returns 
`Object` because marshalling-side `ClassMeta.getOpti [...]
+  - **`BeanFilter`** interface in `commons.bean`. Declares the per-class 
filter surface (property includes/excludes, read/write-only sets, 
propertyNamer, beanDictionary, interfaceClass/stopClass/implClass, typeName, 
example, fluentSetters flag, unsortedProperties flag, plus 
`readProperty`/`writeProperty` BeanInterceptor wrappers). `MarshalledFilter` 
now `implements BeanFilter` — no source changes inside `MarshalledFilter` were 
needed (every method on `BeanFilter` already existed on `Mars [...]
+  - **`BeanRegistryLookup`** interface in `commons.bean` (minimal surface: 
`String getTypeName(Class<?>)` and `boolean hasName(String)`). `BeanRegistry` 
implements it. The `Map<BeanPropertyMeta,BeanRegistry>` side-map on `BeanMeta` 
is unchanged for now (retyping it to `BeanRegistryLookup` would force casts on 
the 3 marshalling-side callers in 
`ParserSession`/`SerializerSession`/`XmlParserSession` that call 
`BeanRegistry`-only methods like `getClassMeta(typeName)` on the result — 
deferred [...]
+  - **`Delegate<T>` moved** from `org.apache.juneau` to 
`org.apache.juneau.commons.bean` via `git mv`. The interface's `getClassMeta()` 
method now returns `BeanTypeInfo<T>` instead of `ClassMeta<T>`. All five 
in-tree `Delegate` implementations (`BeanMap`, `DelegateBeanMap`, 
`DelegateList`, `DelegateMap`, `FilteredKeyMap`) keep their `ClassMeta<T>` 
return types via Java covariant returns (no source changes needed in those 
classes). Five marshalling-side files that used the raw-typed `(Del [...]
+  - **Build/test verification.** `python3 scripts/test.py --full` passed clean 
(juneau + juneau-rest + juneau-microservice + examples + utests, ~70k tests). 
`cd juneau-core/juneau-commons && mvn clean compile` passes standalone — 
`juneau-commons` still compiles without depending on `juneau-marshall`. 
`ReadLints` clean on all modified files.
+- [ ] **Step 8b-ii** — Continuation of the bean-runtime move. Remaining work 
to physically relocate the 8 target files (`BeanMap`, `BeanMapEntry`, 
`BeanMeta`, `BeanMetaFiltered`, `BeanPropertyMeta`, `BeanPropertyValue`, 
`BeanPropertyConsumer`, `BeanProxyInvocationHandler`) into `commons.bean`. The 
SPI seams from 8b-i are in place; this step is the field-retype + 
cross-module-leakage cleanup pass:
+  - **`BeanPropertyMeta.rawTypeMeta` / `typeMeta`** — retype from 
`ClassMeta<?>` to `BeanTypeInfo<?>`. `BeanPropertyMeta.getClassMeta()` return 
type widens to `BeanTypeInfo<?>`. The ~70 marshalling-side call sites that call 
`pMeta.getClassMeta().<method>` mostly work unchanged (inherited 
`ClassInfo`/`ClassInfoTyped` methods + new `BeanTypeInfo` abstract methods are 
sufficient). The few sites that call `ClassMeta`-only methods (e.g. 
`getSerializedClassMeta(this)`, `getMarshallingContext() [...]
+  - **`BeanPropertyMeta.swap`** — retype from `ObjectSwap` to `Object`. 
Internal references (`setPropertyValue`'s defensive double-unswap check, 
`properties()` debug method, Javadocs) cast or remove the check (it's 
belt-and-braces — the install-time `writeTransform` already normalizes the 
value before this point).
+  - **`BeanPropertyMeta.Builder.bc`** — retype from `MarshallingContext` to 
`Object`. `Builder.bc.resolveClassMeta(...)`, `Builder.bc.getClassMeta(...)`, 
`Builder.bc.object()` calls inside `validate(...)` lift out to a 
marshalling-side post-processor (or route through a `BeanSession`-style narrow 
SPI). The bean-modeling-side path already short-circuits when `bc == null`, so 
the lift-out is mostly about giving marshalling-side callers a place to install 
type metadata after the commons-sid [...]
+  - **`@MarshalledProp` annotation reads** inside 
`BeanPropertyMeta.Builder.validate(...)` — lift out to a marshalling-side 
post-processor. After `Builder.build()` returns, the marshalling layer reads 
`@MarshalledProp` annotations off the property's getter/setter/field and 
post-processes the just-built `BeanPropertyMeta` to install the swap transforms 
/ dictionary classes / property override list.
+  - **`BeanMeta.beanRegistry`** and **`propertyBeanRegistries`** — retype from 
`BeanRegistry` to `BeanRegistryLookup`. `BeanMeta.getBeanRegistry()` and 
`getPropertyBeanRegistry(...)` return types narrow to `BeanRegistry` via casts 
(only concrete impl in-tree), OR the public getter return types widen to 
`BeanRegistryLookup` and the 3 callers 
(ParserSession/SerializerSession/XmlParserSession) cast at their call sites. 
The `findBeanRegistry()` helper either: (a) stays in `BeanMeta` with `Ob [...]
+  - **`BeanMeta` constructor signatures** — the `protected 
BeanMeta(ClassMeta<T>, BeanFilter, String[], ClassInfo)` constructor takes a 
`ClassMeta` that the bean-modeling layer should not know about. Three options: 
(a) keep this constructor on `BeanMeta` typed as `Object`/`BeanTypeInfo`, 
marshalling-side callers cast; (b) move it to a marshalling-side factory helper 
(e.g. `ClassMeta.findBeanMeta()` was the only original caller) and delete it 
from `BeanMeta`; (c) introduce a marshalling-s [...]
+  - **`BeanMetaFiltered.super(...)`** call — uses the old `(ClassMeta, 
MarshalledFilter, String[], ClassInfo)` BeanMeta constructor. After Step 
8b-ii's constructor refactor, this call site needs to either inline a private 
helper-build path or route through the new commons-side `BeanMeta.of(Class, 
BeanConfigContext)` factory.
+  - **`BeanMap.load(Reader, ReaderParser)`** and **`BeanMap.load(String)`** — 
these use `JsonMap.ofText(...)` / `JsonMap.ofJson(...)` which are 
marshalling-side. Move both methods to a marshalling-side helper class (e.g. 
`BeanMapLoader`); the static methods take a `BeanMap<T>` + `Reader`/`String` 
and call `putAll(JsonMap.ofText(...))`. Updates the JsonMap-aware Javadoc 
references on `BeanMap`.
+  - **`BeanMap.getBean()`** uses `Json5Serializer.DEFAULT.toString(...)` for 
an error message — replace with `Arrays.toString(...)` or simple class-name 
iteration.
+  - **`BeanProxyInvocationHandler.toString()`** uses 
`Json5Serializer.DEFAULT.toString(...)` — replace with `Objects.toString(...)` 
or a hand-rolled "{prop1: val1, prop2: val2}" formatter.
+  - **`JsonMap` / `JsonList` constructions** inside 
`BeanPropertyMeta.add(...)` and `BeanPropertyMeta.setPropertyValue(...)` — 
replace with `LinkedHashMap` / `ArrayList` (the only reason 
`JsonMap`/`JsonList` were used is for the `MarshallingSession`-aware 
member-type conversion; after Step 3 those conversions go through 
`BeanSession.convertToMemberType(...)` directly, so plain JDK collections work).
+  - **`BeanInstantiator.of(Collection.class)/of(Map.class)` calls** inside 
`setPropertyValue` — `BeanInstantiator` is already in `commons.inject`, but the 
`type(rawTypeMeta)` overload may need updating to take `BeanTypeInfo<?>` rather 
than `ClassMeta<?>`.
+  - **`ParseException`** imports — `BeanMap.load(...)` and 
`BeanPropertyMeta.set` throw `ParseException` which lives in 
`org.apache.juneau.parser`. After the load() methods move to a marshalling-side 
helper, the only remaining `ParseException` site is `BeanPropertyMeta.set` 
where it's caught from `session.convertToMemberType(...)` and re-thrown as a 
`BeanRuntimeException` (which is the desired bean-modeling-side exception). The 
catch block already wraps it; just delete the `throws ParseE [...]
+  - **`Surrogate.class`** reference inside 
`BeanPropertyMeta.Builder.swapSwap(...)` — already gated by an 
`unsupportedOp("TODO - Surrogate swaps not yet supported on bean properties.")` 
throw. Either move `Surrogate` to commons (it's a marker class), or skip the 
check entirely on the commons-side path (rely on the marshalling-side 
post-processor to do `@Swap`/`@Surrogate` validation).
+  - **`StringFormatSwap`** instantiation inside 
`BeanPropertyMeta.Builder.marshalledPropSwap(...)` — marshalling-side type. 
Move into the marshalling-side post-processor along with the `@MarshalledProp` 
annotation read (one location).
+  - **Final cleanup** — `git mv` the 8 files into 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/`, 
update `package` declarations, repo-wide reference sweep (`import 
org.apache.juneau.BeanMap` → `import org.apache.juneau.commons.bean.BeanMap`, 
etc., plus `{@link …}` references in Javadoc), verify `cd 
juneau-core/juneau-commons && mvn clean compile` standalone passes, run 
`python3 scripts/test.py --full`.
+- [ ] **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