You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/04/08 21:38:29 UTC

[isis] 01/01: ISIS-2602: refine LayoutGroupFacet(s)

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit dc3d904559f9d9ce2b75fffa1deeb70f7af7edfb
Author: ahuber@apache.org <ah...@luna>
AuthorDate: Thu Apr 8 23:37:46 2021 +0200

    ISIS-2602: refine LayoutGroupFacet(s)
---
 .../isis/applib/layout/component/FieldSet.java     |   6 +-
 .../action/ActionAnnotationFacetFactory.java       |   2 -
 .../actions/layout/ActionLayoutFacetFactory.java   |   6 +
 .../members/layout/group/GroupIdAndName.java       | 124 +++++++++++++++++++++
 .../members/layout/group/LayoutGroupFacet.java     |  20 +++-
 .../layout/group/LayoutGroupFacetAbstract.java     |  11 +-
 ...ayoutGroupFacetFromActionLayoutAnnotation.java} |  22 ++--
 ...youtGroupFacetFromPropertyLayoutAnnotation.java |  18 +--
 .../layout/group/LayoutGroupFacetFromXml.java      |  33 +++++-
 .../isis/core/metamodel/layout/DeweyOrderSet.java  |   6 +-
 .../services/grid/GridSystemServiceAbstract.java   |  29 ++---
 .../grid/bootstrap3/GridSystemServiceBS3.java      |   4 +-
 .../core/metamodel/spec/feature/ObjectAction.java  |  14 +--
 .../metamodel/spec/feature/ObjectAssociation.java  |   6 +-
 .../core/metamodel/spec/feature/ObjectMember.java  |  12 +-
 .../memberorder/DeweyOrderComparatorTest.java      |   5 +-
 .../ordering/memberorder/DeweyOrderSetTest.java    |   5 +-
 .../menubars/bootstrap3/MenuBarsServiceBS3.java    |   2 +-
 18 files changed, 249 insertions(+), 76 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/layout/component/FieldSet.java b/api/applib/src/main/java/org/apache/isis/applib/layout/component/FieldSet.java
index f60808d..9547889 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/layout/component/FieldSet.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/layout/component/FieldSet.java
@@ -63,14 +63,14 @@ Serializable {
         setName(name);
     }
 
-
-
+    
+    
     private String id;
 
     /**
      * As per &lt;div id=&quot;...&quot;&gt;...&lt;/div&gt; : must be unique across entire page.
      */
