You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2018/12/13 06:13:53 UTC

[isis] 03/13: ISIS-2050: replaces IntrospectionStrategy with the concept of introspecting "up to" a certain state.

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

danhaywood pushed a commit to branch ISIS-2050
in repository https://gitbox.apache.org/repos/asf/isis.git

commit d352736e2e5771385ca821db40161da77e051b35
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Dec 12 22:49:55 2018 +0100

    ISIS-2050: replaces IntrospectionStrategy with the concept of introspecting "up to" a certain state.
---
 .../metamodel/specloader/SpecificationLoader.java  | 86 ++++++++--------------
 .../specloader/specimpl/IntrospectionState.java    | 33 +++++++++
 .../specimpl/ObjectSpecificationAbstract.java      | 46 ++++++------
 .../IntrospectionState_comparable_Test.java        | 72 ++++++++++++++++++
 4 files changed, 158 insertions(+), 79 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
index fb106ca..8b21cce 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/SpecificationLoader.java
@@ -41,8 +41,6 @@ import org.apache.isis.core.commons.ensure.Assert;
 import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.commons.lang.ClassUtil;
 import org.apache.isis.core.metamodel.facetapi.Facet;
-import org.apache.isis.core.metamodel.facets.FacetFactory;
-import org.apache.isis.core.metamodel.facets.object.autocomplete.AutoCompleteFacet;
 import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.core.metamodel.layoutmetadata.LayoutMetadataReader;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
@@ -55,6 +53,7 @@ import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitut
 import org.apache.isis.core.metamodel.specloader.facetprocessor.FacetProcessor;
 import org.apache.isis.core.metamodel.specloader.postprocessor.PostProcessor;
 import org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilderContext;
+import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState;
 import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract;
 import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
 import org.apache.isis.core.metamodel.specloader.specimpl.standalonelist.ObjectSpecificationOnStandaloneList;
@@ -182,22 +181,28 @@ public class SpecificationLoader implements ApplicationScopedComponent {
 
         loadSpecificationsFor(
                 CommonDtoUtils.VALUE_TYPES, null,
-                IntrospectionStrategy.STUB, specificationsFromRegistry);
+                specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+        );
         loadSpecificationsFor(
                 allServiceClasses(), NatureOfService.DOMAIN,
-                IntrospectionStrategy.STUB, specificationsFromRegistry);
+                specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+        );
         loadSpecificationsFor(
                 AppManifest.Registry.instance().getMixinTypes(), null,
-                IntrospectionStrategy.STUB, specificationsFromRegistry);
+                specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+        );
         loadSpecificationsFor(
                 AppManifest.Registry.instance().getDomainObjectTypes(), null,
-                IntrospectionStrategy.STUB, specificationsFromRegistry);
+                specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+        );
         loadSpecificationsFor(
                 AppManifest.Registry.instance().getViewModelTypes(), null,
-                IntrospectionStrategy.STUB, specificationsFromRegistry);
+                specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+        );
         loadSpecificationsFor(
                 AppManifest.Registry.instance().getXmlElementTypes(), null,
-                IntrospectionStrategy.STUB, specificationsFromRegistry);
+                specificationsFromRegistry, IntrospectionState.NOT_INTROSPECTED
+        );
 
         state = State.INTROSPECTING;
         final Collection<ObjectSpecification> cachedSpecifications = allCachedSpecifications();
