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 2022/01/21 19:03:57 UTC

[isis] branch master updated: ISIS-2944: fixes non-public member support detection when inherited without overriding

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 43d196c  ISIS-2944: fixes non-public member support detection when inherited without overriding
43d196c is described below

commit 43d196ca69f5e20ea05e13c527aedcd3347c28c7
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Jan 21 20:03:44 2022 +0100

    ISIS-2944: fixes non-public member support detection when inherited
    without overriding
    
    - reason type.getDeclaredMethods() cannot detect non overridden
    inherited methods
---
 .../commons/internal/reflection/_ClassCache.java   | 12 ++-
 .../internal/reflection/ClassCacheTest.java        | 99 ++++++++++++++++++++++
 .../DomainModelTest_usingGoodDomain.java           | 22 ++---
 .../model/good/ProperMemberSupportDiscovery.java   | 14 ++-
 4 files changed, 130 insertions(+), 17 deletions(-)

diff --git a/commons/src/main/java/org/apache/isis/commons/internal/reflection/_ClassCache.java b/commons/src/main/java/org/apache/isis/commons/internal/reflection/_ClassCache.java
index 3e30047..ef03c66 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/reflection/_ClassCache.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/reflection/_ClassCache.java
@@ -64,6 +64,13 @@ public final class _ClassCache implements AutoCloseable {
         return _Context.computeIfAbsent(_ClassCache.class, _ClassCache::new);
     }
 
+    /**
+     * JUnit support.
+     */
+    public static void invalidate() {
+        _Context.put(_ClassCache.class, new _ClassCache(), true);
+    }
+
     public void add(final Class<?> type) {
         inspectType(type);
     }
