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 07:15:00 UTC

[isis] branch master updated: ISIS-2642: introduce _Generics utility to consolidate type argument logic

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 a5c3884  ISIS-2642: introduce _Generics utility to consolidate type argument logic
a5c3884 is described below

commit a5c38849d27bac9e8fbaece883fda553e48798fb
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue May 4 09:14:43 2021 +0200

    ISIS-2642: introduce _Generics utility to consolidate type argument
    logic
---
 .../commons/internal/reflection/_Generics.java     | 153 +++++++++++++++++++++
 .../specloader/specimpl/FacetedMethodsBuilder.java |   6 +
 .../specloader/typeextract/TypeExtractor.java      |  45 +-----
 .../DomainModelTest_usingGoodDomain.java           |  19 ++-
 .../model/good/ElementTypeInterface.java           |   3 -
 .../testdomain/model/good/ProperElementTypeVm.java |   8 +-
 6 files changed, 186 insertions(+), 48 deletions(-)

diff --git a/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Generics.java b/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Generics.java
new file mode 100644
index 0000000..91813e3
--- /dev/null
+++ b/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Generics.java
@@ -0,0 +1,153 @@
+/*
+ *  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.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.commons.internal.functions._Predicates;
+
+import lombok.NonNull;
+import lombok.val;
+
+/**
+ * <h1>- internal use only -</h1>
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/>
+ * These may be changed or removed without notice!
+ * </p>
+ * @since 2.0
+ */
+public final class _Generics {
+
+    /**
+     * Visits given {@code genericType} for its actual type arguments
+     * and calls back {@code onTypeArgument} on each type argument found. 
+     * @param genericType
+     * @param onTypeArgument
+     */
+    public static void visitGenericTypeArgumentsOf(
+            final @NonNull Type genericType,
+            final @NonNull Consumer<Class<?>> onTypeArgument) {
+        visitGenericTypeArgumentsOf(genericType, _Predicates.alwaysTrue(), onTypeArgument);
+    }
+    
+    /**
+     * Visits given {@code genericTypes} for their actual type arguments
+     * and calls back {@code onTypeArgument} on each type argument found. 
+     * @param genericTypes
+     * @param onTypeArgument
+     */
+    public static void visitGenericTypeArgumentsOf(
+            final @Nullable Type[] genericTypes,
+            final @NonNull Consumer<Class<?>> onTypeArgument) {
+        visitGenericTypeArgumentsOf(genericTypes, _Predicates.alwaysTrue(), onTypeArgument);
+    }
+    
+    /**
+     * Visits given {@code genericType} for its actual type arguments
+     * and calls back {@code onTypeArgument} on each type argument found, that passes
+     * given {@code typeArgumentFilter}. 
+     * @param genericType
+     * @param typeArgumentFilter
+     * @param onTypeArgument
+     */
+    public static void visitGenericTypeArgumentsOf(
+            final @NonNull Type genericType,
+            final @NonNull Predicate<Class<?>> typeArgumentFilter,
+            final @NonNull Consumer<Class<?>> onTypeArgument) {
+        
+        if (genericType instanceof ParameterizedType) {
+            for (val type : ((ParameterizedType) genericType).getActualTypeArguments()) {
+                visitTypeArgument((Class<?>) type, typeArgumentFilter, onTypeArgument);
+            }
+        }
+    }
+    
+    /**
+     * Visits given {@code genericTypes} for their actual type arguments
+     * and calls back {@code onTypeArgument} on each type argument found, that passes
+     * given {@code typeArgumentFilter}. 
+     * @param genericTypes
+     * @param typeArgumentFilter
+     * @param onTypeArgument
+     */
+    public static void visitGenericTypeArgumentsOf(
+            final @Nullable Type[] genericTypes,
+            final @NonNull Predicate<Class<?>> typeArgumentFilter,
+            final @NonNull Consumer<Class<?>> onTypeArgument) {
+        
+        if(genericTypes==null) {
+            return; // no-op
+        }
+        for (val genericType : genericTypes) {
+            visitGenericTypeArgumentsOf(genericType, typeArgumentFilter, onTypeArgument);
+        }
+    }
+    
+    // -- HELPER
+
+    private static void visitTypeArgument(
+            final Type type,
+            final Predicate<Class<?>> filter,
+            final Consumer<Class<?>> onClass) {
+        
+        if (type instanceof WildcardType) {
+            acceptWildcardType((WildcardType) type, filter, onClass);
+        }
+        if (type instanceof Class) {
+            acceptNonWildcardType((Class<?>) type, filter, onClass);
+        }
+        // unexpected code reach
+    }
+    
+    private static void acceptWildcardType(
+            final WildcardType wildcardType, 
+            final Predicate<Class<?>> filter,
+            final Consumer<Class<?>> onClass) {
+        
+        for (val lower : wildcardType.getLowerBounds()) {
+            if (lower instanceof Class) {
+                visitTypeArgument((Class<?>) lower, filter, onClass);
+            }
+        }
+        for (val upper : wildcardType.getUpperBounds()) {
+            if (upper instanceof Class) {
+                visitTypeArgument((Class<?>) upper, filter, onClass);
+            }
+        }
+    }
+    
+    private static void acceptNonWildcardType(
+            final Class<?> cls,
+            final Predicate<Class<?>> filter,
+            final Consumer<Class<?>> onClass) {
+        
+        if(filter.test(cls)) {
+            onClass.accept(cls);
+        }    
+        
+    }
+    
+}
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 f5959e4..34a63d7 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,6 +38,7 @@ 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;
@@ -228,6 +229,11 @@ 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/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/typeextract/TypeExtractor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/typeextract/TypeExtractor.java
index 2e7d96d..32922dc 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/typeextract/TypeExtractor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/typeextract/TypeExtractor.java
@@ -19,13 +19,11 @@
 package org.apache.isis.core.metamodel.specloader.typeextract;
 
 import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.WildcardType;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
 import org.apache.isis.commons.internal.collections._Sets;