@@ -233,7 +238,10 @@ public class SpecificationLoader implements ApplicationScopedComponent {
             Callable<Object> callable = new Callable<Object>() {
                 @Override
                 public Object call() {
-                    introspectIfRequired(specification);
+
+                    final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) specification;
+                    specSpi.introspectIfRequired(IntrospectionState.INTROSPECTED);
+
                     return null;
                 }
                 public String toString() {
@@ -279,13 +287,13 @@ public class SpecificationLoader implements ApplicationScopedComponent {
     private void loadSpecificationsFor(
             final Collection<Class<?>> domainTypes,
             final NatureOfService natureOfServiceFallback,
-            final IntrospectionStrategy introspectionStrategy,
-            final List<ObjectSpecification> appendTo) {
+            final List<ObjectSpecification> appendTo,
+            final IntrospectionState upTo) {
 
         for (final Class<?> domainType : domainTypes) {
 
             ObjectSpecification objectSpecification =
-                internalLoadSpecification(domainType, natureOfServiceFallback, introspectionStrategy);
+                internalLoadSpecification(domainType, natureOfServiceFallback, upTo);
 
             if(objectSpecification != null) {
                 appendTo.add(objectSpecification);
@@ -419,36 +427,25 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         return spec;
     }
 
-    enum IntrospectionStrategy {
-        /**
-         * Used in a first pass to create ObjectSpecifications that haven't been introspected,
-         * but are cached.
-         */
-        STUB,
-        /**
-         * Second phase (default), to fully introspect.
-         */
-        COMPLETE
-    }
     private ObjectSpecification internalLoadSpecification(final Class<?> type) {
-        return internalLoadSpecification(type, null, IntrospectionStrategy.COMPLETE);
+        return internalLoadSpecification(type, null, IntrospectionState.INTROSPECTED);
     }
 
     private ObjectSpecification internalLoadSpecification(
             final Class<?> type,
             final NatureOfService natureFallback,
-            final IntrospectionStrategy introspectionStrategy) {
+            final IntrospectionState upTo) {
 
         final Class<?> substitutedType = classSubstitutor.getClass(type);
         return substitutedType != null
-                ? loadSpecificationForSubstitutedClass(substitutedType, natureFallback, introspectionStrategy)
+                ? loadSpecificationForSubstitutedClass(substitutedType, natureFallback, upTo)
                 : null;
     }
 
     private ObjectSpecification loadSpecificationForSubstitutedClass(
             final Class<?> type,
             final NatureOfService natureFallback,
-            final IntrospectionStrategy introspectionStrategy) {
+            final IntrospectionState upTo) {
         Assert.assertNotNull(type);
 
         final String typeName = type.getName();
@@ -457,14 +454,14 @@ public class SpecificationLoader implements ApplicationScopedComponent {
             return spec;
         }
 
-        return loadSpecificationForSubstitutedClassSynchronized(type, natureFallback, introspectionStrategy);
+        return loadSpecificationForSubstitutedClassSynchronized(type, natureFallback, upTo);
     }
 
 
     private synchronized ObjectSpecification loadSpecificationForSubstitutedClassSynchronized(
             final Class<?> type,
             final NatureOfService natureOfServiceFallback,
-            final IntrospectionStrategy introspectionStrategy) {
+            final IntrospectionState upTo) {
 
         final String typeName = type.getName();
         final ObjectSpecification spec = cache.get(typeName);
@@ -479,8 +476,9 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         // infinite loops
         cache.cache(typeName, specification);
 
-        if(introspectionStrategy == IntrospectionStrategy.COMPLETE) {
-            introspectIfRequired(specification);
+        if (state == State.INTROSPECTING) {
+            final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract) specification;
+            specSpi.introspectIfRequired(upTo);
         }
 
         return specification;
@@ -495,8 +493,8 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         boolean anyLoadedAsNull = false;
         for (final Class<?> typeToLoad : typesToLoad) {
             if (typeToLoad != typeToIgnore) {
-                final ObjectSpecification noSpec = internalLoadSpecification(typeToLoad);
-                final boolean loadedAsNull = (noSpec == null);
+                final ObjectSpecification objectSpecification = internalLoadSpecification(typeToLoad);
+                final boolean loadedAsNull = (objectSpecification == null);
                 anyLoadedAsNull = loadedAsNull || anyLoadedAsNull;
             }
         }
@@ -558,28 +556,6 @@ public class SpecificationLoader implements ApplicationScopedComponent {
         return Class.forName(className);
     }
 
-    /**
-     * Typically does not need to be called, but is available for {@link FacetFactory}s to force
-     * early introspection of referenced specs in certain circumstances.
-     *
-     * <p>
-     * Originally introduced to support {@link AutoCompleteFacet}.
-     */
-    private void introspectIfRequired(final ObjectSpecification spec) {
-
-        if(state != State.INTROSPECTING) {
-            return;
-        }
-
-        if (!(spec instanceof ObjectSpecificationAbstract)) {
-            // shouldn't happen... the only other implementation is a test stub.
-            return;
-        }
-
-        final ObjectSpecificationAbstract specSpi = (ObjectSpecificationAbstract)spec;
-        specSpi.introspectIfRequired();
-
-    }
 
     @Programmatic
     public void postProcess() {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java
new file mode 100644
index 0000000..83c91c4
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState.java
@@ -0,0 +1,33 @@
+/**
+ *  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.specloader.specimpl;
+
+public enum IntrospectionState implements Comparable<IntrospectionState> {
+    /**
+     * ObjectSpecIdFacet only.
+     */
+    NOT_INTROSPECTED,
+    /**
+     * Interim stage, to avoid infinite loops while on way to being {@link #INTROSPECTED}
+     */
+    BEING_INTROSPECTED,
+    /**
+     * Fully introspected... class and also its members.
+     */
+    INTROSPECTED
+
+}
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 1c39e64..81460d8 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
@@ -246,13 +246,6 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
         return fullName;
     }
 
-    
-    public enum IntrospectionState {
-        NOT_INTROSPECTED,
-        BEING_INTROSPECTED,
-        INTROSPECTED,
-    }
-
     protected boolean isNotIntrospected() {
         return !(introspectionState == IntrospectionState.INTROSPECTED);
     }
@@ -269,25 +262,30 @@ public abstract class ObjectSpecificationAbstract extends FacetHolderImpl implem
         facetProcessor.processObjectSpecId(correspondingClass, this);
     }
 
-    public void introspectIfRequired() {
+    /**
+     * Keeps introspecting up to the level required.
+     */
+    public void introspectIfRequired(final IntrospectionState upTo) {
 
-        switch (introspectionState) {
-        case NOT_INTROSPECTED:
-            // avoid infinite loops
-            this.introspectionState = IntrospectionState.BEING_INTROSPECTED;
-            try {
-                introspectTypeHierarchyAndMembers();
-                updateFromFacetValues();
-                this.introspectionState = IntrospectionState.INTROSPECTED;
-            } catch(Exception ex) {
-                this.introspectionState = IntrospectionState.NOT_INTROSPECTED;
-                throw ex;
+        while(this.introspectionState.compareTo(upTo) < 0) {
+            switch (introspectionState) {
+            case NOT_INTROSPECTED:
+                // avoid infinite loops
+                this.introspectionState = IntrospectionState.BEING_INTROSPECTED;
+                try {
+                    introspectTypeHierarchyAndMembers();
+                    updateFromFacetValues();
+                    this.introspectionState = IntrospectionState.INTROSPECTED;
+                } catch(Exception ex) {
+                    this.introspectionState = IntrospectionState.NOT_INTROSPECTED;
+                    throw ex;
+                }
+                break;
+            case BEING_INTROSPECTED:
+            case INTROSPECTED:
+                // nothing to do
+                break;
             }
-            break;
-        case BEING_INTROSPECTED:
-        case INTROSPECTED:
-            // nothing to do
-            break;
         }
     }
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState_comparable_Test.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState_comparable_Test.java
new file mode 100644
index 0000000..eefda8e
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/specloader/specimpl/IntrospectionState_comparable_Test.java
@@ -0,0 +1,72 @@
+package org.apache.isis.core.metamodel.specloader.specimpl;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+import static org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState.BEING_INTROSPECTED;
+import static org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState.INTROSPECTED;
+import static org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState.NOT_INTROSPECTED;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class IntrospectionState_comparable_Test {
+
+    @Test
+    public void all_of_em() throws Exception {
+        assertComparison(NOT_INTROSPECTED, NOT_INTROSPECTED, equal());
+        assertComparison(NOT_INTROSPECTED, BEING_INTROSPECTED, negative());
+        assertComparison(NOT_INTROSPECTED, INTROSPECTED, negative());
+
+        assertComparison(BEING_INTROSPECTED, NOT_INTROSPECTED, positive());
+        assertComparison(BEING_INTROSPECTED, BEING_INTROSPECTED, equal());
+        assertComparison(BEING_INTROSPECTED, INTROSPECTED, negative());
+
+        assertComparison(INTROSPECTED, NOT_INTROSPECTED, positive());
+        assertComparison(INTROSPECTED, BEING_INTROSPECTED, positive());
+        assertComparison(INTROSPECTED, INTROSPECTED, equal());
+    }
+
+    private Matcher<Integer> equal() {
+        return new TypeSafeMatcher<Integer>() {
+            @Override protected boolean matchesSafely(final Integer integer) {
+                return integer == 0;
+            }
+
+            @Override public void describeTo(final Description description) {
+                description.appendText("equal");
+            }
+        };
+    }
+
+    private static Matcher<Integer> negative() {
+        return new TypeSafeMatcher<Integer>() {
+            @Override protected boolean matchesSafely(final Integer integer) {
+                return integer < 0;
+            }
+
+            @Override public void describeTo(final Description description) {
+                description.appendText("negative");
+            }
+        };
+    }
+
+    private static Matcher<Integer> positive() {
+        return new TypeSafeMatcher<Integer>() {
+            @Override protected boolean matchesSafely(final Integer integer) {
+                return integer > 0;
+            }
+
+            @Override public void describeTo(final Description description) {
+                description.appendText("positive");
+            }
+        };
+    }
+
+    private static void assertComparison(
+            final IntrospectionState a,
+            final IntrospectionState b, final Matcher<Integer> matcher) {
+        assertThat(a.compareTo(b), is(matcher));
+    }
+}
\ No newline at end of file