This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3937-grid.api.overhaul
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/3937-grid.api.overhaul by this 
push:
     new caea12b1a25 CAUSEWAY-3937: removal of GridSystemService from API
caea12b1a25 is described below

commit caea12b1a25063ce1cb5ab535957c1f2f414b164
Author: Andi Huber <[email protected]>
AuthorDate: Tue Oct 28 08:24:10 2025 +0100

    CAUSEWAY-3937: removal of GridSystemService from API
---
 .../causeway/applib/services/grid/GridService.java |  2 +
 .../applib/services/grid/GridSystemService.java    | 99 ----------------------
 .../metamodel/CausewayModuleCoreMetamodel.java     | 26 +++++-
 .../metamodel/facets/object/grid/GridFacet.java    |  3 +-
 .../core/metamodel/services/grid/GridCache.java    | 19 ++---
 .../services/grid/GridLoadingContext.java          | 91 ++++++++++++++++++++
 ...ootstrap.java => GridObjectMemberResolver.java} | 97 ++++++---------------
 .../services/grid/GridServiceDefault.java          | 56 ++++++------
 .../services/grid/GridCache_resourceNameTest.java  | 24 +++++-
 .../mmtestsupport/MetaModelContext_forTesting.java | 40 ++++-----
 10 files changed, 215 insertions(+), 242 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
