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/06/26 06:18:21 UTC

[isis] branch master updated: ISIS-1720: add facet post-processor to synthesize a ObjectNamedFacetSynthesized, which in any case must provide both noun-forms of a domain-object

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 654835f  ISIS-1720: add facet post-processor to synthesize a ObjectNamedFacetSynthesized, which in any case must provide both noun-forms of a domain-object
654835f is described below

commit 654835f49d0341143a8561354da13b03145e3ca9
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sat Jun 26 08:18:12 2021 +0200

    ISIS-1720: add facet post-processor to synthesize a
    ObjectNamedFacetSynthesized, which in any case must provide both
    noun-forms of a domain-object
    
    with that we also introduced a new Facet.Precedence: SYNTHESIZED
---
 .../apache/isis/core/metamodel/facetapi/Facet.java |   9 ++
 .../metamodel/facets/all/i8n/noun/NounForms.java   |  11 +-
 .../facets/all/named/ObjectNamedFacetAbstract.java |  13 ++-
 .../all/named/ObjectNamedFacetSynthesized.java     |  21 ++++
 .../NamedFacetForDomainObjectLayoutAnnotation.java |   3 -
 .../NamedFacetForDomainObjectXml.java              |   3 -
 ...NamedFacetForDomainServiceLayoutAnnotation.java |  28 ++---
 .../all/i18n/SynthesizeObjectNaming.java           | 114 +++++++++++++++++++++
 .../dflt/ProgrammingModelFacetsJava8.java          |   4 +
 .../specimpl/ObjectSpecificationAbstract.java      |   4 +-
 10 files changed, 173 insertions(+), 37 deletions(-)

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 e4ed23a..8a35304a 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
@@ -25,6 +25,7 @@ import java.util.function.Consumer;
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionInvocationFacet;
+import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet;
 
 public interface Facet
 extends
