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/07 21:59:49 UTC

[isis] 08/10: ISIS-2523: migrate ObjectMember layout order comparators

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 b7f78f307ecb23e00c36ce078015c1894a99532e
Author: ahuber@apache.org <ah...@luna>
AuthorDate: Wed Apr 7 22:49:29 2021 +0200

    ISIS-2523: migrate ObjectMember layout order comparators
---
 .../isis/applib/layout/component/FieldSet.java     |  2 +-
 .../members/layout/group/LayoutGroupFacet.java     |  8 ++
 .../layout/group/LayoutGroupFacetFromXml.java      |  5 ++
 .../annotprop/MemberOrderFacetAnnotation.java      | 37 ---------
 .../MemberOrderFacetForActionAnnotation.java       | 34 --------
 .../annotprop/MemberOrderFacetProperties.java      | 51 ------------
 .../memberorderfacet/MemberOrderComparator.java    | 44 +++++-----
 .../MemberOrderFacetComparator.java                | 56 -------------
 .../services/grid/bootstrap3/GridModel.java        |  4 -
 .../grid/bootstrap3/GridSystemServiceBS3.java      | 93 +++++++++------------
 .../core/metamodel/spec/feature/ObjectAction.java  | 58 ++-----------
 .../core/metamodel/spec/feature/ObjectMember.java  | 59 +++++++++++---
 .../memberorder/DeweyOrderComparatorTest.java      | 65 ++++++++-------
 .../ordering/memberorder/DeweyOrderSetTest.java    | 94 ++++++++++++----------
 14 files changed, 211 insertions(+), 399 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 6ee36dc..f60808d 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