+import org.apache.isis.commons.internal.reflection._Generics;
 
 import lombok.val;
 import lombok.experimental.UtilityClass;
@@ -62,7 +60,7 @@ public class TypeExtractor {
                 continue;
             }
             acceptNonVoid(set::add, method.getParameterTypes());
-            visitParameterizedTypes(set::add, method.getGenericParameterTypes());
+            _Generics.visitGenericTypeArgumentsOf(method.getGenericParameterTypes(), set::add);
         }
         
         return set.stream();
@@ -92,7 +90,7 @@ public class TypeExtractor {
                 continue;
             }
             acceptNonVoid(set::add, method.getReturnType());
-            visitParameterizedTypes(set::add, method.getGenericReturnType());
+            _Generics.visitGenericTypeArgumentsOf(method.getGenericReturnType(), set::add);
         }
         
         return set.stream();
@@ -108,7 +106,7 @@ public class TypeExtractor {
                 continue;
             }
             acceptNonVoid(set::add, method.getReturnType());
-            visitParameterizedTypes(set::add, method.getGenericReturnType());
+            _Generics.visitGenericTypeArgumentsOf(method.getGenericReturnType(), set::add);
         }
         
         return set.stream();
@@ -116,39 +114,6 @@ public class TypeExtractor {
     
     // -- HELPER
     
-    private static void visitParameterizedTypes(
-            final Consumer<Class<?>> onClass, 
-            final Type... genericTypes) {
-        
-        for (val genericType : genericTypes) {
-            if (genericType instanceof ParameterizedType) {
-                final ParameterizedType parameterizedType = (ParameterizedType) genericType;
-                final Type[] typeArguments = parameterizedType.getActualTypeArguments();
-                for (val type : typeArguments) {
-                    if (type instanceof Class) {
-                        acceptNonVoid(onClass, (Class<?>) type);
-                    }
-                    if (type instanceof WildcardType) {
-                        acceptWildcardType(onClass, (WildcardType) type);
-                    }
-                }
-            }
-        }
-    }
-
-    private static void acceptWildcardType(Consumer<Class<?>> onClass, WildcardType wildcardType) {
-        for (val lower : wildcardType.getLowerBounds()) {
-            if (lower instanceof Class) {
-                acceptNonVoid(onClass, (Class<?>) lower);
-            }
-        }
-        for (val upper : wildcardType.getUpperBounds()) {
-            if (upper instanceof Class) {
-                acceptNonVoid(onClass, (Class<?>) upper);
-            }
-        }
-    }
-    
     private static void acceptNonVoid(
             final Consumer<Class<?>> onClass, 
             final Class<?>... classes) {
@@ -161,6 +126,4 @@ public class TypeExtractor {
         }
     }
     
-
-    
 }
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 2f7dcd2..34934b1 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
@@ -292,9 +292,22 @@ class DomainModelTest_usingGoodDomain {
         val interfaceColl = vmSpec.getCollectionElseFail("interfaceColl");
         val interfaceCollSpec = interfaceColl.getSpecification();
         
-        //FIXME yet test fails
-        //assertEquals(ElementTypeInterface.class, interfaceCollSpec.getCorrespondingClass());
-        //assertEquals(BeanSort.ABSTRACT, interfaceCollSpec.getBeanSort());
+        assertEquals(ElementTypeInterface.class, interfaceCollSpec.getCorrespondingClass());
+        assertEquals(BeanSort.ABSTRACT, interfaceCollSpec.getBeanSort());
+        
+        // when using generic type wild-cards
+        
+        val concreteColl2 = vmSpec.getCollectionElseFail("concreteColl2");
+        val concreteCollSpec2 = concreteColl2.getSpecification();
+        
+        assertEquals(ElementTypeConcrete.class, concreteCollSpec2.getCorrespondingClass());
+        assertEquals(BeanSort.VIEW_MODEL, concreteCollSpec2.getBeanSort());
+        
+        val interfaceColl2 = vmSpec.getCollectionElseFail("interfaceColl2");
+        val interfaceCollSpec2 = interfaceColl2.getSpecification();
+        
+        assertEquals(ElementTypeInterface.class, interfaceCollSpec2.getCorrespondingClass());
+        assertEquals(BeanSort.ABSTRACT, interfaceCollSpec2.getBeanSort());
         
         // TODO for the abstract case, we also want to see any members and the title-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/ElementTypeInterface.java
index 12a059a..8d0d788 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,9 +18,6 @@
  */
 package org.apache.isis.testdomain.model.good;
 
-import org.apache.isis.applib.annotation.DomainObject;
-
-@DomainObject(objectType = "isis.testdomain.ElementTypeInterface")
 public interface ElementTypeInterface {
 
     default String title() {
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 b92621c..afd6990 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
@@ -31,9 +31,15 @@ import lombok.Setter;
 public class ProperElementTypeVm {
 
     @Collection
-    @Getter @Setter private List<? extends ElementTypeInterface> interfaceColl;
+    @Getter @Setter private List<ElementTypeInterface> interfaceColl;
     
     @Collection
     @Getter @Setter private List<ElementTypeConcrete> concreteColl;
     
+    @Collection
+    @Getter @Setter private List<? extends ElementTypeInterface> interfaceColl2;
+    
+    @Collection
+    @Getter @Setter private List<? extends ElementTypeConcrete> concreteColl2;
+    
 }