@@ -69,6 +70,14 @@ extends
         HIGH,
 
         /**
+         * Higher precedence than {@link #HIGH}. In other words, overrules {@link #HIGH}.
+         * <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)
+         */
+        SYNTHESIZED,
+
+        /**
          * Highest precedence, with special behavior and restrictions:
          * <ul>
          * <li>
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/noun/NounForms.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/noun/NounForms.java
index d8dd489..48fba49 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/noun/NounForms.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/i8n/noun/NounForms.java
@@ -41,23 +41,19 @@ import lombok.val;
 @Value @Builder
 public class NounForms {
 
-    public static NounFormsBuilder preferredSingular(@Nullable final String singular) {
+    public static NounFormsBuilder builderSingular(@Nullable final String singular) {
         return NounForms.builder()
-                .preferredNounForm(NounForm.SINGULAR)
                 .singular(singular);
     }
 
-    public static NounFormsBuilder preferredPlural(@Nullable final String plural) {
+    public static NounFormsBuilder builderPlural(@Nullable final String plural) {
         return NounForms.builder()
-                .preferredNounForm(NounForm.PLURAL)
                 .plural(plural);
     }
 
     private final @Nullable String singular;
     private final @Nullable String plural;
 
-    private final @NonNull NounForm preferredNounForm;
-
     @Getter(lazy = true)
     final ImmutableEnumSet<NounForm> supportedNounForms = supportedNounForms();
 
@@ -98,8 +94,7 @@ public class NounForms {
             final TranslationContext context) {
 
         val builder = NounForms
-                .builder()
-                .preferredNounForm(preferredNounForm);
+                .builder();
 
         getSupportedNounForms()
         .forEach(nounForm->{
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetAbstract.java
index 72bc2f1..b53d86c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetAbstract.java
@@ -39,10 +39,21 @@ implements ObjectNamedFacet {
     protected ObjectNamedFacetAbstract(
             final NounForms nounForms,
             final FacetHolder holder) {
+        this(
+                nounForms,
+                holder,
+                Precedence.DEFAULT);
+    }
+
+    protected ObjectNamedFacetAbstract(
+            final NounForms nounForms,
+            final FacetHolder holder,
+            final Precedence precedence) {
         super(type(),
                 TranslationContext.forTranslationContextHolder(holder.getFeatureIdentifier()),
                 nounForms,
-                holder);
+                holder,
+                precedence);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java
new file mode 100644
index 0000000..d50fff9
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/all/named/ObjectNamedFacetSynthesized.java
@@ -0,0 +1,21 @@
+package org.apache.isis.core.metamodel.facets.all.named;
+
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForms;
+
+/**
+ * To be installed by facet-post-processing only,
+ * which in any case must provide both noun-forms (non-empty).
+ *
+ * @since 2.0
+ */
+public class ObjectNamedFacetSynthesized
+extends ObjectNamedFacetAbstract {
+
+    public ObjectNamedFacetSynthesized(
+            final NounForms nounForms,
+            final FacetHolder holder) {
+        super(nounForms, holder, Precedence.SYNTHESIZED);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectLayoutAnnotation.java
index a96f47d..4066caa 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectLayoutAnnotation.java
@@ -23,7 +23,6 @@ import java.util.Optional;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForm;
 import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForms;
 import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet;
 import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacetAbstract;
@@ -46,10 +45,8 @@ extends ObjectNamedFacetAbstract {
         val singular = _Strings.emptyToNull(domainObjectLayout.named());
         val plural = _Strings.emptyToNull(domainObjectLayout.plural());
 
-        //TODO[1720] if singular is not explicit (is empty), infer
         val nounForms = NounForms
                 .builder()
-                .preferredNounForm(singular!=null ? NounForm.SINGULAR : NounForm.PLURAL)
                 .singular(singular)
                 .plural(plural)
                 .build();
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectXml.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectXml.java
index 52deac4..561398a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectXml.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/NamedFacetForDomainObjectXml.java
@@ -24,7 +24,6 @@ import java.util.Optional;
 import org.apache.isis.applib.layout.component.DomainObjectLayoutData;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForm;
 import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForms;
 import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet;
 import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacetAbstract;
@@ -45,10 +44,8 @@ extends ObjectNamedFacetAbstract {
         val singular = _Strings.emptyToNull(domainObjectLayout.getNamed());
         val plural = _Strings.emptyToNull(domainObjectLayout.getPlural());
 
-        //TODO[1720] if singular is not explicit (is empty), infer
         val nounForms = NounForms
                 .builder()
-                .preferredNounForm(singular!=null ? NounForm.SINGULAR : NounForm.PLURAL)
                 .singular(singular)
                 .plural(plural)
                 .build();
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainservicelayout/NamedFacetForDomainServiceLayoutAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainservicelayout/NamedFacetForDomainServiceLayoutAnnotation.java
index a0a6d0f..a3b279c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainservicelayout/NamedFacetForDomainServiceLayoutAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainservicelayout/NamedFacetForDomainServiceLayoutAnnotation.java
@@ -18,7 +18,6 @@
  */
 package org.apache.isis.core.metamodel.facets.object.domainservicelayout;
 
-import java.util.Objects;
 import java.util.Optional;
 
 import org.apache.isis.applib.annotation.DomainServiceLayout;
@@ -28,8 +27,6 @@ import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForms;
 import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet;
 import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacetAbstract;
 
-import lombok.val;
-
 public class NamedFacetForDomainServiceLayoutAnnotation
 extends ObjectNamedFacetAbstract {
 
@@ -37,24 +34,15 @@ extends ObjectNamedFacetAbstract {
             final Optional<DomainServiceLayout> domainServiceLayoutIfAny,
             final FacetHolder facetHolder) {
 
-        val serviceNamed = domainServiceLayoutIfAny
+        return domainServiceLayoutIfAny
                 .map(DomainServiceLayout::named)
-                .map(_Strings::emptyToNull)
-                .filter(Objects::nonNull)
-                .orElse(null);
-
-        if(_Strings.isEmpty(serviceNamed)) {
-            return Optional.empty();
-        }
-
-        val nounForms = NounForms
-                .preferredSingular(serviceNamed)
-                .build();
-
-        return Optional.of(
-                new NamedFacetForDomainServiceLayoutAnnotation(
-                            nounForms,
-                            facetHolder));
+                .filter(_Strings::isNotEmpty)
+                .map(serviceNamed->NounForms
+                        .builderSingular(serviceNamed)
+                        .build())
+                .map(nounForms->new NamedFacetForDomainServiceLayoutAnnotation(
+                        nounForms,
+                        facetHolder));
     }
 
     private NamedFacetForDomainServiceLayoutAnnotation(
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNaming.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNaming.java
new file mode 100644
index 0000000..6c18cd6
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/all/i18n/SynthesizeObjectNaming.java
@@ -0,0 +1,114 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.metamodel.postprocessors.all.i18n;
+
+
+import javax.inject.Inject;
+
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.core.metamodel.commons.StringExtensions;
+import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForm;
+import org.apache.isis.core.metamodel.facets.all.i8n.noun.NounForms;
+import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet;
+import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacetSynthesized;
+import org.apache.isis.core.metamodel.postprocessors.ObjectSpecificationPostProcessorAbstract;
+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.ObjectActionParameter;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+import lombok.val;
+
+public class SynthesizeObjectNaming
+extends ObjectSpecificationPostProcessorAbstract {
+
+    @Inject
+    public SynthesizeObjectNaming(final MetaModelContext metaModelContext) {
+        super(metaModelContext);
+    }
+
+    @Override
+    protected void doPostProcess(final ObjectSpecification objectSpecification) {
+
+        val topRank = objectSpecification
+        .lookupFacet(ObjectNamedFacet.class)
+        .flatMap(Facet::getSharedFacetRanking)
+        .map(facetRanking->facetRanking.getTopRank(ObjectNamedFacet.class))
+        .orElse(Can.empty())
+        .reverse(); // historically last have higher precedence, so when reverted we can use findFirst logic
+
+        val singular = topRank
+                .stream()
+                .filter(objectNamedFacet->objectNamedFacet.getSupportedNounForms().contains(NounForm.SINGULAR))
+                .findFirst()
+                .map(ObjectNamedFacet::singular)
+                .filter(_Strings::isNotEmpty)
+                .orElseGet(()->getSingularFallbackNoun(objectSpecification));
+
+        val plural = topRank
+                .stream()
+                .filter(objectNamedFacet->objectNamedFacet.getSupportedNounForms().contains(NounForm.PLURAL))
+                .findFirst()
+                .map(ObjectNamedFacet::plural)
+                .filter(_Strings::isNotEmpty)
+                .orElseGet(()->getPluralFallbackNoun(singular));
+
+        FacetUtil.addFacet(
+                new ObjectNamedFacetSynthesized(
+                        NounForms.builder()
+                            .singular(singular)
+                            .plural(plural)
+                            .build(),
+                        objectSpecification)
+                );
+
+    }
+
+    @Override
+    protected void doPostProcess(final ObjectSpecification objectSpecification, final ObjectAction act) {
+    }
+
+    @Override
+    protected void doPostProcess(final ObjectSpecification objectSpecification, final ObjectAction objectAction, final ObjectActionParameter param) {
+    }
+
+    @Override
+    protected void doPostProcess(final ObjectSpecification objectSpecification, final OneToOneAssociation prop) {
+    }
+
+    @Override
+    protected void doPostProcess(final ObjectSpecification objectSpecification, final OneToManyAssociation coll) {
+    }
+
+    // -- HELEPR
+
+    private String getSingularFallbackNoun(final ObjectSpecification spec) {
+        return spec.getFeatureIdentifier().getClassNaturalName();
+    }
+
+    private String getPluralFallbackNoun(final String singular) {
+        return StringExtensions.asPluralName(singular);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
index b8b7fdb..6df91c9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava8.java
@@ -145,6 +145,7 @@ import org.apache.isis.core.metamodel.methods.MethodByClassMap;
 import org.apache.isis.core.metamodel.methods.OrphanedSupportingMethodValidator;
 import org.apache.isis.core.metamodel.postprocessors.DeriveMixinMembersPostProcessor;
 import org.apache.isis.core.metamodel.postprocessors.all.DeriveDescribedAsFromTypePostProcessor;
+import org.apache.isis.core.metamodel.postprocessors.all.i18n.SynthesizeObjectNaming;
 import org.apache.isis.core.metamodel.postprocessors.all.i18n.TranslationPostProcessor;
 import org.apache.isis.core.metamodel.postprocessors.allbutparam.authorization.AuthorizationFacetPostProcessor;
 import org.apache.isis.core.metamodel.postprocessors.collparam.DeriveCollectionParamDefaultsAndChoicesPostProcessor;
@@ -384,6 +385,9 @@ extends ProgrammingModelAbstract {
 
         // only after this point have any mixin members been resolved and are available on the ObjectSpecification.
 
+        // must run before Object nouns are used
+        addPostProcessor(PostProcessingOrder.A1_BUILTIN, new SynthesizeObjectNaming(mmc));
+
         addPostProcessor(PostProcessingOrder.A1_BUILTIN, new DeriveDescribedAsFromTypePostProcessor(mmc));
         addPostProcessor(PostProcessingOrder.A1_BUILTIN, new DeriveTypicalLengthFromTypePostProcessor(mmc));
         addPostProcessor(PostProcessingOrder.A1_BUILTIN, new DeriveDefaultFromTypePostProcessor(mmc));
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index dfbf254..b09e3f0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -486,14 +486,14 @@ implements ObjectSpecification {
     public String getSingularName() {
         return lookupFacet(ObjectNamedFacet.class)
             .flatMap(textFacet->textFacet.translated(NounForm.SINGULAR))
-            .orElseGet(this::getFullIdentifier);
+            .orElseGet(this::getFullIdentifier); // unexpected code reach, however keep for JUnit testing
     }
 
     @Override
     public String getPluralName() {
         return lookupFacet(ObjectNamedFacet.class)
             .flatMap(textFacet->textFacet.translated(NounForm.PLURAL))
-            .orElseGet(this::getFullIdentifier);
+            .orElseGet(this::getFullIdentifier); // unexpected code reach, however keep for JUnit testing
     }
 
     /**