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/05/06 19:15:20 UTC

[isis] 01/01: ISIS-2648: split the entity coll. model into 3 concrete types

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

ahuber pushed a commit to branch 2648-wkt.coll.model
in repository https://gitbox.apache.org/repos/asf/isis.git

commit da9a3fd46bfb24875658c7687a4578a1c822b4af
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu May 6 21:14:58 2021 +0200

    ISIS-2648: split the entity coll. model into 3 concrete types
---
 .../metamodel/facets/object/paged/PagedFacet.java  |  16 +
 .../interactions/managed/ManagedProperty.java      |  64 +--
 .../core/metamodel/spec/feature/ObjectAction.java  |  62 +--
 .../isis/core/runtime/memento/ObjectMemento.java   |  18 +-
 .../ui/components/ScalarPanelAbstractLegacy.java   |  26 +-
 .../viewer/wicket/model/links/LinkAndLabel.java    |  39 +-
 .../viewer/wicket/model/links/LinksProvider.java   |   5 +-
 .../wicket/model/links/ListOfLinksModel.java       |   9 +-
 .../wicket/model/models/EntityCollectionModel.java | 558 +++------------------
 .../models/EntityCollectionModelAbstract.java      | 105 ++++
 .../model/models/EntityCollectionModelDummy.java   |  93 ++++
 .../models/EntityCollectionModelParented.java      | 191 +++++++
 .../models/EntityCollectionModelStandalone.java    | 133 +++++
 .../viewer/wicket/model/models/ScalarModel.java    |  59 ++-
 ...odel.java => _EntityCollectionModelLegacy.java} |  64 ++-
 .../actionresponse/ActionResultResponseType.java   |  23 +-
 .../AdditionalLinksAsDropDownPanel.java            |   5 +-
 .../AdditionalLinksAsListInlinePanel.java          |   5 +-
 .../entityactions/AdditionalLinksPanel.java        |  33 +-
 .../actionmenu/entityactions/LinkAndLabelUtil.java |  43 +-
 .../collection/AssociatedWithActionsHelper.java    |  70 ---
 .../ui/components/collection/CollectionPanel.java  |  43 +-
 .../selector/CollectionSelectorHelper.java         |  94 ++--
 .../selector/CollectionSelectorPanel.java          |  11 +-
 .../CollectionContentsAsAjaxTablePanel.java        |   8 +-
 .../columns/ObjectAdapterPropertyColumn.java       |  13 +-
 .../columns/ObjectAdapterTitleColumn.java          |  42 +-
 .../CollectionContentsMultipleViewsPanel.java      |  30 +-
 .../entity/collection/EntityCollectionPanel.java   |  44 +-
 .../components/entity/fieldset/PropertyGroup.java  |  33 +-
 .../entity/header/EntityHeaderPanel.java           |  21 +-
 .../wicket/ui/components/layout/bs3/col/Col.java   |  37 +-
 .../ui/components/scalars/ScalarPanelAbstract.java |  88 ++--
 .../StandaloneCollectionPanel.java                 |  17 +-
 .../StandaloneCollectionPanelFactory.java          |  10 +-
 .../StandaloneCollectionPage.java                  |  18 +-
 36 files changed, 1063 insertions(+), 1067 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/paged/PagedFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/paged/PagedFacet.java
index 64baa31..4efc72e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/paged/PagedFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/paged/PagedFacet.java
@@ -19,6 +19,8 @@
 
 package org.apache.isis.core.metamodel.facets.object.paged;
 
