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/04 19:08:58 UTC

[isis] branch master updated: ISIS-2641: MM: fix abstract type detection

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 35298a3  ISIS-2641: MM: fix abstract type detection
35298a3 is described below

commit 35298a360bb839d12eb18b4f28b6506b417c01a6
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue May 4 21:07:57 2021 +0200

    ISIS-2641: MM: fix abstract type detection
    
    also adding tests
---
 .../config/beans/IsisBeanTypeClassifierImpl.java   | 35 ++++++-----
 .../specloader/specimpl/FacetedMethodsBuilder.java |  6 --
 .../DomainModelTest_usingGoodDomain.java           | 69 ++++++++++++++++++----
 ...TypeInterface.java => ElementTypeAbstract.java} | 21 ++++++-
 .../testdomain/model/good/ElementTypeConcrete.java |  5 +-
 .../model/good/ElementTypeInterface.java           | 18 +++++-
 .../testdomain/model/good/ProperElementTypeVm.java |  9 +++
 7 files changed, 127 insertions(+), 36 deletions(-)

diff --git a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java
index acf91ff..4e2e72e 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/beans/IsisBeanTypeClassifierImpl.java
@@ -55,10 +55,30 @@ implements IsisBeanTypeClassifier {
     @Override
     public BeanClassification classify(final @NonNull Class<?> type) {
 
+        // handle arbitrary types ...
+        
         if(type.isPrimitive()) {
             return BeanClassification.delegated(BeanSort.VALUE);
         }
         
+        if(Collection.class.isAssignableFrom(type)
+                || Can.class.isAssignableFrom(type)
+                || type.isArray()) {
+            return BeanClassification.selfManaged(BeanSort.COLLECTION);
+        }
+
+        if(type.isInterface()
+                // modifier predicate must be called after testing for non-scalar type above, 
+                // otherwise we'd get false positives
+                || Modifier.isAbstract(type.getModifiers())) {
+            
+            // apiNote: abstract types and interfaces cannot be vetoed 
+            // and should also never be identified as ENTITY, VIEWMODEL or MIXIN
+            return BeanClassification.delegated(BeanSort.ABSTRACT);
+        }
+
+        // handle actual bean types ...
+        
         if(findNearestAnnotation(type, Vetoed.class).isPresent()
                 || findNearestAnnotation(type, Programmatic.class).isPresent()) {
             return BeanClassification.selfManaged(BeanSort.VETOED); // reject
@@ -71,7 +91,7 @@ implements IsisBeanTypeClassifier {
                 && !profiles.stream().anyMatch(this::isProfileActive)) {
             return BeanClassification.selfManaged(BeanSort.VETOED); // reject
         }
-
+        
         val aDomainService = findNearestAnnotation(type, DomainService.class);
         if(aDomainService.isPresent()) {
             return BeanClassification
@@ -142,19 +162,6 @@ implements IsisBeanTypeClassifier {
             return BeanClassification.delegated(BeanSort.MANAGED_BEAN_NOT_CONTRIBUTING);
         }
         
-        if(Collection.class.isAssignableFrom(type)
-                || Can.class.isAssignableFrom(type)
-                || type.isArray()) {
-            return BeanClassification.selfManaged(BeanSort.COLLECTION);
-        }
-
-        if(type.isInterface()
-                // modifier predicate must be called after testing for non-scalar type above, 
-                // otherwise we'd get false positives
-                || Modifier.isAbstract(type.getModifiers())) {
-            return BeanClassification.delegated(BeanSort.ABSTRACT);
-        }
-        
         if(Serializable.class.isAssignableFrom(type)) {
             return BeanClassification.delegated(BeanSort.VALUE);
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
index 34a63d7..f5959e4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
@@ -38,7 +38,6 @@ import org.apache.isis.applib.exceptions.unrecoverable.MetaModelException;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Sets;
-import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.commons.internal.reflection._Annotations;
 import org.apache.isis.core.metamodel.commons.CanBeVoid;
 import org.apache.isis.core.metamodel.commons.MethodUtil;
@@ -229,11 +228,6 @@ public class FacetedMethodsBuilder {
         
         TypeExtractor.streamMethodReturn(associationCandidateMethods)
         .filter(typeToLoad->typeToLoad!=introspectedClass)
-        .peek(typeToLoad->{
-            if(getClassName().contains("ProperElementTypeVm")) {
-                _Probe.errOut("load %s", typeToLoad);
-            }
-        })
         .forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, IntrospectionState.TYPE_INTROSPECTED));
 
         // now create FacetedMethods for collections and for properties
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
index 34934b1..bcabb68 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
@@ -45,12 +45,14 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.members.publish.execution.ExecutionPublishingFacet;
 import org.apache.isis.core.metamodel.facets.object.icon.IconFacet;
 import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.MixedIn;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState;
 import org.apache.isis.schema.metamodel.v2.DomainClassDto;
 import org.apache.isis.testdomain.conf.Configuration_headless;
 import org.apache.isis.testdomain.model.good.Configuration_usingValidDomain;
+import org.apache.isis.testdomain.model.good.ElementTypeAbstract;
 import org.apache.isis.testdomain.model.good.ElementTypeConcrete;
 import org.apache.isis.testdomain.model.good.ElementTypeInterface;
 import org.apache.isis.testdomain.model.good.ProperElementTypeVm;
@@ -278,7 +280,9 @@ class DomainModelTest_usingGoodDomain {
     }
     
     @Test
-    void elementTypes_shouldBeIntrospected_whenNotConcrete() {
+    void elementTypes_shouldBeIntrospected_whenDiscoveredViaGenerics_usingNoWildcards() {
+        
+        // when using generic type (no wild-cards)
         
         val vmSpec = specificationLoader.loadSpecification(ProperElementTypeVm.class,
                 IntrospectionState.FULLY_INTROSPECTED);
@@ -288,32 +292,75 @@ class DomainModelTest_usingGoodDomain {
         
         assertEquals(ElementTypeConcrete.class, concreteCollSpec.getCorrespondingClass());
         assertEquals(BeanSort.VIEW_MODEL, concreteCollSpec.getBeanSort());
+        assertHasAction(concreteCollSpec, "abstractAction");
+        assertHasAction(concreteCollSpec, "interfaceAction");
+        assertHasProperty(concreteCollSpec, "abstractProp");
+        assertHasProperty(concreteCollSpec, "interfaceProp");
         
         val interfaceColl = vmSpec.getCollectionElseFail("interfaceColl");
         val interfaceCollSpec = interfaceColl.getSpecification();
         
         assertEquals(ElementTypeInterface.class, interfaceCollSpec.getCorrespondingClass());
         assertEquals(BeanSort.ABSTRACT, interfaceCollSpec.getBeanSort());
+        assertHasAction(interfaceCollSpec, "interfaceAction");
+        assertHasProperty(interfaceCollSpec, "interfaceProp");
+        
+        val abstractColl = vmSpec.getCollectionElseFail("abstractColl");
+        val abstractCollSpec = abstractColl.getSpecification();
+        
+        assertEquals(ElementTypeAbstract.class, abstractCollSpec.getCorrespondingClass());
+        assertEquals(BeanSort.ABSTRACT, abstractCollSpec.getBeanSort());
+        assertHasAction(abstractCollSpec, "abstractAction");
+        assertHasProperty(abstractCollSpec, "abstractProp");
+        
+    }
+    
+    @Test
+    void elementTypes_shouldBeIntrospected_whenDiscoveredViaGenerics_usingWildcards() {
+        
+        // when using generic type (w/ wild-cards)
+        
+        val vmSpec = specificationLoader.loadSpecification(ProperElementTypeVm.class,
+                IntrospectionState.FULLY_INTROSPECTED);
         
-        // when using generic type wild-cards
+        val concreteColl = vmSpec.getCollectionElseFail("concreteColl2");
+        val concreteCollSpec = concreteColl.getSpecification();
         
-        val concreteColl2 = vmSpec.getCollectionElseFail("concreteColl2");
-        val concreteCollSpec2 = concreteColl2.getSpecification();
+        assertEquals(ElementTypeConcrete.class, concreteCollSpec.getCorrespondingClass());
+        assertEquals(BeanSort.VIEW_MODEL, concreteCollSpec.getBeanSort());
+        assertHasAction(concreteCollSpec, "abstractAction");
+        assertHasAction(concreteCollSpec, "interfaceAction");
+        assertHasProperty(concreteCollSpec, "abstractProp");
+        assertHasProperty(concreteCollSpec, "interfaceProp");
         
-        assertEquals(ElementTypeConcrete.class, concreteCollSpec2.getCorrespondingClass());
-        assertEquals(BeanSort.VIEW_MODEL, concreteCollSpec2.getBeanSort());
+        val interfaceColl = vmSpec.getCollectionElseFail("interfaceColl2");
+        val interfaceCollSpec = interfaceColl.getSpecification();
         
-        val interfaceColl2 = vmSpec.getCollectionElseFail("interfaceColl2");
-        val interfaceCollSpec2 = interfaceColl2.getSpecification();
+        assertEquals(ElementTypeInterface.class, interfaceCollSpec.getCorrespondingClass());
+        assertEquals(BeanSort.ABSTRACT, interfaceCollSpec.getBeanSort());
+        assertHasAction(interfaceCollSpec, "interfaceAction");
+        assertHasProperty(interfaceCollSpec, "interfaceProp");
         
-        assertEquals(ElementTypeInterface.class, interfaceCollSpec2.getCorrespondingClass());
-        assertEquals(BeanSort.ABSTRACT, interfaceCollSpec2.getBeanSort());
+        val abstractColl = vmSpec.getCollectionElseFail("abstractColl2");
+        val abstractCollSpec = abstractColl.getSpecification();
+        
+        assertEquals(ElementTypeAbstract.class, abstractCollSpec.getCorrespondingClass());
+        assertEquals(BeanSort.ABSTRACT, abstractCollSpec.getBeanSort());
+        assertHasAction(abstractCollSpec, "abstractAction");
+        assertHasProperty(abstractCollSpec, "abstractProp");
         
-        // TODO for the abstract case, we also want to see any members and the title-facet 
     }
 
     // -- HELPER
 
+    private void assertHasProperty(ObjectSpecification spec, String propertyId) {
+        spec.getPropertyElseFail(propertyId);
+    }
+
+    private void assertHasAction(ObjectSpecification spec, String actionId) {
+        spec.getActionElseFail(actionId);
+    }
+
     private void assertHasPublishedActionFacet(FacetHolder facetHolder) {
         val facet = facetHolder.getFacet(ExecutionPublishingFacet.class);
         assertNotNull(facet);
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeAbstract.java
similarity index 65%
copy from regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java
copy to regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeAbstract.java
index 8d0d788..9e588ad 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeAbstract.java
@@ -18,10 +18,25 @@
  */
 package org.apache.isis.testdomain.model.good;
 
-public interface ElementTypeInterface {
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.Property;
 
-    default String title() {
-        return "aTitle";
+import lombok.Getter;
+import lombok.Setter;
+
+public abstract class ElementTypeAbstract {
+
+    public String title() {
+        return "AbstractTitle";
     }
     
+    @Action
+    public void abstractAction(String p0, String p1) {
+        // no-op, just testing meta-data
+    }
+    
+    @Property
+    @Getter @Setter
+    private String abstractProp = "abstractProp";
+    
 }
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeConcrete.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeConcrete.java
index aebf26b..c0c558c 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeConcrete.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeConcrete.java
@@ -21,6 +21,9 @@ package org.apache.isis.testdomain.model.good;
 import org.apache.isis.applib.annotation.DomainObject;
 
 @DomainObject(objectType = "isis.testdomain.ElementTypeConcrete")
-public class ElementTypeConcrete implements ElementTypeInterface {
+public class ElementTypeConcrete 
+extends ElementTypeAbstract
+implements ElementTypeInterface {
+
 
 }
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java
index 8d0d788..8c612f8 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ElementTypeInterface.java
@@ -18,10 +18,26 @@
  */
 package org.apache.isis.testdomain.model.good;
 
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.Property;
+
 public interface ElementTypeInterface {
 
     default String title() {
-        return "aTitle";
+        return "InterfaceTitle";
+    }
+    
+    @Action
+    default void interfaceAction(String p0, String p1) {
+        // no-op, just testing meta-data
+    }
+    
+    @Property
+    default String getInterfaceProp() {
+        return "InterfaceProp";
+    }
+    default void setInterfaceProp(String prop) {
+        // no-op, just testing meta-data
     }
     
 }
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperElementTypeVm.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperElementTypeVm.java
index afd6990..e5db5aa 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperElementTypeVm.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperElementTypeVm.java
@@ -27,6 +27,9 @@ import org.apache.isis.applib.annotation.Nature;
 import lombok.Getter;
 import lombok.Setter;
 
+/**
+ * @see <a href="https://issues.apache.org/jira/browse/ISIS-2499">ISIS-2499</a>
+ */
 @DomainObject(nature = Nature.VIEW_MODEL)
 public class ProperElementTypeVm {
 
@@ -34,12 +37,18 @@ public class ProperElementTypeVm {
     @Getter @Setter private List<ElementTypeInterface> interfaceColl;
     
     @Collection
+    @Getter @Setter private List<ElementTypeAbstract> abstractColl;
+    
+    @Collection
     @Getter @Setter private List<ElementTypeConcrete> concreteColl;
     
     @Collection
     @Getter @Setter private List<? extends ElementTypeInterface> interfaceColl2;
     
     @Collection
+    @Getter @Setter private List<? extends ElementTypeAbstract> abstractColl2;
+    
+    @Collection
     @Getter @Setter private List<? extends ElementTypeConcrete> concreteColl2;
     
 }