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 2020/01/09 18:25:43 UTC

[isis] 04/11: ISIS-2250: introduces ValueTypeProvider, and beefs up ClassSubstitutor.

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

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

commit 5c154b13aea5f797d1cd93520504f7611ff042f4
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jan 9 14:27:17 2020 +0000

    ISIS-2250: introduces ValueTypeProvider, and beefs up ClassSubstitutor.
    
    Each have a corresponding "Registry" that implements the chain-of-responsibility pattern around them.
    For now, ValueTypeProviderDefault delegates to CommonDtoUtils.
---
 .../isis/applib/util/schema/CommonDtoUtils.java    |  2 +-
 .../apache/isis/metamodel/IsisModuleMetamodel.java | 12 +++-
 ...jectSpecIdFacetDerivedFromClassNameFactory.java | 13 +++-
 .../classsubstitutor/ClassSubstitutor.java         |  2 +-
 .../classsubstitutor/ClassSubstitutorAbstract.java |  3 +-
 .../classsubstitutor/ClassSubstitutorDefault.java  |  4 ++
 .../ClassSubstitutorForCollections.java            | 57 +++++++++++++++
 .../classsubstitutor/ClassSubstitutorRegistry.java | 80 ++++++++++++++++++++++
 .../isis/metamodel/spec/ObjectSpecification.java   |  2 +-
 .../metamodel/specloader/SpecificationLoader.java  |  2 +-
 .../specloader/SpecificationLoaderDefault.java     | 46 +++++++++----
 .../specloader/specimpl/FacetedMethodsBuilder.java | 12 ++--
 .../specimpl/dflt/ObjectSpecificationDefault.java  | 14 ++--
 .../metamodel/valuetypes/ValueTypeDefinition.java  | 27 ++++++++
 .../metamodel/valuetypes/ValueTypeProvider.java    | 14 +++-
 .../valuetypes/ValueTypeProviderBuiltIn.java       |  7 --
 .../valuetypes/ValueTypeProviderDefault.java       | 37 ++++++++++
 .../ValueTypeProviderForCollections.java           | 38 ++++++++++
 .../metamodel/valuetypes/ValueTypeRegistry.java    | 78 +++++++++++++++++++++
 ...SpecIdFacetDerivedFromClassNameFactoryTest.java |  6 +-
 .../JdoDiscriminatorAnnotationFacetFactory.java    |  6 +-
 21 files changed, 411 insertions(+), 51 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java b/core/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