-    @XmlAttribute(required = true)
+    @XmlAttribute(required = false)
     public String getId() {
         return id;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
index 149d0a0..9f9b072 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
@@ -42,7 +42,6 @@ import org.apache.isis.core.metamodel.facets.actions.action.prototype.PrototypeF
 import org.apache.isis.core.metamodel.facets.actions.action.semantics.ActionSemanticsFacetForActionAnnotation;
 import org.apache.isis.core.metamodel.facets.actions.action.typeof.TypeOfFacetForActionAnnotation;
 import org.apache.isis.core.metamodel.facets.actions.fileaccept.FileAcceptFacetForActionAnnotation;
-import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacetFromActionAnnotation;
 import org.apache.isis.core.metamodel.facets.members.publish.command.CommandPublishingFacetForActionAnnotation;
 import org.apache.isis.core.metamodel.facets.members.publish.execution.ExecutionPublishingActionFacetForActionAnnotation;
 import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation;
@@ -270,7 +269,6 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract {
         actionIfAny.ifPresent(action->{
             val associateWith = action.associateWith();
             if(_Strings.isNotEmpty(associateWith)) {
-                super.addFacet(LayoutGroupFacetFromActionAnnotation.create(actionIfAny, facetedMethod));
                 super.addFacet(new AssociatedWithFacetForActionAnnotation(associateWith, facetedMethod));
             }
         });
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
index 5a9592f..040ebae 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
@@ -30,6 +30,8 @@ import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
 import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFacet;
+import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
+import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacetFromActionLayoutAnnotation;
 import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacet;
 import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetFromActionLayoutAnnotation;
 import org.apache.isis.core.metamodel.facets.object.bookmarkpolicy.BookmarkPolicyFacet;
@@ -67,6 +69,10 @@ extends FacetFactoryAbstract {
         DescribedAsFacet describedAsFacet = DescribedAsFacetForActionLayoutAnnotation.create(actionLayoutIfAny, facetHolder);
         super.addFacet(describedAsFacet);
 
+        // fieldSet
+        LayoutGroupFacet fieldSetFacet = LayoutGroupFacetFromActionLayoutAnnotation.create(actionLayoutIfAny, facetHolder);
+        super.addFacet(fieldSetFacet);
+        
         // hidden
         HiddenFacet hiddenFacet = HiddenFacetForActionLayoutAnnotation.create(actionLayoutIfAny, facetHolder);
         super.addFacet(hiddenFacet);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/GroupIdAndName.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/GroupIdAndName.java
new file mode 100644
index 0000000..70fabc5
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/GroupIdAndName.java
@@ -0,0 +1,124 @@
+/*
+ *  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.isis.core.metamodel.facets.members.layout.group;
+
+import java.io.Serializable;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.layout.component.FieldSet;
+import org.apache.isis.commons.internal.base._Strings;
+
+import lombok.NonNull;
+import lombok.Value;
+import lombok.val;
+
+@Value(staticConstructor = "of")
+public class GroupIdAndName 
+implements
+    Comparable<GroupIdAndName>,
+    Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Id of a layout group.
+     */
+    private final @NonNull String id;
+    
+    /**
+     * (Friendly) name of a layout group.
+     */
+    private final @NonNull String name;
+
+    @Override
+    public int compareTo(GroupIdAndName other) {
+        if(other==null) {
+            return -1; // null last
+        }
+        return this.getId().compareTo(other.getId());
+    }
+    
+    // -- FACTORIES
+
+    public static @Nullable GroupIdAndName forActionLayout(final @NonNull ActionLayout actionLayout) {
+        return GroupIdAndName.inferIfOneMissing(actionLayout.fieldSetId(), actionLayout.fieldSetName());
+    }
+
+    public static @Nullable GroupIdAndName forPropertyLayout(final @NonNull PropertyLayout propertyLayout) {
+        return GroupIdAndName.inferIfOneMissing(propertyLayout.fieldSetId(), propertyLayout.fieldSetName());
+    }
+
+    public static @Nullable GroupIdAndName forFieldSet(final @NonNull FieldSet fieldSet) {
+        return GroupIdAndName.inferIfOneMissing(
+                fieldSet.getId() == null ? "__infer" : fieldSet.getId(), 
+                fieldSet.getName() == null ? "__infer" : fieldSet.getName());
+    }
+    
+    // -- HELPER
+    
+    private static @Nullable GroupIdAndName inferIfOneMissing(
+            final @NonNull String id, 
+            final @NonNull String name) {
+        
+        val isIdUnspecified = isUnspecified(id) || id.isEmpty();
+        val isNameUnspecified = isUnspecified(name);
+        if(isIdUnspecified
+                && isNameUnspecified) {
+            return null; // fully unspecified, don't create a LayoutGroupFacet down the line
+        }
+        if(isIdUnspecified) {
+            val inferredId = inferIdFromName(name);
+            if(inferredId.isEmpty()) {
+                return null; // cannot infer a usable id, so don't create a LayoutGroupFacet down the line
+            }
+            return GroupIdAndName.of(inferIdFromName(name), name);
+        } else if(isNameUnspecified) {
+            val inferredName = inferNameFromId(id);
+            return GroupIdAndName.of(id, inferredName);
+        }
+        return GroupIdAndName.of(id, name);
+    }
+    
+    // note: this is a copy of the original logic from GridSystemServiceBS3
+    private static @NonNull String inferIdFromName(final @NonNull String name) {
+        if(name.isEmpty()) {
+            return name;
+        }
+        final char c = name.charAt(0);
+        return Character.toLowerCase(c) + name.substring(1).replaceAll("\\s+", "");
+    }
+    
+    // note: could be potentially improved to work similar as the title service
+    private static @NonNull String inferNameFromId(final @NonNull String id) {
+        return _Strings.asNaturalName2.apply(id);
+    }
+
+    /**
+     * Corresponds to the defaults set in {@link ActionLayout#fieldSetId()} etc.
+     */
+    private static boolean isUnspecified(final @NonNull String idOrName) {
+        return "__infer".equals(idOrName);
+    }
+    
+
+    
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacet.java
index e2e26ac..6d6cde2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacet.java
@@ -33,17 +33,31 @@ import org.apache.isis.core.metamodel.facetapi.Facet;
  * </p>
  * 
  * @see Action#associateWith() 
+ * @see ActionLayout#fieldSetId()
  * @see PropertyLayout#fieldSetId()
  * 
  * @since 2.0
  */
 public interface LayoutGroupFacet extends Facet {
+    
+    /**
+     * Id and (Friendly) name of the layout group this member is associated with.
+     */
+    public GroupIdAndName getGroupIdAndName();
 
     /**
-     * Name of the (layout) group, this member belongs to.
-     * Collections don't support grouping.
+     * Id of the layout group this member is associated with.
+     */
+    public default String getGroupId() {
+        return getGroupIdAndName().getId();
+    }
+    
+    /**
+     * (Friendly) name of the layout group this member is associated with.
      */
-    public String getGroup();
+    public default String getGroupName() {
+        return getGroupIdAndName().getName();
+    }
     
     /** 
      * {@code true} for layouts originating from XML, 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetAbstract.java
index 68a385a..b053d2b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetAbstract.java
@@ -30,17 +30,20 @@ extends FacetAbstract
 implements LayoutGroupFacet {
 
     @Getter(onMethod_ = {@Override})
-    private final String group;
+    private final GroupIdAndName groupIdAndName;
     
-    protected LayoutGroupFacetAbstract(String group, FacetHolder holder) {
+    protected LayoutGroupFacetAbstract(
+            final GroupIdAndName groupIdAndName, 
+            final FacetHolder holder) {
         super(LayoutGroupFacet.class, holder);
-        this.group = group;
+        this.groupIdAndName = groupIdAndName;
     }
     
     @Override 
     public void appendAttributesTo(final Map<String, Object> attributeMap) {
         super.appendAttributesTo(attributeMap);
-        attributeMap.put("group", group);
+        attributeMap.put("groupId", getGroupId());
+        attributeMap.put("groupName", getGroupName());
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromActionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromActionLayoutAnnotation.java
similarity index 60%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromActionAnnotation.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromActionLayoutAnnotation.java
index 2f82913..50d2686 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromActionAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromActionLayoutAnnotation.java
@@ -20,24 +20,26 @@ package org.apache.isis.core.metamodel.facets.members.layout.group;
 
 import java.util.Optional;
 
-import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 
-public class LayoutGroupFacetFromActionAnnotation
+public class LayoutGroupFacetFromActionLayoutAnnotation
 extends LayoutGroupFacetAbstract {
 
-    public static LayoutGroupFacetFromActionAnnotation create(
-            final Optional<Action> actionIfAny, 
+    public static LayoutGroupFacetFromActionLayoutAnnotation create(
+            final Optional<ActionLayout> actionLayoutIfAny, 
             final FacetHolder holder) {
         
-        return actionIfAny
-                .map(Action::associateWith)
-                .map(group -> new LayoutGroupFacetFromActionAnnotation(group, holder))
-                .orElse(null);
+        return actionLayoutIfAny
+            .map(GroupIdAndName::forActionLayout)
+            .filter(_NullSafe::isPresent)
+            .map(groupIdAndName->new LayoutGroupFacetFromActionLayoutAnnotation(groupIdAndName, holder))
+            .orElse(null);
     }
 
-    public LayoutGroupFacetFromActionAnnotation(final String group, final FacetHolder holder) {
-        super(group, holder);
+    private LayoutGroupFacetFromActionLayoutAnnotation(GroupIdAndName groupIdAndName, FacetHolder holder) {
+        super(groupIdAndName, holder);
     }
     
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromPropertyLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromPropertyLayoutAnnotation.java
index bb774c0..571106a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromPropertyLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromPropertyLayoutAnnotation.java
@@ -21,23 +21,25 @@ package org.apache.isis.core.metamodel.facets.members.layout.group;
 import java.util.Optional;
 
 import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 
 public class LayoutGroupFacetFromPropertyLayoutAnnotation
 extends LayoutGroupFacetAbstract {
 
     public static LayoutGroupFacetFromPropertyLayoutAnnotation create(
-            final Optional<PropertyLayout> actionIfAny, 
+            final Optional<PropertyLayout> propertyLayoutIfAny, 
             final FacetHolder holder) {
-        
-        return actionIfAny
-                .map(PropertyLayout::fieldSetId)
-                .map(group -> new LayoutGroupFacetFromPropertyLayoutAnnotation(group, holder))
+
+        return propertyLayoutIfAny
+                .map(GroupIdAndName::forPropertyLayout)
+                .filter(_NullSafe::isPresent)
+                .map(groupIdAndName->new LayoutGroupFacetFromPropertyLayoutAnnotation(groupIdAndName, holder))
                 .orElse(null);
     }
 
-    public LayoutGroupFacetFromPropertyLayoutAnnotation(final String group, final FacetHolder holder) {
-        super(group, holder);
+    private LayoutGroupFacetFromPropertyLayoutAnnotation(GroupIdAndName groupIdAndName, FacetHolder holder) {
+        super(groupIdAndName, holder);
     }
-    
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromXml.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromXml.java
index aa40db1..866feee 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromXml.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/layout/group/LayoutGroupFacetFromXml.java
@@ -18,20 +18,40 @@
  */
 package org.apache.isis.core.metamodel.facets.members.layout.group;
 
+import javax.annotation.Nullable;
+
+import org.apache.isis.applib.layout.component.FieldSet;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 
+import lombok.NonNull;
+import lombok.val;
+
 public class LayoutGroupFacetFromXml
 extends LayoutGroupFacetAbstract {
 
-    public static LayoutGroupFacetFromXml create(
-            final String group,
-            final FacetHolder holder) {
+    // -- FACTORIES
+    
+    public static @Nullable LayoutGroupFacetFromXml create(
+            final @Nullable GroupIdAndName groupIdAndName, 
+            final @NonNull  FacetHolder holder) {
+        
+        return groupIdAndName!=null        
+                ? new LayoutGroupFacetFromXml(groupIdAndName, holder)
+                : null;
+    }
+    
+    public static @Nullable LayoutGroupFacetFromXml create(
+            final @NonNull FieldSet fieldSet,
+            final @NonNull FacetHolder holder) {
         
-        return new LayoutGroupFacetFromXml(group, holder);
+        val groupIdAndName = GroupIdAndName.forFieldSet(fieldSet);
+        return create(groupIdAndName, holder);
     }
+    
+    // -- IMPLEMENTATION
 
-    public LayoutGroupFacetFromXml(final String group, final FacetHolder holder) {
-        super(group, holder);
+    private LayoutGroupFacetFromXml(GroupIdAndName groupIdAndName, FacetHolder holder) {
+        super(groupIdAndName, holder);
     }
     
     @Override
@@ -39,4 +59,5 @@ extends LayoutGroupFacetAbstract {
         return true;
     }
     
+    
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/DeweyOrderSet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/DeweyOrderSet.java
index edf478e..1d9d980 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/DeweyOrderSet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/DeweyOrderSet.java
@@ -88,9 +88,9 @@ public class DeweyOrderSet implements Comparable<DeweyOrderSet>, Iterable<Object
                 nonAnnotatedGroup.add(identifiedHolder);
                 continue;
             }
-            final SortedSet<IdentifiedHolder> sortedMembersForGroup = 
-                    getSortedSet(sortedMembersByGroup, layoutGroupFacet.getGroup());
-            sortedMembersForGroup.add(identifiedHolder);
+            final SortedSet<IdentifiedHolder> sortedMembersForFieldSet = 
+                    getSortedSet(sortedMembersByGroup, layoutGroupFacet.getGroupId());
+            sortedMembersForFieldSet.add(identifiedHolder);
         }
 
         // add the non-annotated group to the first "" group.
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
index 2b4fec4..a30b522 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/GridSystemServiceAbstract.java
@@ -40,7 +40,6 @@ import org.apache.isis.applib.services.grid.GridSystemService;
 import org.apache.isis.applib.services.i18n.TranslationService;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.message.MessageService;
-import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.core.config.environment.IsisSystemEnvironment;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -62,6 +61,7 @@ import org.apache.isis.core.metamodel.facets.collections.layout.HiddenFacetForCo
 import org.apache.isis.core.metamodel.facets.collections.layout.NamedFacetForCollectionXml;
 import org.apache.isis.core.metamodel.facets.collections.layout.PagedFacetForCollectionXml;
 import org.apache.isis.core.metamodel.facets.collections.layout.SortedByFacetForCollectionXml;
+import org.apache.isis.core.metamodel.facets.members.layout.group.GroupIdAndName;
 import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacetFromXml;
 import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetFromXml;
 import org.apache.isis.core.metamodel.facets.object.domainobjectlayout.BookmarkPolicyFacetForDomainObjectXml;
@@ -212,7 +212,7 @@ implements GridSystemService<G> {
                     return;
                 }
 
-                String memberOrderName = null;
+                GroupIdAndName groupIdAndName = null;
                 int memberOrderSequence;
                 if(actionLayoutDataOwner instanceof FieldSet) {
                     final FieldSet fieldSet = (FieldSet) actionLayoutDataOwner;
@@ -221,28 +221,33 @@ implements GridSystemService<G> {
                         final String propertyId = propertyLayoutData.getId();
                         // any will do; choose the first one that we know is valid
                         if(oneToOneAssociationById.containsKey(propertyId)) {
-                            memberOrderName = propertyLayoutData.getId();                            
+                            groupIdAndName = GroupIdAndName.of(
+                                    propertyLayoutData.getId(),
+                                    propertyLayoutData.getNamed());                            
                             break;
                         }
                     }
                     memberOrderSequence = actionPropertyGroupSequence++;
                 } else if(actionLayoutDataOwner instanceof PropertyLayoutData) {
                     final PropertyLayoutData propertyLayoutData = (PropertyLayoutData) actionLayoutDataOwner;
-                    memberOrderName = propertyLayoutData.getId();
+                    groupIdAndName = GroupIdAndName.of(
+                            propertyLayoutData.getId(),
+                            propertyLayoutData.getNamed());
                     memberOrderSequence = actionPropertySequence++;
                 } else if(actionLayoutDataOwner instanceof CollectionLayoutData) {
                     final CollectionLayoutData collectionLayoutData = (CollectionLayoutData) actionLayoutDataOwner;
-                    memberOrderName = collectionLayoutData.getId();
+                    groupIdAndName = GroupIdAndName.of(
+                            collectionLayoutData.getId(),
+                            collectionLayoutData.getNamed());
                     memberOrderSequence = actionCollectionSequence++;
                 } else {
                     // don't add: any existing metadata should be preserved
-                    memberOrderName = null;
+                    groupIdAndName = null;
                     memberOrderSequence = actionDomainObjectSequence++;
                 }
                 addOrReplaceFacet(LayoutOrderFacetFromXml.create(memberOrderSequence, objectAction));
-                if(_Strings.isNotEmpty(memberOrderName)) {
-                    addOrReplaceFacet(LayoutGroupFacetFromXml.create(memberOrderName, objectAction));
-                }
+                addOrReplaceFacet(LayoutGroupFacetFromXml.create(groupIdAndName, objectAction));
+                
 
                 // fix up the action position if required
                 if(actionLayoutDataOwner instanceof FieldSet) {
@@ -303,13 +308,9 @@ implements GridSystemService<G> {
                 // nb for any given field set the sequence won't reset to zero; however this is what we want so that
                 // table columns are shown correctly (by fieldset, then property order within that fieldset).
                 final FieldSet fieldSet = propertyLayoutData.getOwner();
-                final String groupName = fieldSet.getName();
                 
                 addOrReplaceFacet(LayoutOrderFacetFromXml.create(propertySequence.incrementAndGet(), oneToOneAssociation));
-                if(_Strings.isNotEmpty(groupName)) {
-                    addOrReplaceFacet(LayoutGroupFacetFromXml.create(groupName, oneToOneAssociation));
-                }
-                
+                addOrReplaceFacet(LayoutGroupFacetFromXml.create(fieldSet, oneToOneAssociation));
             }
 
             @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
index 54b23a2..89b63e9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridSystemServiceBS3.java
@@ -229,7 +229,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
             if(layoutGroupFacet == null) {
                 continue;
             }
-            val id = layoutGroupFacet.getGroup();
+            val id = layoutGroupFacet.getGroupId();
             if(gridModel.containsFieldSetId(id)) {
                 Set<String> boundAssociationIds =
                         boundAssociationIdsByFieldSetId.computeIfAbsent(id, k -> _Sets.newLinkedHashSet());
@@ -341,7 +341,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
             if(layoutGroupFacet == null) {
                 continue;
             }
-            final String layoutGroupName = layoutGroupFacet.getGroup();
+            final String layoutGroupName = layoutGroupFacet.getGroupId();
             if (layoutGroupName == null) {
                 continue;
             }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
index cd1dac4..617e929 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAction.java
@@ -524,12 +524,12 @@ public interface ObjectAction extends ObjectMember {
                 if (layoutGroupFacet == null) {
                     return false;
                 }
-                val memberGroupName = layoutGroupFacet.getGroup();
-                if (_Strings.isNullOrEmpty(memberGroupName)) {
+                val fieldSetId = layoutGroupFacet.getGroupId();
+                if (_Strings.isNullOrEmpty(fieldSetId)) {
                     return false;
                 }
-                return memberGroupName.equalsIgnoreCase(assocName) 
-                        || memberGroupName.equalsIgnoreCase(assocId);
+                return fieldSetId.equalsIgnoreCase(assocName) 
+                        || fieldSetId.equalsIgnoreCase(assocId);
             };
         }
 
@@ -549,11 +549,11 @@ public interface ObjectAction extends ObjectMember {
                 if (layoutGroupFacet == null) {
                     return true;
                 }
-                val memberGroupName = layoutGroupFacet.getGroup();
-                if (_Strings.isNullOrEmpty(memberGroupName)) {
+                val fieldSetId = layoutGroupFacet.getGroupId();
+                if (_Strings.isNullOrEmpty(fieldSetId)) {
                     return true;
                 }
-                return !associationNamesAndIds.contains(memberGroupName.toLowerCase());
+                return !associationNamesAndIds.contains(fieldSetId.toLowerCase());
             };
         }
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociation.java
index 5c03f25..bc19041 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectAssociation.java
@@ -187,9 +187,9 @@ public interface ObjectAssociation extends ObjectMember, CurrentHolder {
             
             val layoutGroupFacet = association.getFacet(LayoutGroupFacet.class);
             if(layoutGroupFacet != null) {
-                val groupName = layoutGroupFacet.getGroup();
-                if(_Strings.isNotEmpty(groupName)) {
-                    getFrom(associationsByGroup, groupName).add(association);
+                val fieldSetId = layoutGroupFacet.getGroupId();
+                if(_Strings.isNotEmpty(fieldSetId)) {
+                    getFrom(associationsByGroup, fieldSetId).add(association);
                     return;
                 }
             }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMember.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMember.java
index 9a8a89b..74052a4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMember.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectMember.java
@@ -238,14 +238,14 @@ public interface ObjectMember extends ObjectFeature {
                         
                         val groupFacet1 = m1.getFacet(LayoutGroupFacet.class);
                         val groupFacet2 = m2.getFacet(LayoutGroupFacet.class);
-                        val group1 = _Strings.nullToEmpty(groupFacet1==null ? null : groupFacet1.getGroup());
-                        val group2 = _Strings.nullToEmpty(groupFacet2==null ? null : groupFacet2.getGroup());
+                        val groupId1 = _Strings.nullToEmpty(groupFacet1==null ? null : groupFacet1.getGroupId());
+                        val groupId2 = _Strings.nullToEmpty(groupFacet2==null ? null : groupFacet2.getGroupId());
                         
-                        if(!Objects.equals(group1, group2)) {
+                        if(!Objects.equals(groupId1, groupId2)) {
                             throw _Exceptions.illegalArgument(
-                                    "Not in same group when comparing: '%s', '%s'", 
-                                    group1, 
-                                    group2);
+                                    "Not in same fieldSetId1 when comparing: '%s', '%s'", 
+                                    groupId1, 
+                                    groupId2);
                         }
                     }
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderComparatorTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderComparatorTest.java
index 5b1a00e..47b486c 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderComparatorTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderComparatorTest.java
@@ -30,6 +30,7 @@ import org.apache.isis.applib.services.i18n.TranslationService;
 import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
+import org.apache.isis.core.metamodel.facets.members.layout.group.GroupIdAndName;
 import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacetAbstract;
 import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetAbstract;
 import org.apache.isis.core.metamodel.layout.memberorderfacet.MemberOrderComparator;
@@ -184,8 +185,8 @@ public class DeweyOrderComparatorTest extends TestCase {
     
     // -- HELPER
     
-    void setupLayoutFacets(String group, String sequence, FacetedMethod facetedMethod) {
-        facetedMethod.addFacet(new LayoutGroupFacetAbstract(group, facetedMethod) {});
+    void setupLayoutFacets(String groupId, String sequence, FacetedMethod facetedMethod) {
+        facetedMethod.addFacet(new LayoutGroupFacetAbstract(GroupIdAndName.of(groupId, ""), facetedMethod) {});
         facetedMethod.addFacet(new LayoutOrderFacetAbstract(sequence, facetedMethod) {});
     }
         
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderSetTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderSetTest.java
index 17e9947..97c2bf4 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderSetTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/ordering/memberorder/DeweyOrderSetTest.java
@@ -34,6 +34,7 @@ import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
+import org.apache.isis.core.metamodel.facets.members.layout.group.GroupIdAndName;
 import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacetAbstract;
 import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetAbstract;
 import org.apache.isis.core.metamodel.layout.DeweyOrderSet;
@@ -269,8 +270,8 @@ public class DeweyOrderSetTest extends TestCase {
     
     // -- HELPER
     
-    void setupLayoutFacets(String group, String sequence, IdentifiedHolder facetedHolder) {
-        facetedHolder.addFacet(new LayoutGroupFacetAbstract(group, facetedHolder) {});
+    void setupLayoutFacets(String groupId, String sequence, IdentifiedHolder facetedHolder) {
+        facetedHolder.addFacet(new LayoutGroupFacetAbstract(GroupIdAndName.of(groupId, ""), facetedHolder) {});
         facetedHolder.addFacet(new LayoutOrderFacetAbstract(sequence, facetedHolder) {});
     }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
index afe5a47..1bb2bb8 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
@@ -375,7 +375,7 @@ public class MenuBarsServiceBS3 implements MenuBarsService {
                 .filter(objectAction->objectAction.getFacet(NotInServiceMenuFacet.class) == null)
                 .map(objectAction->{
                     val layoutGroupFacet = objectAction.getFacet(LayoutGroupFacet.class);
-                    String serviceName = layoutGroupFacet != null ? layoutGroupFacet.getGroup(): null;
+                    String serviceName = layoutGroupFacet != null ? layoutGroupFacet.getGroupId(): null;
                     if(_Strings.isNullOrEmpty(serviceName)){
                         serviceName = serviceSpec.getFacet(NamedFacet.class).value();
                     }