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

ahuber pushed a commit to branch 3971-layout.switching
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/3971-layout.switching by this 
push:
     new bc4b64f8224 CAUSEWAY-3971: LayoutService to support layout variant 
exports
bc4b64f8224 is described below

commit bc4b64f8224991a08818c9c13c59a5f10af87c1a
Author: andi-huber <[email protected]>
AuthorDate: Wed Mar 4 14:00:57 2026 +0100

    CAUSEWAY-3971: LayoutService to support layout variant exports
---
 .../mixins/layout/Object_downloadLayout.java       | 22 +++++---
 .../applib/services/layout/LayoutService.java      | 13 ++++-
 .../services/layout/LayoutServiceDefault.java      | 62 +++++++++++++++-------
 3 files changed, 67 insertions(+), 30 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/mixins/layout/Object_downloadLayout.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/mixins/layout/Object_downloadLayout.java
index 4ead12fa251..4c1c72a1117 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/mixins/layout/Object_downloadLayout.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/mixins/layout/Object_downloadLayout.java
@@ -64,7 +64,9 @@ public class Object_downloadLayout {
     public static class ActionDomainEvent
     extends 
org.apache.causeway.applib.CausewayModuleApplib.ActionDomainEvent<Object_downloadLayout>
 {}
 
-    private final Object holder;
+    @Inject LayoutService layoutService;
+
+    private final Object mixee;
 
     @MemberSupport public Object act(
             @ParameterLayout(
@@ -74,15 +76,19 @@ public static class ActionDomainEvent
             final LayoutExportStyle style,
             final CommonMimeType format) {
 
-        var xmlString = layoutService.objectLayout(holder.getClass(), style, 
format);
-        return Clob.of(fileName, format, xmlString);
+        var layoutKey = layoutService.layoutKey(mixee).orElseThrow();
+        var xmlString = layoutService.objectLayout(layoutKey, style, format);
+        var infix= layoutKey.isVariant()
+            ? "-" + layoutKey.layoutIfAny()
+            : "";
+        return Clob.of(fileName + infix + ".layout", format, xmlString);
     }
 
     /**
      * Defaults to the (simple) name of the domain object's class, with a 
<code>.layout</code> suffix
      */
     @MemberSupport public String default0Act() {
-        return holder.getClass().getSimpleName() + ".layout";
+        return mixee.getClass().getSimpleName();
     }
     /**
      * Default style is {@link LayoutExportStyle#MINIMAL}.
@@ -92,10 +98,10 @@ public static class ActionDomainEvent
     }
 
     @MemberSupport public CommonMimeType default2Act() {
-        return layoutService.supportedObjectLayoutFormats().iterator().next(); 
}
+        return layoutService.supportedObjectLayoutFormats().iterator().next();
+    }
     @MemberSupport public Set<CommonMimeType> choices2Act() {
-        return layoutService.supportedObjectLayoutFormats(); }
-
-    @Inject LayoutService layoutService;
+        return layoutService.supportedObjectLayoutFormats();
+    }
 
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
index 2266a75f092..0c5581f1ddb 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/layout/LayoutService.java
@@ -19,7 +19,11 @@
 package org.apache.causeway.applib.services.layout;
 
 import java.util.EnumSet;
+import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
+
+import org.apache.causeway.applib.services.grid.GridService.LayoutKey;
 import org.apache.causeway.applib.services.menu.MenuBarsService;
 import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 
@@ -27,12 +31,17 @@
  * Provides the ability to obtain the serialized layout (eg. XML) for a single 
domain object or
  * for all domain objects, as well as the serialized layout for the 
application's menu-bars.
  *
- * @since 1.x - revised for 2.0 {@index}
+ * @since 1.x - revised for 2.0 and 4.0 {@index}
  */
 public interface LayoutService {
 
     // -- OBJECT LAYOUT
 
+    /**
+     * @since 4.0
+     */
+    Optional<LayoutKey> layoutKey(@Nullable Object domainObject);
+
     /**
      * Supported format(s) for {@link #objectLayout(Class, LayoutExportStyle, 
CommonMimeType)}
      * and {@link #toZip(LayoutExportStyle, CommonMimeType)}.
@@ -43,7 +52,7 @@ public interface LayoutService {
      * Obtains the serialized form of the object layout (grid) for the 
specified domain class.
      * @throws UnsupportedOperationException when format is not supported
      */
-    String objectLayout(Class<?> domainClass, LayoutExportStyle style, 
CommonMimeType format);
+    String objectLayout(LayoutKey layoutKey, LayoutExportStyle style, 
CommonMimeType format);
 
     /**
      * Obtains a zip file of the serialized layouts (grids) of all domain 
entities and view models.
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
index 3f50fee3b38..895cd42f957 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/layout/LayoutServiceDefault.java
@@ -20,6 +20,7 @@
 
 import java.io.File;
 import java.util.EnumSet;
+import java.util.Optional;
 
 import jakarta.annotation.Priority;
 import jakarta.inject.Named;
@@ -48,7 +49,11 @@
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.commons.io.ZipUtils;
 import org.apache.causeway.core.metamodel.CausewayModuleCoreMetamodel;
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.facetapi.FacetRanking;
+import 
org.apache.causeway.core.metamodel.facets.object.layout.LayoutPrefixFacet;
 import 
org.apache.causeway.core.metamodel.layout.LayoutFacetUtil.MetamodelToGridOverridingVisitor;
+import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 
@@ -85,14 +90,27 @@ public String menuBarsLayout(
 
     // -- OBJECT LAYOUT
 
+    @Override
+    public Optional<LayoutKey> layoutKey(final @Nullable Object domainObject) {
+        var mo = MetaModelContext.instanceElseFail()
+            .getObjectManager()
+            .adapt(domainObject);
+        if(ManagedObjects.isNullOrUnspecifiedOrEmpty(mo))
+            return Optional.empty();
+        var layoutPrefix = mo.objSpec().lookupFacet(LayoutPrefixFacet.class)
+                .map(layoutPrefixFacet->layoutPrefixFacet.layoutPrefix(mo))
+                .orElse(null);
+        return Optional.of(new LayoutKey(domainObject.getClass(), 
layoutPrefix));
+    }
+
     @Override
     public EnumSet<CommonMimeType> supportedObjectLayoutFormats() {
         return gridService.supportedFormats();
     }
 
     @Override
-    public String objectLayout(final Class<?> domainClass, final 
LayoutExportStyle style, final CommonMimeType format) {
-        return tryGridToFormatted(domainClass, style, format)
+    public String objectLayout(final LayoutKey layoutKey, final 
LayoutExportStyle style, final CommonMimeType format) {
+        return tryGridToFormatted(layoutKey, style, format)
                 .ifFailureFail()
                 .getValue()
                 .orElse(null);
@@ -110,14 +128,14 @@ public byte[] toZip(final LayoutExportStyle style, final 
CommonMimeType format)
         for (var objectSpec : domainObjectSpecs) {
             var domainClass = objectSpec.getCorrespondingClass();
 
-            tryGridToFormatted(domainClass, style, format)
-            .accept(
-                failure->
-                    log.warn("failed to generate layout file for {}", 
domainClass),
-                contentIfAny->{
-                    contentIfAny.ifPresent(contentString->
-                        zipBuilder.addAsUtf8(zipEntryNameFor(objectSpec, 
format), contentString));
-                });
+            tryGridToFormatted(new LayoutKey(domainClass), style, format)
+                .accept(
+                    failure->
+                        log.warn("failed to generate layout file for {}", 
domainClass),
+                    contentIfAny->{
+                        contentIfAny.ifPresent(contentString->
+                            zipBuilder.addAsUtf8(zipEntryNameFor(objectSpec, 
format), contentString));
+                    });
         }
 
         return zipBuilder.toBytes();
@@ -126,20 +144,25 @@ public byte[] toZip(final LayoutExportStyle style, final 
CommonMimeType format)
     // -- HELPER
 
     private BSGrid toGridForExport(
-        final Class<?> domainClass,
+        final LayoutKey layoutKey,
         final LayoutExportStyle style) {
         // making a deep copy so, we don't modify the cached grid
-        var grid = BSUtil.deepCopy(gridService.load(new 
LayoutKey(domainClass)));
+        var grid = BSUtil.deepCopy(gridService.load(layoutKey));
 
-        if (style == LayoutExportStyle.COMPLETE) return toComplete(grid, 
domainClass);
-        if (style == LayoutExportStyle.MINIMAL) return toMinimal(grid, 
domainClass);
+        if (style == LayoutExportStyle.COMPLETE) return toComplete(grid, 
layoutKey);
+        if (style == LayoutExportStyle.MINIMAL) return toMinimal(grid, 
layoutKey.domainClass());
 
         throw _Exceptions.unmatchedCase(style);
     }
 
-    private BSGrid toComplete(final BSGrid grid, final Class<?> domainClass) {
-        var objectSpec = specLoader().specForTypeElseFail(domainClass);
-        grid.visit(new MetamodelToGridOverridingVisitor(objectSpec));
+    private BSGrid toComplete(final BSGrid grid, final LayoutKey layoutKey) {
+        var objectSpec = 
specLoader().specForTypeElseFail(layoutKey.domainClass());
+        try {
+            FacetRanking.setQualifier(layoutKey);
+            grid.visit(new MetamodelToGridOverridingVisitor(objectSpec));
+        } finally {
+            FacetRanking.removeQualifier();
+        }
         return grid;
     }
 
@@ -149,7 +172,6 @@ private BSGrid toMinimal(final BSGrid grid, final Class<?> 
domainClass) {
                 BSUtil.remove(actionLayoutData);
             }
             @Override public void visit(final CollectionLayoutData 
collectionLayoutData) {
-
                 BSUtil.remove(collectionLayoutData);
             }
             @Override public void visit(final PropertyLayoutData 
propertyLayoutData) {
@@ -163,11 +185,11 @@ private BSGrid toMinimal(final BSGrid grid, final 
Class<?> domainClass) {
     }
 
     private Try<String> tryGridToFormatted(
-            final Class<?> domainClass,
+            final LayoutKey layoutKey,
             final LayoutExportStyle style,
             final CommonMimeType format) {
         return Try.call(()->
-            gridToFormatted(toGridForExport(domainClass, style), format));
+            gridToFormatted(toGridForExport(layoutKey, style), format));
     }
 
     private String gridToFormatted(final @Nullable BSGrid grid, final 
CommonMimeType format) {

Reply via email to