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/07/05 11:14:35 UTC

[isis] branch master updated: ISIS-2787: prepare FacetRanking to also serve lower than winning rank

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d4a5884  ISIS-2787: prepare FacetRanking to also serve lower than winning rank
d4a5884 is described below

commit d4a5884d59430e5b870e02eee0f90748dd802f6b
Author: andi-huber <ah...@apache.org>
AuthorDate: Mon Jul 5 13:14:22 2021 +0200

    ISIS-2787: prepare FacetRanking to also serve lower than winning rank
---
 .../isis/applib/services/menu/MenuBarsService.java |  4 +-
 .../apache/isis/core/metamodel/facetapi/Facet.java | 18 ++++++
 .../isis/core/metamodel/facetapi/FacetRanking.java | 29 +++++++++-
 .../imperative/HasImperativeTextFacetAbstract.java |  2 +-
 ...ctFacetForRecreatableDomainObjectInterface.java |  3 +-
 .../specimpl/ObjectAssociationAbstract.java        | 25 +++++++--
 .../menubars/bootstrap3/MenuBarsServiceBS3.java    | 64 +++++++++++++---------
 7 files changed, 107 insertions(+), 38 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java b/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java
index e013781..8a2c3e3 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/menu/MenuBarsService.java
@@ -48,9 +48,9 @@ public interface MenuBarsService {
         DEFAULT,
 
         /**
-         * As derived from annotations.
+         * As derived from annotations only.
          */
-        FALLBACK
+        ANNOTATED
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/Facet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/Facet.java
index 8a35304a..2ccf02c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/Facet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/Facet.java
@@ -71,6 +71,11 @@ extends
 
         /**
          * Higher precedence than {@link #HIGH}. In other words, overrules {@link #HIGH}.
+         */
+        IMPERATIVE,
+
+        /**
+         * Higher precedence than {@link #IMPERATIVE}. In other words, overrules {@link #IMPERATIVE}.
          * <p>
          * Reserved for facet post-processing, when synthesizing a 'virtual' facet
          * from facets of lower rank. (eg. {@link ObjectNamedFacet} with its singular and plural noun-form support)
@@ -132,6 +137,16 @@ extends
     }
 
     /**
+     * Whether to collect all facets of this type into the facet ranks,
+     * or (when {@code false}) allow for (heap) optimization,
+     * such that only (current) top ranks are populated.
+     * @since 2.0
+     */
+    default boolean isPopulateAllFacetRanks() {
+        return false;
+    }
+
+    /**
      * Determines the type of this facet to be stored under.
      *
      * <p>
@@ -170,4 +185,7 @@ extends
      */
     void forEachContributedFacet(Consumer<Facet> onContributedFacet);
 
+
+
+
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java
index e2b88fd..f0eaf5c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetRanking.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.core.metamodel.facetapi;
 
+import java.util.Comparator;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
@@ -105,8 +106,12 @@ public final class FacetRanking {
 
         val changesTopRank = facetPreference.ordinal() >= currentTopRankOrdinal;
 
-        // as an optimization (heap), don't store to lower ranks, as these have no effect any way when picking a winning facet
-        if(changesTopRank) {
+        // As an optimization (heap), don't store to lower ranks,
+        // as these are not considered when picking a winning facet.
+        // However, there are use-cases, where access to all facets of a given type are required,
+        // regardless of facet-precedence (eg. MemberNamedFacets).
+        if(changesTopRank
+            || facet.isPopulateAllFacetRanks()) {
             facetsByPrecedence.putElement(facetPreference, facet);
         }
 
@@ -159,6 +164,26 @@ public final class FacetRanking {
                 : Can.empty();
     }
 
+    /**
+     * Returns a defensive copy of the selected rank of given precedence constraint.
+     * @param facetType - for convenience, so the caller does not need to cast the result
+     * @param precedenceUpper - upper bound
+     */
+    public <F extends Facet> Can<F> getRankLowerOrEqualTo(final @NonNull Class<F> facetType, Precedence precedenceUpper) {
+        _Assert.assertEquals(this.facetType, facetType);
+
+        val precedenceSelected = facetsByPrecedence
+        .keySet()
+        .stream()
+        .filter(precedence->precedence.ordinal()<=precedenceUpper.ordinal())
+        .max(Comparator.comparing(Precedence::ordinal));
+
+        return precedenceSelected
+        .map(facetsByPrecedence::get)
+        .map(facetsOfSameRank->Can.<F>ofCollection(_Casts.uncheckedCast(facetsOfSameRank)))
+        .orElseGet(Can::empty);
+    }
+
     public Optional<Facet.Precedence> getTopPrecedence() {
         return Optional.ofNullable(topPrecedenceRef.get());
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/imperative/HasImperativeTextFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/imperative/HasImperativeTextFacetAbstract.java
index 599ae1b..d50941f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/imperative/HasImperativeTextFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/imperative/HasImperativeTextFacetAbstract.java
@@ -46,7 +46,7 @@ implements
 
     protected HasImperativeTextFacetAbstract(
             final Class<? extends Facet> facetType,
-            final TranslationContext translationContext, //TranslationContext.forTranslationContextHolder(holder.getFeatureIdentifier());
+            final TranslationContext translationContext,
             final Method method,
             final FacetHolder holder) {
         // imperative takes precedence over any other (except for events)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/recreatable/RecreatableObjectFacetForRecreatableDomainObjectInterface.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/recreatable/RecreatableObjectFacetForRecreatableDomainObjectInterface.java
index 2cecf3f..59ecabf 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/recreatable/RecreatableObjectFacetForRecreatableDomainObjectInterface.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/recreatable/RecreatableObjectFacetForRecreatableDomainObjectInterface.java
@@ -23,7 +23,8 @@ import org.apache.isis.applib.RecreatableDomainObject;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.PostConstructMethodCache;
 
-public class RecreatableObjectFacetForRecreatableDomainObjectInterface extends RecreatableObjectFacetAbstract {
+public class RecreatableObjectFacetForRecreatableDomainObjectInterface
+extends RecreatableObjectFacetAbstract {
 
     public RecreatableObjectFacetForRecreatableDomainObjectInterface(
             final FacetHolder holder,
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstract.java
index 2d09b85..8cf581f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectAssociationAbstract.java
@@ -20,12 +20,15 @@
 package org.apache.isis.core.metamodel.specloader.specimpl;
 
 import org.apache.isis.applib.Identifier;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.isis.core.metamodel.facetapi.Facet.Precedence;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
-import org.apache.isis.core.metamodel.facets.all.described.CanonicalDescribedFacet;
-import org.apache.isis.core.metamodel.facets.all.named.CanonicalNamedFacet;
+import org.apache.isis.core.metamodel.facets.all.described.MemberDescribedFacet;
+import org.apache.isis.core.metamodel.facets.all.i8n.staatic.HasStaticText;
+import org.apache.isis.core.metamodel.facets.all.named.MemberNamedFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.facets.properties.choices.PropertyChoicesFacet;
@@ -92,8 +95,13 @@ implements ObjectAssociation {
 
     @Override
     public final String getCanonicalFriendlyName() {
-        return lookupFacet(CanonicalNamedFacet.class)
-        .map(CanonicalNamedFacet::translated)
+        return lookupFacet(MemberNamedFacet.class)
+        .flatMap(MemberNamedFacet::getSharedFacetRanking)
+        .map(facetRanking->facetRanking.getRankLowerOrEqualTo(MemberNamedFacet.class, Precedence.HIGH))
+        .flatMap(Can::getLast) // TODO ranking implementation detail
+        .map(MemberNamedFacet::getSpecialization)
+        .flatMap(specialization->specialization.left())
+        .map(HasStaticText::translated)
         //we have a facet-post-processor to ensure following code path is unreachable,
         // however, we keep it in support of JUnit testing
         .orElseGet(()->getFeatureIdentifier().getMemberNaturalName());
@@ -101,8 +109,13 @@ implements ObjectAssociation {
 
     @Override
     public final String getCanonicalDescription() {
-        return lookupFacet(CanonicalDescribedFacet.class)
-        .map(CanonicalDescribedFacet::translated)
+        return lookupFacet(MemberDescribedFacet.class)
+        .flatMap(MemberDescribedFacet::getSharedFacetRanking)
+        .map(facetRanking->facetRanking.getRankLowerOrEqualTo(MemberDescribedFacet.class, Precedence.HIGH))
+        .flatMap(Can::getLast) // TODO ranking implementation detail
+        .map(MemberDescribedFacet::getSpecialization)
+        .flatMap(specialization->specialization.left())
+        .map(HasStaticText::translated)
         .orElse(null);
     }
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
index 9de5cdb..43158cb 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/menubars/bootstrap3/MenuBarsServiceBS3.java
@@ -45,14 +45,17 @@ import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.menu.MenuBarsLoaderService;
 import org.apache.isis.applib.services.menu.MenuBarsService;
 import org.apache.isis.applib.services.message.MessageService;
-import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.core.config.environment.IsisSystemEnvironment;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.facetapi.Facet.Precedence;
 import org.apache.isis.core.metamodel.facets.actions.notinservicemenu.NotInServiceMenuFacet;
+import org.apache.isis.core.metamodel.facets.all.named.MemberNamedFacet;
 import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
 import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
 import org.apache.isis.core.metamodel.facets.object.domainservicelayout.DomainServiceLayoutFacet;
@@ -91,20 +94,22 @@ implements MenuBarsService {
     private final IsisSystemEnvironment isisSystemEnvironment;
     private final MetaModelContext metaModelContext;
 
+    private final _Lazy<BS3MenuBars> menuBarsFromAnnotationsOnly = _Lazy.threadSafe(this::menuBarsFromAnnotationsOnly);
+
     BS3MenuBars menuBars;
 
     @Override
     public BS3MenuBars menuBars(final Type type) {
 
-        val fallbackMenuBars = deriveMenuBarsFromMetaModelFacets();
+        val menuBarsFromAnnotationsOnly = this.menuBarsFromAnnotationsOnly.get();
 
-        if(type == Type.FALLBACK) {
-            return fallbackMenuBars;
+        if(type == Type.ANNOTATED) {
+            return menuBarsFromAnnotationsOnly;
         }
 
         // else load (and only fallback if nothing could be loaded)...
         if(menuBars == null || menuBarsLoaderService.supportsReloading()) {
-            this.menuBars = loadOrElse(fallbackMenuBars);
+            this.menuBars = loadOrElse(menuBarsFromAnnotationsOnly);
         }
 
         return menuBars;
@@ -112,22 +117,22 @@ implements MenuBarsService {
 
     // -- HELPER
 
-    private BS3MenuBars loadOrElse(final BS3MenuBars fallbackMenuBars) {
+    private BS3MenuBars loadOrElse(final BS3MenuBars menuBarsFromAnnotationsOnly) {
 
         val menuBars = Optional.ofNullable(menuBarsLoaderService.menuBars())
                 .map(this::addTnsAndSchemaLocation)
-                .orElse(fallbackMenuBars);
+                .orElse(menuBarsFromAnnotationsOnly);
 
         val unreferencedActionsMenu = validateAndGetUnreferencedActionMenu(menuBars);
         if (unreferencedActionsMenu == null) {
             // just use fallback
-            return fallbackMenuBars;
+            return menuBarsFromAnnotationsOnly;
         }
 
         // add in any missing actions from the fallback
         val referencedActionsByObjectTypeAndId = menuBars.getAllServiceActionsByObjectTypeAndId();
 
-        fallbackMenuBars.visit(BS3MenuBars.VisitorAdapter.visitingMenuSections(menuSection->{
+        menuBarsFromAnnotationsOnly.visit(BS3MenuBars.VisitorAdapter.visitingMenuSections(menuSection->{
 
             // created only if required to collect unreferenced actions
             // for this menuSection into a new section within the designated
@@ -202,16 +207,16 @@ implements MenuBarsService {
         return null;
     }
 
-    private BS3MenuBars deriveMenuBarsFromMetaModelFacets() {
+    private BS3MenuBars menuBarsFromAnnotationsOnly() {
         final BS3MenuBars menuBars = new BS3MenuBars();
 
-        final List<ManagedObject> visibleServiceAdapters = metaModelContext.streamServiceAdapters()
+        val visibleServiceAdapters = metaModelContext.streamServiceAdapters()
                 .filter(this::isVisibleAdapterForMenu)
-                .collect(Collectors.toList());
+                .collect(Can.toCan());
 
-        append(visibleServiceAdapters, menuBars.getPrimary(), DomainServiceLayout.MenuBar.PRIMARY);
-        append(visibleServiceAdapters, menuBars.getSecondary(), DomainServiceLayout.MenuBar.SECONDARY);
-        append(visibleServiceAdapters, menuBars.getTertiary(), DomainServiceLayout.MenuBar.TERTIARY);
+        appendFromAnnotationsOnly(visibleServiceAdapters, menuBars.getPrimary(), DomainServiceLayout.MenuBar.PRIMARY);
+        appendFromAnnotationsOnly(visibleServiceAdapters, menuBars.getSecondary(), DomainServiceLayout.MenuBar.SECONDARY);
+        appendFromAnnotationsOnly(visibleServiceAdapters, menuBars.getTertiary(), DomainServiceLayout.MenuBar.TERTIARY);
 
         menuBars.setTnsAndSchemaLocation(tnsAndSchemaLocation());
 
@@ -236,15 +241,15 @@ implements MenuBarsService {
     }
 
 
-    private void append(
-            final List<ManagedObject> serviceAdapters,
+    private void appendFromAnnotationsOnly(
+            final Can<ManagedObject> serviceAdapters,
             final BS3MenuBar menuBar,
             final DomainServiceLayout.MenuBar menuBarPos) {
 
         val serviceActions = _Lists.<ServiceAndAction>newArrayList();
 
         // cf ServiceActionsModel & ServiceActionUtil#buildMenu in Wicket viewer
-        _NullSafe.stream(serviceAdapters)
+        serviceAdapters.stream()
         .filter(with(menuBarPos))
         .forEach(serviceAdapter->{
 
@@ -259,11 +264,11 @@ implements MenuBarsService {
         // prune any service names that have no service actions
         serviceNamesInOrder.retainAll(serviceActionsByName.keySet());
 
-        List<BS3Menu> menus = buildMenuItems(serviceNamesInOrder, serviceActionsByName);
+        List<BS3Menu> menus = buildMenuItemsFromAnnotationsOnly(serviceNamesInOrder, serviceActionsByName);
         menuBar.getMenus().addAll(menus);
     }
 
-    private static List<BS3Menu> buildMenuItems(
+    private static List<BS3Menu> buildMenuItemsFromAnnotationsOnly(
             final Set<String> serviceNamesInOrder,
             final Map<String, List<ServiceAndAction>> serviceActionsByName) {
 
@@ -282,12 +287,19 @@ implements MenuBarsService {
                     menuSection = new BS3MenuSection();
                 }
 
-                ObjectAction objectAction = serviceAndAction.getObjectAction();
+                val objectAction = serviceAndAction.getObjectAction();
                 val service = serviceAndAction.getServiceAdapter();
-                final String logicalTypeName = serviceAndAction.getServiceAdapter().getSpecification().getLogicalTypeName();
-                ServiceActionLayoutData action = new ServiceActionLayoutData(logicalTypeName, objectAction.getId());
-                action.setNamed(objectAction.getFriendlyName(service.asProvider()));
-                menuSection.getServiceActions().add(action);
+                val logicalTypeName = serviceAndAction.getServiceAdapter().getSpecification().getLogicalTypeName();
+                val actionLayoutData = new ServiceActionLayoutData(logicalTypeName, objectAction.getId());
+
+                objectAction
+                .getFacetRanking(MemberNamedFacet.class)
+                .get()
+                .getRankLowerOrEqualTo(MemberNamedFacet.class, Precedence.DEFAULT)
+                .getLastOrFail();
+
+                actionLayoutData.setNamed(objectAction.getFriendlyName(service.asProvider()));
+                menuSection.getServiceActions().add(actionLayoutData);
             }
             if(!menuSection.getServiceActions().isEmpty()) {
                 menu.getSections().add(menuSection);
@@ -303,7 +315,7 @@ implements MenuBarsService {
      * straight copy from Wicket UI
      */
     private static Set<String> serviceNamesInOrder(
-            final List<ManagedObject> serviceAdapters,
+            final Can<ManagedObject> serviceAdapters,
             final List<ServiceAndAction> serviceActions) {
         final Set<String> serviceNameOrder = _Sets.newLinkedHashSet();