@@ -70,7 +70,7 @@ Serializable {
     /**
      * As per &lt;div id=&quot;...&quot;&gt;...&lt;/div&gt; : must be unique across entire page.
      */
-    @XmlAttribute(required = false)
+    @XmlAttribute(required = true)
     public String getId() {
         return id;
     }
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 0f04d95..f070c52 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
@@ -45,4 +45,12 @@ public interface LayoutGroupFacet extends Facet {
      */
     public String getGroup();
     
+    /** 
+     * {@code true} for layouts originating from XML, 
+     * {@code false} for layouts originating from annotations.
+     */
+    public default boolean isExplicitBinding() {
+        return false;
+    }
+    
 }
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 52139fd..aa40db1 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
@@ -34,4 +34,9 @@ extends LayoutGroupFacetAbstract {
         super(group, holder);
     }
     
+    @Override
+    public boolean isExplicitBinding() {
+        return true;
+    }
+    
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetAnnotation.java
deleted file mode 100644
index cea600f..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetAnnotation.java
+++ /dev/null
@@ -1,37 +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.isis.core.metamodel.facets.members.order.annotprop;
-
-import org.apache.isis.applib.services.i18n.TranslationContext;
-import org.apache.isis.applib.services.i18n.TranslationService;
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacetAbstract;
-
-public class MemberOrderFacetAnnotation extends MemberOrderFacetAbstract {
-
-    public MemberOrderFacetAnnotation(
-    		TranslationContext context,
-            final String name,
-            final String sequence,
-            final TranslationService translationService, final FacetHolder holder) {
-        super(context, name, sequence, translationService, holder);
-    }
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetForActionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetForActionAnnotation.java
deleted file mode 100644
index 0f5dc91..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetForActionAnnotation.java
+++ /dev/null
@@ -1,34 +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.isis.core.metamodel.facets.members.order.annotprop;
-
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacetAbstract;
-
-public class MemberOrderFacetForActionAnnotation extends MemberOrderFacetAbstract {
-
-    public MemberOrderFacetForActionAnnotation(
-            final String name,
-            final String sequence,
-            final FacetHolder holder) {
-        super(name, sequence, holder);
-    }
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetProperties.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetProperties.java
deleted file mode 100644
index ba1d622..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/members/order/annotprop/MemberOrderFacetProperties.java
+++ /dev/null
@@ -1,51 +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.isis.core.metamodel.facets.members.order.annotprop;
-
-import java.util.Properties;
-
-import org.apache.isis.applib.services.i18n.TranslationContext;
-import org.apache.isis.applib.services.i18n.TranslationService;
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacetAbstract;
-
-public class MemberOrderFacetProperties extends MemberOrderFacetAbstract {
-
-    public MemberOrderFacetProperties(
-    		TranslationContext context,
-            final Properties properties,
-            final TranslationService translationService,
-            final FacetHolder holder) {
-        this(context, name(properties), sequence(properties), translationService, holder);
-    }
-
-    private static String sequence(final Properties properties) {
-        return properties.getProperty("sequence");
-    }
-
-    private static String name(final Properties properties) {
-        return properties.getProperty("name");
-    }
-
-    private MemberOrderFacetProperties(TranslationContext context,final String name, final String sequence, final TranslationService translationService, final FacetHolder holder) {
-        super(context, name, sequence, translationService, holder);
-    }
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java
index 048c871..45b7652 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java
@@ -21,15 +21,17 @@ package org.apache.isis.core.metamodel.layout.memberorderfacet;
 
 import java.util.Comparator;
 
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
+import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacet;
 import org.apache.isis.core.metamodel.layout.DeweyOrderSet;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+
+import lombok.val;
 
 /**
- * Compares by {@link MemberOrderFacet} obtained from each {@link FacetedMethod}
- * ).
+ * Compares by {@link ObjectMember}(s) obtained based on their {@link LayoutOrderFacet}.
  *
  * <p>
  * Will also compare {@link DeweyOrderSet}s; these are put after any
@@ -45,19 +47,24 @@ import org.apache.isis.core.metamodel.layout.DeweyOrderSet;
  */
 public class MemberOrderComparator implements Comparator<Object> {
 
-    private final MemberOrderFacetComparator memberOrderFacetComparator;
+    private final Comparator<IdentifiedHolder> memberComparator;
     private final MemberIdentifierComparator memberIdentifierComparator = new MemberIdentifierComparator();
     private final OrderSetGroupNameComparator orderSetComparator = new OrderSetGroupNameComparator(true);
 
     public MemberOrderComparator(final boolean ensureGroupIsSame) {
-        memberOrderFacetComparator = new MemberOrderFacetComparator(ensureGroupIsSame);
+        memberComparator = ObjectMember.Comparators.byMemberOrderSequence(ensureGroupIsSame);
     }
 
-
     @Override
     public int compare(final Object o1, final Object o2) {
         if (o1 instanceof IdentifiedHolder && o2 instanceof IdentifiedHolder) {
-            return compare((IdentifiedHolder) o1, (IdentifiedHolder) o2);
+            val m1 = (IdentifiedHolder) o1;
+            val m2 = (IdentifiedHolder) o2;
+            final int memberOrderComparison = memberComparator.compare(m1, m2);
+            if(memberOrderComparison != 0) {
+                return memberOrderComparison;
+            }
+            return memberIdentifierComparator.compare(m1, m2);
         }
         if (o1 instanceof DeweyOrderSet && o2 instanceof DeweyOrderSet) {
             return orderSetComparator.compare((DeweyOrderSet) o1, (DeweyOrderSet) o2);
@@ -68,23 +75,10 @@ public class MemberOrderComparator implements Comparator<Object> {
         if (o1 instanceof DeweyOrderSet && o2 instanceof IdentifiedHolder) {
             return +1; // members before OrderSets.
         }
-        throw new IllegalArgumentException("can only compare IdentifiedHolders and DeweyOrderSets");
-    }
-
-    public int compare(final IdentifiedHolder o1, final IdentifiedHolder o2) {
-        final MemberOrderFacet m1 = getMemberOrder(o1);
-        final MemberOrderFacet m2 = getMemberOrder(o2);
-
-        final int memberOrderComparison = memberOrderFacetComparator.compare(m1, m2);
-        if(memberOrderComparison != 0) {
-            return memberOrderComparison;
-        }
-        return memberIdentifierComparator.compare(o1, o2);
-    }
-
-
-    private MemberOrderFacet getMemberOrder(final FacetHolder facetHolder) {
-        return facetHolder.getFacet(MemberOrderFacet.class);
+        throw _Exceptions.illegalArgument(
+                "can only compare IdentifiedHolders and DeweyOrderSets, got: %s, %s", 
+                o1==null ? null : o1.getClass().getName(), 
+                o2==null ? null : o2.getClass().getName());
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderFacetComparator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderFacetComparator.java
deleted file mode 100644
index ceb74c6..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderFacetComparator.java
+++ /dev/null
@@ -1,56 +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.isis.core.metamodel.layout.memberorderfacet;
-
-import java.util.Comparator;
-
-import org.apache.isis.commons.internal.compare._Comparators;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
-
-public class MemberOrderFacetComparator implements Comparator<MemberOrderFacet> {
-
-    private boolean ensureInSameGroup;
-    public MemberOrderFacetComparator(boolean ensureInSameGroup) {
-        this.ensureInSameGroup = ensureInSameGroup;
-    }
-
-    @Override
-    public int compare(final MemberOrderFacet m1, final MemberOrderFacet m2) {
-        if (m1 == null && m2 == null) {
-            return 0;
-        }
-
-        if (m1 == null && m2 != null) {
-            return +1; // annotated before non-annotated
-        }
-        if (m1 != null && m2 == null) {
-            return -1; // annotated before non-annotated
-        }
-
-        if (ensureInSameGroup && !m1.name().equals(m2.name())) {
-            throw new IllegalArgumentException("Not in same group");
-        }
-
-        return _Comparators.deweyOrderCompare(m1.sequence(), m2.sequence());
-
-    }
-
-
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridModel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridModel.java
index 928caa9..28be252 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridModel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/grid/bootstrap3/GridModel.java
@@ -113,10 +113,6 @@ final class GridModel {
                 @Override
                 public void visit(final FieldSet fieldSet) {
                     String id = fieldSet.getId();
-                    if(id == null) {
-                        final String name = fieldSet.getName();
-                        fieldSet.setId(id = GridSystemServiceBS3.asId(name));
-                    }
                     if(gridModel.contains(id)) {
                         fieldSet.setMetadataError("There is another element in the grid with this id");
                         gridModel.duplicateIdDetected = true;
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 a85d73d..54b23a2 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
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.core.metamodel.services.grid.bootstrap3;
 
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -52,14 +54,12 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
 import org.apache.isis.applib.layout.grid.bootstrap3.Size;
 import org.apache.isis.applib.mixins.layout.LayoutMixinConstants;
 import org.apache.isis.commons.internal.base._NullSafe;
-import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.commons.internal.resources._Resources;
 import org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacet;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
-import org.apache.isis.core.metamodel.facets.members.order.annotprop.MemberOrderFacetAnnotation;
+import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
 import org.apache.isis.core.metamodel.layout.LayoutFacetUtil.LayoutDataFactory;
 import org.apache.isis.core.metamodel.services.grid.GridReaderUsingJaxb;
 import org.apache.isis.core.metamodel.services.grid.GridSystemServiceAbstract;
@@ -71,8 +71,6 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
-import static org.apache.isis.commons.internal.base._NullSafe.stream;
-
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
@@ -226,17 +224,18 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
         val unboundMetadataContributingIds = _Sets.<String>newHashSet();
 
         // along with any specified by existing metadata
-        for (OneToOneAssociation otoa : oneToOneAssociationById.values()) {
-            val memberOrderFacet = otoa.getFacet(MemberOrderFacet.class);
-            if(memberOrderFacet != null) {
-                val id = asId(memberOrderFacet.name());
-                if(gridModel.containsFieldSetId(id)) {
-                    Set<String> boundAssociationIds =
-                            boundAssociationIdsByFieldSetId.computeIfAbsent(id, k -> _Sets.newLinkedHashSet());
-                    boundAssociationIds.add(otoa.getId());
-                } else if(id.equals(LayoutMixinConstants.METADATA_LAYOUT_GROUPNAME)) {
-                    unboundMetadataContributingIds.add(otoa.getId());
-                }
+        for (final OneToOneAssociation oneToOneAssociation : oneToOneAssociationById.values()) {
+            val layoutGroupFacet = oneToOneAssociation.getFacet(LayoutGroupFacet.class);
+            if(layoutGroupFacet == null) {
+                continue;
+            }
+            val id = layoutGroupFacet.getGroup();
+            if(gridModel.containsFieldSetId(id)) {
+                Set<String> boundAssociationIds =
+                        boundAssociationIdsByFieldSetId.computeIfAbsent(id, k -> _Sets.newLinkedHashSet());
+                boundAssociationIds.add(oneToOneAssociation.getId());
+            } else if(id.equals(LayoutMixinConstants.METADATA_LAYOUT_GROUPNAME)) {
+                unboundMetadataContributingIds.add(oneToOneAssociation.getId());
             }
         }
 
@@ -257,7 +256,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
                         associationIds.stream()
                         .map(oneToOneAssociationById::get)
                         .filter(_NullSafe::isPresent)
-                        .sorted(ObjectMember.Comparators.byMemberOrderSequence())
+                        .sorted(ObjectMember.Comparators.byMemberOrderSequence(false))
                         .map(ObjectAssociation::getId)
                         .collect(Collectors.toList());
 
@@ -295,7 +294,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
             final List<OneToManyAssociation> sortedCollections =
                     _Lists.map(missingCollectionIds, oneToManyAssociationById::get);
 
-            sortedCollections.sort(ObjectMember.Comparators.byMemberOrderSequence());
+            sortedCollections.sort(ObjectMember.Comparators.byMemberOrderSequence(false));
 
             final List<String> sortedMissingCollectionIds =
                     _Lists.map(sortedCollections, ObjectAssociation::getId);
@@ -330,41 +329,38 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
         final List<ObjectAction> sortedPossiblyMissingActions =
                 _Lists.map(possiblyMissingActionIds, objectActionById::get);
 
-        sortedPossiblyMissingActions.sort(ObjectMember.Comparators.byMemberOrderSequence());
+        sortedPossiblyMissingActions.sort(ObjectMember.Comparators.byMemberOrderSequence(false));
 
         final List<String> sortedPossiblyMissingActionIds =
                 _Lists.map(sortedPossiblyMissingActions, ObjectMember::getId);
 
-        for (String actionId : sortedPossiblyMissingActionIds) {
-            final ObjectAction oa = objectActionById.get(actionId);
-            final MemberOrderFacet memberOrderFacet = oa.getFacet(MemberOrderFacet.class);
-            if(memberOrderFacet == null) {
+        for (final String actionId : sortedPossiblyMissingActionIds) {
+            val objectAction = objectActionById.get(actionId);
+            
+            val layoutGroupFacet = objectAction.getFacet(LayoutGroupFacet.class);
+            if(layoutGroupFacet == null) {
                 continue;
             }
-            final String memberOrderName = memberOrderFacet.name();
-            if (memberOrderName == null) {
+            final String layoutGroupName = layoutGroupFacet.getGroup();
+            if (layoutGroupName == null) {
                 continue;
             }
-            final String id = asId(memberOrderName);
 
-            if (oneToOneAssociationById.containsKey(id)) {
+            if (oneToOneAssociationById.containsKey(layoutGroupName)) {
                 associatedActionIds.add(actionId);
 
-                if(!(memberOrderFacet instanceof MemberOrderFacetAnnotation)) {
-                    // if binding not via annotation, then explicitly bind this
-                    // action to the property
-                    final PropertyLayoutData propertyLayoutData = propertyLayoutDataById.get(id);
+                if(layoutGroupFacet.isExplicitBinding()) {
+                    final PropertyLayoutData propertyLayoutData = propertyLayoutDataById.get(layoutGroupName);
                     final ActionLayoutData actionLayoutData = new ActionLayoutData(actionId);
-
-                    final ActionPositionFacet actionPositionFacet = oa.getFacet(ActionPositionFacet.class);
+                    final ActionPositionFacet actionPositionFacet = objectAction.getFacet(ActionPositionFacet.class);
                     final ActionLayoutDataOwner owner;
                     final ActionLayout.Position position;
                     if(actionPositionFacet != null) {
                         position = actionPositionFacet.position();
-                        owner = position == ActionLayout.Position.PANEL ||
-                                position == ActionLayout.Position.PANEL_DROPDOWN
+                        owner = position == ActionLayout.Position.PANEL
+                                || position == ActionLayout.Position.PANEL_DROPDOWN
                                 ? propertyLayoutData.getOwner()
-                                        : propertyLayoutData;
+                                : propertyLayoutData;
                     } else {
                         position = ActionLayout.Position.BELOW;
                         owner = propertyLayoutData;
@@ -375,15 +371,13 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
 
                 continue;
             }
-            if (oneToManyAssociationById.containsKey(id)) {
+            if (oneToManyAssociationById.containsKey(layoutGroupName)) {
                 associatedActionIds.add(actionId);
 
-                if(!(memberOrderFacet instanceof MemberOrderFacetAnnotation)) {
-                    // if binding not via annotation, then explicitly bind this
-                    // action to the property
-                    val collectionLayoutData = collectionLayoutDataById.get(id);
+                if(layoutGroupFacet.isExplicitBinding()) {
+                    val collectionLayoutData = collectionLayoutDataById.get(layoutGroupName);
                     if(collectionLayoutData==null) {
-                        log.warn("failed to lookup CollectionLayoutData by id '{}'", id);
+                        log.warn("failed to lookup CollectionLayoutData by layoutGroupName '{}'", layoutGroupName);
                     } else {
                         val actionLayoutData = new ActionLayoutData(actionId);
                         addActionTo(collectionLayoutData, actionLayoutData);
@@ -394,7 +388,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
             // if the @MemberOrder for the action references a field set (that has bound
             // associations), then don't mark it as missing, but instead explicitly add it to the
             // list of actions of that field-set.
-            final Set<String> boundAssociationIds = boundAssociationIdsByFieldSetId.get(id);
+            final Set<String> boundAssociationIds = boundAssociationIdsByFieldSetId.get(layoutGroupName);
             if(boundAssociationIds != null && !boundAssociationIds.isEmpty()) {
 
                 associatedActionIds.add(actionId);
@@ -402,7 +396,7 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
                 final ActionLayoutData actionLayoutData = new ActionLayoutData(actionId);
 
                 actionLayoutData.setPosition(ActionLayout.Position.PANEL_DROPDOWN);
-                final FieldSet fieldSet = gridModel.getFieldSet(id);
+                final FieldSet fieldSet = gridModel.getFieldSet(layoutGroupName);
                 addActionTo(fieldSet, actionLayoutData);
             }
         }
@@ -539,15 +533,4 @@ public class GridSystemServiceBS3 extends GridSystemServiceAbstract<BS3Grid> {
         actionLayoutData.setOwner(owner);
     }
 
-    static String asId(final String str) {
-        if(_Strings.isNullOrEmpty(str)) {
-            return str;
-        }
-        final char c = str.charAt(0);
-        return Character.toLowerCase(c) + str.substring(1).replaceAll("\\s+", "");
-    }
-
-
-
-
 }
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 3b7a625..7793f2a 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
@@ -17,8 +17,9 @@
 
 package org.apache.isis.core.metamodel.spec.feature;
 
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -54,14 +55,11 @@ import org.apache.isis.core.metamodel.facets.object.promptStyle.PromptStyleFacet
 import org.apache.isis.core.metamodel.facets.object.wizard.WizardFacet;
 import org.apache.isis.core.metamodel.interactions.InteractionHead;
 import org.apache.isis.core.metamodel.interactions.managed.ActionInteractionHead;
-import org.apache.isis.core.metamodel.layout.memberorderfacet.MemberOrderFacetComparator;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.specimpl.MixedInMember;
 
-import static org.apache.isis.commons.internal.base._NullSafe.stream;
-
 import lombok.NonNull;
 import lombok.val;
 
@@ -257,12 +255,11 @@ public interface ObjectAction extends ObjectMember {
         final String ownerId = getOnType().getLogicalTypeName().replace(".", "-");
         return prefix + ownerId + "-" + getId();
     }
-
-    // -- Util
+    
+    // -- UTIL
+    
     public static final class Util {
 
-        static final MemberOrderFacetComparator memberOrderFacetComparator = new MemberOrderFacetComparator(false);
-
         private Util() {
         }
         
@@ -382,15 +379,7 @@ public interface ObjectAction extends ObjectMember {
                 addActions(adapter, ActionType.PROTOTYPE, association, associatedActions);
             }
 
-            Collections.sort(associatedActions, new Comparator<ObjectAction>() {
-
-                @Override
-                public int compare(ObjectAction o1, ObjectAction o2) {
-                    final MemberOrderFacet m1 = o1.getFacet(MemberOrderFacet.class);
-                    final MemberOrderFacet m2 = o2.getFacet(MemberOrderFacet.class);
-                    return memberOrderFacetComparator.compare(m1, m2);
-                }
-            });
+            Collections.sort(associatedActions, Comparators.byMemberOrderSequence(false));
             return associatedActions;
         }
 
@@ -511,34 +500,6 @@ public interface ObjectAction extends ObjectMember {
             return (ObjectAction oa) -> oa.getType() == type;
         }
 
-        //        public static Predicate<ObjectAction> bulk() {
-        //            return new Predicate<ObjectAction>() {
-        //
-        //                @Override
-        //                public boolean test(ObjectAction oa) {
-        //
-        //                    final BulkFacet bulkFacet = oa.getFacet(BulkFacet.class);
-        //                    if(bulkFacet == null || bulkFacet.isNoop() || bulkFacet.value() == InvokeOn.OBJECT_ONLY) {
-        //                        return false;
-        //                    }
-        //                    if (oa.getParameterCount() != 0) {
-        //                        return false;
-        //                    }
-        //
-        //                    // currently don't support returning Blobs or Clobs
-        //                    // (because haven't figured out how to rerender the current page, but also to do a download)
-        //                    ObjectSpecification returnSpec = oa.getReturnType();
-        //                    if (returnSpec != null) {
-        //                        Class<?> returnType = returnSpec.getCorrespondingClass();
-        //                        if (returnType == Blob.class || returnType == Clob.class) {
-        //                            return false;
-        //                        }
-        //                    }
-        //                    return true;
-        //                }
-        //            };
-        //        }
-
         public static Predicate<ObjectAction> dynamicallyVisible(
                 final ManagedObject target,
                 final InteractionInitiatedBy interactionInitiatedBy,
@@ -550,13 +511,6 @@ public interface ObjectAction extends ObjectMember {
             };
         }
 
-        //        public static Predicate<ObjectAction> notBulkOnly() {
-        //            return (ObjectAction t) -> {
-        //                    BulkFacet facet = t.getFacet(BulkFacet.class);
-        //                    return facet == null || facet.value() != InvokeOn.COLLECTION_ONLY;
-        //            };
-        //        }
-
         public static Predicate<ObjectAction> excludeWizardActions(final ObjectSpecification objectSpecification) {
             return wizardActions(objectSpecification).negate();
         }
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 199ad9d..3eae914 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
@@ -21,17 +21,21 @@ package org.apache.isis.core.metamodel.spec.feature;
 
 import java.util.Comparator;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Stream;
 
 import javax.annotation.meta.When;
 
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.commons.internal.compare._Comparators;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder;
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
-import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
-import org.apache.isis.core.metamodel.layout.memberorderfacet.MemberOrderFacetComparator;
+import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
+import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 
 import lombok.val;
@@ -204,20 +208,49 @@ public interface ObjectMember extends ObjectFeature {
     }
 
 
-    // //////////////////////////////////////////////////////
-    // Comparators
-    // //////////////////////////////////////////////////////
+    // -- COMPARATORS
 
     public static class Comparators {
-        public static Comparator<ObjectMember> byMemberOrderSequence() {
-            return new Comparator<ObjectMember>() {
-                private final MemberOrderFacetComparator memberOrderFacetComparator =
-                        new MemberOrderFacetComparator(false);
+        
+        public static <T extends IdentifiedHolder> Comparator<T> byMemberOrderSequence(
+                final boolean ensureInSameGroup) {
+            
+            return new Comparator<T>() {
+
                 @Override
-                public int compare(final ObjectMember o1, final ObjectMember o2) {
-                    return memberOrderFacetComparator.compare(
-                            o1.getFacet(MemberOrderFacet.class),
-                            o2.getFacet(MemberOrderFacet.class));
+                public int compare(final T m1, final T m2) {
+                    
+                    val orderFacet1 = m1==null ? null : m1.getFacet(LayoutOrderFacet.class);
+                    val orderFacet2 = m2==null ? null : m2.getFacet(LayoutOrderFacet.class);
+                    
+                    if (orderFacet1 == null && orderFacet2 == null) {
+                        return 0;
+                    }
+                    if (orderFacet1 == null && orderFacet2 != null) {
+                        return +1; // annotated before non-annotated
+                    }
+                    if (orderFacet1 != null && orderFacet2 == null) {
+                        return -1; // annotated before non-annotated
+                    }
+
+                    if (ensureInSameGroup) { 
+                        
+                        val groupFacet1 = m1.getFacet(LayoutGroupFacet.class);
+                        val groupFacet2 = m2.getFacet(LayoutGroupFacet.class);
+                        val group1 = groupFacet1==null ? null : groupFacet1.getGroup();
+                        val group2 = groupFacet2==null ? null : groupFacet2.getGroup();
+                        
+                        if(!Objects.equals(group1, group2)) {
+                            throw _Exceptions.illegalArgument(
+                                    "Not in same group when comparing: %s, %s", 
+                                    group1, 
+                                    group2);
+                        }
+                    }
+
+                    return _Comparators.deweyOrderCompare(
+                            orderFacet1.getSequence(), 
+                            orderFacet2.getSequence());    
                 }
             };
         }
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 84d4a42..5b1a00e 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,7 +30,8 @@ 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.order.annotprop.MemberOrderFacetAnnotation;
+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;
 
 import junit.framework.TestCase;
@@ -93,74 +94,74 @@ public class DeweyOrderComparatorTest extends TestCase {
     }
 
     public void testDefaultGroupOneComponent() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, m2));
+        setupLayoutFacets("", "1", m1);
+        setupLayoutFacets("", "2", m2);
         assertEquals(-1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneComponentOtherWay() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, m2));
+        setupLayoutFacets("", "2", m1);
+        setupLayoutFacets("", "1", m2);
         assertEquals(+1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneComponentSame() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, m2));
+        setupLayoutFacets("", "1", m1);
+        setupLayoutFacets("", "1", m2);
         assertEquals(0, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsOutOfComponentsFirst() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.1", mockTranslationService, m2));
+        setupLayoutFacets("", "1", m1);
+        setupLayoutFacets("", "1.1", m2);
         assertEquals(-1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsOutOfComponentsFirstOtherWay() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, m2));
+        setupLayoutFacets("", "1.1", m1);
+        setupLayoutFacets("", "1", m2);
         assertEquals(+1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsTwoComponents() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2", mockTranslationService, m2));
+        setupLayoutFacets("", "1.1", m1);
+        setupLayoutFacets("", "1.2", m2);
         assertEquals(-1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsTwoComponentsOtherWay() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.1", mockTranslationService, m2));
+        setupLayoutFacets("", "1.2", m1);
+        setupLayoutFacets("", "1.1", m2);
         assertEquals(+1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsLotsOfComponents() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2.5.8.3.3", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2.5.8.3.4", mockTranslationService, m2));
+        setupLayoutFacets("", "1.2.5.8.3.3", m1);
+        setupLayoutFacets("", "1.2.5.8.3.4", m2);
         assertEquals(-1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsLotsOfComponentsOtherWay() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2.5.8.3.4", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2.5.8.3.3", mockTranslationService, m2));
+        setupLayoutFacets("", "1.2.5.8.3.4", m1);
+        setupLayoutFacets("", "1.2.5.8.3.3", m2);
         assertEquals(+1, comparator.compare(m1, m2));
     }
 
     public void testDefaultGroupOneSideRunsLotsOfComponentsSame() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2.5.8.3.3", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1.2.5.8.3.3", mockTranslationService, m2));
+        setupLayoutFacets("", "1.2.5.8.3.3", m1);
+        setupLayoutFacets("", "1.2.5.8.3.3", m2);
         assertEquals(0, comparator.compare(m1, m2));
     }
 
     public void testNamedGroupOneSideRunsLotsOfComponents() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "abc", "1.2.5.8.3.3", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "abc", "1.2.5.8.3.4", mockTranslationService, m2));
+        setupLayoutFacets("abc", "1.2.5.8.3.3", m1);
+        setupLayoutFacets("abc", "1.2.5.8.3.4", m2);
         assertEquals(-1, comparator.compare(m1, m2));
     }
 
     public void testEnsuresInSameGroup() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "abc", "1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "def", "2", mockTranslationService, m2));
+        setupLayoutFacets("abc", "1", m1);
+        setupLayoutFacets("def", "2", m2);
         try {
             assertEquals(-1, comparator.compare(m1, m2));
             fail("Exception should have been thrown");
@@ -170,15 +171,23 @@ public class DeweyOrderComparatorTest extends TestCase {
     }
 
     public void testEnsuresInSameGroupCanBeDisabled() {
-        m1.addFacet(new MemberOrderFacetAnnotation(ctx, "abc", "1", mockTranslationService, m1));
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "def", "2", mockTranslationService, m2));
+        setupLayoutFacets("abc", "1", m1);
+        setupLayoutFacets("def", "2", m2);
         assertEquals(-1, laxComparator.compare(m1, m2));
     }
 
     public void testNonAnnotatedAfterAnnotated() {
         // don't annotate m1
-        m2.addFacet(new MemberOrderFacetAnnotation(ctx, "def", "2", mockTranslationService, m2));
+        setupLayoutFacets("def", "2", m2);
         assertEquals(+1, comparator.compare(m1, m2));
     }
+    
+    // -- HELPER
+    
+    void setupLayoutFacets(String group, String sequence, FacetedMethod facetedMethod) {
+        facetedMethod.addFacet(new LayoutGroupFacetAbstract(group, 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 7d34cba..17e9947 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,7 +34,8 @@ 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.order.annotprop.MemberOrderFacetAnnotation;
+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;
 
 import junit.framework.TestCase;
@@ -119,8 +120,8 @@ public class DeweyOrderSetTest extends TestCase {
 
     public void testDefaultGroup() {
     	    	
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(lastNameAndFirstName);
         assertEquals("", orderSet.getGroupName());
@@ -129,8 +130,8 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testDefaultGroupSize() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(lastNameAndFirstName);
         assertEquals(2, orderSet.size());
@@ -139,8 +140,8 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testDefaultGroupTwoMembersSorted() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(lastNameAndFirstName);
         assertEquals(lastNameMember, orderSet.elementList().get(0));
@@ -148,8 +149,8 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testTwoMembersAtDefaultGroupOtherWay() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, firstNameMember));
+        setupLayoutFacets("", "2", lastNameMember);
+        setupLayoutFacets("", "1", firstNameMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(lastNameAndFirstName);
         assertEquals(firstNameMember, orderSet.elementList().get(0));
@@ -157,11 +158,11 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testWithChildGroupDefaultGroupName() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
-        houseNumberMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "1", mockTranslationService, houseNumberMember));
-        streetNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "2", mockTranslationService, streetNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "3", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
+        setupLayoutFacets("address", "1", houseNumberMember);
+        setupLayoutFacets("address", "2", streetNameMember);
+        setupLayoutFacets("address", "3", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(nameAndAddressMembers);
         assertEquals("", orderSet.getGroupName());
@@ -170,11 +171,11 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testWithChildGroupSize() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
-        houseNumberMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "1", mockTranslationService, houseNumberMember));
-        streetNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "2", mockTranslationService, streetNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "3", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
+        setupLayoutFacets("address", "1", houseNumberMember);
+        setupLayoutFacets("address", "2", streetNameMember);
+        setupLayoutFacets("address", "3", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(nameAndAddressMembers);
         assertEquals(1, orderSet.children().size());
@@ -182,11 +183,11 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testWithChildGroupChildsGroupName() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
-        houseNumberMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "1", mockTranslationService, houseNumberMember));
-        streetNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "2", mockTranslationService, streetNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "3", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
+        setupLayoutFacets("address", "1", houseNumberMember);
+        setupLayoutFacets("address", "2", streetNameMember);
+        setupLayoutFacets("address", "3", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(nameAndAddressMembers);
         final List<?> children = orderSet.children();
@@ -197,11 +198,11 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testWithChildGroupChildsGroupSize() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
-        houseNumberMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "1", mockTranslationService, houseNumberMember));
-        streetNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "2", mockTranslationService, streetNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "3", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
+        setupLayoutFacets("address", "1", houseNumberMember);
+        setupLayoutFacets("address", "2", streetNameMember);
+        setupLayoutFacets("address", "3", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(nameAndAddressMembers);
         final DeweyOrderSet childOrderSet = orderSet.children().get(0);
@@ -210,11 +211,11 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testWithChildGroupChildsGroupElementOrdering() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
-        houseNumberMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "6", mockTranslationService, houseNumberMember));
-        streetNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "5", mockTranslationService, streetNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "4", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
+        setupLayoutFacets("address", "6", houseNumberMember);
+        setupLayoutFacets("address", "5", streetNameMember);
+        setupLayoutFacets("address", "4", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(nameAndAddressMembers);
         final DeweyOrderSet childOrderSet = orderSet.children().get(0);
@@ -224,11 +225,11 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testWithChildGroupOrderedAtEnd() {
-        houseNumberMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "6", mockTranslationService, houseNumberMember));
-        streetNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "5", mockTranslationService, streetNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "4", mockTranslationService, postalTownMember));
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "3", mockTranslationService, lastNameMember));
-        firstNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, firstNameMember));
+        setupLayoutFacets("address", "6", houseNumberMember);
+        setupLayoutFacets("address", "5", streetNameMember);
+        setupLayoutFacets("address", "4", postalTownMember);
+        setupLayoutFacets("", "3", lastNameMember);
+        setupLayoutFacets("", "2", firstNameMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(nameAndAddressMembers);
         assertEquals(firstNameMember, orderSet.elementList().get(0));
@@ -248,16 +249,16 @@ public class DeweyOrderSetTest extends TestCase {
     }
 
     public void testDefaultGroupMixOfAnnotatedAndNotSize() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "address", "2", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("address", "2", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(lastNameFirstNameAndPostalTown);
         assertEquals(3, orderSet.elementList().size());
     }
 
     public void testDefaultGroupMixOfAnnotatedAndNotOrderedWithAnnotatedFirst() {
-        lastNameMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "1", mockTranslationService, lastNameMember));
-        postalTownMember.addFacet(new MemberOrderFacetAnnotation(ctx, "", "2", mockTranslationService, postalTownMember));
+        setupLayoutFacets("", "1", lastNameMember);
+        setupLayoutFacets("", "2", postalTownMember);
 
         final DeweyOrderSet orderSet = DeweyOrderSet.createOrderSet(lastNameFirstNameAndPostalTown);
 
@@ -265,5 +266,12 @@ public class DeweyOrderSetTest extends TestCase {
         assertEquals(postalTownMember, orderSet.elementList().get(1));
         assertEquals(firstNameMember, orderSet.elementList().get(2));
     }
+    
+    // -- HELPER
+    
+    void setupLayoutFacets(String group, String sequence, IdentifiedHolder facetedHolder) {
+        facetedHolder.addFacet(new LayoutGroupFacetAbstract(group, facetedHolder) {});
+        facetedHolder.addFacet(new LayoutOrderFacetAbstract(sequence, facetedHolder) {});
+    }
 
 }