index df396a12c1e..c83e0dc3fd3 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java
@@ -44,6 +44,8 @@ public interface GridService {
 
     /**
      * To support metamodel invalidation/rebuilding of spec.
+     *
+     * <p> Acts as a no-op if not {@link #supportsReloading()}.
      */
     void remove(Class<?> domainClass);
 
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java
deleted file mode 100644
index bef92c7cc4e..00000000000
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- *  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.causeway.applib.services.grid;
-
-import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
-
-/**
- * Encapsulates a single layout grid system which can be used to customize the 
layout
- * of domain objects.
- *
- * <p>In particular this means being able to return a "normalized" form
- * (validating and associating domain object members into the various regions
- * of the grid) and in providing a default grid if there is no other metadata
- * available.
- *
- * @since 1.x revised for 4.0 {@index}
- */
-public interface GridSystemService {
-
-    /**
-     * A default grid, used when no grid layout can be found for the domain
-     * class.
-     *
-     * <p>For example, this layout could define two columns in ratio 4:8.
-     *
-     * @param domainClass
-     */
-    BSGrid defaultGrid(Class<?> domainClass);
-
-    /**
-     * Validates and normalizes a grid, modifying the grid so that all of the
-     * domain object's members (properties, collections, actions) are bound to
-     * regions of the grid.
-     *
-     * <p>E.g. for properties (and similar for collections and actions) the 
annotation attributes
-     * {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()}
-     * and
-     * {@link 
org.apache.causeway.applib.annotation.PropertyLayout#fieldSetId()}
-     * or
-     * {@link 
org.apache.causeway.applib.annotation.PropertyLayout#fieldSetName()}
-     * are used.
-     * Such a grid, if persisted as the layout XML file for the domain class,
-     * allows the various layout annotation attributes to be unspecified or 
removed from the source code
-     * of the domain class.
-     */
-    void normalize(BSGrid grid, Class<?> domainClass);
-
-    /**
-     * Takes a normalized grid and enriches it with all the available metadata
-     * (taken from Apache Causeway' internal metadata) that can be represented 
in
-     * the layout XML.
-     *
-     * <p>Such a grid, if persisted as the layout XML file for the domain 
class,
-     * allows all layout annotations
-     * ({@link org.apache.causeway.applib.annotation.ActionLayout},
-     * {@link org.apache.causeway.applib.annotation.PropertyLayout},
-     * {@link org.apache.causeway.applib.annotation.CollectionLayout}) to be
-     * removed from the source code of the domain class.
-     * @param grid
-     * @param domainClass
-     */
-    void complete(BSGrid grid, Class<?> domainClass);
-
-    /**
-     * Takes a normalized grid and strips out removes all members, leaving only
-     * the grid structure.
-     *
-     * <p>Such a grid, if persisted as the layout XML file for the domain 
class,
-     * requires that e.g. for properties (and similar for collections and 
actions) the annotation attributes
-     * {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()}
-     * and
-     * {@link 
org.apache.causeway.applib.annotation.PropertyLayout#fieldSetId()}
-     * or
-     * {@link 
org.apache.causeway.applib.annotation.PropertyLayout#fieldSetName()}
-     * are retained in the source code of said class in order to bind
-     * members to the regions of the grid.
-     *
-     * @param grid
-     * @param domainClass
-     */
-    void minimal(BSGrid grid, Class<?> domainClass);
-
-}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
index 8231c1b4ebb..7de1f847710 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/CausewayModuleCoreMetamodel.java
@@ -18,8 +18,11 @@
  */
 package org.apache.causeway.core.metamodel;
 
+import java.util.List;
 import java.util.stream.Stream;
 
+import jakarta.inject.Provider;
+
 import org.jspecify.annotations.NonNull;
 
 import org.springframework.context.annotation.Bean;
@@ -30,11 +33,15 @@
 import org.apache.causeway.applib.CausewayModuleApplib;
 import org.apache.causeway.applib.graph.tree.TreeAdapter;
 import org.apache.causeway.applib.services.appfeat.ApplicationFeatureSort;
+import org.apache.causeway.applib.services.grid.GridMarshaller;
+import org.apache.causeway.applib.services.message.MessageService;
 import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.functional.Railway;
 import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.commons.semantics.CollectionSemantics;
+import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.config.CausewayModuleCoreConfig;
+import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.context.MetaModelContextFactory;
 import 
org.apache.causeway.core.metamodel.facets.object.logicaltype.LogicalTypeMalformedValidator;
 import 
org.apache.causeway.core.metamodel.inspect.CausewayModuleCoreMetamodelMixins;
@@ -48,9 +55,11 @@
 import 
org.apache.causeway.core.metamodel.services.columnorder.ColumnOrderTxtFileServiceDefault;
 import 
org.apache.causeway.core.metamodel.services.events.MetamodelEventService;
 import 
org.apache.causeway.core.metamodel.services.exceprecog.ExceptionRecognizerForRecoverableException;
+import org.apache.causeway.core.metamodel.services.grid.GridLoadingContext;
 import org.apache.causeway.core.metamodel.services.grid.GridMarshallerXml;
 import org.apache.causeway.core.metamodel.services.grid.GridServiceDefault;
-import 
org.apache.causeway.core.metamodel.services.grid.GridSystemServiceBootstrap;
+import 
org.apache.causeway.core.metamodel.services.grid.GridObjectMemberResolver.FallbackLayoutDataSource;
+import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader;
 import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault;
 import 
org.apache.causeway.core.metamodel.services.idstringifier.IdStringifierLookupService;
 import 
org.apache.causeway.core.metamodel.services.inject.ServiceInjectorDefault;
@@ -61,6 +70,7 @@
 import 
org.apache.causeway.core.metamodel.services.tablecol.TableColumnOrderServiceUsingTxtFile;
 import org.apache.causeway.core.metamodel.services.title.TitleServiceDefault;
 import 
org.apache.causeway.core.metamodel.spec.impl.CausewayModuleCoreMetamodelConfigurationDefault;
+import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 import 
org.apache.causeway.core.metamodel.valuesemantics.ApplicationFeatureIdValueSemantics;
 import 
org.apache.causeway.core.metamodel.valuesemantics.BigDecimalValueSemantics;
 import 
org.apache.causeway.core.metamodel.valuesemantics.BigIntegerValueSemantics;
@@ -173,7 +183,6 @@
         ExceptionRecognizerForRecoverableException.class,
         GridMarshallerXml.class,
         GridServiceDefault.class,
-        GridSystemServiceBootstrap.class,
         IdStringifierLookupService.class,
         LayoutResourceLoaderDefault.class,
         LayoutServiceDefault.class,
@@ -201,6 +210,19 @@ public static interface PreloadableTypes {
         @NonNull Stream<Class<?>> stream();
     }
 
+    @Bean
+    public GridLoadingContext gridLoadingContext(
+        final CausewaySystemEnvironment causewaySystemEnvironment,
+        final CausewayConfiguration causewayConfiguration,
+        final MessageService messageService,
+        final Provider<SpecificationLoader> specLoaderProvider,
+        final List<GridMarshaller> marshallers,
+        final List<LayoutResourceLoader> layoutResourceLoaders,
+        final List<FallbackLayoutDataSource> fallbackLayoutDataSources) {
+        return GridLoadingContext.create(causewaySystemEnvironment, 
causewayConfiguration, messageService,
+            specLoaderProvider, marshallers, layoutResourceLoaders, 
fallbackLayoutDataSources);
+    }
+
     @Bean
     @Primary
     public PreloadableTypes preloadableTypes() {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java
index 5a74ff77707..00e674a137a 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java
@@ -21,7 +21,6 @@
 import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
-import org.apache.causeway.applib.services.grid.GridSystemService;
 import org.apache.causeway.applib.services.layout.LayoutService;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
@@ -30,7 +29,7 @@
  * Obtain the current grid, derived either from a <code>.layout.xml</code> 
file, and normalized, or synthesized from
  * existing layout metadata (annotations or <code>layout.json</code>).
  * <p>
- * Most of the heavy lifting is done by delegating to the {@link 
LayoutService} and {@link GridSystemService} services.
+ * Most of the heavy lifting is done by delegating to the {@link 
LayoutService} services.
  */
 public interface GridFacet extends Facet {
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java
index e75c2a80565..e064c4e3dc0 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridCache.java
@@ -20,7 +20,6 @@
 
 import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -31,14 +30,9 @@
 import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
 import org.apache.causeway.applib.mixins.metamodel.Object_rebuildMetamodel;
 import org.apache.causeway.applib.services.grid.GridMarshaller;
-import org.apache.causeway.applib.services.grid.GridService;
-import org.apache.causeway.applib.services.grid.GridSystemService;
 import org.apache.causeway.applib.services.message.MessageService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
-import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.core.metamodel.services.grid.GridLoader.LayoutKey;
-import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader;
-
 import lombok.extern.slf4j.Slf4j;
 
 /**
@@ -63,12 +57,10 @@ record GridCache(
     Map<LayoutKey, String> badContentByKey) {
 
     public GridCache(
-            final MessageService messageService,
-            final boolean supportsReloading,
-            final List<LayoutResourceLoader> layoutResourceLoaders) {
-        this(new GridLoader(Can.ofCollection(layoutResourceLoaders)),
-            messageService,
-            supportsReloading,
+            final GridLoadingContext gridLoadingContext) {
+        this(new GridLoader(gridLoadingContext.layoutResourceLoaders()),
+            gridLoadingContext.messageService(),
+            gridLoadingContext.supportsReloading(),
             new HashMap<>(), new HashMap<>());
     }
 
@@ -90,8 +82,7 @@ public void remove(final Class<?> domainClass) {
     /**
      * Whether any persisted layout metadata (eg a <code>.layout.xml</code> 
file) exists for this domain class.
      *
-     * <p>If none exists, will return null (and the calling {@link 
GridService} will use {@link GridSystemService}
-     * to obtain a default grid for the domain class).
+     * <p>If none exists, will return null.
      */
     public boolean existsFor(final Class<?> domainClass, final 
EnumSet<CommonMimeType> supportedFormats) {
         return gridLoader.loadLayoutResource(new LayoutKey(domainClass, null), 
supportedFormats).isPresent();
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingContext.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingContext.java
new file mode 100644
index 00000000000..8a0d6dd7818
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoadingContext.java
@@ -0,0 +1,91 @@
+/*
+ *  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.causeway.core.metamodel.services.grid;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import jakarta.inject.Provider;
+
+import org.apache.causeway.applib.services.grid.GridMarshaller;
+import org.apache.causeway.applib.services.message.MessageService;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.core.config.CausewayConfiguration;
+import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
+import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
+import 
org.apache.causeway.core.metamodel.services.grid.GridObjectMemberResolver.FallbackLayoutDataSource;
+import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader;
+import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
+
+/**
+ * Instantiated by {@link CausewayModuleCoreMetamodel} (Spring managed bean).
+ */
+public record GridLoadingContext(
+    CausewaySystemEnvironment causewaySystemEnvironment,
+    CausewayConfiguration causewayConfiguration,
+    MessageService messageService,
+    Provider<SpecificationLoader> specLoaderProvider,
+    Map<CommonMimeType, GridMarshaller> marshallersByMime,
+    Can<LayoutResourceLoader> layoutResourceLoaders,
+    Can<FallbackLayoutDataSource> fallbackLayoutDataSources,
+    boolean supportsReloading) {
+
+    /** Factory also to be used for JUnit tests. */
+    public static GridLoadingContext create(
+        final CausewaySystemEnvironment causewaySystemEnvironment,
+        final CausewayConfiguration causewayConfiguration,
+        final MessageService messageService,
+        final Provider<SpecificationLoader> specLoaderProvider,
+        final List<GridMarshaller> registeredMarshallers,
+        final List<LayoutResourceLoader> layoutResourceLoaders,
+        final List<FallbackLayoutDataSource> fallbackLayoutDataSources) {
+
+        var marshallers = Can.ofCollection(registeredMarshallers);
+        var marshallersByMime = new HashMap<CommonMimeType, GridMarshaller>();
+
+        marshallers.forEach(marshaller->
+            marshaller.supportedFormats().stream()
+                .forEach(mime->
+                    marshallersByMime.computeIfAbsent(mime, __->marshaller)));
+
+        return new GridLoadingContext(
+            causewaySystemEnvironment,
+            causewayConfiguration,
+            messageService,
+            specLoaderProvider,
+            Collections.unmodifiableMap(marshallersByMime),
+            Can.ofCollection(layoutResourceLoaders),
+            Can.ofCollection(fallbackLayoutDataSources),
+            causewaySystemEnvironment.isPrototyping());
+    }
+
+    public EnumSet<CommonMimeType> supportedFormats() {
+        return EnumSet.copyOf(marshallersByMime().keySet());
+    }
+
+    public Optional<GridMarshaller> gridMarshaller(final CommonMimeType 
format) {
+        return Optional.ofNullable(marshallersByMime.get(format));
+    }
+
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceBootstrap.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridObjectMemberResolver.java
similarity index 92%
rename from 
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceBootstrap.java
rename to 
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridObjectMemberResolver.java
index c675b83a9c3..08c6621edb6 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceBootstrap.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridObjectMemberResolver.java
@@ -30,17 +30,7 @@
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import jakarta.annotation.Priority;
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-import jakarta.inject.Provider;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
-
 import org.apache.causeway.applib.annotation.ActionLayout;
-import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.layout.LayoutConstants;
 import org.apache.causeway.applib.layout.component.ActionLayoutData;
 import org.apache.causeway.applib.layout.component.ActionLayoutDataOwner;
@@ -50,19 +40,14 @@
 import org.apache.causeway.applib.layout.component.FieldSet;
 import org.apache.causeway.applib.layout.component.PropertyLayoutData;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSCol;
+import 
org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSRow;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSTab;
 import org.apache.causeway.applib.layout.grid.bootstrap.BSTabGroup;
 import org.apache.causeway.applib.layout.grid.bootstrap.Size;
-import 
org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor;
 import org.apache.causeway.applib.services.grid.GridMarshaller;
-import org.apache.causeway.applib.services.grid.GridSystemService;
-import org.apache.causeway.applib.services.i18n.TranslationService;
-import org.apache.causeway.applib.services.jaxb.JaxbService;
-import org.apache.causeway.applib.services.message.MessageService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
-import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.collections._Lists;
@@ -70,9 +55,6 @@
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.commons.internal.functions._Functions;
 import org.apache.causeway.commons.internal.resources._Resources;
-import org.apache.causeway.core.config.CausewayConfiguration;
-import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
-import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.facetapi.FacetUtil;
 import 
org.apache.causeway.core.metamodel.facets.actions.layout.ActionPositionFacetForActionLayoutXml;
@@ -118,29 +100,28 @@
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
 import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
-
 import static org.apache.causeway.commons.internal.base._NullSafe.stream;
 import static 
org.apache.causeway.core.metamodel.facetapi.FacetUtil.updateFacet;
 import static 
org.apache.causeway.core.metamodel.facetapi.FacetUtil.updateFacetIfPresent;
 
-import lombok.Setter;
-import lombok.experimental.Accessors;
 import lombok.extern.slf4j.Slf4j;
 
 /**
- * Default implementation of {@link 
org.apache.causeway.applib.services.grid.GridSystemService} using DTOs based on
+ * Encapsulates a single layout grid system which can be used to customize the 
layout
+ * of domain objects.
+ *
+ * <p>In particular this means being able to return a "normalized" form
+ * (validating and associating domain object members into the various regions
+ * of the grid) and in providing a default grid if there is no other metadata
+ * available.
+ *
+ * <p> Using DTOs based on
  * <a href="https://getbootstrap.com>Bootstrap</a> design system.
  *
- * @since 2.0 {@index}
  */
-@Service
-@Named(CausewayModuleCoreMetamodel.NAMESPACE + ".GridSystemServiceBootstrap")
-@Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("Bootstrap")
 @Slf4j
-public class GridSystemServiceBootstrap
-implements GridSystemService {
+public record GridObjectMemberResolver(
+    GridLoadingContext gridLoadingContext) {
 
     /**
      * SPI to customize layout fallback behavior on a per class basis.
@@ -153,42 +134,21 @@ public static interface FallbackLayoutDataSource {
         Try<String> tryLoadAsStringUtf8(Class<?> domainClass);
     }
 
-    @Inject @Lazy // circular dependency (late binding)
-    @Setter @Accessors(chain = true) // JUnit support
-    private GridMarshaller marshaller;
-
-    private final Provider<SpecificationLoader> specLoaderProvider;
-    private final MessageService messageService;
-    private final CausewaySystemEnvironment causewaySystemEnvironment;
-    private final CausewayConfiguration config;
-    private final Can<FallbackLayoutDataSource> fallbackLayoutDataSources;
-
-    @Inject
-    public GridSystemServiceBootstrap(
-            final CausewayConfiguration causewayConfiguration,
-            final Provider<SpecificationLoader> specLoaderProvider,
-            final TranslationService translationService,
-            final JaxbService jaxbService,
-            final MessageService messageService,
-            final CausewaySystemEnvironment causewaySystemEnvironment,
-            final List<FallbackLayoutDataSource> fallbackLayoutDataSources) {
-        this.specLoaderProvider = specLoaderProvider;
-        this.messageService = messageService;
-        this.causewaySystemEnvironment = causewaySystemEnvironment;
-        this.config = causewayConfiguration;
-        this.fallbackLayoutDataSources = 
Can.ofCollection(fallbackLayoutDataSources);
+    @Deprecated
+    private GridMarshaller marshaller() {
+        return 
gridLoadingContext().gridMarshaller(CommonMimeType.XML).orElseThrow();
     }
 
     private String toXml(final BSGrid grid) {
-        return marshaller.marshal(grid, CommonMimeType.XML);
+        return marshaller()
+            .marshal(grid, CommonMimeType.XML);
     }
 
-    @Override
     public BSGrid defaultGrid(final Class<?> domainClass) {
         final Try<String> content = 
loadFallbackLayoutAsStringUtf8(domainClass);
         try {
             return content.getValue()
-                    .flatMap(xml -> marshaller.unmarshal(domainClass, xml, 
CommonMimeType.XML)
+                    .flatMap(xml -> marshaller().unmarshal(domainClass, xml, 
CommonMimeType.XML)
                         .getValue())
                     .filter(BSGrid.class::isInstance)
                     .map(BSGrid.class::cast)
@@ -200,12 +160,12 @@ public BSGrid defaultGrid(final Class<?> domainClass) {
     }
 
     private Try<String> loadFallbackLayoutAsStringUtf8(final Class<?> 
domainClass) {
-        return fallbackLayoutDataSources.stream()
+        return gridLoadingContext.fallbackLayoutDataSources().stream()
             .map(ds->ds.tryLoadAsStringUtf8(domainClass))
             .filter(tried->tried.getValue().isPresent())
             .findFirst()
             .orElseGet(()->{
-                return 
Try.call(()->_Resources.loadAsStringUtf8(GridSystemServiceBootstrap.class, 
"GridFallbackLayout.xml"));
+                return 
Try.call(()->_Resources.loadAsStringUtf8(GridObjectMemberResolver.class, 
"GridFallbackLayout.xml"));
             });
     }
 
@@ -277,7 +237,7 @@ private boolean validateAndNormalize(
         if(!gridModelIfValid.isPresent()) return false; // only present if 
valid
 
         var gridModel = gridModelIfValid.get();
-        var objSpec = 
specLoaderProvider.get().specForTypeElseFail(domainClass);
+        var objSpec = 
gridLoadingContext().specLoaderProvider().get().specForTypeElseFail(domainClass);
 
         var oneToOneAssociationById = 
ObjectMember.mapById(objSpec.streamProperties(MixedIn.INCLUDED));
         var oneToManyAssociationById = 
ObjectMember.mapById(objSpec.streamCollections(MixedIn.INCLUDED));
@@ -367,7 +327,7 @@ private boolean validateAndNormalize(
 
                 // add unbound properties respecting configured sequence policy
                 var sortedUnboundPropertyIds = _UnreferencedSequenceUtil
-                    .sortProperties(config, unboundPropertyIds.stream()
+                    
.sortProperties(gridLoadingContext.causewayConfiguration(), 
unboundPropertyIds.stream()
                             .map(oneToOneAssociationById::get)
                             .filter(_NullSafe::isPresent));
 
@@ -390,7 +350,7 @@ private boolean validateAndNormalize(
 
             // add missing collections respecting configured sequence policy
             var sortedMissingCollectionIds = _UnreferencedSequenceUtil
-                    .sortCollections(config, 
collectionDisjunction.right().stream()
+                    
.sortCollections(gridLoadingContext.causewayConfiguration(), 
collectionDisjunction.right().stream()
                             .map(oneToManyAssociationById::get)
                             .filter(_NullSafe::isPresent));
 
@@ -654,7 +614,6 @@ private void addActionTo(
         actionLayoutData.owner(owner);
     }
 
-    @Override
     public void normalize(final BSGrid grid, final Class<?> domainClass) {
         final boolean valid = validateAndNormalize(grid, domainClass);
         if (valid) {
@@ -663,8 +622,8 @@ public void normalize(final BSGrid grid, final Class<?> 
domainClass) {
                 log.debug("Grid:\n\n{}\n\n", toXml(grid));
             }
         } else {
-            if(causewaySystemEnvironment.isPrototyping()) {
-                messageService.warnUser("Grid metadata errors for " + 
grid.domainClass().getName() + "; check the error log");
+            if(gridLoadingContext.causewaySystemEnvironment().isPrototyping()) 
{
+                gridLoadingContext.messageService().warnUser("Grid metadata 
errors for " + grid.domainClass().getName() + "; check the error log");
             }
             log.error("Grid metadata errors in {}:\n\n{}\n\n", 
grid.domainClass().getName(), toXml(grid));
         }
@@ -680,7 +639,7 @@ private void overwriteFacets(
             final BSGrid fcGrid,
             final Class<?> domainClass) {
 
-        var objectSpec = 
specLoaderProvider.get().specForTypeElseFail(domainClass);
+        var objectSpec = 
gridLoadingContext.specLoaderProvider().get().specForTypeElseFail(domainClass);
 
         var oneToOneAssociationById = 
ObjectMember.mapById(objectSpec.streamProperties(MixedIn.INCLUDED));
         var oneToManyAssociationById = 
ObjectMember.mapById(objectSpec.streamCollections(MixedIn.INCLUDED));
@@ -910,14 +869,12 @@ public void visit(final CollectionLayoutData 
collectionLayoutData) {
 
     // --
 
-    @Override
     public void complete(final BSGrid grid, final Class<?> domainClass) {
         normalize(grid, domainClass);
-        var objectSpec = 
specLoaderProvider.get().specForTypeElseFail(domainClass);
+        var objectSpec = 
gridLoadingContext.specLoaderProvider().get().specForTypeElseFail(domainClass);
         grid.visit(new MetamodelToGridOverridingVisitor(objectSpec));
     }
 
-    @Override
     public void minimal(final BSGrid grid, final Class<?> domainClass) {
         normalize(grid, domainClass);
         grid.visit(new BSElementVisitor() {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
index 55e64297794..2838a7e510e 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java
@@ -18,8 +18,6 @@
  */
 package org.apache.causeway.core.metamodel.services.grid;
 
-import java.util.List;
-
 import jakarta.annotation.Priority;
 import jakarta.inject.Inject;
 import jakarta.inject.Named;
@@ -31,12 +29,9 @@
 import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid;
 import org.apache.causeway.applib.services.grid.GridMarshaller;
 import org.apache.causeway.applib.services.grid.GridService;
-import org.apache.causeway.applib.services.grid.GridSystemService;
-import org.apache.causeway.applib.services.message.MessageService;
+import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.commons.internal.base._Casts;
-import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
-import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader;
 
 /**
  * Default implementation of {@link GridService}.
@@ -48,18 +43,14 @@
 @Priority(PriorityPrecedence.MIDPOINT)
 @Qualifier("Default")
 public record GridServiceDefault(
-    GridMarshaller marshaller,
-    List<GridSystemService> gridSystemServices,
+    GridLoadingContext gridLoadingContext,
+    GridObjectMemberResolver gridSystemService,
     GridCache gridCache) implements GridService {
 
     @Inject
     public GridServiceDefault(
-            final CausewaySystemEnvironment causewaySystemEnvironment,
-            final GridMarshaller marshaller,
-            final MessageService messageService,
-            final List<GridSystemService> gridSystemServices,
-            final List<LayoutResourceLoader> layoutResourceLoaders) {
-        this(marshaller, gridSystemServices, new GridCache(messageService, 
causewaySystemEnvironment.isPrototyping(), layoutResourceLoaders));
+            final GridLoadingContext gridLoadingContext) {
+        this(gridLoadingContext, new 
GridObjectMemberResolver(gridLoadingContext), new 
GridCache(gridLoadingContext));
     }
 
     @Override
@@ -74,27 +65,33 @@ public void remove(final Class<?> domainClass) {
 
     @Override
     public boolean existsFor(final Class<?> domainClass) {
-        return gridCache.existsFor(domainClass, marshaller.supportedFormats());
+        return gridCache.existsFor(domainClass, 
gridLoadingContext.supportedFormats());
+    }
+
+    @Deprecated //FIXME bad API
+    @Override
+    public GridMarshaller marshaller() {
+        return 
gridLoadingContext().marshallersByMime().get(CommonMimeType.XML);
     }
 
     @Override
     public BSGrid load(final Class<?> domainClass) {
-        return gridCache.load(domainClass, marshaller).orElse(null);
+        return gridCache.load(domainClass, marshaller()).orElse(null);
     }
 
     @Override
     public BSGrid load(final Class<?> domainClass, final String layout) {
-        return gridCache.load(domainClass, layout, marshaller).orElse(null);
+        return gridCache.load(domainClass, layout, marshaller()).orElse(null);
     }
 
     // --
 
     @Override
     public BSGrid defaultGridFor(final Class<?> domainClass) {
-        for (var gridSystemService : gridSystemServices()) {
-            var grid = gridSystemService.defaultGrid(domainClass);
-            if(grid != null) return grid;
-        }
+
+        var grid = gridSystemService.defaultGrid(domainClass);
+        if(grid != null) return grid;
+
         throw new IllegalStateException(
                 "No GridSystemService available to create grid for '" + 
domainClass.getName() + "'");
     }
@@ -104,27 +101,26 @@ public BSGrid normalize(final BSGrid grid) {
         if(grid.isNormalized()) return grid;
 
         var domainClass = grid.domainClass();
-        for (var gridSystemService : gridSystemServices()) {
-            gridSystemService.normalize(_Casts.uncheckedCast(grid), 
domainClass);
-        }
+        gridSystemService().normalize(_Casts.uncheckedCast(grid), domainClass);
+
         return grid;
     }
 
     @Override
     public BSGrid complete(final BSGrid grid) {
         var domainClass = grid.domainClass();
-        for (var gridSystemService : gridSystemServices()) {
-            gridSystemService.complete(_Casts.uncheckedCast(grid), 
domainClass);
-        }
+        var gridSystemService = gridSystemService();
+        gridSystemService.complete(_Casts.uncheckedCast(grid), domainClass);
+
         return grid;
     }
 
     @Override
     public BSGrid minimal(final BSGrid grid) {
         var domainClass = grid.domainClass();
-        for (var gridSystemService : gridSystemServices()) {
-            gridSystemService.minimal(_Casts.uncheckedCast(grid), domainClass);
-        }
+        var gridSystemService = gridSystemService();
+        gridSystemService.minimal(_Casts.uncheckedCast(grid), domainClass);
+
         return grid;
     }
 
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java
index dc64a762030..02f3a885a2c 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/services/grid/GridCache_resourceNameTest.java
@@ -19,18 +19,27 @@
 package org.apache.causeway.core.metamodel.services.grid;
 
 import java.util.EnumSet;
-import java.util.List;
+import java.util.Map;
+
+import jakarta.inject.Provider;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import org.apache.causeway.applib.services.grid.GridMarshaller;
+import org.apache.causeway.applib.services.message.MessageService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.core.config.CausewayConfiguration;
+import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.services.grid.GridLoader.LayoutKey;
+import 
org.apache.causeway.core.metamodel.services.grid.GridObjectMemberResolver.FallbackLayoutDataSource;
 import org.apache.causeway.core.metamodel.services.grid.spi.LayoutResource;
 import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoader;
 import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault;
+import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 
 class GridCache_resourceNameTest {
 
@@ -40,7 +49,18 @@ class GridCache_resourceNameTest {
     @BeforeEach
     void setUp() throws Exception {
         layoutResourceLoader = new LayoutResourceLoaderDefault();
-        gridCache = new GridCache(null, false, List.of(layoutResourceLoader));
+
+        var ctx = new GridLoadingContext(
+            (CausewaySystemEnvironment) null,
+            (CausewayConfiguration) null,
+            (MessageService) null,
+            (Provider<SpecificationLoader>) null,
+            (Map<CommonMimeType, GridMarshaller>) null,
+            Can.of(layoutResourceLoader),
+            Can.<FallbackLayoutDataSource>empty(),
+            false); // reloading supported
+
+        gridCache = new GridCache(ctx);
     }
 
     @Test
diff --git 
a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
 
b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
index ed22ea59e17..8f31ff84b97 100644
--- 
a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
+++ 
b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java
@@ -37,7 +37,6 @@
 import org.apache.causeway.applib.services.factory.FactoryService;
 import org.apache.causeway.applib.services.grid.GridMarshaller;
 import org.apache.causeway.applib.services.grid.GridService;
-import org.apache.causeway.applib.services.grid.GridSystemService;
 import org.apache.causeway.applib.services.i18n.TranslationService;
 import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
 import org.apache.causeway.applib.services.iactnlayer.InteractionService;
@@ -71,6 +70,7 @@
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants;
 import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
+import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
 import org.apache.causeway.core.metamodel.commons.ClassUtil;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.context.MetaModelContextFactory;
@@ -89,9 +89,10 @@
 import 
org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.causeway.core.metamodel.services.command.CommandDtoFactory;
 import 
org.apache.causeway.core.metamodel.services.events.MetamodelEventService;
+import org.apache.causeway.core.metamodel.services.grid.GridLoadingContext;
 import org.apache.causeway.core.metamodel.services.grid.GridMarshallerXml;
 import org.apache.causeway.core.metamodel.services.grid.GridServiceDefault;
-import 
org.apache.causeway.core.metamodel.services.grid.GridSystemServiceBootstrap;
+import 
org.apache.causeway.core.metamodel.services.grid.GridObjectMemberResolver.FallbackLayoutDataSource;
 import 
org.apache.causeway.core.metamodel.services.grid.spi.LayoutResourceLoaderDefault;
 import org.apache.causeway.core.metamodel.services.layout.LayoutServiceDefault;
 import org.apache.causeway.core.metamodel.services.message.MessageServiceNoop;
@@ -452,35 +453,28 @@ private final MenuBarsService createMenuBarsService() {
     }
 
     @Getter(lazy = true)
-    private final GridMarshaller gridMarshaller = createGridMarshaller();
-    private final GridMarshaller createGridMarshaller() {
-        return new GridMarshallerXml(getJaxbService());
+    private final GridLoadingContext gridLoadingContext = 
createGridLoadingContext();
+    private final GridLoadingContext createGridLoadingContext() {
+        return new CausewayModuleCoreMetamodel().gridLoadingContext(
+            getSystemEnvironment(),
+            getConfiguration(),
+            getMessageService(),
+            ()->getSpecificationLoader(),
+            List.of(getGridMarshaller()),
+            List.of(new LayoutResourceLoaderDefault()),
+            List.<FallbackLayoutDataSource>of());
     }
 
     @Getter(lazy = true)
-    private final List<GridSystemService> gridSystemServices = 
createGridSystemService();
-    private final List<GridSystemService> createGridSystemService() {
-        return List.of(
-            new GridSystemServiceBootstrap(
-                getConfiguration(),
-                ()->getSpecificationLoader(),
-                getTranslationService(),
-                getJaxbService(),
-                getMessageService(),
-                getSystemEnvironment(),
-                List.of())
-                .setMarshaller(getGridMarshaller()));
+    private final GridMarshaller gridMarshaller = createGridMarshaller();
+    private final GridMarshaller createGridMarshaller() {
+        return new GridMarshallerXml(getJaxbService());
     }
 
     @Getter(lazy = true)
     private final GridService gridService = createGridService();
     private final GridService createGridService() {
-        return new GridServiceDefault(
-            getSystemEnvironment(),
-            getGridMarshaller(),
-            getMessageService(),
-            getGridSystemServices(),
-            List.of(new LayoutResourceLoaderDefault()));
+        return new GridServiceDefault(getGridLoadingContext());
     }
 
     @Getter(lazy = true)


Reply via email to