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 2018/12/27 21:09:06 UTC

[isis] 01/02: ISIS-2054: simplify type hierarchy traversal when including interfaces

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

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

commit 77de83314f1dbd64f1615237f535b18c988c5378
Author: andi-huber <ah...@apache.org>
AuthorDate: Thu Dec 27 21:40:47 2018 +0100

    ISIS-2054: simplify type hierarchy traversal when including interfaces
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-2054
---
 .../isis/commons/internal/reflection/_Reflect.java |  45 ++-----
 .../commons/internal/reflection/ReflectTest.java   | 132 +++++++++++++++++++++
 2 files changed, 141 insertions(+), 36 deletions(-)

diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Reflect.java b/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Reflect.java
index 638fdef..f8db6a5 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Reflect.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/reflection/_Reflect.java
@@ -146,7 +146,7 @@ public final class _Reflect {
             @Nullable Class<?> type,
             final boolean ignoreAccess) {
         
-        return streamTypeHierarchyAndInterfaces(type)
+        return streamTypeHierarchy(type, /*includeInterfaces*/ false) // interfaces don't have fields
                 .filter(Object.class::equals) // do not process Object class.
                 .flatMap(t->streamFields(t, ignoreAccess));
     }
@@ -182,7 +182,7 @@ public final class _Reflect {
             final boolean ignoreAccess
             ) {
         
-        return streamTypeHierarchyAndInterfaces(type)
+        return streamTypeHierarchy(type, /*includeInterfaces*/ true)
                 .filter(t->!t.equals(Object.class)) // do not process Object class.
                 .flatMap(t->streamMethods(t, ignoreAccess));
     }