+import javax.annotation.Nullable;
+
 import org.apache.isis.core.metamodel.facetapi.Facet;
 
 /**
@@ -28,4 +30,18 @@ public interface PagedFacet extends Facet {
 
     int value();
 
+    /**
+     * Returns the page-size as held by given {@code pagedFacet} if present, otherwise
+     * falls back to {@code defaultPageSize}.
+     * @param pagedFacet - null-able
+     * @param defaultPageSize
+     */
+    static int pageSizeOrDefault(
+            final @Nullable PagedFacet pagedFacet,
+            final int defaultPageSize) {
+        return pagedFacet != null
+                ? pagedFacet.value()
+                : defaultPageSize;
+    }
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java
index 7e1aa9d..37e704f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/ManagedProperty.java
@@ -38,31 +38,31 @@ import lombok.NonNull;
 import lombok.val;
 
 public final class ManagedProperty extends ManagedMember {
-    
+
     // -- FACTORIES
-    
+
     public static final ManagedProperty of(
-            final @NonNull ManagedObject owner, 
+            final @NonNull ManagedObject owner,
             final @NonNull OneToOneAssociation property,
             final @NonNull Where where) {
         return new ManagedProperty(owner, property, where);
     }
-    
+
     public static final Optional<ManagedProperty> lookupProperty(
             @NonNull final ManagedObject owner,
-            @NonNull final String memberId, 
+            @NonNull final String memberId,
             @NonNull final Where where) {
-        
+
         return ManagedMember.<OneToOneAssociation>lookup(owner, MemberType.PROPERTY, memberId)
         .map(objectAction -> of(owner, objectAction, where));
     }
-    
+
     // -- IMPLEMENTATION
-    
+
     @Getter private final OneToOneAssociation property;
-    
+
     private ManagedProperty(
-            final @NonNull ManagedObject owner, 
+            final @NonNull ManagedObject owner,
             final @NonNull OneToOneAssociation property,
             final @NonNull Where where) {
         super(owner, where);
@@ -79,39 +79,39 @@ public final class ManagedProperty extends ManagedMember {
     public MemberType getMemberType() {
         return MemberType.PROPERTY;
     }
-    
+
     public Can<ObjectAction> getAssociatedActions() {
-        return Can.ofCollection(ObjectAction.Util.findForAssociation(getOwner(), getProperty()));
+        return Can.ofStream(ObjectAction.Util.findForAssociation(getOwner(), getProperty()));
     }
-    
+
     // -- INTERACTION
-    
+
     public Optional<InteractionVeto> checkValidity(ManagedObject proposedNewValue) {
         try {
-            
-            val validityConsent = 
+
+            val validityConsent =
                     property.isAssociationValid(getOwner(), proposedNewValue, InteractionInitiatedBy.USER);
-            
+
             return validityConsent.isVetoed()
-                    ? Optional.of(InteractionVeto.invalid(validityConsent)) 
+                    ? Optional.of(InteractionVeto.invalid(validityConsent))
                     : Optional.empty();
-            
+
         } catch (final Exception ex) {
-            
+
             return Optional.of(InteractionVeto
                     .invalid(
                             new Veto(ex.getLocalizedMessage())));
-            
+
         }
     }
-    
-    
+
+
     /**
      * @param proposedNewValue
      * @return non-empty if the interaction is not valid for given {@code proposedNewValue}
      */
     public Optional<InteractionVeto> modifyProperty(@Nullable ManagedObject proposedNewValue) {
-            
+
         val interactionVeto = checkValidity(proposedNewValue);
         if(interactionVeto.isPresent()) {
             return interactionVeto;
@@ -131,14 +131,14 @@ public final class ManagedProperty extends ManagedMember {
     private ManagedObject reassessPropertyValue() {
         val property = getProperty();
         val owner = getOwner();
-        
-        return property.isVisible(owner, InteractionInitiatedBy.FRAMEWORK, getWhere()).isAllowed() 
+
+        return property.isVisible(owner, InteractionInitiatedBy.FRAMEWORK, getWhere()).isAllowed()
                 && property.isVisible(owner, InteractionInitiatedBy.USER, getWhere()).isAllowed()
             ? property.get(owner, InteractionInitiatedBy.USER)
             : ManagedObject.empty(property.getSpecification());
     }
-    
-    
+
+
     // -- NEGOTIATION
 
     public PropertyNegotiationModel startNegotiation() {
@@ -146,9 +146,9 @@ public final class ManagedProperty extends ManagedMember {
     }
 
     // -- BINDING
-    
+
     @NonNull private final LazyObservable<ManagedObject> observablePropValue;
-    
+
     public Observable<ManagedObject> getValue() {
         return observablePropValue;
     }
@@ -156,7 +156,7 @@ public final class ManagedProperty extends ManagedMember {
     public ManagedObject getPropertyValue() {
         return getValue().getValue();
     }
-    
-    
+
+
 }
 
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 5a42745..79fc27d 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,6 @@
 
 package org.apache.isis.core.metamodel.spec.feature;
 
-import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -37,12 +35,10 @@ import org.apache.isis.applib.value.Clob;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.collections.CanVector;
 import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.consent.InteractionResultSet;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.facets.actions.action.associateWith.AssociatedWithFacet;
 import org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacet;
 import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
@@ -268,13 +264,6 @@ public interface ObjectAction extends ObjectMember {
 
     public static final class Util {
 
-        private Util() {
-        }
-
-        private static boolean isPrototyping(ManagedObject adapter) {
-            return MetaModelContext.from(adapter).getSystemEnvironment().isPrototyping();
-        }
-
         public static String nameFor(final ObjectAction objAction) {
             final String actionName = objAction.getName();
             if (actionName != null) {
@@ -343,60 +332,28 @@ public interface ObjectAction extends ObjectMember {
             return cssClassFacet != null ? cssClassFacet.cssClass(objectAdapter) : null;
         }
 
-        public static List<ObjectAction> findTopLevel(
+        public static Stream<ObjectAction> streamTopLevelActions(
                 final ManagedObject adapter) {
 
-            val topLevelActions = _Lists.<ObjectAction>newArrayList();
-
-            addTopLevelActions(adapter, ActionType.USER, topLevelActions);
-            if(isPrototyping(adapter)) {
-                addTopLevelActions(adapter, ActionType.PROTOTYPE, topLevelActions);
-            }
-            return topLevelActions;
-        }
-
-        static void addTopLevelActions(
-                final ManagedObject adapter,
-                final ActionType actionType,
-                final List<ObjectAction> topLevelActions) {
-
             val spec = adapter.getSpecification();
 
-            spec.streamDeclaredActions(actionType, MixedIn.INCLUDED)
+            return spec.streamRuntimeActions(MixedIn.INCLUDED)
             .filter(ObjectAction.Predicates.memberOrderNotAssociationOf(spec))
             .filter(ObjectAction.Predicates.dynamicallyVisible(adapter,
                     InteractionInitiatedBy.USER, Where.ANYWHERE))
-            .filter(ObjectAction.Predicates.excludeWizardActions(spec))
-            .forEach(topLevelActions::add);
-
+            .filter(ObjectAction.Predicates.excludeWizardActions(spec));
         }
 
-        public static List<ObjectAction> findForAssociation(
+        public static Stream<ObjectAction> findForAssociation(
                 final ManagedObject adapter,
                 final ObjectAssociation association) {
 
-            val associatedActions = _Lists.<ObjectAction>newArrayList();
-
-            addActions(adapter, ActionType.USER, association, associatedActions);
-            if(isPrototyping(adapter)) {
-                addActions(adapter, ActionType.PROTOTYPE, association, associatedActions);
-            }
-
-            Collections.sort(associatedActions, Comparators.byMemberOrderSequence(false));
-            return associatedActions;
-        }
-
-        static void addActions(
-                final ManagedObject adapter,
-                final ActionType type,
-                final ObjectAssociation association, final List<ObjectAction> associatedActions) {
-
-            val objectSpecification = adapter.getSpecification();
+            val spec = adapter.getSpecification();
 
-            objectSpecification.streamDeclaredActions(type, MixedIn.INCLUDED)
+            return spec.streamRuntimeActions(MixedIn.INCLUDED)
             .filter(ObjectAction.Predicates.actionIsAssociatedWith(association))
-            .filter(ObjectAction.Predicates.excludeWizardActions(objectSpecification))
-            .forEach(associatedActions::add);
+            .filter(ObjectAction.Predicates.excludeWizardActions(spec))
+            .sorted(Comparators.byMemberOrderSequence(false));
         }
 
         public static PromptStyle promptStyleFor(final ObjectAction objectAction) {
@@ -434,9 +391,6 @@ public interface ObjectAction extends ObjectMember {
 
     public static final class Predicates {
 
-        private Predicates() {
-        }
-
         public static Predicate<ObjectAction> associatedWith(final ObjectAssociation objectAssociation) {
             return new AssociatedWith(objectAssociation);
         }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/ObjectMemento.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/ObjectMemento.java
index 56b8071..df5a7bd 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/ObjectMemento.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/memento/ObjectMemento.java
@@ -36,27 +36,27 @@ import org.apache.isis.commons.internal.collections._Lists;
 public interface ObjectMemento extends HasLogicalType, Serializable {
 
     String asString();
-    
+
     /**
-     * Returns a bookmark only if 
+     * Returns a bookmark only if
      * {@link org.apache.isis.viewer.wicket.viewer.services.mementos.ObjectMementoWkt.RecreateStrategy#LOOKUP} and
      * {@link #getCardinality() sort} is {@link Cardinality#SCALAR scalar}.
-     * Returns {@code null} otherwise. 
+     * Returns {@code null} otherwise.
      */
     Bookmark asBookmarkIfSupported();
 
     /**
-     * Returns a bookmark only if 
+     * Returns a bookmark only if
      * {@link org.apache.isis.viewer.wicket.viewer.services.mementos.ObjectMementoWkt.RecreateStrategy#LOOKUP} and
      * {@link #getCardinality() sort} is {@link Cardinality#SCALAR scalar}.
-     * Returns {@code null} otherwise. 
+     * Returns {@code null} otherwise.
      */
     Bookmark asHintingBookmarkIfSupported();
-    
+
     // -- FACTORIES
 
     static ObjectMemento wrapMementoList(
-            Collection<ObjectMemento> container, 
+            Collection<ObjectMemento> container,
             LogicalType logicalType) {
 
         // ArrayList is serializable
@@ -76,6 +76,6 @@ public interface ObjectMemento extends HasLogicalType, Serializable {
         }
         return Optional.ofNullable(((ObjectMementoCollection)memento).unwrapList());
     }
-    
-    
+
+
 }
diff --git a/extensions/vw/pdfjs/ui/src/main/java/org/apache/isis/extensions/viewer/wicket/pdfjs/ui/components/ScalarPanelAbstractLegacy.java b/extensions/vw/pdfjs/ui/src/main/java/org/apache/isis/extensions/viewer/wicket/pdfjs/ui/components/ScalarPanelAbstractLegacy.java
index d9be8ce..85bf2db 100644
--- a/extensions/vw/pdfjs/ui/src/main/java/org/apache/isis/extensions/viewer/wicket/pdfjs/ui/components/ScalarPanelAbstractLegacy.java
+++ b/extensions/vw/pdfjs/ui/src/main/java/org/apache/isis/extensions/viewer/wicket/pdfjs/ui/components/ScalarPanelAbstractLegacy.java
@@ -32,6 +32,7 @@ import org.apache.wicket.model.Model;
 
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.labelat.LabelAtFacet;
@@ -60,8 +61,8 @@ import lombok.val;
  *     It is however still used by some wicket addons (specifically, pdfjs).
  * </p>
  */
-abstract class ScalarPanelAbstractLegacy 
-extends PanelAbstract<ManagedObject, ScalarModel> 
+abstract class ScalarPanelAbstractLegacy
+extends PanelAbstract<ManagedObject, ScalarModel>
 implements ScalarModelProvider {
 
     private static final long serialVersionUID = 1L;
@@ -186,7 +187,7 @@ implements ScalarModelProvider {
         } else {
             if (scalarModel.isViewMode()) {
                 onBeforeRenderWhenViewMode();
-            } else {        
+            } else {
                 onBeforeRenderWhenEnabled();
             }
         }
@@ -310,16 +311,23 @@ implements ScalarModelProvider {
      * @param markupContainer The form group element
      * @param entityActionLinks
      */
-    protected void addPositioningCssTo(final MarkupContainer markupContainer, final List<LinkAndLabel> entityActionLinks) {
+    protected void addPositioningCssTo(
+            final MarkupContainer markupContainer,
+            final Can<LinkAndLabel> entityActionLinks) {
         CssClassAppender.appendCssClassTo(markupContainer, determinePropParamLayoutCss(getModel()));
         CssClassAppender.appendCssClassTo(markupContainer, determineActionLayoutPositioningCss(entityActionLinks));
     }
 
-    protected void addEntityActionLinksBelowAndRight(final MarkupContainer labelIfRegular, final List<LinkAndLabel> entityActions) {
-        final List<LinkAndLabel> entityActionsBelow = LinkAndLabel.positioned(entityActions, ActionLayout.Position.BELOW);
+    protected void addEntityActionLinksBelowAndRight(
+            final MarkupContainer labelIfRegular,
+            final Can<LinkAndLabel> entityActions) {
+
+        final Can<LinkAndLabel> entityActionsBelow = entityActions
+                .filter(LinkAndLabel.positioned(ActionLayout.Position.BELOW));
         AdditionalLinksPanel.addAdditionalLinks(labelIfRegular, ID_ASSOCIATED_ACTION_LINKS_BELOW, entityActionsBelow, AdditionalLinksPanel.Style.INLINE_LIST);
 
-        final List<LinkAndLabel> entityActionsRight = LinkAndLabel.positioned(entityActions, ActionLayout.Position.RIGHT);
+        final Can<LinkAndLabel> entityActionsRight = entityActions
+                .filter(LinkAndLabel.positioned(ActionLayout.Position.RIGHT));
         AdditionalLinksPanel.addAdditionalLinks(labelIfRegular, ID_ASSOCIATED_ACTION_LINKS_RIGHT, entityActionsRight, AdditionalLinksPanel.Style.DROPDOWN);
     }
 
@@ -342,12 +350,12 @@ implements ScalarModelProvider {
         return "label-left";
     }
 
-    private static String determineActionLayoutPositioningCss(List<LinkAndLabel> entityActionLinks) {
+    private static String determineActionLayoutPositioningCss(Can<LinkAndLabel> entityActionLinks) {
         boolean actionsPositionedOnRight = hasActionsPositionedOn(entityActionLinks, ActionLayout.Position.RIGHT);
         return actionsPositionedOnRight ? "actions-right" : null;
     }
 
-    private static boolean hasActionsPositionedOn(final List<LinkAndLabel> entityActionLinks, final ActionLayout.Position position) {
+    private static boolean hasActionsPositionedOn(final Can<LinkAndLabel> entityActionLinks, final ActionLayout.Position position) {
         for (LinkAndLabel entityActionLink : entityActionLinks) {
             if(entityActionLink.getActionUiMetaModel().getPosition() == position) {
                 return true;
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
index 3453823..81b2df3 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinkAndLabel.java
@@ -21,14 +21,17 @@ package org.apache.isis.viewer.wicket.model.links;
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.annotation.Nullable;
 
 import org.apache.isis.applib.annotation.ActionLayout.Position;
 import org.apache.isis.applib.id.LogicalType;
-import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.common.model.action.ActionUiMetaModel;
@@ -41,7 +44,7 @@ import lombok.val;
 public final class LinkAndLabel extends LinkAndLabelAbstract {
 
     private static final long serialVersionUID = 1L;
-    
+
     public static LinkAndLabel of(
             final ActionLinkUiComponentFactoryWkt uiComponentFactory,
             final String named,
@@ -49,7 +52,7 @@ public final class LinkAndLabel extends LinkAndLabelAbstract {
             final ObjectAction objectAction) {
         return new LinkAndLabel(uiComponentFactory, named, actionHolderModel, objectAction);
     }
-    
+
     private LinkAndLabel(
             final ActionLinkUiComponentFactoryWkt uiComponentFactory,
             final String named,
@@ -58,14 +61,22 @@ public final class LinkAndLabel extends LinkAndLabelAbstract {
         super(uiComponentFactory, named, actionHolderModel, objectAction);
     }
 
-    public static List<LinkAndLabel> positioned(List<LinkAndLabel> list, Position pos) {
-        return _Lists.filter(list, ActionUiMetaModel.positioned(pos, LinkAndLabel::getActionUiMetaModel));
+    public static Can<LinkAndLabel> positioned(Position pos, Stream<LinkAndLabel> stream) {
+        return stream.filter(LinkAndLabel.positioned(pos))
+        .collect(Can.toCan());
+        //.collect(Collectors.toCollection(ArrayList::new));
+    }
+
+    public static Predicate<LinkAndLabel> positioned(Position pos) {
+        return ActionUiMetaModel.positioned(pos, LinkAndLabel::getActionUiMetaModel);
     }
 
-    public static List<LinkAndLabel> recoverFromIncompleteDeserialization(List<SerializationProxy> list) {
-        return _Casts.uncheckedCast(_Lists.map(list, SerializationProxy::readResolve));
+    public static List<LinkAndLabel> recoverFromIncompleteDeserialization(List<SerializationProxy> proxies) {
+        return proxies.stream()
+                .map(SerializationProxy::readResolve)
+                .collect(Collectors.toCollection(ArrayList::new));
     }
-    
+
     // -- SERIALIZATION PROXY
 
     private Object writeReplace() {
@@ -83,18 +94,18 @@ public final class LinkAndLabel extends LinkAndLabelAbstract {
         @NonNull  private final EntityModel actionHolder;
         @NonNull  private final LogicalType actionHolderLogicalType;
         @NonNull  private final String objectActionId;
-        
+
         private SerializationProxy(LinkAndLabel target) {
-            this.uiComponentFactory = (ActionLinkUiComponentFactoryWkt)target.uiComponentFactory;
+            this.uiComponentFactory = target.uiComponentFactory;
             this.named = target.getNamed();
             this.actionHolder = (EntityModel) target.getActionHolder();
             // make sure we do this without side-effects
             this.actionHolderLogicalType = actionHolder.getLogicalElementType()
-                    .orElseThrow(_Exceptions::unexpectedCodeReach); 
+                    .orElseThrow(_Exceptions::unexpectedCodeReach);
             this.objectActionId = target.getObjectAction().getId();
         }
 
-        private Object readResolve() {
+        private LinkAndLabel readResolve() {
             val commonContext = CommonContextUtils.getCommonContext();
             val objectMember = commonContext.getSpecificationLoader()
             .specForLogicalType(actionHolderLogicalType)
@@ -104,5 +115,5 @@ public final class LinkAndLabel extends LinkAndLabelAbstract {
             return new LinkAndLabel(uiComponentFactory, named, actionHolder, (ObjectAction) objectMember);
         }
     }
-    
+
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinksProvider.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinksProvider.java
index c65a220..bd1ed0e 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinksProvider.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/LinksProvider.java
@@ -18,10 +18,9 @@
  */
 package org.apache.isis.viewer.wicket.model.links;
 
-import java.util.List;
-
 import org.apache.wicket.markup.html.link.Link;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 
 /**
@@ -29,5 +28,5 @@ import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
  * additional list of {@link Link}s to be rendered.
  */
 public interface LinksProvider {
-    List<LinkAndLabel> getLinks();
+    Can<LinkAndLabel> getLinks();
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/ListOfLinksModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/ListOfLinksModel.java
index 5337ecb..ec45dec 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/ListOfLinksModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/links/ListOfLinksModel.java
@@ -22,8 +22,8 @@ import java.util.List;
 
 import org.apache.wicket.model.LoadableDetachableModel;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.commons.internal.collections._Lists;
 
 import lombok.val;
 
@@ -33,9 +33,8 @@ public class ListOfLinksModel extends LoadableDetachableModel<List<LinkAndLabel>
 
     private List<LinkAndLabel> links;
 
-    public ListOfLinksModel(List<LinkAndLabel> links) {
-        // copy, in case supplied list is a non-serializable guava list using lazy evaluation;
-        this.links = _Lists.newArrayList(links);
+    public ListOfLinksModel(Can<LinkAndLabel> links) {
+        this.links = links.toList();
     }
 
     @Override
@@ -60,7 +59,7 @@ public class ListOfLinksModel extends LoadableDetachableModel<List<LinkAndLabel>
         if(links.size()>0) {
             if(! (links.get(0) instanceof LinkAndLabel)) {
                 return links = LinkAndLabel.recoverFromIncompleteDeserialization(_Casts.uncheckedCast(links));
-            } 
+            }
         }
         return links;
     }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
index 66ea614..95492b4 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
@@ -16,141 +16,38 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
 package org.apache.isis.viewer.wicket.model.models;
 
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.annotation.Nullable;
 
-import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
 
 import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.layout.component.CollectionLayoutData;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.Can;
-import org.apache.isis.commons.internal.base._NullSafe;
-import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.commons.internal.factory._InstanceUtil;
-import org.apache.isis.core.metamodel.commons.ClassExtensions;
-import org.apache.isis.core.metamodel.commons.ClassUtil;
-import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
-import org.apache.isis.core.metamodel.facets.collections.sortedby.SortedByFacet;
-import org.apache.isis.core.metamodel.facets.object.paged.PagedFacet;
-import org.apache.isis.core.metamodel.facets.object.plural.PluralFacet;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-import org.apache.isis.core.runtime.context.IsisAppCommonContext;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.runtime.context.IsisAppCommonContext.HasCommonContext;
 import org.apache.isis.core.runtime.memento.ObjectMemento;
-import org.apache.isis.core.runtime.memento.ObjectMementoService;
-import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
-import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.links.LinksProvider;
-import org.apache.isis.viewer.wicket.model.mementos.CollectionMemento;
-
-import static org.apache.isis.commons.internal.base._NullSafe.stream;
 
 import lombok.Getter;
-import lombok.val;
-
-/**
- * Model representing a collection of entities, either {@link Variant#STANDALONE
- * standalone} (eg result of invoking an action) or {@link Variant#PARENTED
- * parented} (contents of the collection of an entity).
- *
- * <p>
- * So that the model is {@link Serializable}, the {@link ManagedObject}s within
- * the collection are stored as {@link ObjectMemento}s.
- */
-public class EntityCollectionModel
-extends ModelAbstract<List<ManagedObject>>
-implements
-    LinksProvider,
-    UiHintContainer {
-
-    private static final long serialVersionUID = 1L;
-
-    private static final int PAGE_SIZE_DEFAULT_FOR_PARENTED = 12;
-    private static final int PAGE_SIZE_DEFAULT_FOR_STANDALONE = 25;
-
-    // -- FACTORIES
-
-    public static EntityCollectionModel createParented(EntityModel entityModel) {
-
-        val oneToManyAssociation = collectionFor(entityModel);
-        val typeOf = forName(oneToManyAssociation.getSpecification());
-        final int pageSize = pageSize(oneToManyAssociation.getFacet(PagedFacet.class), PAGE_SIZE_DEFAULT_FOR_PARENTED);
-        val sortedByFacet = oneToManyAssociation.getFacet(SortedByFacet.class);
-
-        val entityCollectionModel = new EntityCollectionModel(
-                entityModel.getCommonContext(),
-                Variant.PARENTED,
-                oneToManyAssociation.getIdentifier(),
-                entityModel,
-                typeOf,
-                pageSize);
-        entityCollectionModel.collectionMemento = new CollectionMemento(oneToManyAssociation);
-        entityCollectionModel.sortedBy = (sortedByFacet != null)
-                ? sortedByFacet.value()
-                : null;
-        return entityCollectionModel;
-    }
-
-    public static EntityCollectionModel createStandalone(
-            ManagedObject collectionAsAdapter,
-            ModelAbstract<?> model) {
-
-        // dynamically determine the spec of the elements
-        // (ie so a List<Object> can be rendered according to the runtime type of its elements,
-        // rather than the compile-time type
-        val commonSuperClassFinder = new ClassExtensions.CommonSuperclassFinder();
-
-        val mementoService = model.getMementoService();
-
-        final List<ObjectMemento> mementoList = streamElementsOf(collectionAsAdapter) // pojos
-                .filter(_NullSafe::isPresent)
-                .peek(commonSuperClassFinder::collect)
-                .map(mementoService::mementoForPojo)
-                .collect(Collectors.toList());
-
-        val specificationLoader = model.getSpecificationLoader();
-
-        val elementSpec = commonSuperClassFinder.getCommonSuperclass()
-                .flatMap(specificationLoader::specForType)
-                .orElseGet(()->collectionAsAdapter.getSpecification().getElementSpecification().orElse(null));
-
-        final int pageSize = (elementSpec != null)
-                ? pageSize(elementSpec.getFacet(PagedFacet.class), PAGE_SIZE_DEFAULT_FOR_STANDALONE)
-                : PAGE_SIZE_DEFAULT_FOR_STANDALONE;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
 
-        val elementType = (elementSpec != null)
-                ? elementSpec.getCorrespondingClass()
-                : Object.class;
-
-        val entityCollectionModel = new EntityCollectionModel(
-                model.getCommonContext(),
-                Variant.STANDALONE,
-                /*Identifier*/ null,
-                /*EntityModel*/null,
-                elementType,
-                pageSize);
-        entityCollectionModel.mementoList = mementoList;
-        return entityCollectionModel;
-
-    }
+public interface EntityCollectionModel
+extends
+    IModel<List<ManagedObject>>,
+    HasCommonContext,
+    LinksProvider {
 
     // -- VARIANTS
 
+    @RequiredArgsConstructor
     public enum Variant {
         /**
          * A simple list of object mementos, eg the result of invoking an action
@@ -158,426 +55,95 @@ implements
          * <p>
          * This deals with both persisted and transient objects.
          */
-        STANDALONE {
-            @Override
-            List<ManagedObject> load(EntityCollectionModel colModel) {
-
-                return loadElementsOneByOne(colModel).collect(Collectors.toList());
-            }
-
-
-            private Stream<ManagedObject> loadElementsOneByOne(final EntityCollectionModel model) {
-
-                return stream(model.mementoList)
-                        .map(model.getCommonContext()::reconstructObject)
-                        .filter(_NullSafe::isPresent);
-            }
-
-            @Override
-            void setObject(EntityCollectionModel colModel, List<ManagedObject> adapterList) {
-
-                //XXX lombok issue, cannot use val here
-                final ObjectMementoService mementoService = colModel.getMementoService();
-
-                colModel.mementoList = _NullSafe.stream(adapterList)
-                        .map(mementoService::mementoForObject)
-                        .filter(_NullSafe::isPresent)
-                        .collect(Collectors.toList());
-            }
-
-            @Override
-            public String getId(EntityCollectionModel colModel) {
-                return null;
-            }
-
-            @Override
-            public String getName(EntityCollectionModel colModel) {
-                PluralFacet facet = colModel.getTypeOfSpecification().getFacet(PluralFacet.class);
-                return facet.value();
-            }
-
-            @Override
-            public int getCount(EntityCollectionModel colModel) {
-                return colModel.mementoList.size();
-            }
-
-            @Override
-            public EntityModel.RenderingHint renderingHint() {
-                return EntityModel.RenderingHint.STANDALONE_PROPERTY_COLUMN;
-            }
-
-        },
+        STANDALONE(EntityModel.RenderingHint.STANDALONE_PROPERTY_COLUMN, 25),
 
         /**
          * A collection of an entity (eg Order/OrderDetail).
          */
-        PARENTED {
-            @Override
-            List<ManagedObject> load(EntityCollectionModel colModel) {
-
-                final ManagedObject adapter = colModel.getCommonContext()
-                        .reconstructObject(colModel.getParentObjectAdapterMemento());
-
-                final OneToManyAssociation collection = colModel.collectionMemento
-                        .getCollection(colModel.getSpecificationLoader());
-
-                final ManagedObject collectionAsAdapter = collection.get(adapter, InteractionInitiatedBy.USER);
-
-                final List<Object> objectList = asIterable(collectionAsAdapter);
+        PARENTED(EntityModel.RenderingHint.PARENTED_PROPERTY_COLUMN, 12),
+        ;
 
-                final Class<? extends Comparator<?>> sortedBy = colModel.sortedBy;
-                if(sortedBy != null) {
-                    @SuppressWarnings("unchecked")
-                    final Comparator<Object> comparator = (Comparator<Object>) _InstanceUtil.createInstance(sortedBy);
-                    colModel.getCommonContext().injectServicesInto(comparator);
-                    Collections.sort(objectList, comparator);
-                }
-
-                final List<ManagedObject> adapterList =
-                        _Lists.map(objectList, x-> colModel.getObjectManager().adapt(x));
-
-                return adapterList;
-            }
-
-            @SuppressWarnings("unchecked")
-            private List<Object> asIterable(ManagedObject collectionAsAdapter) {
-                if(collectionAsAdapter==null) {
-                    return Collections.emptyList();
-                }
-                final Iterable<Object> objects = (Iterable<Object>) collectionAsAdapter.getPojo();
-                return stream(objects).collect(Collectors.toList());
-            }
-
-            @Override
-            void setObject(EntityCollectionModel colModel, List<ManagedObject> list) {
-                // no-op
-                throw new UnsupportedOperationException();
-            }
-
-            @Override public String getId(EntityCollectionModel colModel) {
-                return colModel.getCollectionMemento().getCollectionId();
-            }
-
-            @Override
-            public String getName(EntityCollectionModel colModel) {
-                return colModel.getCollectionMemento().getCollectionName();
-            }
-
-            @Override
-            public int getCount(EntityCollectionModel colModel) {
-                return load(colModel).size();
-            }
-
-            @Override
-            public EntityModel.RenderingHint renderingHint() {
-                return EntityModel.RenderingHint.PARENTED_PROPERTY_COLUMN;
-            }
-
-        };
-
-        abstract List<ManagedObject> load(EntityCollectionModel entityCollectionModel);
-
-        abstract void setObject(EntityCollectionModel entityCollectionModel, List<ManagedObject> list);
-
-        public abstract String getId(EntityCollectionModel entityCollectionModel);
-        public abstract String getName(EntityCollectionModel entityCollectionModel);
-
-        public abstract int getCount(EntityCollectionModel entityCollectionModel);
-
-        public abstract EntityModel.RenderingHint renderingHint();
-    }
-
-
-    /**
-     * The {@link ActionModel model} of the {@link ObjectAction action}
-     * that generated this {@link EntityCollectionModel}.
-     *
-     * <p>
-     * Populated only for {@link Variant#STANDALONE standalone} collections.
-     *
-     * @see #setActionHint(ActionModel)
-     */
-    public ActionModel getActionModelHint() {
-        return actionModelHint;
-    }
-    /**
-     * Called only for {@link Variant#STANDALONE standalone} collections.
-     *
-     * @see #getActionModelHint()
-     */
-    public void setActionHint(ActionModel actionModelHint) {
-        this.actionModelHint = actionModelHint;
-    }
-
-    @Getter private final Variant variant;
-
-    private final Class<?> typeOf;
-    private transient Optional<ObjectSpecification> typeOfSpec;
-
-    /**
-     * Populated only if {@link Variant#STANDALONE}.
-     */
-    private List<ObjectMemento> mementoList;
-
-    /**
-     * Populated only if {@link Variant#STANDALONE}.
-     */
-    private Map<String, ObjectMemento> toggledMementos;
-
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    private final EntityModel entityModel;
-
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     * <p>
-     * This collection's <i>feature</i> {@link Identifier}.
-     * @see Identifier
-     */
-    @Getter private final Identifier identifier;
+        @Getter private final EntityModel.RenderingHint columnRenderingHint;
+        @Getter private final int pageSizeDefault;
 
 
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    private CollectionMemento collectionMemento;
-
-    @Getter private final int pageSize;
-
-    /**
-     * Additional links to render (if any)
-     */
-    private List<LinkAndLabel> linkAndLabels = _Lists.newArrayList();
-
-    /**
-     * Optionally populated only if {@link Variant#PARENTED}.
-     */
-    private Class<? extends Comparator<?>> sortedBy;
-
-    /**
-     * Optionally populated, only if {@link Variant#STANDALONE} (ie called from an action).
-     */
-    private ActionModel actionModelHint;
-
-    private EntityCollectionModel(
-            IsisAppCommonContext commonContext,
-            Variant type,
-            Identifier identifier,
-            EntityModel entityModel,
-            Class<?> typeOf,
-            int pageSize) {
-
-        super(commonContext);
-        this.variant = type;
-        this.identifier = identifier;
-        this.entityModel = entityModel;
-        this.typeOf = typeOf;
-        this.pageSize = pageSize;
-        this.toggledMementos = _Maps.<String, ObjectMemento>newLinkedHashMap();
-    }
-
-
-    private static OneToManyAssociation collectionFor(EntityModel entityModel) {
-
-        val collectionLayoutData = entityModel.getCollectionLayoutData();
-        if(collectionLayoutData == null) {
-            throw new IllegalArgumentException("EntityModel must have a CollectionLayoutMetadata");
+        public boolean isStandalone() {
+            return this == STANDALONE;
         }
 
-        val collectionId = collectionLayoutData.getId();
-        val spec = entityModel.getTypeOfSpecification();
-
-        return (OneToManyAssociation) spec.getAssociationElseFail(collectionId);
-    }
-
-    private static Class<?> forName(final ObjectSpecification objectSpec) {
-        final String fullName = objectSpec.getFullIdentifier();
-        return ClassUtil.forNameElseFail(fullName);
-    }
-
-
-    private static int pageSize(final PagedFacet pagedFacet, final int defaultPageSize) {
-        return pagedFacet != null ? pagedFacet.value(): defaultPageSize;
-    }
-
-    public boolean isParented() {
-        return variant == Variant.PARENTED;
-    }
-
-    public boolean isStandalone() {
-        return variant == Variant.STANDALONE;
-    }
-
-    /**
-     * The name of the collection (if has an entity, ie, if
-     * {@link #isParented() is parented}.)
-     *
-     * <p>
-     * If {@link #isStandalone()}, returns the {@link PluralFacet} of the {@link #getTypeOfSpecification() specification}
-     * (eg 'Customers').
-     */
-    public String getName() {
-        return variant.getName(this);
-    }
-
-    public int getCount() {
-        return this.variant.getCount(this);
-    }
-
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    public CollectionLayoutData getLayoutData() {
-        return entityModel != null
-                ? entityModel.getCollectionLayoutData()
-                : null;
-    }
-
-    @Override
-    protected List<ManagedObject> load() {
-        return variant.load(this);
-    }
-
-    public @Nullable ObjectSpecification getTypeOfSpecification() {
-        if (typeOfSpec == null) {
-            typeOfSpec = getSpecificationLoader().specForType(typeOf);
+        public boolean isParented() {
+            return this == PARENTED;
         }
-        return typeOfSpec.orElse(null);
-    }
-
-    @Override
-    public void setObject(List<ManagedObject> list) {
-        super.setObject(list);
-        variant.setObject(this, list);
     }
 
-    /**
-     * Not API, but to refresh the model list.
-     */
-    public void setObjectList(ManagedObject resultAdapter) {
-        this.mementoList = streamElementsOf(resultAdapter)
-                .map(super.getMementoService()::mementoForPojo)
-                .collect(Collectors.toList());
-    }
+    // -- FACTORIES
 
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    public EntityModel getEntityModel() {
-        return entityModel;
+    static EntityCollectionModelParented createParented(final @NonNull EntityModel entityModel) {
+        return EntityCollectionModelParented.forParentObjectModel(entityModel);
     }
 
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    public Optional<Bookmark> getParentObjectBookmark() {
-        return entityModel != null
-                ? entityModel.getManagedObject().getBookmark()
-                : Optional.empty();
+    static EntityCollectionModelStandalone createStandalone(
+            ManagedObject collectionAsAdapter,
+            ActionModel actionModel) {
+        return EntityCollectionModelStandalone.forActionModel(collectionAsAdapter, actionModel);
     }
 
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    public Optional<ObjectSpecification> getParentObjectSpecification() {
-        return getParentObjectBookmark()
-                .flatMap(bookmark->getCommonContext().getSpecificationLoader().specForBookmark(bookmark));
-    }
+    // -- IDENTITY
 
     /**
-     * Populated only if {@link Variant#PARENTED}.
+     * This collection's <i>feature</i> {@link Identifier}.
+     * @see Identifier
      */
-    @Deprecated // don't expose this implementation detail, use getParentObjectBookmark() instead
-    public ObjectMemento getParentObjectAdapterMemento() {
-        return entityModel != null? entityModel.memento(): null;
-    }
+    Identifier getIdentifier();
 
-    /**
-     * Populated only if {@link Variant#PARENTED}.
-     */
-    @Deprecated // don't expose this implementation detail
-    public CollectionMemento getCollectionMemento() {
-        return collectionMemento;
-    }
+    // -- VARIANT SUPPORT
 
-    private static Stream<?> streamElementsOf(ManagedObject resultAdapter) {
-        return _NullSafe.streamAutodetect(resultAdapter.getPojo());
-    }
+    Variant getVariant();
 
-    public boolean toggleSelectionOn(ManagedObject selectedAdapter) {
-        //XXX lombok issue, cannot use val here
-        final ObjectMemento selectedAsMemento = super.getMementoService().mementoForObject(selectedAdapter);
-        final String selectedKey = selectedAsMemento.asString();
+    default boolean isStandalone() { return getVariant().isStandalone(); }
+    default boolean isParented() { return getVariant().isParented(); }
 
-        final boolean isSelected = _Maps.toggleElement(toggledMementos, selectedKey, selectedAsMemento);
-        return isSelected;
+    // -- METAMODEL SUPPORT
 
-    }
+    ObjectSpecification getTypeOfSpecification();
+    ObjectMember getMetaModel();
 
-    public Can<ObjectMemento> getToggleMementosList() {
-        return Can.ofCollection(this.toggledMementos.values());
+    default Can<ObjectAction> getAssociatedActions() {
+        return Can.empty();
     }
 
-    public void clearToggleMementosList() {
-        this.toggledMementos.clear();
-    }
+    // -- INTERACTION SUPPORT
 
-    public void addLinkAndLabels(List<LinkAndLabel> linkAndLabels) {
-        this.linkAndLabels.clear();
-        this.linkAndLabels.addAll(linkAndLabels);
-    }
+    Optional<ManagedCollection> getManagedCollection();
 
-    @Override
-    public List<LinkAndLabel> getLinks() {
-        return Collections.unmodifiableList(linkAndLabels);
+    default Optional<ManagedObject> getParentObject() {
+        return getManagedCollection()
+                .map(ManagedCollection::getOwner);
     }
 
-    public EntityCollectionModel asDummy() {
-        final EntityCollectionModel dummy = new EntityCollectionModel(
-                super.getCommonContext(), Variant.STANDALONE, null, null, typeOf, pageSize);
-        dummy.mementoList = Collections.<ObjectMemento>emptyList();
-        return dummy;
+    default Optional<ObjectSpecification> getParentObjectSpecification() {
+        return getParentObject()
+                .map(ManagedObject::getSpecification);
     }
 
-    // //////////////////////////////////////
+    // -- TOGGLE SUPPORT
 
-    public static final String HINT_KEY_SELECTED_ITEM = "selectedItem";
+    Can<ObjectMemento> getToggleMementosList();
+    void clearToggleMementosList();
+    boolean toggleSelectionOn(ManagedObject selectedAdapter);
 
-    /**
-     * Just delegates to the {@link #getEntityModel() entity model} (if parented, else no-op).
-     */
-    @Override
-    public String getHint(final Component component, final String attributeName) {
-        if(getEntityModel() == null) {
-            return null;
-        }
-        return getEntityModel().getHint(component, attributeName);
-    }
+    // -- BASIC PROPERTIES
 
-    /**
-     * Just delegates to the {@link #getEntityModel() entity model} (if parented, else no-op).
-     */
-    @Override
-    public void setHint(final Component component, final String attributeName, final String attributeValue) {
-        if(getEntityModel() == null) {
-            return;
-        }
-        getEntityModel().setHint(component, attributeName, attributeValue);
-    }
-
-    /**
-     * Just delegates to the {@link #getEntityModel() entity model} (if parented, else no-op).
-     */
-    @Override
-    public void clearHint(final Component component, final String attributeName) {
-        if(getEntityModel() == null) {
-            return;
-        }
-        getEntityModel().clearHint(component, attributeName);
-    }
+    int getCount();
+    String getName();
+    int getPageSize();
 
+    // -- REFACTORING TODO ...
 
+    @Deprecated
+    ObjectMemento getParentObjectAdapterMemento();
 
+    @Deprecated
+    Bookmark asHintingBookmarkIfSupported();
 
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java
new file mode 100644
index 0000000..909f3f6
--- /dev/null
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java
@@ -0,0 +1,105 @@
+package org.apache.isis.viewer.wicket.model.models;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.paged.PagedFacet;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.runtime.context.IsisAppCommonContext;
+import org.apache.isis.core.runtime.memento.ObjectMemento;
+import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NonNull;
+
+public abstract class EntityCollectionModelAbstract
+extends ModelAbstract<List<ManagedObject>>
+implements EntityCollectionModel {
+
+    private static final long serialVersionUID = 1L;
+
+    @Getter(onMethod_ = {@Override}) private final @NonNull Identifier identifier;
+    @Getter private final int pageSize;
+
+    protected EntityCollectionModelAbstract(
+            final @NonNull IsisAppCommonContext commonContext,
+            final @NonNull Identifier identifier,
+            final @NonNull ObjectSpecification typeOfSpecification,
+            final @NonNull Can<FacetHolder> facetHolders) {
+        super(commonContext);
+        this.identifier = identifier;
+        this.typeOfSpecification = Optional.of(typeOfSpecification); // as an optimization: memoize transient
+        this.elementType = typeOfSpecification.getCorrespondingClass();
+        this.pageSize = facetHolders.stream()
+        .map(facetHolder->facetHolder.getFacet(PagedFacet.class))
+        .filter(_NullSafe::isPresent)
+        .findFirst()
+        .map(PagedFacet::value)
+        .orElse(getVariant().getPageSizeDefault());
+
+        this.toggledMementos = _Maps.<String, ObjectMemento>newLinkedHashMap();
+    }
+
+    // -- TYPE OF (ELEMENT TYPE)
+
+    @Getter(value = AccessLevel.PROTECTED) private final @NonNull Class<?> elementType;
+
+    private transient Optional<ObjectSpecification> typeOfSpecification;
+
+    @Override
+    public ObjectSpecification getTypeOfSpecification() {
+        if(typeOfSpecification==null) {
+            typeOfSpecification = getSpecificationLoader().specForType(elementType);
+        }
+        return typeOfSpecification.orElse(null);
+    }
+
+    // -- LINKS PROVIDER
+
+    /**
+     * Additional links to render (if any)
+     */
+    private List<LinkAndLabel> linkAndLabels = _Lists.newArrayList();
+
+    public void setLinkAndLabels(final @NonNull Iterable<LinkAndLabel> linkAndLabels) {
+        this.linkAndLabels.clear();
+        linkAndLabels.forEach(this.linkAndLabels::add);
+    }
+
+    @Override
+    public final Can<LinkAndLabel> getLinks() {
+        return Can.ofCollection(linkAndLabels);
+    }
+
+    // -- TOGGLE SUPPORT
+
+    @Getter private LinkedHashMap<String, ObjectMemento> toggledMementos;
+
+    @Override
+    public Can<ObjectMemento> getToggleMementosList() {
+        return Can.ofCollection(this.toggledMementos.values());
+    }
+
+    @Override
+    public void clearToggleMementosList() {
+        this.toggledMementos.clear();
+    }
+
+    @Override
+    public boolean toggleSelectionOn(final ManagedObject selectedAdapter) {
+        final ObjectMemento selectedAsMemento = super.getMementoService().mementoForObject(selectedAdapter);
+        final String selectedKey = selectedAsMemento.asString();
+        final boolean isSelected = _Maps.toggleElement(toggledMementos, selectedKey, selectedAsMemento);
+        return isSelected;
+    }
+
+}
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelDummy.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelDummy.java
new file mode 100644
index 0000000..305dd89
--- /dev/null
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelDummy.java
@@ -0,0 +1,93 @@
+/*
+ *  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.viewer.wicket.model.models;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.runtime.memento.ObjectMemento;
+
+import lombok.NonNull;
+
+public class EntityCollectionModelDummy
+extends EntityCollectionModelAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public static EntityCollectionModelDummy forCollectionModel(
+            final @NonNull EntityCollectionModel collectionModel) {
+        return new EntityCollectionModelDummy(collectionModel);
+    }
+
+    protected EntityCollectionModelDummy(
+            final @NonNull EntityCollectionModel collectionModel) {
+        super(collectionModel.getCommonContext(),
+                collectionModel.getIdentifier(),
+                collectionModel.getTypeOfSpecification(),
+                Can.empty());
+    }
+
+    @Override
+    public Variant getVariant() {
+        return Variant.STANDALONE;
+    }
+
+    @Override
+    public Optional<ManagedCollection> getManagedCollection() {
+        return Optional.empty();
+    }
+
+    @Override
+    public int getCount() {
+        return 0;
+    }
+
+    @Override
+    public String getName() {
+        return "dummy";
+    }
+
+    @Override
+    protected List<ManagedObject> load() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public OneToManyAssociation getMetaModel() {
+        throw _Exceptions.unsupportedOperation();
+    }
+
+    @Override
+    public ObjectMemento getParentObjectAdapterMemento() {
+        throw _Exceptions.unsupportedOperation();
+    }
+
+    @Override
+    public Bookmark asHintingBookmarkIfSupported() {
+        return null;
+    }
+
+}
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelParented.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelParented.java
new file mode 100644
index 0000000..eae2837
--- /dev/null
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelParented.java
@@ -0,0 +1,191 @@
+/*
+ *  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.viewer.wicket.model.models;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.wicket.Component;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.layout.component.CollectionLayoutData;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.assertions._Assert;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.MixedIn;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.runtime.memento.ObjectMemento;
+import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.val;
+
+public class EntityCollectionModelParented
+extends EntityCollectionModelAbstract
+implements
+    UiHintContainer {
+
+    private static final long serialVersionUID = 1L;
+
+    private final @NonNull _EntityCollectionModelLegacy legacy;
+
+    // TODO parent object model, should be private
+    @Getter private final @NonNull EntityModel entityModel;
+
+    // -- FACTORIES
+
+    public static EntityCollectionModelParented forParentObjectModel(
+            final @NonNull EntityModel entityModel) {
+
+        val collectionMetaModel =
+                Optional.ofNullable(entityModel.getCollectionLayoutData())
+                .map(collectionLayoutData->
+                    entityModel
+                        .getTypeOfSpecification()
+                        .getCollectionElseFail(collectionLayoutData.getId()))
+                .orElseThrow(()->_Exceptions
+                        .illegalArgument("EntityModel must have CollectionLayoutMetadata"));
+
+        val typeOfFacet = collectionMetaModel.lookupFacet(TypeOfFacet.class)
+                .orElseThrow(()->_Exceptions
+                        .illegalArgument("CollectionMetaModel must have a TypeOfFacet"));
+
+        val typeOfSpecification = typeOfFacet.valueSpec();
+
+        final Can<FacetHolder> facetHolders = Can.of(collectionMetaModel, typeOfSpecification);
+
+//      val sortedByFacet = collectionMetaModel.getFacet(SortedByFacet.class);
+//
+//        entityCollectionModel.collectionMemento = new CollectionMemento(collectionMetaModel);
+//        entityCollectionModel.sortedBy = (sortedByFacet != null)
+//                ? sortedByFacet.value()
+//                : null;
+
+        return new EntityCollectionModelParented(
+                collectionMetaModel, typeOfSpecification, entityModel, facetHolders);
+    }
+
+    // -- CONSTRUCTOR
+
+    protected EntityCollectionModelParented(
+            final @NonNull OneToManyAssociation collectionMetaModel,
+            final @NonNull ObjectSpecification typeOfSpecification,
+            final @NonNull EntityModel parentObjectModel,
+            final @NonNull Can<FacetHolder> facetHolders) {
+        super(
+                parentObjectModel.getCommonContext(),
+                collectionMetaModel.getIdentifier(),
+                typeOfSpecification,
+                facetHolders);
+        this.entityModel = parentObjectModel;
+        this.legacy = _EntityCollectionModelLegacy.createParented(entityModel);
+    }
+
+    // -- VARIANT SUPPORT
+
+    @Override
+    public Variant getVariant() {
+        return Variant.PARENTED;
+    }
+
+    // -- METAMODEL
+
+    @Override
+    public Can<ObjectAction> getAssociatedActions() {
+        val managedCollection = getManagedCollection().orElse(null);
+        if(managedCollection==null) {
+            return Can.empty();
+        }
+        final OneToManyAssociation collection = managedCollection.getCollection();
+        return managedCollection.getOwner().getSpecification()
+                .streamRuntimeActions(MixedIn.INCLUDED)
+                .filter(ObjectAction.Predicates.associatedWithAndWithCollectionParameterFor(collection))
+                .collect(Can.toCan());
+    }
+
+    // -- UI HINT CONTAINER
+
+    public static final String HINT_KEY_SELECTED_ITEM = "selectedItem";
+
+    @Override
+    public String getHint(final Component component, final String attributeName) {
+        return getEntityModel().getHint(component, attributeName);
+    }
+
+    @Override
+    public void setHint(final Component component, final String attributeName, final String attributeValue) {
+        getEntityModel().setHint(component, attributeName, attributeValue);
+    }
+
+    @Override
+    public void clearHint(final Component component, final String attributeName) {
+        getEntityModel().clearHint(component, attributeName);
+    }
+
+    @Override
+    protected List<ManagedObject> load() {
+        return legacy.load();
+    }
+
+    @Override
+    public Optional<ManagedCollection> getManagedCollection() {
+        return Optional.of(ManagedCollection
+                .of(entityModel.getManagedObject(), getMetaModel(), Where.NOT_SPECIFIED));
+    }
+
+    @Override
+    public int getCount() {
+        return legacy.getCount();
+    }
+
+    @Override
+    public String getName() {
+        _Assert.assertEquals(getMetaModel().getName(), legacy.getName());
+        return getMetaModel().getName();
+    }
+
+    @Override
+    public OneToManyAssociation getMetaModel() {
+        return legacy.getCollectionMemento().getCollection(getSpecificationLoader());
+    }
+
+    public CollectionLayoutData getLayoutData() {
+        return entityModel.getCollectionLayoutData();
+    }
+
+    @Override
+    public ObjectMemento getParentObjectAdapterMemento() {
+        return entityModel.memento();
+    }
+
+    @Override
+    public Bookmark asHintingBookmarkIfSupported() {
+        return entityModel.asHintingBookmarkIfSupported();
+    }
+
+
+}
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelStandalone.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelStandalone.java
new file mode 100644
index 0000000..2ee8cc7
--- /dev/null
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelStandalone.java
@@ -0,0 +1,133 @@
+/*
+ *  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.viewer.wicket.model.models;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.plural.PluralFacet;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.runtime.memento.ObjectMemento;
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.val;
+
+public class EntityCollectionModelStandalone
+extends EntityCollectionModelAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    private final @NonNull _EntityCollectionModelLegacy legacy;
+
+    // parent object model
+    @Getter
+    private final @NonNull ActionModel actionModel;
+
+    // -- FACTORIES
+
+    public static EntityCollectionModelStandalone forActionModel(
+            final @NonNull ManagedObject collectionAsAdapter,
+            final @NonNull ActionModel actionModel) {
+
+        val typeOfSpecification = actionModel.getMetaModel().getReturnType().getElementSpecification()
+                .orElseThrow(()->_Exceptions
+                        .illegalArgument("ActionModel must have an ElementSpecification for its return type"));
+
+        final Can<FacetHolder> facetHolders = Can.of(actionModel.getMetaModel(), typeOfSpecification);
+
+        // take a copy of the actionModel,
+        // because the original can get mutated (specifically: its arguments cleared)
+        return new EntityCollectionModelStandalone(
+                typeOfSpecification, collectionAsAdapter, actionModel.copy(), facetHolders);
+    }
+
+    // -- CONSTRUCTOR
+
+    protected EntityCollectionModelStandalone(
+            final @NonNull ObjectSpecification typeOfSpecification,
+            final @NonNull ManagedObject collectionAsAdapter,
+            final @NonNull ActionModel actionModel,
+            final @NonNull Can<FacetHolder> facetHolders) {
+        super(
+                actionModel.getCommonContext(),
+                actionModel.getMetaModel().getIdentifier(),
+                typeOfSpecification,
+                facetHolders);
+        this.actionModel = actionModel;
+        this.legacy = _EntityCollectionModelLegacy.createStandalone(collectionAsAdapter, actionModel);
+    }
+
+    // -- VARIANT SUPPORT
+
+    @Override
+    public Variant getVariant() {
+        return Variant.STANDALONE;
+    }
+
+    // -- INTERACTION SUPPORT
+
+    @Override
+    public Optional<ManagedCollection> getManagedCollection() {
+        return Optional.empty();
+    }
+
+    // --
+
+    @Override
+    protected List<ManagedObject> load() {
+        return legacy.load();
+    }
+
+    @Override
+    public int getCount() {
+        return legacy.getCount();
+    }
+
+    @Override
+    public String getName() {
+        return getTypeOfSpecification().lookupFacet(PluralFacet.class)
+                .map(PluralFacet::value)
+                .orElse(getMetaModel().getName());
+    }
+
+    @Override
+    public ObjectMember getMetaModel() {
+        return actionModel.getMetaModel();
+    }
+
+    @Override
+    public ObjectMemento getParentObjectAdapterMemento() {
+        return legacy.getParentObjectAdapterMemento();
+    }
+
+    @Override
+    public Bookmark asHintingBookmarkIfSupported() {
+        return null;
+    }
+
+
+}
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java
index 9a881e6..6c21b99 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ScalarModel.java
@@ -18,7 +18,6 @@
  */
 package org.apache.isis.viewer.wicket.model.models;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 
@@ -59,8 +58,8 @@ import lombok.Setter;
  *
  */
 //@Log4j2
-public abstract class ScalarModel 
-extends ManagedObjectModel 
+public abstract class ScalarModel
+extends ManagedObjectModel
 implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext {
 
     private static final long serialVersionUID = 1L;
@@ -72,18 +71,18 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
     @NonNull private final Kind kind;
     public boolean isProperty() { return kind == Kind.PROPERTY; }
     public boolean isParameter() { return kind == Kind.PARAMETER; }
-    
+
 
     private final EntityModel parentEntityModel;
-    
-    @Getter(onMethod = @__(@Override)) 
-    @Setter(onMethod = @__(@Override)) 
+
+    @Getter(onMethod = @__(@Override))
+    @Setter(onMethod = @__(@Override))
     private Mode mode;
-    
-    @Getter(onMethod = @__(@Override)) 
-    @Setter(onMethod = @__(@Override)) 
+
+    @Getter(onMethod = @__(@Override))
+    @Setter(onMethod = @__(@Override))
     private RenderingHint renderingHint;
-    
+
 
     /**
      * Creates a model representing an action parameter of an action of a parent
@@ -91,9 +90,9 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
      * value (if any) of that action parameter.
      */
     protected ScalarModel(EntityModel parentEntityModel, ActionParameterMemento apm) {
-        
+
         super(parentEntityModel.getCommonContext());
-        
+
         this.kind = Kind.PARAMETER;
         this.parentEntityModel = parentEntityModel;
         this.pendingModel = new PendingModel(this);
@@ -107,11 +106,11 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
      * property.
      */
     protected ScalarModel(
-            EntityModel parentEntityModel, 
+            EntityModel parentEntityModel,
             PropertyMemento pm,
-            ObjectUiModel.Mode mode, 
+            ObjectUiModel.Mode mode,
             ObjectUiModel.RenderingHint renderingHint) {
-        
+
         super(parentEntityModel.getCommonContext());
         this.kind = Kind.PROPERTY;
         this.parentEntityModel = parentEntityModel;
@@ -134,12 +133,12 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
     @Override
     public ManagedObject getOwner() {
 //        if(owner==null) {
-//            owner = getParentUiModel().load(); 
+//            owner = getParentUiModel().load();
 //        }
 //        return owner;
         return getParentUiModel().load();
     }
-    
+
     /**
      * Whether the scalar represents a {@link Kind#PROPERTY property} or a
      * {@link Kind#PARAMETER}.
@@ -151,7 +150,7 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
     /**
      * Overrides superclass' implementation, because a {@link ScalarModel} can
      * know the {@link ObjectSpecification of} the {@link ManagedObject adapter}
-     * without there necessarily having any adapter 
+     * without there necessarily having any adapter
      * {@link #setObject(ManagedObject) set}.
      */
     @Override
@@ -164,7 +163,7 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
         return Optional.ofNullable(getScalarTypeSpec())
                 .map(ObjectSpecification::getLogicalType);
     }
-    
+
 
     public boolean isScalarTypeAnyOf(final Class<?>... requiredClass) {
         final String fullName = getTypeOfSpecification().getFullIdentifier();
@@ -221,8 +220,8 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
     private List<LinkAndLabel> linkAndLabels = _Lists.newArrayList();
 
     @Override
-    public List<LinkAndLabel> getLinks() {
-        return Collections.unmodifiableList(linkAndLabels);
+    public Can<LinkAndLabel> getLinks() {
+        return Can.ofCollection(linkAndLabels);
     }
 
     @Override
@@ -322,7 +321,7 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
      * @return <tt>true</tt> if the widget for this model must be editable.
      */
     public boolean mustBeEditable() {
-        return getMode() == Mode.EDIT 
+        return getMode() == Mode.EDIT
                 || getKind() == Kind.PARAMETER
                 || hasAssociatedActionWithInlineAsIfEdit();
     }
@@ -334,7 +333,7 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
      */
     @Override
     public boolean isInlinePrompt() {
-        return (getPromptStyle().isInline() && canEnterEditMode()) 
+        return (getPromptStyle().isInline() && canEnterEditMode())
                 || hasAssociatedActionWithInlineAsIfEdit();
     }
 
@@ -355,15 +354,15 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
         }
         return associatedActions;
     }
-    
+
     protected abstract Can<ObjectAction> calcAssociatedActions();
-    
+
     public final boolean hasAssociatedActionWithInlineAsIfEdit() {
         return getAssociatedActions().hasAssociatedActionWithInlineAsIfEdit();
     }
-    
+
     // -- PENDING STUFF
-    
+
     @Getter(value = AccessLevel.PACKAGE)
     private final PendingModel pendingModel;
 
@@ -375,7 +374,7 @@ implements HasRenderingHints, ScalarUiModel, LinksProvider, FormExecutorContext
         pendingModel.clearPending();
     }
 
-    
+
     // --
-    
+
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/_EntityCollectionModelLegacy.java
similarity index 88%
copy from viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
copy to viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/_EntityCollectionModelLegacy.java
index 66ea614..0bf2c71 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/_EntityCollectionModelLegacy.java
@@ -72,7 +72,8 @@ import lombok.val;
  * So that the model is {@link Serializable}, the {@link ManagedObject}s within
  * the collection are stored as {@link ObjectMemento}s.
  */
-public class EntityCollectionModel
+@Deprecated
+class _EntityCollectionModelLegacy
 extends ModelAbstract<List<ManagedObject>>
 implements
     LinksProvider,
@@ -85,14 +86,15 @@ implements
 
     // -- FACTORIES
 
-    public static EntityCollectionModel createParented(EntityModel entityModel) {
+    public static _EntityCollectionModelLegacy createParented(EntityModel entityModel) {
 
         val oneToManyAssociation = collectionFor(entityModel);
         val typeOf = forName(oneToManyAssociation.getSpecification());
-        final int pageSize = pageSize(oneToManyAssociation.getFacet(PagedFacet.class), PAGE_SIZE_DEFAULT_FOR_PARENTED);
+        final int pageSize = PagedFacet.pageSizeOrDefault(
+                oneToManyAssociation.getFacet(PagedFacet.class), PAGE_SIZE_DEFAULT_FOR_PARENTED);
         val sortedByFacet = oneToManyAssociation.getFacet(SortedByFacet.class);
 
-        val entityCollectionModel = new EntityCollectionModel(
+        val entityCollectionModel = new _EntityCollectionModelLegacy(
                 entityModel.getCommonContext(),
                 Variant.PARENTED,
                 oneToManyAssociation.getIdentifier(),
@@ -106,7 +108,7 @@ implements
         return entityCollectionModel;
     }
 
-    public static EntityCollectionModel createStandalone(
+    public static _EntityCollectionModelLegacy createStandalone(
             ManagedObject collectionAsAdapter,
             ModelAbstract<?> model) {
 
@@ -130,14 +132,14 @@ implements
                 .orElseGet(()->collectionAsAdapter.getSpecification().getElementSpecification().orElse(null));
 
         final int pageSize = (elementSpec != null)
-                ? pageSize(elementSpec.getFacet(PagedFacet.class), PAGE_SIZE_DEFAULT_FOR_STANDALONE)
+                ? PagedFacet.pageSizeOrDefault(elementSpec.getFacet(PagedFacet.class), PAGE_SIZE_DEFAULT_FOR_STANDALONE)
                 : PAGE_SIZE_DEFAULT_FOR_STANDALONE;
 
         val elementType = (elementSpec != null)
                 ? elementSpec.getCorrespondingClass()
                 : Object.class;
 
-        val entityCollectionModel = new EntityCollectionModel(
+        val entityCollectionModel = new _EntityCollectionModelLegacy(
                 model.getCommonContext(),
                 Variant.STANDALONE,
                 /*Identifier*/ null,
@@ -160,13 +162,13 @@ implements
          */
         STANDALONE {
             @Override
-            List<ManagedObject> load(EntityCollectionModel colModel) {
+            List<ManagedObject> load(_EntityCollectionModelLegacy colModel) {
 
                 return loadElementsOneByOne(colModel).collect(Collectors.toList());
             }
 
 
-            private Stream<ManagedObject> loadElementsOneByOne(final EntityCollectionModel model) {
+            private Stream<ManagedObject> loadElementsOneByOne(final _EntityCollectionModelLegacy model) {
 
                 return stream(model.mementoList)
                         .map(model.getCommonContext()::reconstructObject)
@@ -174,7 +176,7 @@ implements
             }
 
             @Override
-            void setObject(EntityCollectionModel colModel, List<ManagedObject> adapterList) {
+            void setObject(_EntityCollectionModelLegacy colModel, List<ManagedObject> adapterList) {
 
                 //XXX lombok issue, cannot use val here
                 final ObjectMementoService mementoService = colModel.getMementoService();
@@ -186,18 +188,18 @@ implements
             }
 
             @Override
-            public String getId(EntityCollectionModel colModel) {
+            public String getId(_EntityCollectionModelLegacy colModel) {
                 return null;
             }
 
             @Override
-            public String getName(EntityCollectionModel colModel) {
+            protected String getName(_EntityCollectionModelLegacy colModel) {
                 PluralFacet facet = colModel.getTypeOfSpecification().getFacet(PluralFacet.class);
                 return facet.value();
             }
 
             @Override
-            public int getCount(EntityCollectionModel colModel) {
+            public int getCount(_EntityCollectionModelLegacy colModel) {
                 return colModel.mementoList.size();
             }
 
@@ -213,7 +215,7 @@ implements
          */
         PARENTED {
             @Override
-            List<ManagedObject> load(EntityCollectionModel colModel) {
+            List<ManagedObject> load(_EntityCollectionModelLegacy colModel) {
 
                 final ManagedObject adapter = colModel.getCommonContext()
                         .reconstructObject(colModel.getParentObjectAdapterMemento());
@@ -249,22 +251,22 @@ implements
             }
 
             @Override
-            void setObject(EntityCollectionModel colModel, List<ManagedObject> list) {
+            void setObject(_EntityCollectionModelLegacy colModel, List<ManagedObject> list) {
                 // no-op
                 throw new UnsupportedOperationException();
             }
 
-            @Override public String getId(EntityCollectionModel colModel) {
+            @Override public String getId(_EntityCollectionModelLegacy colModel) {
                 return colModel.getCollectionMemento().getCollectionId();
             }
 
             @Override
-            public String getName(EntityCollectionModel colModel) {
+            protected String getName(_EntityCollectionModelLegacy colModel) {
                 return colModel.getCollectionMemento().getCollectionName();
             }
 
             @Override
-            public int getCount(EntityCollectionModel colModel) {
+            public int getCount(_EntityCollectionModelLegacy colModel) {
                 return load(colModel).size();
             }
 
@@ -275,14 +277,14 @@ implements
 
         };
 
-        abstract List<ManagedObject> load(EntityCollectionModel entityCollectionModel);
+        abstract List<ManagedObject> load(_EntityCollectionModelLegacy entityCollectionModel);
 
-        abstract void setObject(EntityCollectionModel entityCollectionModel, List<ManagedObject> list);
+        abstract void setObject(_EntityCollectionModelLegacy entityCollectionModel, List<ManagedObject> list);
 
-        public abstract String getId(EntityCollectionModel entityCollectionModel);
-        public abstract String getName(EntityCollectionModel entityCollectionModel);
+        public abstract String getId(_EntityCollectionModelLegacy entityCollectionModel);
+        protected abstract String getName(_EntityCollectionModelLegacy entityCollectionModel);
 
-        public abstract int getCount(EntityCollectionModel entityCollectionModel);
+        public abstract int getCount(_EntityCollectionModelLegacy entityCollectionModel);
 
         public abstract EntityModel.RenderingHint renderingHint();
     }
@@ -290,7 +292,7 @@ implements
 
     /**
      * The {@link ActionModel model} of the {@link ObjectAction action}
-     * that generated this {@link EntityCollectionModel}.
+     * that generated this {@link _EntityCollectionModelLegacy}.
      *
      * <p>
      * Populated only for {@link Variant#STANDALONE standalone} collections.
@@ -360,7 +362,7 @@ implements
      */
     private ActionModel actionModelHint;
 
-    private EntityCollectionModel(
+    private _EntityCollectionModelLegacy(
             IsisAppCommonContext commonContext,
             Variant type,
             Identifier identifier,
@@ -397,10 +399,6 @@ implements
     }
 
 
-    private static int pageSize(final PagedFacet pagedFacet, final int defaultPageSize) {
-        return pagedFacet != null ? pagedFacet.value(): defaultPageSize;
-    }
-
     public boolean isParented() {
         return variant == Variant.PARENTED;
     }
@@ -529,12 +527,12 @@ implements
     }
 
     @Override
-    public List<LinkAndLabel> getLinks() {
-        return Collections.unmodifiableList(linkAndLabels);
+    public Can<LinkAndLabel> getLinks() {
+        return Can.ofCollection(linkAndLabels);
     }
 
-    public EntityCollectionModel asDummy() {
-        final EntityCollectionModel dummy = new EntityCollectionModel(
+    public _EntityCollectionModelLegacy asDummy() {
+        final _EntityCollectionModelLegacy dummy = new _EntityCollectionModelLegacy(
                 super.getCommonContext(), Variant.STANDALONE, null, null, typeOf, pageSize);
         dummy.mementoList = Collections.<ObjectMemento>emptyList();
         return dummy;
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
index a441988..edb1835 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
@@ -49,15 +49,15 @@ import lombok.val;
 public enum ActionResultResponseType {
     OBJECT {
         @Override
-        public ActionResultResponse interpretResult(ActionModel model, AjaxRequestTarget target, ManagedObject resultAdapter) {
-            val commonContext = model.getCommonContext();
+        public ActionResultResponse interpretResult(ActionModel actionModel, AjaxRequestTarget target, ManagedObject resultAdapter) {
+            val commonContext = actionModel.getCommonContext();
             val actualAdapter = determineActualAdapter(commonContext, resultAdapter); // intercepts collections
-            return toEntityPage(model, actualAdapter);
+            return toEntityPage(actionModel, actualAdapter);
         }
 
         @Override
-        public ActionResultResponse interpretResult(ActionModel model, ManagedObject targetAdapter) {
-            final ActionResultResponse actionResultResponse = toEntityPage(model, targetAdapter);
+        public ActionResultResponse interpretResult(ActionModel actionModel, ManagedObject targetAdapter) {
+            final ActionResultResponse actionResultResponse = toEntityPage(actionModel, targetAdapter);
             return actionResultResponse;
         }
     },
@@ -65,9 +65,6 @@ public enum ActionResultResponseType {
         @Override
         public ActionResultResponse interpretResult(ActionModel actionModel, AjaxRequestTarget target, ManagedObject resultAdapter) {
             val collectionModel = EntityCollectionModel.createStandalone(resultAdapter, actionModel);
-            // take a copy of the actionModel, because the original can get mutated (specifically: its arguments cleared)
-            val actionModelCopy = actionModel.copy();
-            collectionModel.setActionHint(actionModelCopy);
             return ActionResultResponse.toPage(new StandaloneCollectionPage(collectionModel));
         }
     },
@@ -151,7 +148,7 @@ public enum ActionResultResponseType {
     }
 
     private static ManagedObject determineActualAdapter(
-            IsisAppCommonContext commonContext, 
+            IsisAppCommonContext commonContext,
             ManagedObject resultAdapter) {
 
         if (resultAdapter.getSpecification().isNotCollection()) {
@@ -169,7 +166,7 @@ public enum ActionResultResponseType {
     }
 
     private static ActionResultResponse toEntityPage(
-            final ActionModel model, 
+            final ActionModel model,
             final ManagedObject actualAdapter) {
 
         // this will not preserve the URL (because pageParameters are not copied over)
@@ -186,7 +183,7 @@ public enum ActionResultResponseType {
             final ActionModel model,
             final AjaxRequestTarget targetIfAny,
             final ManagedObject resultAdapter) {
-        
+
         ActionResultResponseType arrt = determineFor(resultAdapter, targetIfAny);
         return arrt.interpretResult(model, targetIfAny, resultAdapter);
     }
@@ -194,11 +191,11 @@ public enum ActionResultResponseType {
     private static ActionResultResponseType determineFor(
             final ManagedObject resultAdapter,
             AjaxRequestTarget targetIfAny) {
-        
+
         if(resultAdapter == null) {
             return ActionResultResponseType.VOID;
         }
-        
+
         final ObjectSpecification resultSpec = resultAdapter.getSpecification();
         if (resultSpec.isNotCollection()) {
             if (resultSpec.getFacet(ValueFacet.class) != null) {
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsDropDownPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsDropDownPanel.java
index ee7d79e..5652db7 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsDropDownPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsDropDownPanel.java
@@ -19,15 +19,14 @@
 
 package org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions;
 
-import java.util.List;
-
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 
 public class AdditionalLinksAsDropDownPanel extends AdditionalLinksPanel {
 
     private static final long serialVersionUID = 1L;
 
-    public AdditionalLinksAsDropDownPanel(String id, List<LinkAndLabel> links) {
+    public AdditionalLinksAsDropDownPanel(String id, Can<LinkAndLabel> links) {
         super(id, links);
     }
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsListInlinePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsListInlinePanel.java
index c627ebb..9f5e4fd 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsListInlinePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksAsListInlinePanel.java
@@ -19,15 +19,14 @@
 
 package org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions;
 
-import java.util.List;
-
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 
 public class AdditionalLinksAsListInlinePanel extends AdditionalLinksPanel {
 
     private static final long serialVersionUID = 1L;
 
-    public AdditionalLinksAsListInlinePanel(String id, List<LinkAndLabel> links) {
+    public AdditionalLinksAsListInlinePanel(String id, Can<LinkAndLabel> links) {
         super(id, links);
     }
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
index 76e1a6f..cc19b50 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/AdditionalLinksPanel.java
@@ -28,6 +28,7 @@ import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.model.Model;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.viewer.common.model.decorator.confirm.ConfirmUiModel;
 import org.apache.isis.viewer.common.model.decorator.confirm.ConfirmUiModel.Placement;
@@ -42,7 +43,7 @@ import org.apache.isis.viewer.wicket.ui.util.Tooltips;
 
 import lombok.val;
 
-public class AdditionalLinksPanel 
+public class AdditionalLinksPanel
 extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
 
     private static final long serialVersionUID = 1L;
@@ -56,23 +57,23 @@ extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
     public enum Style {
         INLINE_LIST {
             @Override
-            AdditionalLinksPanel newPanel(String id, List<LinkAndLabel> links) {
+            AdditionalLinksPanel newPanel(String id, Can<LinkAndLabel> links) {
                 return new AdditionalLinksAsListInlinePanel(id, links);
             }
         },
         DROPDOWN {
             @Override
-            AdditionalLinksPanel newPanel(String id, List<LinkAndLabel> links) {
+            AdditionalLinksPanel newPanel(String id, Can<LinkAndLabel> links) {
                 return new AdditionalLinksAsDropDownPanel(id, links);
             }
         };
-        abstract AdditionalLinksPanel newPanel(String id, List<LinkAndLabel> links);
+        abstract AdditionalLinksPanel newPanel(String id, Can<LinkAndLabel> links);
     }
 
     public static AdditionalLinksPanel addAdditionalLinks(
             final MarkupContainer markupContainer,
             final String id,
-            final List<LinkAndLabel> links,
+            final Can<LinkAndLabel> links,
             final Style style) {
         if(links.isEmpty()) {
             Components.permanentlyHide(markupContainer, id);
@@ -85,9 +86,9 @@ extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
     }
 
     protected AdditionalLinksPanel(
-            String id, 
-            List<LinkAndLabel> linksDoNotUseDirectlyInsteadUseOfListOfLinksModel) {
-        
+            String id,
+            Can<LinkAndLabel> linksDoNotUseDirectlyInsteadUseOfListOfLinksModel) {
+
         super(id, new ListOfLinksModel(linksDoNotUseDirectlyInsteadUseOfListOfLinksModel));
 
 
@@ -104,8 +105,8 @@ extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
         container.setOutputMarkupId(true);
 
         setOutputMarkupId(true);
-        
-        final ListView<LinkAndLabel> listView = 
+
+        final ListView<LinkAndLabel> listView =
                 new ListView<LinkAndLabel>(ID_ADDITIONAL_LINK_ITEM, getModel()) {
 
             private static final long serialVersionUID = 1L;
@@ -124,9 +125,9 @@ extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
                         final String reasonDisabledIfAny = actionLink.getReasonDisabledIfAny();
                         return first(reasonDisabledIfAny, actionMeta.getDescription());
                     }
-                } 
+                }
                 : Model.of(actionMeta.getDescription());
-                
+
                 Tooltips.addTooltip(link, tooltipModel.getObject());
 
                 val viewTitleLabel = new Label(ID_ADDITIONAL_LINK_TITLE, actionMeta.getLabel());
@@ -138,10 +139,10 @@ extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
                 }
                 link.add(new CssClassAppender(actionMeta.getActionIdentifier()));
 
-                if (actionMeta.getSemantics().isAreYouSure()) { 
+                if (actionMeta.getSemantics().isAreYouSure()) {
                     if(actionMeta.getParameters().isNoParameters()) {
-                        val hasDisabledReason = link instanceof ActionLink 
-                                ? _Strings.isNotEmpty(((ActionLink)link).getReasonDisabledIfAny()) 
+                        val hasDisabledReason = link instanceof ActionLink
+                                ? _Strings.isNotEmpty(((ActionLink)link).getReasonDisabledIfAny())
                                 : false;
                         if (!hasDisabledReason) {
                             val confirmUiModel = ConfirmUiModel.ofAreYouSure(getTranslationService(), Placement.BOTTOM);
@@ -164,7 +165,7 @@ extends PanelAbstract<List<LinkAndLabel>, ListOfLinksModel> {
 
                 item.addOrReplace(link);
             }
-            
+
         };
 
         container.addOrReplace(listView);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java
index f7c1522..1502630 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actionmenu/entityactions/LinkAndLabelUtil.java
@@ -18,11 +18,8 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
@@ -35,23 +32,23 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 public final class LinkAndLabelUtil {
 
-    public static List<LinkAndLabel> asActionLinks(
+    public static Stream<LinkAndLabel> asActionLinks(
             final ScalarModel scalarModel,
-            final List<ObjectAction> associatedActions) {
+            final Stream<ObjectAction> associatedActions) {
 
         final EntityModel parentEntityModel = scalarModel.getParentUiModel();
         return asActionLinksForAdditionalLinksPanel(parentEntityModel, associatedActions, scalarModel);
     }
 
-    public static LinkAndLabel asActionLink(final ScalarModel scalarModel, final ObjectAction inlineActionIfAny) {
-        if(inlineActionIfAny == null) {
-            return null;
-        }
-        return asActionLinks(scalarModel, Collections.singletonList(inlineActionIfAny)).get(0);
+    public static Stream<LinkAndLabel> asActionLink(
+            final ScalarModel scalarModel,
+            final ObjectAction inlineAction) {
+        return asActionLinks(scalarModel, Stream.of(inlineAction));
     }
 
     /**
-     * Converts an {@link org.apache.isis.viewer.wicket.model.models.EntityModel} and a (subset of its) {@link ObjectAction}s into a
+     * Converts an {@link org.apache.isis.viewer.wicket.model.models.EntityModel} and a (subset of its)
+     * {@link ObjectAction}s into a
      * list of {@link org.apache.isis.viewer.wicket.model.links.LinkAndLabel}s intended to be passed
      * to the {@link AdditionalLinksPanel}.
      *
@@ -60,32 +57,28 @@ public final class LinkAndLabelUtil {
      *     (for invisible actions) will be discarded.
      * </p>
      */
-    public static List<LinkAndLabel> asActionLinksForAdditionalLinksPanel(
+    public static Stream<LinkAndLabel> asActionLinksForAdditionalLinksPanel(
             final EntityModel parentEntityModel,
-            final List<ObjectAction> objectActions,
+            final Stream<ObjectAction> objectActions,
             final ScalarModel scalarModelIfAny) {
 
         return asActionLinksForAdditionalLinksPanel(parentEntityModel, objectActions, scalarModelIfAny, null);
     }
 
-    public static List<LinkAndLabel> asActionLinksForAdditionalLinksPanel(
+    public static Stream<LinkAndLabel> asActionLinksForAdditionalLinksPanel(
             final EntityModel parentEntityModel,
-            final List<ObjectAction> objectActions,
+            final Stream<ObjectAction> objectActions,
             final ScalarModel scalarModelIfAny,
             final ToggledMementosProvider toggledMementosProviderIfAny) {
 
         val actionLinkFactory = new EntityActionLinkFactory(
-                AdditionalLinksPanel.ID_ADDITIONAL_LINK, 
-                parentEntityModel, 
+                AdditionalLinksPanel.ID_ADDITIONAL_LINK,
+                parentEntityModel,
                 scalarModelIfAny,
                 toggledMementosProviderIfAny);
-        
-        val named = (String)null;
-        
-        return _NullSafe.stream(objectActions)
-                .map(objectAction->actionLinkFactory.newActionLink(objectAction, named))
-                .filter(_NullSafe::isPresent)
-                .collect(Collectors.toList());
+
+        return objectActions
+                .map(objectAction->actionLinkFactory.newActionLink(objectAction, /*named*/null));
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
deleted file mode 100644
index 2dff2bf..0000000
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
+++ /dev/null
@@ -1,70 +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.viewer.wicket.ui.components.collection;
-
-import java.io.Serializable;
-
-import org.apache.isis.commons.collections.Can;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.spec.feature.MixedIn;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
-import org.apache.isis.viewer.wicket.ui.components.collection.bulk.BulkActionsProvider;
-
-import lombok.val;
-
-/**
- * See also {@link BulkActionsProvider}.
- */
-public class AssociatedWithActionsHelper implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    private final EntityCollectionModel collectionModel;
-
-    public AssociatedWithActionsHelper(final EntityCollectionModel collectionModel) {
-        this.collectionModel = collectionModel;
-    }
-
-    //TODO refactor: move to EntityCollectionModel
-    public Can<ObjectAction> getAssociatedActions(final SpecificationLoader specLoader) {
-
-        if(collectionModel.isStandalone()) {
-            return Can.empty();
-        }
-        final OneToManyAssociation collection = collectionModel.getCollectionMemento()
-                .getCollection(specLoader);
-
-        return getObjectSpecification()
-                .streamRuntimeActions(MixedIn.INCLUDED)
-                .filter(ObjectAction.Predicates.associatedWithAndWithCollectionParameterFor(collection))
-                .collect(Can.toCan());
-    }
-
-    // -- HELPER
-
-    //TODO refactor: move to EntityCollectionModel
-    private ObjectSpecification getObjectSpecification() {
-        val parentMemento = collectionModel.getParentObjectAdapterMemento();
-        val parentAdapter = collectionModel.getCommonContext().reconstructObject(parentMemento);
-        return parentAdapter.getSpecification();
-    }
-
-}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
index 1728f5d..acd4260 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
@@ -28,15 +28,11 @@ import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
 import org.apache.wicket.markup.html.basic.Label;
 
 import org.apache.isis.commons.collections.Can;
-import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.runtime.memento.ObjectMemento;
 import org.apache.isis.viewer.wicket.model.common.OnSelectionHandler;
-import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelParented;
 import org.apache.isis.viewer.wicket.model.models.ToggledMementosProvider;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.LinkAndLabelUtil;
@@ -57,7 +53,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel
  * of) {@link ScalarPanelAbstract}.
  */
 public class CollectionPanel
-extends PanelAbstract<List<ManagedObject>, EntityCollectionModel>
+extends PanelAbstract<List<ManagedObject>, EntityCollectionModelParented>
 implements CollectionSelectorProvider, BulkActionsProvider {
 
     private static final long serialVersionUID = 1L;
@@ -68,33 +64,25 @@ implements CollectionSelectorProvider, BulkActionsProvider {
 
     private Label label;
 
-    private final AssociatedWithActionsHelper associatedWithActionsHelper;
-
     public CollectionPanel(
             final String id,
-            final EntityCollectionModel collectionModel) {
+            final EntityCollectionModelParented collectionModel) {
         super(id, collectionModel);
 
-        final List<LinkAndLabel> entityActionLinks = _Lists.newArrayList();
-
-        final OneToManyAssociation otma = collectionModel.getCollectionMemento().getCollection(collectionModel.getSpecificationLoader());
-        final EntityModel entityModel = collectionModel.getEntityModel();
-        val adapter = entityModel.getManagedObject();
-
-        final List<ObjectAction> associatedActions =
-                ObjectAction.Util.findForAssociation(adapter, otma);
+        val associatedActions = collectionModel.getAssociatedActions();
 
-        associatedWithActionsHelper = new AssociatedWithActionsHelper(collectionModel);
-
-        final ToggledMementosProvider toggledMementosProvider =
+        val toggledMementosProvider =
                 new MyToggledMementosProvider(collectionModel, this, this);
 
-        entityActionLinks.addAll(
-                LinkAndLabelUtil
+        val entityActionLinks = LinkAndLabelUtil
                 .asActionLinksForAdditionalLinksPanel(
-                        entityModel, associatedActions, null, toggledMementosProvider));
+                        collectionModel.getEntityModel(),
+                        associatedActions.stream(),
+                        null,
+                        toggledMementosProvider)
+                .collect(Can.toCan());
 
-        collectionModel.addLinkAndLabels(entityActionLinks);
+        collectionModel.setLinkAndLabels(entityActionLinks);
 
     }
 
@@ -138,11 +126,10 @@ implements CollectionSelectorProvider, BulkActionsProvider {
     public ObjectAdapterToggleboxColumn getToggleboxColumn() {
 
         if(toggleboxColumn == null) {
-            val associatedActions =
-                    associatedWithActionsHelper.getAssociatedActions(getSpecificationLoader());
-
             val entityCollectionModel = getModel();
-            if(associatedActions.isEmpty() || entityCollectionModel.isStandalone()) {
+
+            val associatedActions = entityCollectionModel.getAssociatedActions();
+            if(associatedActions.isEmpty()) {
                 return null;
             }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorHelper.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorHelper.java
index 7f6e129..c39643d 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorHelper.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorHelper.java
@@ -22,6 +22,9 @@ package org.apache.isis.viewer.wicket.ui.components.collection.selector;
 import java.io.Serializable;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
@@ -33,7 +36,7 @@ import org.apache.isis.core.metamodel.facets.collections.collection.defaultview.
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelParented;
 import org.apache.isis.viewer.wicket.model.util.ComponentHintKey;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
@@ -42,37 +45,39 @@ import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.multiple.CollectionContentsMultipleViewsPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.unresolved.CollectionContentsHiddenPanelFactory;
 
+import lombok.val;
+
 public class CollectionSelectorHelper implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    static final String UIHINT_EVENT_VIEW_KEY = EntityCollectionModel.HINT_KEY_SELECTED_ITEM;
+    static final String UIHINT_EVENT_VIEW_KEY = EntityCollectionModelParented.HINT_KEY_SELECTED_ITEM;
 
-    private final EntityCollectionModel model;
+    private final EntityCollectionModel collectionModel;
 
     private final List<ComponentFactory> componentFactories;
     private final ComponentHintKey componentHintKey;
 
     public CollectionSelectorHelper(
-            final EntityCollectionModel model,
+            final EntityCollectionModel collectionModel,
             final ComponentFactoryRegistry componentFactoryRegistry) {
-        this(model, componentFactoryRegistry, ComponentHintKey.noop());
+        this(collectionModel, componentFactoryRegistry, ComponentHintKey.noop());
     }
 
     public CollectionSelectorHelper(
-            final EntityCollectionModel model,
+            final EntityCollectionModel collectionModel,
             final ComponentFactoryRegistry componentFactoryRegistry,
             final ComponentHintKey componentHintKey) {
-        this.model = model;
+        this.collectionModel = collectionModel;
         this.componentFactories = locateComponentFactories(componentFactoryRegistry);
-        this.componentHintKey = componentHintKey != null 
-                ? componentHintKey 
+        this.componentHintKey = componentHintKey != null
+                ? componentHintKey
                 : ComponentHintKey.noop();
     }
 
     private List<ComponentFactory> locateComponentFactories(ComponentFactoryRegistry componentFactoryRegistry) {
-        final List<ComponentFactory> componentFactories = componentFactoryRegistry.findComponentFactories(ComponentType.COLLECTION_CONTENTS, model);
-        final List<ComponentFactory> otherFactories = _Lists.filter(componentFactories, 
+        final List<ComponentFactory> componentFactories = componentFactoryRegistry.findComponentFactories(ComponentType.COLLECTION_CONTENTS, collectionModel);
+        final List<ComponentFactory> otherFactories = _Lists.filter(componentFactories,
                 (final ComponentFactory input) ->
         input.getClass() != CollectionContentsMultipleViewsPanelFactory.class);
         return ordered(otherFactories);
@@ -118,7 +123,10 @@ public class CollectionSelectorHelper implements Serializable {
         }
 
         // else grid layout hint
-        final CollectionLayoutData layoutData = this.model.getLayoutData();
+        final CollectionLayoutData layoutData = toParentedEntityCollectionModel(collectionModel)
+                .map(EntityCollectionModelParented::getLayoutData)
+                .orElse(null);
+
         if(layoutData != null) {
             final String defaultView = layoutData.getDefaultView();
             if(defaultView != null) {
@@ -127,8 +135,8 @@ public class CollectionSelectorHelper implements Serializable {
         }
 
         // else @CollectionLayout#defaultView attribute
-        if (hasDefaultViewFacet(model)) {
-            DefaultViewFacet defaultViewFacet = model.getCollectionMemento().getCollection(model.getSpecificationLoader()).getFacet(DefaultViewFacet.class);
+        if (hasDefaultViewFacet(collectionModel)) {
+            DefaultViewFacet defaultViewFacet = collectionModel.getMetaModel().getFacet(DefaultViewFacet.class);
             for (ComponentFactory componentFactory : componentFactories) {
                 final String componentName = componentFactory.getName();
                 final String viewName = defaultViewFacet.value();
@@ -139,16 +147,14 @@ public class CollectionSelectorHelper implements Serializable {
         }
 
         // else honour @CollectionLayout#renderEagerly
-        return hasRenderEagerlyFacet(model) || model.isStandalone()
+        return hasRenderEagerlyFacet(collectionModel) || collectionModel.isStandalone()
                 ? CollectionContentsAsAjaxTablePanelFactory.NAME
-                        : CollectionContentsHiddenPanelFactory.NAME;
+                : CollectionContentsHiddenPanelFactory.NAME;
 
     }
 
     private Bookmark bookmarkHintIfAny() {
-        final EntityModel entityModel = this.model.getEntityModel();
-        return entityModel != null
-                ? entityModel.asHintingBookmarkIfSupported(): null;
+        return collectionModel.asHintingBookmarkIfSupported();
     }
 
     private static List<ComponentFactory> ordered(List<ComponentFactory> componentFactories) {
@@ -177,60 +183,39 @@ public class CollectionSelectorHelper implements Serializable {
     }
 
     private static UiHintContainer getUiHintContainer(final Component component) {
-        return UiHintContainer.Util.hintContainerOf(component, EntityCollectionModel.class);
+        return UiHintContainer.Util.hintContainerOf(component, EntityCollectionModelParented.class);
     }
 
     private static boolean hasRenderEagerlyFacet(IModel<?> model) {
-        final EntityCollectionModel entityCollectionModel = toEntityCollectionModel(model);
-        if (entityCollectionModel == null) {
-            return false;
-        }
-
-        final OneToManyAssociation collection =
-                entityCollectionModel.getCollectionMemento().getCollection(entityCollectionModel.getSpecificationLoader());
-        return renderEagerly(collection);
+        return toParentedEntityCollectionModel(model)
+        .map(EntityCollectionModelParented::getMetaModel)
+        .map(CollectionSelectorHelper::isRenderEagerly)
+        .orElse(false);
     }
 
-    private static boolean renderEagerly(final OneToManyAssociation otma) {
-        final DefaultViewFacet defaultViewFacet = otma.getFacet(DefaultViewFacet.class);
+    private static boolean isRenderEagerly(final OneToManyAssociation collectionMetaModel) {
+        final DefaultViewFacet defaultViewFacet = collectionMetaModel.getFacet(DefaultViewFacet.class);
         return defaultViewFacet != null && Objects.equals(defaultViewFacet.value(), "table");
     }
 
 
     private static boolean hasDefaultViewFacet(IModel<?> model) {
-        final EntityCollectionModel entityCollectionModel = toEntityCollectionModel(model);
+        val entityCollectionModel = toParentedEntityCollectionModel(model).orElse(null);
         if (entityCollectionModel == null) {
             return false;
         }
-
-        final OneToManyAssociation collection =
-                entityCollectionModel.getCollectionMemento().getCollection(entityCollectionModel.getSpecificationLoader());
+        final OneToManyAssociation collection = entityCollectionModel.getMetaModel();
         DefaultViewFacet defaultViewFacet = collection.getFacet(DefaultViewFacet.class);
         return defaultViewFacet != null;
     }
 
-    private static EntityCollectionModel toEntityCollectionModel(IModel<?> model) {
-        if (!(model instanceof EntityCollectionModel)) {
-            return null;
-        }
-
-        final EntityCollectionModel entityCollectionModel = (EntityCollectionModel) model;
-        if (!entityCollectionModel.isParented()) {
-            return null;
-        }
-
-        return entityCollectionModel;
-    }
-
     public ComponentFactory find(final String selected) {
         ComponentFactory componentFactory = doFind(selected);
         if (componentFactory != null) {
             return componentFactory;
         }
 
-        final EntityCollectionModel entityCollectionModel = model;
-        final String fallback;
-        fallback = entityCollectionModel.isParented()
+        final String fallback = collectionModel.isParented()
                 ? CollectionContentsHiddenPanelFactory.NAME
                 : CollectionContentsAsAjaxTablePanelFactory.NAME;
         componentFactory = doFind(fallback);
@@ -262,6 +247,15 @@ public class CollectionSelectorHelper implements Serializable {
         return 0;
     }
 
+    // -- HELPER
+
+    private static Optional<EntityCollectionModelParented> toParentedEntityCollectionModel(
+            final @Nullable IModel<?> model) {
+        if (model instanceof EntityCollectionModelParented) {
+            return Optional.of((EntityCollectionModelParented) model);
+        }
+        return Optional.empty();
+    }
 
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorPanel.java
index e969441..bc6b97f 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorPanel.java
@@ -39,7 +39,6 @@ import org.apache.isis.core.metamodel.commons.StringExtensions;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.viewer.wicket.model.hints.IsisSelectorEvent;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.util.ComponentHintKey;
 import org.apache.isis.viewer.wicket.ui.CollectionContentsAsFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
@@ -53,7 +52,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
  * {@link org.apache.isis.viewer.wicket.ui.ComponentType#COLLECTION_CONTENTS} with a backing
  * {@link org.apache.isis.viewer.wicket.model.models.EntityCollectionModel}.
  */
-public class CollectionSelectorPanel 
+public class CollectionSelectorPanel
 extends PanelAbstract<List<ManagedObject>, EntityCollectionModel> {
 
     private static final long serialVersionUID = 1L;
@@ -76,7 +75,7 @@ extends PanelAbstract<List<ManagedObject>, EntityCollectionModel> {
     public CollectionSelectorPanel(
             final String id,
             final EntityCollectionModel model) {
-        this(id, model, ComponentHintKey.<String>noop());
+        this(id, model, ComponentHintKey.noop());
     }
 
     public CollectionSelectorPanel(
@@ -150,11 +149,7 @@ extends PanelAbstract<List<ManagedObject>, EntityCollectionModel> {
                         }
 
                         Bookmark domainObjectBookmarkIfAny() {
-                            final EntityCollectionModel entityCollectionModel = CollectionSelectorPanel.this.getModel();
-                            final EntityModel entityModel = entityCollectionModel.getEntityModel();
-                            return entityModel != null
-                                    ? entityModel.asHintingBookmarkIfSupported()
-                                    : null;
+                            return CollectionSelectorPanel.this.getModel().asHintingBookmarkIfSupported();
                         }
 
                         @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
index 2651e88..52c23e7 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
@@ -45,13 +45,13 @@ import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet;
 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.object.grid.GridFacet;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.MixedIn;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.runtime.memento.ObjectMemento;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.components.collection.bulk.BulkActionsProvider;
 import org.apache.isis.viewer.wicket.ui.components.collection.count.CollectionCountProvider;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.ObjectAdapterPropertyColumn;
@@ -106,7 +106,7 @@ implements CollectionCountProvider {
             bulkActionsProvider.configureBulkActions(toggleboxColumn);
         }
 
-        final EntityCollectionModel collectionModel = getModel();
+        val collectionModel = getModel();
         addTitleColumn(
                 columns,
                 collectionModel.getParentObjectAdapterMemento(),
@@ -254,8 +254,8 @@ implements CollectionCountProvider {
 //        val parentObject = Optional.ofNullable(collectionModel.getParentObjectAdapterMemento())
 //                .map(getCommonContext()::reconstructObject);
 
-        val parentObject = Optional.ofNullable(collectionModel.getEntityModel())
-                .map(EntityModel::getManagedObject);
+        final Optional<ManagedObject> parentObject = collectionModel.getManagedCollection()
+                .map(ManagedCollection::getOwner);
 
         tableColumnOrderServices.stream()
         .map(tableColumnOrderService->
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterPropertyColumn.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterPropertyColumn.java
index 6848b33..fe10d4a 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterPropertyColumn.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterPropertyColumn.java
@@ -62,7 +62,7 @@ public final class ObjectAdapterPropertyColumn extends ColumnAbstract<ManagedObj
     private final String describedAs;
 
     public ObjectAdapterPropertyColumn(
-            IsisAppCommonContext commonContext, 
+            IsisAppCommonContext commonContext,
             EntityCollectionModel.Variant collectionVariant,
             IModel<String> columnNameModel,
             String sortProperty,
@@ -70,7 +70,7 @@ public final class ObjectAdapterPropertyColumn extends ColumnAbstract<ManagedObj
             boolean escaped,
             String parentTypeName,
             String describedAs) {
-        
+
         super(commonContext, columnNameModel, sortProperty);
         this.collectionVariant = collectionVariant;
         this.propertyExpression = propertyName;
@@ -99,10 +99,10 @@ public final class ObjectAdapterPropertyColumn extends ColumnAbstract<ManagedObj
 
     @Override
     public void populateItem(
-            final Item<ICellPopulator<ManagedObject>> cellItem, 
-            final String componentId, 
+            final Item<ICellPopulator<ManagedObject>> cellItem,
+            final String componentId,
             final IModel<ManagedObject> rowModel) {
-        
+
         final Component component = createComponent(componentId, rowModel);
         cellItem.add(component);
     }
@@ -114,7 +114,8 @@ public final class ObjectAdapterPropertyColumn extends ColumnAbstract<ManagedObj
         final OneToOneAssociation property = (OneToOneAssociation) adapter.getSpecification().getAssociationElseFail(propertyExpression);
         final PropertyMemento pm = new PropertyMemento(property);
 
-        final ScalarModel scalarModel = entityModel.getPropertyModel(pm, EntityModel.Mode.VIEW, collectionVariant.renderingHint());
+        final ScalarModel scalarModel = entityModel
+                .getPropertyModel(pm, EntityModel.Mode.VIEW, collectionVariant.getColumnRenderingHint());
 
         final ComponentFactory componentFactory = findComponentFactory(ComponentType.SCALAR_NAME_AND_VALUE, scalarModel);
         return componentFactory.createComponent(id, scalarModel);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java
index 7229b22..7093c2d 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ObjectAdapterTitleColumn.java
@@ -19,6 +19,8 @@
 
 package org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns;
 
+import javax.annotation.Nullable;
+
 import org.apache.wicket.Component;
 import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
 import org.apache.wicket.markup.repeater.Item;
@@ -41,49 +43,53 @@ public class ObjectAdapterTitleColumn extends ColumnAbstract<ManagedObject> {
     private static final long serialVersionUID = 1L;
     private final ObjectMemento parentAdapterMementoIfAny;
 
-    private static String columnName(final ObjectMemento parentAdapterMementoIfAny, final int maxTitleLength) {
-        if(maxTitleLength == 0) {
-            return "";
-        }
-        return (parentAdapterMementoIfAny != null? "Related ":"") + "Object";
-    }
-
     public ObjectAdapterTitleColumn(
-            IsisAppCommonContext commonContext, 
-            ObjectMemento parentAdapterMementoIfAny, 
+            IsisAppCommonContext commonContext,
+            ObjectMemento parentAdapterMementoIfAny,
             int maxTitleLength) {
-        
+
         super(commonContext, columnName(parentAdapterMementoIfAny, maxTitleLength)); // i18n
         this.parentAdapterMementoIfAny = parentAdapterMementoIfAny;
     }
 
     @Override
     public void populateItem(
-            final Item<ICellPopulator<ManagedObject>> cellItem, 
-            final String componentId, 
+            final Item<ICellPopulator<ManagedObject>> cellItem,
+            final String componentId,
             final IModel<ManagedObject> rowModel) {
-        
+
         final Component component = createComponent(componentId, rowModel);
         cellItem.add(component);
         cellItem.add(new CssClassAppender("title-column"));
     }
 
+    // -- HELPER
+
+    private static String columnName(
+            final @Nullable ObjectMemento parentAdapterMementoIfAny,
+            final int maxTitleLength) {
+        if(maxTitleLength == 0) {
+            return "";
+        }
+        return (parentAdapterMementoIfAny != null? "Related ":"") + "Object";
+    }
+
     private Component createComponent(final String id, final IModel<ManagedObject> rowModel) {
         val adapter = rowModel.getObject();
-        
+
         if(ManagedObjects.isValue(adapter)) {
             val valueModel = new ValueModel(super.getCommonContext(), adapter);
-            
+
             val componentFactory = findComponentFactory(ComponentType.VALUE, valueModel);
             return componentFactory.createComponent(id, valueModel);
         }
-        
+
         val entityModel = EntityModel.ofAdapter(super.getCommonContext(), adapter);
         entityModel.setRenderingHint(parentAdapterMementoIfAny != null
                 ? RenderingHint.PARENTED_TITLE_COLUMN
-                        : RenderingHint.STANDALONE_TITLE_COLUMN);
+                : RenderingHint.STANDALONE_TITLE_COLUMN);
         entityModel.setContextAdapterIfAny(parentAdapterMementoIfAny);
-        
+
         // will use EntityLinkSimplePanelFactory as model is an EntityModel
         val componentFactory = findComponentFactory(ComponentType.ENTITY_LINK, entityModel);
         return componentFactory.createComponent(id, entityModel);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/multiple/CollectionContentsMultipleViewsPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/multiple/CollectionContentsMultipleViewsPanel.java
index 2901d11..aa9aab4 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/multiple/CollectionContentsMultipleViewsPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/multiple/CollectionContentsMultipleViewsPanel.java
@@ -29,7 +29,8 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.viewer.wicket.model.hints.IsisEnvelopeEvent;
 import org.apache.isis.viewer.wicket.model.hints.IsisSelectorEvent;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelDummy;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelParented;
 import org.apache.isis.viewer.wicket.model.util.ComponentHintKey;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
@@ -40,20 +41,20 @@ import org.apache.isis.viewer.wicket.ui.components.collection.selector.Collectio
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
 /**
- * Subscribes to events generated by 
- * {@link org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorPanel}, 
+ * Subscribes to events generated by
+ * {@link org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorPanel},
  * rendering the appropriate {@link ComponentType#COLLECTION_CONTENTS}
  * view for a backing {@link EntityCollectionModel}.
  */
 public class CollectionContentsMultipleViewsPanel
-extends PanelAbstract<List<ManagedObject>, EntityCollectionModel> 
+extends PanelAbstract<List<ManagedObject>, EntityCollectionModel>
 implements CollectionCountProvider {
 
     private static final long serialVersionUID = 1L;
 
     private static final int MAX_NUM_UNDERLYING_VIEWS = 10;
 
-    private static final String UIHINT_VIEW = EntityCollectionModel.HINT_KEY_SELECTED_ITEM;
+    private static final String UIHINT_VIEW = EntityCollectionModelParented.HINT_KEY_SELECTED_ITEM;
 
     private final String underlyingIdPrefix;
     private final CollectionSelectorHelper selectorHelper;
@@ -68,15 +69,14 @@ implements CollectionCountProvider {
         super(id, model);
 
         this.underlyingIdPrefix = ComponentType.COLLECTION_CONTENTS.toString();
-        final EntityModel entityModel = model.getEntityModel();
 
         final ComponentHintKey selectedItemSessionAttribute =
-                entityModel != null
-                ? ComponentHintKey.create(super.getCommonContext(), this, EntityCollectionModel.HINT_KEY_SELECTED_ITEM)
-                        : null;
+                model.isParented()
+                    ? ComponentHintKey.create(super.getCommonContext(), this, UIHINT_VIEW)
+                    : null;
 
-                selectorHelper = new CollectionSelectorHelper(
-                        model, getComponentFactoryRegistry(), selectedItemSessionAttribute);
+        selectorHelper = new CollectionSelectorHelper(
+                model, getComponentFactoryRegistry(), selectedItemSessionAttribute);
     }
 
     /**
@@ -106,12 +106,14 @@ implements CollectionCountProvider {
         int i = 0;
         int selectedIdx = 0;
         underlyingViews = new Component[MAX_NUM_UNDERLYING_VIEWS];
-        final EntityCollectionModel emptyModel = model.asDummy();
+
+        final EntityCollectionModel emptyModel = EntityCollectionModelDummy.forCollectionModel(model);
         for (ComponentFactory componentFactory : componentFactories) {
             final String underlyingId = underlyingIdPrefix + "-" + i;
 
             final boolean isSelected = selected.equals(componentFactory.getName());
-            final Component underlyingView = componentFactory.createComponent(underlyingId, isSelected ? model : emptyModel);
+            final Component underlyingView = componentFactory
+                    .createComponent(underlyingId, isSelected ? model : emptyModel);
             if(isSelected) {
                 selectedIdx = i;
             }
@@ -162,7 +164,7 @@ implements CollectionCountProvider {
 
         int underlyingViewNum = selectorHelper.lookup(selectedView);
 
-        final EntityCollectionModel dummyModel = getModel().asDummy();
+        final EntityCollectionModel dummyModel = EntityCollectionModelDummy.forCollectionModel(getModel());
         for(int i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
             final Component component = underlyingViews[i];
             if(component == null) {
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
index 397a0d1..fe55e99 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/collection/EntityCollectionPanel.java
@@ -30,14 +30,15 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.Model;
 
 import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 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.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelParented;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.model.util.ComponentHintKey;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
@@ -57,8 +58,8 @@ import lombok.val;
  * {@link PanelAbstract Panel} representing the properties of an entity, as per
  * the provided {@link EntityModel}.
  */
-public class EntityCollectionPanel 
-extends PanelAbstract<ManagedObject, EntityModel> 
+public class EntityCollectionPanel
+extends PanelAbstract<ManagedObject, EntityModel>
 implements HasDynamicallyVisibleContent {
 
     private static final long serialVersionUID = 1L;
@@ -79,7 +80,7 @@ implements HasDynamicallyVisibleContent {
     public EntityCollectionPanel(final String id, final EntityModel entityModel) {
         super(id, entityModel);
 
-        selectedItemHintKey = ComponentHintKey.create(super.getCommonContext(), getSelectorDropdownPanel(), EntityCollectionModel.HINT_KEY_SELECTED_ITEM);
+        selectedItemHintKey = ComponentHintKey.create(super.getCommonContext(), getSelectorDropdownPanel(), EntityCollectionModelParented.HINT_KEY_SELECTED_ITEM);
         div = buildGui();
     }
 
@@ -106,46 +107,47 @@ implements HasDynamicallyVisibleContent {
     private WebMarkupContainer buildGui() {
         final WebMarkupContainer div = new WebMarkupContainer(ID_COLLECTION_GROUP);
 
-        final EntityCollectionModel entityCollectionModel = EntityCollectionModel.createParented(getModel());
-        div.setMarkupId("collection-" + entityCollectionModel.getLayoutData().getId());
+        val collectionModel = EntityCollectionModel.createParented(getModel());
+        div.setMarkupId("collection-" + collectionModel.getLayoutData().getId());
 
-        CssClassAppender.appendCssClassTo(div, entityCollectionModel.getCollectionMemento().getId());
-        CssClassAppender.appendCssClassTo(div, entityCollectionModel.getTypeOfSpecification().getFullIdentifier().replace('.','-'));
+        val collectionMetaModel = collectionModel.getMetaModel();
+
+        CssClassAppender.appendCssClassTo(div, collectionModel.getIdentifier().getMemberName());
+        CssClassAppender.appendCssClassTo(div, collectionModel.getTypeOfSpecification().getFullIdentifier().replace('.','-'));
 
-        final OneToManyAssociation association = entityCollectionModel.getCollectionMemento().getCollection(
-                entityCollectionModel.getSpecificationLoader());
         val objectAdapter = getModel().getObject();
-        final Consent visibility = association.isVisible(objectAdapter, InteractionInitiatedBy.USER, Where.OBJECT_FORMS);
+        final Consent visibility = collectionMetaModel
+                .isVisible(objectAdapter, InteractionInitiatedBy.USER, Where.OBJECT_FORMS);
 
         if(visibility.isAllowed()) {
 
             visible = true;
 
-            final CssClassFacet facet = association.getFacet(CssClassFacet.class);
+            final CssClassFacet facet = collectionMetaModel.getFacet(CssClassFacet.class);
             if(facet != null) {
                 final String cssClass = facet.cssClass(objectAdapter);
                 CssClassAppender.appendCssClassTo(div, cssClass);
             }
 
-            final CollectionPanel collectionPanel = newCollectionModel(ID_COLLECTION, entityCollectionModel);
+            final CollectionPanel collectionPanel = newCollectionModel(ID_COLLECTION, collectionModel);
             div.addOrReplace(collectionPanel);
 
 
-            Label labelComponent = collectionPanel.createLabel(ID_COLLECTION_NAME, association.getName());
-            final NamedFacet namedFacet = association.getFacet(NamedFacet.class);
+            Label labelComponent = collectionPanel.createLabel(ID_COLLECTION_NAME, collectionMetaModel.getName());
+            final NamedFacet namedFacet = collectionMetaModel.getFacet(NamedFacet.class);
             labelComponent.setEscapeModelStrings(namedFacet == null || namedFacet.escaped());
             div.add(labelComponent);
 
-            final String description = association.getDescription();
+            final String description = collectionMetaModel.getDescription();
             if(description != null) {
                 Tooltips.addTooltip(labelComponent, description);
             }
 
-            final List<LinkAndLabel> links = entityCollectionModel.getLinks();
-            AdditionalLinksPanel.addAdditionalLinks (div,ID_ADDITIONAL_LINKS, links, AdditionalLinksPanel.Style.INLINE_LIST);
+            final Can<LinkAndLabel> links = collectionModel.getLinks();
+            AdditionalLinksPanel.addAdditionalLinks(div,ID_ADDITIONAL_LINKS, links, AdditionalLinksPanel.Style.INLINE_LIST);
 
             final CollectionSelectorHelper selectorHelper =
-                    new CollectionSelectorHelper(entityCollectionModel, getComponentFactoryRegistry(),
+                    new CollectionSelectorHelper(collectionModel, getComponentFactoryRegistry(),
                             selectedItemHintKey);
 
             final List<ComponentFactory> componentFactories = selectorHelper.getComponentFactories();
@@ -154,7 +156,7 @@ implements HasDynamicallyVisibleContent {
                 permanentlyHide(ID_SELECTOR_DROPDOWN);
             } else {
                 selectorDropdownPanel = new CollectionSelectorPanel(ID_SELECTOR_DROPDOWN,
-                        entityCollectionModel, selectedItemHintKey);
+                        collectionModel, selectedItemHintKey);
 
                 final Model<ComponentFactory> componentFactoryModel = new Model<>();
 
@@ -172,7 +174,7 @@ implements HasDynamicallyVisibleContent {
         return div;
     }
 
-    protected CollectionPanel newCollectionModel(String id, EntityCollectionModel entityCollectionModel) {
+    protected CollectionPanel newCollectionModel(String id, EntityCollectionModelParented entityCollectionModel) {
         return new CollectionPanel(id, entityCollectionModel);
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
index ab21491..f3f0b24 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/fieldset/PropertyGroup.java
@@ -20,6 +20,7 @@ package org.apache.isis.viewer.wicket.ui.components.entity.fieldset;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import org.apache.wicket.Component;
@@ -32,6 +33,7 @@ import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.layout.component.FieldSet;
 import org.apache.isis.applib.layout.component.PropertyLayoutData;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
@@ -77,12 +79,12 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
 
         // the UI is only ever built once.
         childComponents = buildGui();
-        childScalarPanelAbstract2s = 
+        childScalarPanelAbstract2s =
                 _NullSafe.stream(childComponents)
                 .filter(ScalarPanelAbstract.class::isInstance)
                 .map(ScalarPanelAbstract.class::cast)
                 .collect(Collectors.toList());
-                
+
     }
 
     @Override
@@ -114,7 +116,7 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
             final WebMarkupContainer propertyRvContainer = new WebMarkupContainer(propertyRv.newChildId());
             propertyRv.addOrReplace(propertyRvContainer);
             final Component component = addPropertyToForm(getModel(), (OneToOneAssociation) association,
-                    propertyRvContainer, memberGroupActions);
+                    propertyRvContainer, memberGroupActions::add);
             childComponents.add(component);
         }
 
@@ -124,10 +126,14 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
             panelHeading.setVisibilityAllowed(false);
         } else {
             panelHeading.addOrReplace(new Label(ID_MEMBER_GROUP_NAME, groupName));
-            final List<LinkAndLabel> actionsPanel = LinkAndLabel
-                    .positioned(memberGroupActions, ActionLayout.Position.PANEL);
-            final List<LinkAndLabel> actionsPanelDropDown = LinkAndLabel
-                    .positioned(memberGroupActions, ActionLayout.Position.PANEL_DROPDOWN);
+            final Can<LinkAndLabel> actionsPanel = memberGroupActions
+                    .stream()
+                    .filter(LinkAndLabel.positioned(ActionLayout.Position.PANEL))
+                    .collect(Can.toCan());
+            final Can<LinkAndLabel> actionsPanelDropDown = memberGroupActions
+                    .stream()
+                    .filter(LinkAndLabel.positioned(ActionLayout.Position.PANEL_DROPDOWN))
+                    .collect(Can.toCan());
 
             AdditionalLinksPanel.addAdditionalLinks(
                     panelHeading, ID_ASSOCIATED_ACTION_LINKS_PANEL,
@@ -172,7 +178,7 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
 
         val oas = _NullSafe.stream(properties)
                 .filter(propertyLayoutData -> propertyLayoutData.getMetadataError() == null)
-                .map(propertyLayoutData -> 
+                .map(propertyLayoutData ->
                     adapter.getSpecification()
                     .getAssociation(propertyLayoutData.getId())
                     .orElse(null)
@@ -189,7 +195,7 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
                     return true;
                 })
                 .collect(Collectors.toList());
-        
+
         return Collections.unmodifiableList(oas);
     }
 
@@ -197,7 +203,7 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
             final EntityModel entityModel,
             final OneToOneAssociation otoa,
             final WebMarkupContainer container,
-            final List<LinkAndLabel> entityActions) {
+            final Consumer<LinkAndLabel> onEntityAction) {
 
         final PropertyMemento pm = new PropertyMemento(otoa);
 
@@ -212,11 +218,10 @@ public class PropertyGroup extends PanelAbstract<ManagedObject, EntityModel> imp
         }
 
         val adapter = entityModel.getManagedObject();
-        final List<ObjectAction> associatedActions =
-                ObjectAction.Util.findForAssociation(adapter, otoa);
+        val associatedActions = ObjectAction.Util.findForAssociation(adapter, otoa);
 
-        entityActions.addAll(
-                LinkAndLabelUtil.asActionLinksForAdditionalLinksPanel(entityModel, associatedActions, null));
+        LinkAndLabelUtil.asActionLinksForAdditionalLinksPanel(entityModel, associatedActions, null)
+        .forEach(onEntityAction);
 
         return component;
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/header/EntityHeaderPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/header/EntityHeaderPanel.java
index e7fa993..c60cdd0 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/header/EntityHeaderPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/header/EntityHeaderPanel.java
@@ -19,13 +19,11 @@
 
 package org.apache.isis.viewer.wicket.ui.components.entity.header;
 
-import java.util.List;
-
 import org.apache.wicket.Component;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
-import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
@@ -40,7 +38,7 @@ import lombok.val;
  * {@link PanelAbstract Panel} representing the summary details (title, icon and
  * actions) of an entity, as per the provided {@link EntityModel}.
  */
-public class EntityHeaderPanel 
+public class EntityHeaderPanel
 extends PanelAbstract<ManagedObject, EntityModel> {
 
     private static final long serialVersionUID = 1L;
@@ -82,13 +80,14 @@ extends PanelAbstract<ManagedObject, EntityModel> {
         final EntityModel model = getModel();
         val adapter = model.getObject();
         if (adapter != null) {
-            final List<ObjectAction> topLevelActions = ObjectAction.Util
-                    .findTopLevel(adapter);
-
-            final List<LinkAndLabel> entityActionLinks = LinkAndLabelUtil
-                    .asActionLinksForAdditionalLinksPanel(model, topLevelActions, null);
-
-            AdditionalLinksPanel.addAdditionalLinks(this, ID_ENTITY_ACTIONS, entityActionLinks, AdditionalLinksPanel.Style.INLINE_LIST);
+            val topLevelActions = ObjectAction.Util.streamTopLevelActions(adapter);
+            val entityActionLinks = LinkAndLabelUtil
+                    .asActionLinksForAdditionalLinksPanel(model, topLevelActions, null)
+                    .collect(Can.toCan());
+
+            AdditionalLinksPanel
+                    .addAdditionalLinks(this, ID_ENTITY_ACTIONS, entityActionLinks,
+                            AdditionalLinksPanel.Style.INLINE_LIST);
         } else {
             permanentlyHide(ID_ENTITY_ACTIONS);
         }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
index e2e2f8c..2162081 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/col/Col.java
@@ -33,6 +33,7 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3Col;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
@@ -53,8 +54,8 @@ import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 
 import lombok.val;
 
-public class Col 
-extends PanelAbstract<ManagedObject, EntityModel> 
+public class Col
+extends PanelAbstract<ManagedObject, EntityModel>
 implements HasDynamicallyVisibleContent {
 
     private static final long serialVersionUID = 1L;
@@ -122,15 +123,16 @@ implements HasDynamicallyVisibleContent {
 
         // actions
         // (rendering depends on whether also showing the icon/title)
-        final List<ActionLayoutData> actionLayoutDatas = bs3Col.getActions();
-        val visibleActions = _NullSafe.stream(actionLayoutDatas)
-                .filter(actionLayoutData -> actionLayoutData.getMetadataError() == null)
-                .filter(_NullSafe::isPresent)
-                .map(actionLayoutData -> 
-                    getModel().getTypeOfSpecification().getAction(actionLayoutData.getId()).orElse(null)
-                )
-                .filter(_NullSafe::isPresent)
-                .collect(Collectors.toList());
+        final List<ActionLayoutData> actionLayoutDataList = bs3Col.getActions();
+
+        val visibleActions = _NullSafe.stream(actionLayoutDataList)
+        .filter(actionLayoutData -> actionLayoutData.getMetadataError() == null)
+        .filter(_NullSafe::isPresent)
+        .map(actionLayoutData ->
+            getModel().getTypeOfSpecification().getAction(actionLayoutData.getId()).orElse(null)
+        )
+        .filter(_NullSafe::isPresent);
+
         //
         // visibility needs to be determined at point of rendering, by ActionLink itself
         //
@@ -142,8 +144,9 @@ implements HasDynamicallyVisibleContent {
         //    }
         //})
 
-        final List<LinkAndLabel> entityActionLinks =
-                LinkAndLabelUtil.asActionLinksForAdditionalLinksPanel(getModel(), visibleActions, null);
+        final Can<LinkAndLabel> entityActionLinks = LinkAndLabelUtil
+        .asActionLinksForAdditionalLinksPanel(getModel(), visibleActions, null)
+        .collect(Can.toCan());
 
         if (!entityActionLinks.isEmpty()) {
             AdditionalLinksPanel.addAdditionalLinks(actionOwner, actionIdToUse, entityActionLinks, AdditionalLinksPanel.Style.INLINE_LIST);
@@ -172,12 +175,12 @@ implements HasDynamicallyVisibleContent {
         final List<BS3TabGroup> tabGroupsWithNonEmptyTabs =
                 _NullSafe.stream(bs3Col.getTabGroups())
                 .filter(_NullSafe::isPresent)
-                .filter(bs3TabGroup -> 
+                .filter(bs3TabGroup ->
                         _NullSafe.stream(bs3TabGroup.getTabs())
                                 .anyMatch(BS3Tab.Predicates.notEmpty())
                 )
                 .collect(Collectors.toList());
-        
+
         if(!tabGroupsWithNonEmptyTabs.isEmpty()) {
             final RepeatingViewWithDynamicallyVisibleContent tabGroupRv =
                     new RepeatingViewWithDynamicallyVisibleContent(ID_TAB_GROUPS);
@@ -220,12 +223,12 @@ implements HasDynamicallyVisibleContent {
 
 
         // fieldsets
-        final List<FieldSet> fieldSetsWithProperties = 
+        final List<FieldSet> fieldSetsWithProperties =
                 _NullSafe.stream(bs3Col.getFieldSets())
                 .filter(_NullSafe::isPresent)
                 .filter(fieldSet -> ! _NullSafe.isEmpty(fieldSet.getProperties()))
                 .collect(Collectors.toList());
-        
+
         if(!fieldSetsWithProperties.isEmpty()) {
             final RepeatingViewWithDynamicallyVisibleContent fieldSetRv =
                     new RepeatingViewWithDynamicallyVisibleContent(ID_FIELD_SETS);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
index 15cc4ed..411bc45 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
@@ -42,6 +42,8 @@ import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.services.metamodel.BeanSort;
 import org.apache.isis.applib.services.metamodel.MetaModelService;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Refs;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.debug._Probe;
@@ -82,8 +84,8 @@ import lombok.val;
 import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
 
 
-public abstract class ScalarPanelAbstract 
-extends PanelAbstract<ManagedObject, ScalarModel> 
+public abstract class ScalarPanelAbstract
+extends PanelAbstract<ManagedObject, ScalarModel>
 implements ScalarModelSubscriber {
 
     private static final long serialVersionUID = 1L;
@@ -135,10 +137,10 @@ implements ScalarModelSubscriber {
                 onInitializeEditable();
             } else {
                 onInitializeReadonly(usabilityConsent.getReason());
-            }            
-        }; 
+            }
+        };
     }
-    
+
     /**
      *
      * @param argsAndConsents - the action being invoked
@@ -149,9 +151,9 @@ implements ScalarModelSubscriber {
     public Repaint updateIfNecessary(
             @NonNull final FormPendingParamUiModel argsAndConsents,
             @NonNull final Optional<AjaxRequestTarget> target) {
-        
+
         val argModel = argsAndConsents.getParamModel();
-        
+
         // visibility
         val visibilityConsent = argsAndConsents.getVisibilityConsent();
         val visibilityBefore = isVisible();
@@ -169,8 +171,8 @@ implements ScalarModelSubscriber {
         }
 
         val paramValue = argModel.getValue();
-        val valueChanged = !Objects.equals(scalarModel.getObject(), paramValue); 
-        
+        val valueChanged = !Objects.equals(scalarModel.getObject(), paramValue);
+
         if(valueChanged) {
             if(ManagedObjects.isNullOrUnspecifiedOrEmpty(paramValue)) {
                 scalarModel.setObject(null);
@@ -179,7 +181,7 @@ implements ScalarModelSubscriber {
             }
             scalarModel.clearPending();
         }
-        
+
 
         // repaint the entire form if visibility has changed
         if (!visibilityBefore || !visibilityAfter) {
@@ -274,7 +276,7 @@ implements ScalarModelSubscriber {
             postInit.run();
             postInit=null;
         } else {
-        
+
         final String disableReasonIfAny = scalarModel.whetherDisabled();
         final boolean mustBeEditable = scalarModel.mustBeEditable();
         if (disableReasonIfAny != null) {
@@ -286,7 +288,7 @@ implements ScalarModelSubscriber {
         } else {
             if (scalarModel.isViewMode()) {
                 onInitializeNotEditable();
-            } else {        
+            } else {
                 onInitializeEditable();
             }
         }
@@ -344,13 +346,15 @@ implements ScalarModelSubscriber {
 
         // find associated actions for this scalar property (only properties will have any.)
         final ScalarModel.AssociatedActions associatedActions =
-                scalarModel.getAssociatedActions(); 
+                scalarModel.getAssociatedActions();
         final ObjectAction inlineActionIfAny =
                 associatedActions.getFirstAssociatedWithInlineAsIfEdit();
-        final List<ObjectAction> remainingAssociated = associatedActions.getRemainingAssociated();
+        val remainingAssociated = associatedActions.getRemainingAssociated();
 
         // convert those actions into UI layer widgets
-        final List<LinkAndLabel> linkAndLabels  = LinkAndLabelUtil.asActionLinks(this.scalarModel, remainingAssociated);
+        final Can<LinkAndLabel> linkAndLabels  = LinkAndLabelUtil
+                .asActionLinks(this.scalarModel, remainingAssociated.stream())
+                .collect(Can.toCan());
 
         final InlinePromptConfig inlinePromptConfig = getInlinePromptConfig();
         if(inlinePromptConfig.isSupported()) {
@@ -369,38 +373,45 @@ implements ScalarModelSubscriber {
 
             // start off assuming that neither the property nor any of the associated actions
             // are using inline prompts
-            Component componentToHideIfAny = inlinePromptLink;
+
+            val componentToHideRef = _Refs.<Component>objectRef(inlinePromptLink);
+            //Component componentToHideIfAny = inlinePromptLink;
 
             if (this.scalarModel.getPromptStyle().isInline() && scalarModel.canEnterEditMode()) {
                 // we configure the prompt link if _this_ property is configured for inline edits...
                 configureInlinePromptLinkCallback(inlinePromptLink);
-                componentToHideIfAny = inlinePromptConfig.getComponentToHideIfAny();
+                componentToHideRef.setValue(inlinePromptConfig.getComponentToHideIfAny());
 
             } else {
 
                 // not editable property, but maybe one of the actions is.
                 if(inlineActionIfAny != null) {
 
-                    final LinkAndLabel linkAndLabelAsIfEdit = LinkAndLabelUtil.asActionLink(this.scalarModel, inlineActionIfAny);
-                    final ActionLink actionLinkInlineAsIfEdit = (ActionLink) linkAndLabelAsIfEdit.getUiComponent();
+                    LinkAndLabelUtil.asActionLink(this.scalarModel, inlineActionIfAny)
+                    .findFirst()
+                    .map(LinkAndLabel::getUiComponent)
+                    .map(ActionLink.class::cast)
+                    .ifPresent(actionLinkInlineAsIfEdit->{
+
+                        if(actionLinkInlineAsIfEdit.isVisible() && actionLinkInlineAsIfEdit.isEnabled()) {
+                            configureInlinePromptLinkCallback(inlinePromptLink, actionLinkInlineAsIfEdit);
+                            componentToHideRef.setValue(inlinePromptConfig.getComponentToHideIfAny());
+                        }
+
+                    });
 
-                    if(actionLinkInlineAsIfEdit.isVisible() && actionLinkInlineAsIfEdit.isEnabled()) {
-                        configureInlinePromptLinkCallback(inlinePromptLink, actionLinkInlineAsIfEdit);
-                        componentToHideIfAny = inlinePromptConfig.getComponentToHideIfAny();
-                    }
                 }
             }
 
+            componentToHideRef.getValue()
+            .ifPresent(componentToHide->componentToHide.setVisibilityAllowed(false));
 
-            if(componentToHideIfAny != null) {
-                componentToHideIfAny.setVisibilityAllowed(false);
-            }
         }
 
         // prevent from tabbing into non-editable widgets.
-        if(scalarModel.isProperty() 
+        if(scalarModel.isProperty()
                 && scalarModel.getMode() == EntityModel.Mode.VIEW
-                && (scalarModel.getPromptStyle().isDialog() 
+                && (scalarModel.getPromptStyle().isDialog()
                         || !scalarModel.canEnterEditMode())) {
             getScalarValueComponent().add(new AttributeAppender("tabindex", "-1"));
         }
@@ -508,11 +519,11 @@ implements ScalarModelSubscriber {
 
         @Override
         protected void onUpdate(AjaxRequestTarget target) {
-            
+
             _Probe.entryPoint(EntryPoint.USER_INTERACTION, "Wicket Ajax Request, "
                     + "originating from User either having changed a Property value during inline editing "
                     + "or having changed a Parameter value within an open ActionPrompt.");
-            
+
             for (ScalarModelSubscriber subscriber : scalarPanel.subscribers) {
                 subscriber.onUpdate(target, scalarPanel);
             }
@@ -732,7 +743,7 @@ implements ScalarModelSubscriber {
 
             @Override
             protected void onEvent(final AjaxRequestTarget target) {
-                
+
                 _Probe.entryPoint(EntryPoint.USER_INTERACTION, "Wicket Ajax Request, "
                         + "originating from User clicking on an editable Property to start inline editing.");
 
@@ -858,11 +869,14 @@ implements ScalarModelSubscriber {
 
     private void addActionLinksBelowAndRight(
             final MarkupContainer labelIfRegular,
-            final List<LinkAndLabel> linkAndLabels) {
-        final List<LinkAndLabel> linksBelow = LinkAndLabel.positioned(linkAndLabels, ActionLayout.Position.BELOW);
+            final Can<LinkAndLabel> linkAndLabels) {
+
+        val linksBelow = linkAndLabels
+                .filter(LinkAndLabel.positioned(ActionLayout.Position.BELOW));
         AdditionalLinksPanel.addAdditionalLinks(labelIfRegular, ID_ASSOCIATED_ACTION_LINKS_BELOW, linksBelow, AdditionalLinksPanel.Style.INLINE_LIST);
 
-        final List<LinkAndLabel> linksRight = LinkAndLabel.positioned(linkAndLabels, ActionLayout.Position.RIGHT);
+        val linksRight = linkAndLabels
+                .filter(LinkAndLabel.positioned(ActionLayout.Position.RIGHT));
         AdditionalLinksPanel.addAdditionalLinks(labelIfRegular, ID_ASSOCIATED_ACTION_LINKS_RIGHT, linksRight, AdditionalLinksPanel.Style.DROPDOWN);
     }
 
@@ -876,7 +890,7 @@ implements ScalarModelSubscriber {
      */
     private void addPositioningCssTo(
             final MarkupContainer markupContainer,
-            final List<LinkAndLabel> actionLinks) {
+            final Can<LinkAndLabel> actionLinks) {
         CssClassAppender.appendCssClassTo(markupContainer, determinePropParamLayoutCss(getModel()));
         CssClassAppender.appendCssClassTo(markupContainer, determineActionLayoutPositioningCss(actionLinks));
     }
@@ -902,12 +916,12 @@ implements ScalarModelSubscriber {
         return "label-left";
     }
 
-    private static String determineActionLayoutPositioningCss(List<LinkAndLabel> entityActionLinks) {
+    private static String determineActionLayoutPositioningCss(Can<LinkAndLabel> entityActionLinks) {
         boolean actionsPositionedOnRight = hasActionsPositionedOn(entityActionLinks, ActionLayout.Position.RIGHT);
         return actionsPositionedOnRight ? "actions-right" : null;
     }
 
-    private static boolean hasActionsPositionedOn(final List<LinkAndLabel> entityActionLinks, final ActionLayout.Position position) {
+    private static boolean hasActionsPositionedOn(final Can<LinkAndLabel> entityActionLinks, final ActionLayout.Position position) {
         for (LinkAndLabel entityActionLink : entityActionLinks) {
             if(entityActionLink.getActionUiMetaModel().getPosition() == position) {
                 return true;
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
index 2dbe942..8be7752 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
@@ -30,6 +30,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.viewer.wicket.model.models.ActionModel;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelStandalone;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry;
@@ -41,7 +42,7 @@ import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 import org.apache.isis.viewer.wicket.ui.util.Components;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 
-public class StandaloneCollectionPanel 
+public class StandaloneCollectionPanel
 extends PanelAbstract<List<ManagedObject>, EntityCollectionModel>
 implements CollectionCountProvider, CollectionSelectorProvider {
 
@@ -57,24 +58,24 @@ implements CollectionCountProvider, CollectionSelectorProvider {
 
     private MarkupContainer outerDiv = this;
 
-    public StandaloneCollectionPanel(final String id, final EntityCollectionModel entityCollectionModel) {
-        super(id, entityCollectionModel);
+    public StandaloneCollectionPanel(final String id, final EntityCollectionModelStandalone collectionModel) {
+        super(id, collectionModel);
 
         outerDiv = new WebMarkupContainer(ID_STANDALONE_COLLECTION);
 
         addOrReplace(outerDiv);
 
-        ActionModel actionModel = entityCollectionModel.getActionModelHint();
+        ActionModel actionModel = collectionModel.getActionModel();
         ObjectAction action = actionModel.getMetaModel();
         outerDiv.addOrReplace(new Label(StandaloneCollectionPanel.ID_ACTION_NAME, Model.of(action.getName())));
 
         CssClassAppender.appendCssClassTo(outerDiv,
                 CssClassAppender.asCssStyle("isis-" + action.getOnType().getLogicalTypeName().replace('.', '-') + "-" + action.getId()));
         CssClassAppender.appendCssClassTo(outerDiv,
-                CssClassAppender.asCssStyle("isis-" + entityCollectionModel.getTypeOfSpecification().getLogicalTypeName().replace('.','-')));
+                CssClassAppender.asCssStyle("isis-" + collectionModel.getTypeOfSpecification().getLogicalTypeName().replace('.','-')));
 
         // selector
-        final CollectionSelectorHelper selectorHelper = new CollectionSelectorHelper(entityCollectionModel, getComponentFactoryRegistry());
+        final CollectionSelectorHelper selectorHelper = new CollectionSelectorHelper(collectionModel, getComponentFactoryRegistry());
 
         final List<ComponentFactory> componentFactories = selectorHelper.getComponentFactories();
 
@@ -82,7 +83,7 @@ implements CollectionCountProvider, CollectionSelectorProvider {
             Components.permanentlyHide(outerDiv, ID_SELECTOR_DROPDOWN);
             this.selectorDropdownPanel = null;
         } else {
-            CollectionSelectorPanel selectorDropdownPanel = new CollectionSelectorPanel(ID_SELECTOR_DROPDOWN, entityCollectionModel);
+            CollectionSelectorPanel selectorDropdownPanel = new CollectionSelectorPanel(ID_SELECTOR_DROPDOWN, collectionModel);
 
             final Model<ComponentFactory> componentFactoryModel = new Model<>();
 
@@ -98,7 +99,7 @@ implements CollectionCountProvider, CollectionSelectorProvider {
         }
 
         final ComponentFactoryRegistry componentFactoryRegistry = getComponentFactoryRegistry();
-        componentFactoryRegistry.addOrReplaceComponent(outerDiv, ComponentType.COLLECTION_CONTENTS, entityCollectionModel);
+        componentFactoryRegistry.addOrReplaceComponent(outerDiv, ComponentType.COLLECTION_CONTENTS, collectionModel);
     }
 
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanelFactory.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanelFactory.java
index 080f6e4..5c5bd29 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanelFactory.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanelFactory.java
@@ -22,11 +22,13 @@ package org.apache.isis.viewer.wicket.ui.components.standalonecollection;
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelStandalone;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentFactoryAbstract;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 
+import lombok.val;
+
 /**
  * {@link ComponentFactory} for {@link StandaloneCollectionPanel}.
  */
@@ -40,13 +42,13 @@ public class StandaloneCollectionPanelFactory extends ComponentFactoryAbstract {
 
     @Override
     public ApplicationAdvice appliesTo(final IModel<?> model) {
-        return appliesIf(model instanceof EntityCollectionModel);
+        return appliesIf(model instanceof EntityCollectionModelStandalone);
     }
 
     @Override
     public Component createComponent(final String id, final IModel<?> model) {
-        final EntityCollectionModel actionModel = (EntityCollectionModel) model;
-        return new StandaloneCollectionPanel(id, actionModel);
+        val collectionModel = (EntityCollectionModelStandalone) model;
+        return new StandaloneCollectionPanel(id, collectionModel);
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java
index b6c027e..01a8406 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java
@@ -23,8 +23,7 @@ import org.apache.wicket.Component;
 import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
 
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
-import org.apache.isis.viewer.wicket.model.models.ActionModel;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelStandalone;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
 
@@ -39,18 +38,13 @@ public class StandaloneCollectionPage extends PageAbstract {
     /**
      * For use with {@link Component#setResponsePage(org.apache.wicket.request.component.IRequestablePage)}
      */
-    public StandaloneCollectionPage(final EntityCollectionModel model) {
-        super(PageParametersUtils.newPageParameters(), actionNameFrom(model), ComponentType.STANDALONE_COLLECTION);
-        addChildComponents(themeDiv, model);
+    public StandaloneCollectionPage(final EntityCollectionModelStandalone collectionModel) {
+        super(PageParametersUtils.newPageParameters(),
+                collectionModel.getActionModel().getMetaModel().getName(),
+                ComponentType.STANDALONE_COLLECTION);
 
+        addChildComponents(themeDiv, collectionModel);
         addBookmarkedPages(themeDiv);
     }
 
-    private static String actionNameFrom(final EntityCollectionModel model) {
-        ActionModel actionModel = model.getActionModelHint();
-        if(actionModel != null) {
-            return actionModel.getMetaModel().getName();
-        }
-        return "Results"; // fallback, probably not required because hint should always exist on the model.
-    }
 }