You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/05/13 21:30:02 UTC
(juneau) 10/21: refactor: extract BeanTypeInfo/BeanFilter/BeanRegistryLookup SPI seams in commons.bean (TODO-5 Step 8b-i)
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 <ja...@jamesbog-ltmjqf2.internal.salesforce.com>
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 <cu...@cursor.com>
---
.../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<T>} 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.