@@ -192,9 +192,10 @@ public final class _Reflect {
     /**
      * Stream all types of given {@code type}, up the super class hierarchy starting with self
      * @param type (nullable)
+     * @param includeInterfaces - whether to include all interfaces implemented by given {@code type}.
      * @return non-null
      */
-    public static Stream<Class<?>> streamTypeHierarchy(@Nullable Class<?> type) {
+    public static Stream<Class<?>> streamTypeHierarchy(@Nullable Class<?> type, boolean includeInterfaces) {
 
         // https://stackoverflow.com/questions/40240450/java8-streaming-a-class-hierarchy?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
         // Java 9+ will allow ...
@@ -208,31 +209,15 @@ public final class _Reflect {
                     public boolean tryAdvance(Consumer<? super Class<?>> action) {
                         if(current == null) return false;
                         action.accept(current);
+                        if(includeInterfaces) {
+                            for(Class<?> subIface : current.getInterfaces()) {
+                                recur(subIface, action);
+                            }
+                        }
                         current = current.getSuperclass();
                         return true;
                     }
-                }, false);
-    }
-    
-    /**
-     * Stream all interfaces implemented by given {@code type}.
-     * @param type (nullable)
-     * @return non-null
-     */
-    public static Stream<Class<?>> streamAllInterfaces(@Nullable Class<?> type) {
-        return StreamSupport.stream(
-                new Spliterators.AbstractSpliterator<Class<?>>(Long.MAX_VALUE,
-                        Spliterator.ORDERED|Spliterator.IMMUTABLE|Spliterator.NONNULL) {
-                    Class<?> current = type;
                     
-                    @Override
-                    public boolean tryAdvance(Consumer<? super Class<?>> action) {
-                        if(current == null) return false;
-                        recur(current, action);
-                        current = current.getSuperclass();
-                        return true;
-                    }
-
                     private void recur(Class<?> iface, Consumer<? super Class<?>> action) {
                         action.accept(iface);
                         for(Class<?> subIface : iface.getInterfaces()) {
@@ -243,18 +228,6 @@ public final class _Reflect {
                 }, false);
     }
     
-    /**
-     * Stream all types of given {@code type}, up the super class hierarchy starting with self, 
-     * then append all interfaces of given {@code type}.
-     * @param type (nullable)
-     * @return non-null
-     */
-    public static Stream<Class<?>> streamTypeHierarchyAndInterfaces(
-            @Nullable Class<?> type) {
-        
-        return Stream.concat(streamTypeHierarchy(type), streamAllInterfaces(type));
-    }
-    
     // -- ANNOTATIONS
 
     /**
diff --git a/core/detached-tests/src/test/java/org/apache/isis/commons/internal/reflection/ReflectTest.java b/core/detached-tests/src/test/java/org/apache/isis/commons/internal/reflection/ReflectTest.java
new file mode 100644
index 0000000..eed91e9
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/commons/internal/reflection/ReflectTest.java
@@ -0,0 +1,132 @@
+/*
+ *  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 java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.xml.bind.JAXBContext;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.jaxb.JaxbServiceDefault;
+import org.apache.isis.core.metamodel.services.user.UserServiceDefault;
+
+//TODO we are using real word classes from the framework, we could instead isolate these tests
+// if we provide some custom classes for hierarchy traversal here (could be nested); 
+// then move this test to the 'commons' module, where it belongs
+class ReflectTest {
+    
+    @Test
+    void typeHierarchy() {
+        
+        Class<?> type = UserServiceDefault.SudoServiceSpi.class;
+        
+        String typeListLiteral = _Reflect.streamTypeHierarchy(type, false)
+        .map(t->t.getName())
+        .collect(Collectors.joining(", "));
+        
+        Assertions.assertEquals(""
+                + "org.apache.isis.core.metamodel.services.user.UserServiceDefault$SudoServiceSpi, "
+                + "java.lang.Object", 
+                typeListLiteral);
+        
+    }
+
+    @Test
+    void typeHierarchyAndInterfaces() {
+        
+        Class<?> type = UserServiceDefault.SudoServiceSpi.class;
+        
+        String typeListLiteral = _Reflect.streamTypeHierarchy(type, true)
+        .map(t->t.getName())
+        .collect(Collectors.joining(", "));
+        
+        Assertions.assertEquals(
+                "org.apache.isis.core.metamodel.services.user.UserServiceDefault$SudoServiceSpi, "
+                + "org.apache.isis.applib.services.sudo.SudoService$Spi, "
+                + "java.lang.Object", 
+                typeListLiteral);
+        
+    }
+    
+    @Test
+    void allMethods() {
+        
+        Class<?> type = UserServiceDefault.SudoServiceSpi.class;
+        
+        String typeListLiteral = _Reflect.streamAllMethods(type, true)
+        .map(m->m.toString())
+        .collect(Collectors.joining(", "));
+        
+        Assertions.assertEquals(
+                "public void org.apache.isis.core.metamodel.services.user.UserServiceDefault$SudoServiceSpi.runAs(java.lang.String,java.util.List), "
+                + "public void org.apache.isis.core.metamodel.services.user.UserServiceDefault$SudoServiceSpi.releaseRunAs(), "
+                + "public abstract void org.apache.isis.applib.services.sudo.SudoService$Spi.runAs(java.lang.String,java.util.List), "
+                + "public abstract void org.apache.isis.applib.services.sudo.SudoService$Spi.releaseRunAs()", 
+                typeListLiteral);
+        
+    }
+    
+    @Test
+    void annotationLookup() throws NoSuchMethodException, SecurityException {
+        
+        Class<?> type = UserServiceDefault.SudoServiceSpi.class;
+        Method method = type.getMethod("runAs", new Class[] {String.class, List.class});
+        
+        Programmatic annot = _Reflect.getAnnotation(method, Programmatic.class, true, true);
+        
+        Assertions.assertNotNull(annot);
+    }
+    
+    @Test
+    void typeHierarchyAndInterfaces2() {
+        
+        Class<?> type = JaxbServiceDefault.class;
+        
+        String typeListLiteral = _Reflect.streamTypeHierarchy(type, true)
+        .map(t->t.getName())
+        .collect(Collectors.joining(", "));
+        
+        Assertions.assertEquals(
+                "org.apache.isis.applib.services.jaxb.JaxbServiceDefault, "
+                + "org.apache.isis.applib.services.jaxb.JaxbService$Simple, "
+                + "org.apache.isis.applib.services.jaxb.JaxbService, "
+                + "java.lang.Object", 
+                typeListLiteral);
+        
+    }
+    
+    
+    @Test
+    void annotationLookup2() throws NoSuchMethodException, SecurityException {
+        
+        Class<?> type = JaxbServiceDefault.class;
+        Method method = type.getMethod("fromXml", new Class[] {JAXBContext.class, String.class, Map.class});
+        
+        Programmatic annot = _Reflect.getAnnotation(method, Programmatic.class, true, true);
+        
+        Assertions.assertNotNull(annot);
+        
+    }
+    
+}