@@ -197,11 +204,12 @@ public final class _ClassCache implements AutoCloseable {
 
                 val publicConstr = type.getConstructors();
                 val declaredFields = type.getDeclaredFields();
-                val declaredMethods = type.getDeclaredMethods();
+                val declaredMethods = //type.getDeclaredMethods(); ... cannot detect non overridden inherited methods
+                        Can.ofStream(_Reflect.streamAllMethods(type, true));
 
                 val model = new ClassModel(
                         Can.ofArray(declaredFields),
-                        Can.ofArray(declaredMethods));
+                        declaredMethods);
 
                 for(val constr : publicConstr) {
                     model.publicConstructorsByKey.put(ConstructorKey.of(type, constr), constr);
diff --git a/commons/src/test/java/org/apache/isis/commons/internal/reflection/ClassCacheTest.java b/commons/src/test/java/org/apache/isis/commons/internal/reflection/ClassCacheTest.java
new file mode 100644
index 0000000..7281bd9
--- /dev/null
+++ b/commons/src/test/java/org/apache/isis/commons/internal/reflection/ClassCacheTest.java
@@ -0,0 +1,99 @@
+/*
+ *  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.commons.internal.reflection;
+
+import java.lang.reflect.Method;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._NullSafe;
+
+import lombok.val;
+
+class ClassCacheTest {
+
+    static abstract class AbstractBase {
+        void commonAction(){}
+    }
+
+    static class Concrete extends AbstractBase {
+        void specificAction(){}
+    }
+
+    static class ConcreteOverride extends AbstractBase {
+        @Override void commonAction(){}
+        void specificAction(){}
+    }
+
+    private _ClassCache classCache;
+
+    @BeforeEach
+    void setup() {
+        _ClassCache.invalidate();
+        classCache = _ClassCache.getInstance();
+    }
+
+    @Test
+    void inhertitedMethodWhenUsingReflectionUtility() {
+        val declaredMethods = Can.ofStream(
+                _NullSafe.stream(_Reflect.streamAllMethods(Concrete.class, true)));
+        assertContainsMethod(declaredMethods, "commonAction");
+        assertContainsMethod(declaredMethods, "specificAction");
+    }
+
+    @Test
+    void inhertitedMethod() {
+        val declaredMethods = Can.ofStream(
+                classCache.streamPublicOrDeclaredMethods(Concrete.class));
+        assertContainsMethod(declaredMethods, "commonAction");
+        assertContainsMethod(declaredMethods, "specificAction");
+    }
+
+    @Test
+    void inhertitedMethodWhenOverride() {
+        val declaredMethods = Can.ofStream(
+                classCache.streamPublicOrDeclaredMethods(ConcreteOverride.class));
+        assertContainsMethod(declaredMethods, "commonAction");
+        assertContainsMethod(declaredMethods, "specificAction");
+    }
+
+    // -- HELPER
+
+    private void assertContainsMethod(final Can<Method> declaredMethods, final String methodName) {
+
+        final long methodCount =
+            declaredMethods.stream()
+            .filter(m->m.getName().equals(methodName))
+            // using filter over peek here, because peek is unreliable with 'count()' terminal
+            .filter(m->{
+                assertNotNull(m);
+                return true;
+            })
+            .count();
+
+        assertTrue(methodCount>0);
+    }
+
+
+}
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 874b719..bc7ecee 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
@@ -580,7 +580,7 @@ class DomainModelTest_usingGoodDomain {
     @SuppressWarnings("unchecked")
     @ParameterizedTest
     @ValueSource(classes = {
-          //FIXME ProperMemberSupportDiscovery.WhenEncapsulationEnabled.class,
+            ProperMemberSupportDiscovery.WhenEncapsulationEnabled.class,
             ProperMemberSupportDiscovery.WhenAnnotationRequired.class,
             //FIXME ProperMemberSupportDiscovery.WhenAnnotationOptional.class
             })
@@ -650,8 +650,8 @@ class DomainModelTest_usingGoodDomain {
                     val actualChoices =
                             pendingArgsThen.getObservableParamChoices(0).getValue().map(ManagedObject::getPojo);
                     assertEquals(
-                            actualChoices,
-                            Can.of("my choice"));
+                            Can.of("my choice"),
+                            actualChoices);
 
                 });
 
@@ -666,8 +666,8 @@ class DomainModelTest_usingGoodDomain {
                     val actualChoices =
                             pendingArgsThen.getObservableParamChoices(1).getValue().map(ManagedObject::getPojo);
                     assertEquals(
-                            actualChoices,
-                            Can.of("my search arg=hello"));
+                            Can.of("my search arg=hello"),
+                            actualChoices);
 
                 });
 
@@ -679,14 +679,14 @@ class DomainModelTest_usingGoodDomain {
                 pendingArgsWhen->{},
                 pendingArgsThen->{
                     assertEquals(
-                            pendingArgsThen.getObservableParamValidation(0).getValue(),
-                            "my validation-0");
+                            "my validation-0",
+                            pendingArgsThen.getObservableParamValidation(0).getValue());
                     assertEquals(
-                            pendingArgsThen.getObservableParamValidation(1).getValue(),
-                            "my validation-1");
+                            "my validation-1",
+                            pendingArgsThen.getObservableParamValidation(1).getValue());
                     assertEquals(
-                            pendingArgsThen.validateParameterSetForAction().getReason(),
-                            "my validation");
+                            "my validation",
+                            pendingArgsThen.validateParameterSetForAction().getReason());
                 });
 
         // namedEmail(): String = "my email"
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupportDiscovery.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupportDiscovery.java
index 0bc149f..863518b 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupportDiscovery.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupportDiscovery.java
@@ -91,7 +91,8 @@ public class ProperMemberSupportDiscovery {
 
         // -- ACTION
 
-        protected abstract void placeOrder(String x, String y);
+        //FIXME should be allowed without annotation ...
+        //protected abstract void placeOrder(String x, String y);
 
         @MemberSupport protected String namedPlaceOrder() { return "my name"; }
         @MemberSupport protected String describedPlaceOrder() { return "my description"; }
@@ -110,9 +111,14 @@ public class ProperMemberSupportDiscovery {
         @MemberSupport protected java.util.Collection<String> autoComplete1PlaceOrder(
                 final String y,
                 @MinLength(3) final String search) {
-            return List.of("my search");
+            return List.of("my search arg=" + search);
         }
 
+        @MemberSupport protected  String validate0PlaceOrder(final String x) { return "my validation-0";}
+        @MemberSupport protected  String validate1PlaceOrder(final String y) { return "my validation-1";}
+        @MemberSupport protected  String validatePlaceOrder(final String x, final String y) { return "my validation";}
+
+
         // -- PROPERTY
 
         @MemberSupport protected String namedEmail() { return "my email";}
@@ -127,7 +133,7 @@ public class ProperMemberSupportDiscovery {
 
         // -- COLLECTION
 
-        @MemberSupport protected String namedOrders() { return "my oders"; }
+        @MemberSupport protected String namedOrders() { return "my orders"; }
         @MemberSupport protected String describedOrders() { return "my orders described"; }
         @MemberSupport protected boolean hideOrders() { return true;}
         @MemberSupport protected String disableOrders() { return "my orders disabled"; }
@@ -179,7 +185,7 @@ public class ProperMemberSupportDiscovery {
 
         // annotation required, otherwise not picked up as action
         @Action
-        @Override
+        //@Override
         protected void placeOrder(final String x, final String y) {
         }