index 179c420..4bb592d 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/util/schema/CommonDtoUtils.java
@@ -67,7 +67,7 @@ public final class CommonDtoUtils {
     public static final Function<ParamDto, ValueType> PARAM_DTO_TO_TYPE = ParamDto::getType;
 
     // -- asValueType
-    private final static Map<Class<?>, ValueType> valueTypeByClass =
+    public final static Map<Class<?>, ValueType> valueTypeByClass =
             _Maps.unmodifiableEntries(
                     entry(String.class, ValueType.STRING),
                     entry(byte.class, ValueType.BYTE),
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/IsisModuleMetamodel.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/IsisModuleMetamodel.java
index 2b35fd3..42a9bbf 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/IsisModuleMetamodel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/IsisModuleMetamodel.java
@@ -30,6 +30,8 @@ import org.apache.isis.metamodel.services.ServiceInjectorDefault;
 import org.apache.isis.metamodel.services.appfeat.ApplicationFeatureFactory;
 import org.apache.isis.metamodel.services.appfeat.ApplicationFeatureRepositoryDefault;
 import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorDefault;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorForCollections;
 import org.apache.isis.metamodel.services.events.MetamodelEventService;
 import org.apache.isis.metamodel.services.events.MetamodelEventSupport_Spring;
 import org.apache.isis.metamodel.services.exceprecog.ExceptionRecognizerDocDefault;
@@ -45,7 +47,9 @@ import org.apache.isis.metamodel.services.user.UserServiceDefault;
 import org.apache.isis.metamodel.specloader.InjectorMethodEvaluatorDefault;
 import org.apache.isis.metamodel.specloader.ProgrammingModelServiceDefault;
 import org.apache.isis.metamodel.specloader.SpecificationLoaderDefault;
-import org.apache.isis.metamodel.valuetypes.ValueTypeProviderBuiltIn;
+import org.apache.isis.metamodel.valuetypes.ValueTypeProviderDefault;
+import org.apache.isis.metamodel.valuetypes.ValueTypeProviderForCollections;
+import org.apache.isis.metamodel.valuetypes.ValueTypeRegistry;
 import org.apache.isis.security.api.IsisModuleSecurityApi;
 
 @Configuration
@@ -62,7 +66,11 @@ import org.apache.isis.security.api.IsisModuleSecurityApi;
         // @Component's
         ProgrammingModelInitFilterDefault.class,
         ClassSubstitutorDefault.class,
-        ValueTypeProviderBuiltIn.class,
+        ClassSubstitutorForCollections.class,
+        ClassSubstitutorRegistry.class,
+        ValueTypeProviderDefault.class,
+        ValueTypeProviderForCollections.class,
+        ValueTypeRegistry.class,
 
         // @Service's
         ObjectManagerDefault.class,
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java
index efb0ae7..9807ab2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/object/objectspecid/classname/ObjectSpecIdFacetDerivedFromClassNameFactory.java
@@ -19,6 +19,7 @@
 
 package org.apache.isis.metamodel.facets.object.objectspecid.classname;
 
+import java.util.Collections;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
@@ -34,8 +35,8 @@ import org.apache.isis.metamodel.facets.ObjectSpecIdFacetFactory;
 import org.apache.isis.metamodel.facets.object.domainservice.DomainServiceFacet;
 import org.apache.isis.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.metamodel.progmodel.ProgrammingModel;
-import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutor;
 import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorDefault;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.metamodel.spec.feature.Contributed;
 import org.apache.isis.metamodel.spec.feature.ObjectAction;
@@ -49,11 +50,17 @@ extends FacetFactoryAbstract
 implements MetaModelRefiner, ObjectSpecIdFacetFactory {
 
     @Inject
-    private ClassSubstitutor classSubstitutor = new ClassSubstitutorDefault(); // default for testing purposes only, overwritten in prod
+    private ClassSubstitutorRegistry classSubstitutorRegistry;
+
 
     public ObjectSpecIdFacetDerivedFromClassNameFactory() {
         super(FeatureType.OBJECTS_ONLY);
     }
+    // default for testing purposes only, overwritten in prod
+    public ObjectSpecIdFacetDerivedFromClassNameFactory(ClassSubstitutorRegistry classSubstitutorRegistry) {
+        this();
+        this.classSubstitutorRegistry = classSubstitutorRegistry;
+    }
 
     @Override
     public void process(final ProcessObjectSpecIdContext processClassContext) {
@@ -63,7 +70,7 @@ implements MetaModelRefiner, ObjectSpecIdFacetFactory {
             return;
         }
         final Class<?> cls = processClassContext.getCls();
-        final Class<?> substitutedClass = classSubstitutor.getClass(cls);
+        final Class<?> substitutedClass = classSubstitutorRegistry.getClass(cls);
         if(substitutedClass == null) {
             return;
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutor.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutor.java
index 1059824..edb2bf3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutor.java
@@ -24,5 +24,5 @@ package org.apache.isis.metamodel.services.classsubstitutor;
  */
 public interface ClassSubstitutor {
 
-    Class<?> getClass(Class<?> type);
+    Class<?> getClass(@lombok.NonNull @org.springframework.lang.NonNull Class<?> cls);
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
index 8ed9c73..e3a32c1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
@@ -34,7 +34,7 @@ public abstract class ClassSubstitutorAbstract implements ClassSubstitutor {
     @Override
     public Class<?> getClass(final Class<?> cls) {
 
-        if (cls == null) {
+        if(cls == null) {
             return null;
         }
 
@@ -59,6 +59,7 @@ public abstract class ClassSubstitutorAbstract implements ClassSubstitutor {
             return superclass;
         }
         if (ClassUtil.directlyImplements(cls, ProxyEnhanced.class)) {
+            // REVIEW: arguably this should now go back to the ClassSubstitorRegistry
             return getClass(cls.getSuperclass());
         }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java
index 32f0bd2..c383dfe 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorDefault.java
@@ -21,10 +21,14 @@ package org.apache.isis.metamodel.services.classsubstitutor;
 
 import javax.inject.Named;
 
+import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
+import org.apache.isis.applib.annotation.OrderPrecedence;
+
 @Component
 @Named("isisMetaModel.ClassSubstitutorDefault")
+@Order(OrderPrecedence.MIDPOINT)
 public class ClassSubstitutorDefault extends ClassSubstitutorAbstract {
 
     public ClassSubstitutorDefault() {
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
new file mode 100644
index 0000000..b075ad9
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorForCollections.java
@@ -0,0 +1,57 @@
+/*
+ *  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.metamodel.services.classsubstitutor;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+
+import lombok.NonNull;
+
+@Component
+@Named("isisMetaModel.ClassSubstitutorUnmodifiableCollections")
+@Order(OrderPrecedence.MIDPOINT + 1)
+public class ClassSubstitutorForCollections implements ClassSubstitutor {
+
+    @Override
+    public Class<?> getClass(@lombok.NonNull @org.springframework.lang.NonNull Class<?> cls) {
+        if(List.class.isAssignableFrom(cls)) {
+            return List.class;
+        }
+        if(SortedSet.class.isAssignableFrom(cls)) {
+            return SortedSet.class;
+        }
+        if(Set.class.isAssignableFrom(cls)) {
+            return Set.class;
+        }
+        if(Collection.class.isAssignableFrom(cls)) {
+            return Collection.class;
+        }
+        return cls;
+    }
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorRegistry.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorRegistry.java
new file mode 100644
index 0000000..d2d5cff
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/services/classsubstitutor/ClassSubstitutorRegistry.java
@@ -0,0 +1,80 @@
+/*
+ *  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.metamodel.services.classsubstitutor;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.commons.internal.collections._Maps;
+
+/**
+ * Aggregates all {@link ClassSubstitutor}s.
+ */
+@Component
+@Named("isisMetaModel.ClassSubstitutorRegistry")
+@Order(OrderPrecedence.MIDPOINT)
+public class ClassSubstitutorRegistry {
+
+    private final List<ClassSubstitutor> classSubstitutors;
+
+    @Inject
+    public ClassSubstitutorRegistry(final List<ClassSubstitutor> classSubstitutors) {
+        this.classSubstitutors = classSubstitutors;
+    }
+
+    private final Map<Class<?>, Optional<Class<?>>> cache = _Maps.newHashMap();
+
+    // TODO: it would be preferable to return an Optional here.
+    public Class<?> getClass(final Class<?> originalClass) {
+        if(originalClass == null) {
+            return null;
+        }
+
+        Optional<Class<?>> substitutedClass = cache.get(originalClass);
+
+
+        cacheMiss:
+        //noinspection OptionalAssignedToNull
+        if(substitutedClass == null) {
+            // don't yet know if this class needs to be substituted
+            for (ClassSubstitutor classSubstitutor : classSubstitutors) {
+                final Class<?> substitutedType = classSubstitutor.getClass(originalClass);
+                if(substitutedType != originalClass) {
+                    // one of the substitutors has made a substitution
+                    // (though note, it might have substituted it with null).
+                    substitutedClass = Optional.ofNullable(substitutedType);
+                    break cacheMiss;
+                }
+            }
+            // no substitution
+            substitutedClass = Optional.of(originalClass);
+            cache.put(originalClass, substitutedClass);
+        }
+        return substitutedClass.orElse(null);
+    }
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/spec/ObjectSpecification.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/spec/ObjectSpecification.java
index 8c3c24b..ae17d36 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/spec/ObjectSpecification.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/spec/ObjectSpecification.java
@@ -113,7 +113,7 @@ ObjectAssociationContainer, Hierarchical,  DefaultProvider {
      * <p>
      * This will typically be the value of the {@link DomainObject#objectType()} annotation attribute.
      * If none has been specified then will default to the fully qualified class name (with
-     * {@link ClassSubstitutor class name substituted} if necessary to allow for runtime bytecode enhancement.
+     * {@link org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry class name substituted} if necessary to allow for runtime bytecode enhancement.
      *
      * <p>
      * The {@link ObjectSpecification} can be retrieved using {@link SpecificationLoader#lookupBySpecIdO(ObjectSpecId)}.
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
index a5b132b..fd645cb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoader.java
@@ -94,7 +94,7 @@ public interface SpecificationLoader {
      *
      * <p>
      * It is possible for this method to return <tt>null</tt>, for example if
-     * the configured {@link ClassSubstitutor} has filtered out the class.
+     * any of the configured {@link ClassSubstitutor}s has filtered out the class.
      * 
      * @return {@code null} if {@code domainType==null}, or if the type should be ignored.
      */
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
index 59f3d68..d45eb44 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
@@ -20,6 +20,7 @@ package org.apache.isis.metamodel.specloader;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -36,7 +37,6 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
-import org.apache.isis.applib.util.schema.CommonDtoUtils;
 import org.apache.isis.commons.internal.base._Blackhole;
 import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.commons.internal.base._Timing;
@@ -54,6 +54,8 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModelService;
 import org.apache.isis.metamodel.progmodels.dflt.ProgrammingModelFacetsJava8;
 import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutor;
 import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorDefault;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorForCollections;
 import org.apache.isis.metamodel.spec.FreeStandingList;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
@@ -65,6 +67,8 @@ import org.apache.isis.metamodel.specloader.specimpl.dflt.ObjectSpecificationDef
 import org.apache.isis.metamodel.specloader.specimpl.standalonelist.ObjectSpecificationOnStandaloneList;
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorAbstract;
 import org.apache.isis.metamodel.specloader.validator.ValidationFailures;
+import org.apache.isis.metamodel.valuetypes.ValueTypeProviderDefault;
+import org.apache.isis.metamodel.valuetypes.ValueTypeRegistry;
 
 import static org.apache.isis.commons.internal.base._With.requires;
 
@@ -99,7 +103,8 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     private final IsisSystemEnvironment isisSystemEnvironment;
     private final ServiceRegistry serviceRegistry;
     private final IsisBeanTypeRegistryHolder isisBeanTypeRegistryHolder;
-    private final ClassSubstitutor classSubstitutor = new ClassSubstitutorDefault();
+    private final ClassSubstitutorRegistry classSubstitutorRegistry;
+    private final ValueTypeRegistry valueTypeRegistry;
 
     private final ProgrammingModel programmingModel;
     private final PostProcessor postProcessor;
@@ -108,8 +113,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
     private FacetProcessor facetProcessor;
 
-    private final SpecificationCacheDefault<ObjectSpecification> cache = 
-            new SpecificationCacheDefault<>();
+    private final SpecificationCacheDefault<ObjectSpecification> cache = new SpecificationCacheDefault<>();
 
     /**
      * We only ever mark the metamodel as fully introspected if in {@link #isFullIntrospect() full} introspection mode.
@@ -123,13 +127,15 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             final IsisConfiguration isisConfiguration,
             final IsisSystemEnvironment isisSystemEnvironment,
             final ServiceRegistry serviceRegistry,
-            final IsisBeanTypeRegistryHolder isisBeanTypeRegistryHolder) {
+            final IsisBeanTypeRegistryHolder isisBeanTypeRegistryHolder,
+            final ValueTypeRegistry valueTypeRegistry,
+            final ClassSubstitutorRegistry classSubstitutorRegistry) {
         this(
                 programmingModelService.getProgrammingModel(),
                 isisConfiguration,
                 isisSystemEnvironment,
                 serviceRegistry,
-                isisBeanTypeRegistryHolder);
+                isisBeanTypeRegistryHolder, valueTypeRegistry, classSubstitutorRegistry);
     }
 
     SpecificationLoaderDefault(
@@ -137,13 +143,17 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             final IsisConfiguration isisConfiguration,
             final IsisSystemEnvironment isisSystemEnvironment,
             final ServiceRegistry serviceRegistry,
-            final IsisBeanTypeRegistryHolder isisBeanTypeRegistryHolder) {
+            final IsisBeanTypeRegistryHolder isisBeanTypeRegistryHolder,
+            final ValueTypeRegistry valueTypeRegistry,
+            final ClassSubstitutorRegistry classSubstitutorRegistry) {
         this.programmingModel = programmingModel;
         this.postProcessor = new PostProcessor(programmingModel);
         this.isisConfiguration = isisConfiguration;
         this.isisSystemEnvironment = isisSystemEnvironment;
         this.serviceRegistry = serviceRegistry;
         this.isisBeanTypeRegistryHolder = isisBeanTypeRegistryHolder;
+        this.valueTypeRegistry = valueTypeRegistry;
+        this.classSubstitutorRegistry = classSubstitutorRegistry;
     }
 
     /** JUnit Test Support */
@@ -154,7 +164,11 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             final ProgrammingModel programmingModel,
             final IsisBeanTypeRegistryHolder isisBeanTypeRegistryHolder) {
 
-        val instance = new SpecificationLoaderDefault(programmingModel, isisConfiguration, isisSystemEnvironment, serviceRegistry, isisBeanTypeRegistryHolder);
+        val instance = new SpecificationLoaderDefault(
+                programmingModel, isisConfiguration, isisSystemEnvironment,
+                serviceRegistry, isisBeanTypeRegistryHolder,
+                new ValueTypeRegistry(Collections.singletonList(new ValueTypeProviderDefault())),
+                new ClassSubstitutorRegistry(Collections.unmodifiableList(Arrays.asList(new ClassSubstitutorDefault(), new ClassSubstitutorForCollections()))));
 
         instance.metaModelContext = serviceRegistry.lookupServiceElseFail(MetaModelContext.class);
         instance.facetProcessor = new FacetProcessor(programmingModel, instance.metaModelContext);
@@ -192,9 +206,11 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
         val domainServiceSpecs = _Lists.<ObjectSpecification>newArrayList();
         val domainObjectSpecs = _Lists.<ObjectSpecification>newArrayList();
 
-        CommonDtoUtils.VALUE_TYPES.forEach(type->{
-            val spec = loadSpecification(type, IntrospectionState.NOT_INTROSPECTED);
-            if(spec!=null) scannedSpecs.add(spec);
+        valueTypeRegistry.classes().forEach(clazz -> {
+            val spec = loadSpecification(clazz, IntrospectionState.NOT_INTROSPECTED);
+            if(spec!=null) {
+                scannedSpecs.add(spec);
+            }
         });
 
         log.info(" - categorizing types from class-path scan");
@@ -315,7 +331,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
     public boolean loadSpecifications(Class<?>... domainTypes) {
         // ensure that all types are loadable
         if (Arrays.stream(domainTypes)
-                .map(classSubstitutor::getClass)
+                .map(classSubstitutorRegistry::getClass)
                 .anyMatch(Objects::isNull)) {
             return false;
         }
@@ -334,7 +350,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
         requires(upTo, "upTo");
 
-        val substitutedType = classSubstitutor.getClass(type);
+        val substitutedType = classSubstitutorRegistry.getClass(type);
         if (substitutedType == null) {
             return null;
         }
@@ -437,7 +453,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
                             facetProcessor,
                             managedBeanNameIfAny.orElse(null),
                             postProcessor,
-                            classSubstitutor);
+                            classSubstitutorRegistry);
         }
 
         return objectSpec;
@@ -474,7 +490,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
 
     private void invalidateCache(final Class<?> cls) {
 
-        val substitutedType = classSubstitutor.getClass(cls);
+        val substitutedType = classSubstitutorRegistry.getClass(cls);
 
         ObjectSpecification spec = 
                 loadSpecification(substitutedType, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
index 08c3c07..98c92de 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
@@ -52,7 +52,7 @@ import org.apache.isis.metamodel.facets.FacetedMethodParameter;
 import org.apache.isis.metamodel.facets.actcoll.typeof.TypeOfFacet;
 import org.apache.isis.metamodel.facets.object.facets.FacetsFacet;
 import org.apache.isis.metamodel.facets.object.mixin.MixinFacet;
-import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutor;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.isis.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.metamodel.specloader.facetprocessor.FacetProcessor;
 import org.apache.isis.metamodel.specloader.traverser.TypeExtractorMethodReturn;
@@ -139,7 +139,7 @@ public class FacetedMethodsBuilder {
 
     private final FacetProcessor facetProcessor;
 
-    private final ClassSubstitutor classSubstitutor;
+    private final ClassSubstitutorRegistry classSubstitutorRegistry;
 
     private final SpecificationLoader specificationLoader;
 
@@ -153,14 +153,14 @@ public class FacetedMethodsBuilder {
     public FacetedMethodsBuilder(
             final ObjectSpecificationAbstract inspectedTypeSpec,
             final FacetProcessor facetProcessor,
-            final ClassSubstitutor classSubstitutor) {
+            final ClassSubstitutorRegistry classSubstitutorRegistry) {
 
         if (log.isDebugEnabled()) {
             log.debug("creating JavaIntrospector for {}", inspectedTypeSpec.getFullIdentifier());
         }
 
         this.facetProcessor = facetProcessor;
-        this.classSubstitutor = classSubstitutor;
+        this.classSubstitutorRegistry = classSubstitutorRegistry;
 
         val mmContext = inspectedTypeSpec.getMetaModelContext();
 
@@ -315,7 +315,7 @@ public class FacetedMethodsBuilder {
             facetedMethod.setType(elementType);
 
             // skip if class substitutor says so.
-            if (classSubstitutor.getClass(elementType) == null) {
+            if (classSubstitutorRegistry.getClass(elementType) == null) {
                 continue;
             }
 
@@ -333,7 +333,7 @@ public class FacetedMethodsBuilder {
             final Class<?> returnType = accessorMethod.getReturnType();
 
             // skip if class strategy says so.
-            if (classSubstitutor.getClass(returnType) == null) {
+            if (classSubstitutorRegistry.getClass(returnType) == null) {
                 continue;
             }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java
index 14b38aa..ebf4709 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/dflt/ObjectSpecificationDefault.java
@@ -33,8 +33,6 @@ import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Lazy;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.commons.internal.ioc.BeanSort;
 import org.apache.isis.metamodel.commons.StringExtensions;
 import org.apache.isis.metamodel.commons.ToString;
 import org.apache.isis.metamodel.context.MetaModelContext;
@@ -53,7 +51,7 @@ import org.apache.isis.metamodel.facets.object.plural.inferred.PluralFacetInferr
 import org.apache.isis.metamodel.facets.object.value.ValueFacet;
 import org.apache.isis.metamodel.facets.object.viewmodel.ViewModelFacet;
 import org.apache.isis.metamodel.facets.object.wizard.WizardFacet;
-import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutor;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.isis.metamodel.spec.ActionType;
 import org.apache.isis.metamodel.spec.ElementSpecificationProvider;
 import org.apache.isis.metamodel.spec.ManagedObject;
@@ -93,7 +91,7 @@ public class ObjectSpecificationDefault extends ObjectSpecificationAbstract impl
     private Map<Method, ObjectMember> membersByMethod = null;
 
     private final FacetedMethodsBuilder facetedMethodsBuilder;
-    private final ClassSubstitutor classSubstitutor;
+    private final ClassSubstitutorRegistry classSubstitutorRegistry;
 
 
     /**
@@ -107,15 +105,15 @@ public class ObjectSpecificationDefault extends ObjectSpecificationAbstract impl
             final FacetProcessor facetProcessor,
             final String nameIfIsManagedBean,
             final PostProcessor postProcessor,
-            final ClassSubstitutor classSubstitutor) {
+            final ClassSubstitutorRegistry classSubstitutorRegistry) {
         
         super(correspondingClass, determineShortName(correspondingClass), facetProcessor, postProcessor);
 
         setMetaModelContext(metaModelContext);
 
         this.nameIfIsManagedBean = nameIfIsManagedBean;
-        this.facetedMethodsBuilder = new FacetedMethodsBuilder(this, facetProcessor, classSubstitutor);
-        this.classSubstitutor = classSubstitutor;
+        this.facetedMethodsBuilder = new FacetedMethodsBuilder(this, facetProcessor, classSubstitutorRegistry);
+        this.classSubstitutorRegistry = classSubstitutorRegistry;
 
         facetProcessor.processObjectSpecId(correspondingClass, this);
         
@@ -165,7 +163,7 @@ public class ObjectSpecificationDefault extends ObjectSpecificationAbstract impl
         final Class<?>[] interfaceTypes = getCorrespondingClass().getInterfaces();
         final List<ObjectSpecification> interfaceSpecList = _Lists.newArrayList();
         for (val interfaceType : interfaceTypes) {
-            val substitutedInterfaceType = classSubstitutor.getClass(interfaceType);
+            val substitutedInterfaceType = classSubstitutorRegistry.getClass(interfaceType);
             if (substitutedInterfaceType != null) {
                 val interfaceSpec = getSpecificationLoader().loadSpecification(substitutedInterfaceType);
                 interfaceSpecList.add(interfaceSpec);
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeDefinition.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeDefinition.java
new file mode 100644
index 0000000..895be43
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeDefinition.java
@@ -0,0 +1,27 @@
+package org.apache.isis.metamodel.valuetypes;
+
+import java.util.Map;
+
+import org.apache.isis.schema.common.v1.ValueType;
+
+import lombok.Data;
+
+@Data
+public class ValueTypeDefinition {
+
+    public static ValueTypeDefinition collection(Class<?> clazz) {
+        return new ValueTypeDefinition(clazz, ValueType.COLLECTION);
+    }
+    public static ValueTypeDefinition of(Class<?> clazz, ValueType valueType) {
+        return new ValueTypeDefinition(clazz, valueType);
+    }
+    public static ValueTypeDefinition from(final Map.Entry<Class<?>, ValueType> entry) {
+        return new ValueTypeDefinition(entry.getKey(), entry.getValue());
+    }
+    private ValueTypeDefinition(Class<?> clazz, ValueType valueType) {
+        this.clazz = clazz;
+        this.valueType = valueType;
+    }
+    private final Class<?> clazz;
+    private final ValueType valueType;
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProvider.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProvider.java
index 5a226f2..68a61e2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProvider.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProvider.java
@@ -1,4 +1,16 @@
 package org.apache.isis.metamodel.valuetypes;
 
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.isis.schema.common.v1.ValueType;
+
 public interface ValueTypeProvider {
-}
+
+    Collection<ValueTypeDefinition> definitions();
+
+
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderBuiltIn.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderBuiltIn.java
deleted file mode 100644
index 407381f..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderBuiltIn.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.apache.isis.metamodel.valuetypes;
-
-import org.springframework.stereotype.Component;
-
-@Component
-public class ValueTypeProviderBuiltIn implements ValueTypeProvider {
-}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderDefault.java
new file mode 100644
index 0000000..11fe39c
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderDefault.java
@@ -0,0 +1,37 @@
+package org.apache.isis.metamodel.valuetypes;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.util.schema.CommonDtoUtils;
+import org.apache.isis.schema.common.v1.ValueType;
+
+@Component
+@Named("isisMetaModel.ValueTypeProviderBuiltIn")
+@Order(OrderPrecedence.MIDPOINT)
+public class ValueTypeProviderDefault implements ValueTypeProvider {
+
+    private final Map<Class<?>, ValueTypeDefinition> definitions =
+            from(CommonDtoUtils.valueTypeByClass);
+
+    private static Map<Class<?>, ValueTypeDefinition> from(final Map<Class<?>, ValueType> valueTypeByClass) {
+        final Map<Class<?>, ValueTypeDefinition> map = new LinkedHashMap<>();
+        valueTypeByClass.entrySet()
+                .forEach(entry -> map.put(entry.getKey(), ValueTypeDefinition.from(entry)));
+        return map;
+    }
+
+    @Override
+    public Collection<ValueTypeDefinition> definitions() {
+        return definitions.values();
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderForCollections.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderForCollections.java
new file mode 100644
index 0000000..0af096f
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeProviderForCollections.java
@@ -0,0 +1,38 @@
+package org.apache.isis.metamodel.valuetypes;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedSet;
+
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.util.schema.CommonDtoUtils;
+import org.apache.isis.metamodel.spec.FreeStandingList;
+import org.apache.isis.schema.common.v1.ValueType;
+
+@Component
+@Named("isisMetaModel.ValueTypeProviderCollections")
+@Order(OrderPrecedence.MIDPOINT)
+public class ValueTypeProviderForCollections implements ValueTypeProvider {
+
+    @Override
+    public Collection<ValueTypeDefinition> definitions() {
+        return Collections.unmodifiableList(Arrays.asList(
+                    ValueTypeDefinition.collection(List.class),
+                    ValueTypeDefinition.collection(Set.class),
+                    ValueTypeDefinition.collection(SortedSet.class),
+                    ValueTypeDefinition.collection(FreeStandingList.class)
+                ));
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeRegistry.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeRegistry.java
new file mode 100644
index 0000000..dc617f9
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/valuetypes/ValueTypeRegistry.java
@@ -0,0 +1,78 @@
+package org.apache.isis.metamodel.valuetypes;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.schema.common.v1.ValueType;
+
+import lombok.val;
+
+@Component
+@Named("isisMetaModel.ValueTypeRegistry")
+@Order(OrderPrecedence.MIDPOINT)
+public class ValueTypeRegistry {
+
+    private final List<ValueTypeProvider> valueTypeProviders;
+    private final Map<Class<?>, Optional<ValueTypeDefinition>> definitionByClazz;
+
+    @Inject
+    public ValueTypeRegistry(
+            final List<ValueTypeProvider> valueTypeProviders) {
+        this.valueTypeProviders = valueTypeProviders;
+        this.definitionByClazz = Collections.unmodifiableMap(aggregate(valueTypeProviders));
+    }
+
+    static Map<Class<?>, Optional<ValueTypeDefinition>> aggregate(
+            final List<ValueTypeProvider> valueTypeProviders) {
+
+        final Map<Class<?>, Optional<ValueTypeDefinition>> map = new LinkedHashMap<>();
+        for (ValueTypeProvider valueTypeProvider : valueTypeProviders) {
+            final Collection<ValueTypeDefinition> valueTypeDefinitions = valueTypeProvider.definitions();
+            valueTypeDefinitions
+                    .forEach(valueTypeDefinition -> {
+                        map.put(valueTypeDefinition.getClazz(), Optional.of(valueTypeDefinition));
+                    });
+        }
+        return map;
+    }
+
+    public ValueType asValueType(final Class<?> clazz) {
+        return definitionByClazz.getOrDefault(clazz, Optional.empty())
+                .map(ValueTypeDefinition::getValueType)
+                .orElse(fallback(clazz));
+    }
+
+    private static ValueType fallback(final Class<?> clazz) {
+        if (clazz!=null && clazz.isEnum()) {
+            return ValueType.ENUM;
+        }
+        // assume reference otherwise
+        return ValueType.REFERENCE;
+    }
+
+    public Stream<Class<?>> classes() {
+        val classes = new LinkedHashSet<Class<?>>();
+        valueTypeProviders.stream()
+                .map(ValueTypeProvider::definitions)
+                .forEach(definitions -> {
+                    definitions.stream()
+                            .map(ValueTypeDefinition::getClazz)
+                            .forEach(classes::add);
+                });
+        return classes.stream();
+    }
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/objectspecid/ObjectSpecIdFacetDerivedFromClassNameFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/objectspecid/ObjectSpecIdFacetDerivedFromClassNameFactoryTest.java
index 6c7c576..3a9c54d 100644
--- a/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/objectspecid/ObjectSpecIdFacetDerivedFromClassNameFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/metamodel/facets/object/objectspecid/ObjectSpecIdFacetDerivedFromClassNameFactoryTest.java
@@ -19,6 +19,8 @@
 
 package org.apache.isis.metamodel.facets.object.objectspecid;
 
+import java.util.Collections;
+
 import org.datanucleus.testing.dom.CustomerAsProxiedByDataNucleus;
 import org.junit.Before;
 import org.junit.Test;
@@ -32,6 +34,8 @@ import org.apache.isis.metamodel.facets.AbstractFacetFactoryJUnit4TestCase;
 import org.apache.isis.metamodel.facets.ObjectSpecIdFacetFactory;
 import org.apache.isis.metamodel.facets.object.objectspecid.classname.ObjectSpecIdFacetDerivedFromClassName;
 import org.apache.isis.metamodel.facets.object.objectspecid.classname.ObjectSpecIdFacetDerivedFromClassNameFactory;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorDefault;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 
 public class ObjectSpecIdFacetDerivedFromClassNameFactoryTest extends AbstractFacetFactoryJUnit4TestCase {
@@ -40,7 +44,7 @@ public class ObjectSpecIdFacetDerivedFromClassNameFactoryTest extends AbstractFa
 
     @Before
     public void setUp() throws Exception {
-        facetFactory = new ObjectSpecIdFacetDerivedFromClassNameFactory();
+        facetFactory = new ObjectSpecIdFacetDerivedFromClassNameFactory(new ClassSubstitutorRegistry(Collections.singletonList( new ClassSubstitutorDefault())));
     }
 
     public static class Customer {
diff --git a/core/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/metamodel/facets/object/discriminator/JdoDiscriminatorAnnotationFacetFactory.java b/core/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/metamodel/facets/object/discriminator/JdoDiscriminatorAnnotationFacetFactory.java
index 0e24e43..073f23c 100644
--- a/core/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/metamodel/facets/object/discriminator/JdoDiscriminatorAnnotationFacetFactory.java
+++ b/core/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/metamodel/facets/object/discriminator/JdoDiscriminatorAnnotationFacetFactory.java
@@ -31,7 +31,7 @@ import org.apache.isis.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.metamodel.facets.ObjectSpecIdFacetFactory;
 import org.apache.isis.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.metamodel.facets.object.objectspecid.classname.ObjectSpecIdFacetDerivedFromClassName;
-import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutor;
+import org.apache.isis.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
 import org.apache.isis.persistence.jdo.datanucleus5.metamodel.JdoMetamodelUtil;
 
 public class JdoDiscriminatorAnnotationFacetFactory
@@ -39,7 +39,7 @@ extends FacetFactoryAbstract
 implements ObjectSpecIdFacetFactory {
 
     @Inject
-    private ClassSubstitutor classSubstitutor;
+    private ClassSubstitutorRegistry classSubstitutorRegistry;
 
     public JdoDiscriminatorAnnotationFacetFactory() {
         super(FeatureType.OBJECTS_ONLY);
@@ -66,7 +66,7 @@ implements ObjectSpecIdFacetFactory {
             facet = new ObjectSpecIdFacetInferredFromJdoDiscriminatorValueAnnotation(
                     annotationValue, facetHolder);
         } else {
-            final Class<?> substitutedClass = classSubstitutor.getClass(cls);
+            final Class<?> substitutedClass = classSubstitutorRegistry.getClass(cls);
             facet = substitutedClass != null
                         ? new ObjectSpecIdFacetDerivedFromClassName(substitutedClass.getCanonicalName(), facetHolder)
                         : null;