You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/09/08 06:48:36 UTC

[isis] branch master updated: ISIS-3206: allows for more annotation providers to be plugged in the future

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 3cf946aff6 ISIS-3206: allows for more annotation providers to be plugged in the future
3cf946aff6 is described below

commit 3cf946aff63626ca5e8514bd9cec95a0d0730b0d
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Sep 8 08:48:29 2022 +0200

    ISIS-3206: allows for more annotation providers to be plugged in the
    future
---
 .../metamodel/Object_downloadMetamodelXml.java     |  2 +-
 .../isis/applib/services/metamodel/Config.java     | 38 +--------
 .../services/metamodel/MetaModelServiceMenu.java   |  2 +-
 .../metamodel/inspect/Object_inspectMetamodel.java |  4 +-
 .../services/metamodel/MetaModelAnnotator.java     | 92 ++++++++++++++++++++++
 .../services/metamodel/MetaModelExporter.java      | 27 ++++---
 .../metamodel/MetaModelServiceDefault.java         | 15 +++-
 .../metamodel/{_Util.java => TitleAnnotator.java}  | 79 +++++++++----------
 8 files changed, 162 insertions(+), 97 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/mixins/metamodel/Object_downloadMetamodelXml.java b/api/applib/src/main/java/org/apache/isis/applib/mixins/metamodel/Object_downloadMetamodelXml.java
index 7997218e0f..25afe87158 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/mixins/metamodel/Object_downloadMetamodelXml.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/mixins/metamodel/Object_downloadMetamodelXml.java
@@ -87,7 +87,7 @@ public class Object_downloadMetamodelXml {
         final String namespace = logicalTypeIfAny.get().getNamespace();
 
         val config = Config.builder()
-                .ignoreNoopFacets(true)
+                .ignoreFallbackFacets(true)
                 .ignoreAbstractClasses(true)
                 .ignoreInterfaces(true)
                 .ignoreBuiltInValueTypes(true)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/Config.java b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/Config.java
index 7ad6c77686..05238f51c2 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/Config.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/Config.java
@@ -19,11 +19,7 @@
 package org.apache.isis.applib.services.metamodel;
 
 import java.util.Set;
-import java.util.function.UnaryOperator;
 
-import org.springframework.lang.Nullable;
-
-import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Sets;
 
 import lombok.Builder;
@@ -40,15 +36,13 @@ import lombok.val;
 @Getter @Builder
 public class Config {
 
-    private final boolean ignoreNoopFacets;
+    private final boolean ignoreFallbackFacets;
     private final boolean ignoreInterfaces;
     private final boolean ignoreAbstractClasses;
     private final boolean ignoreBuiltInValueTypes;
     private final boolean ignoreMixins;
     private final boolean includeShadowedFacets;
     private final boolean includeTitleAnnotations;
-    @Builder.Default
-    private final UnaryOperator<String> fqcnAbbreviator = Config::abbreviate;
 
     @Builder.Default
     private final Set<String> namespacePrefixes = _Sets.newHashSet();
@@ -69,43 +63,15 @@ public class Config {
 
     public Config.ConfigBuilder asBuilder() {
         return Config.builder()
-                .ignoreNoopFacets(ignoreNoopFacets)
+                .ignoreFallbackFacets(ignoreFallbackFacets)
                 .ignoreInterfaces(ignoreInterfaces)
                 .ignoreAbstractClasses(ignoreAbstractClasses)
                 .ignoreBuiltInValueTypes(ignoreBuiltInValueTypes)
                 .ignoreMixins(ignoreMixins)
                 .includeShadowedFacets(includeShadowedFacets)
                 .includeTitleAnnotations(includeTitleAnnotations)
-                .fqcnAbbreviator(fqcnAbbreviator)
                 .namespacePrefixes(_Sets.newHashSet(namespacePrefixes));
     }
 
-    public String abbrev(final @Nullable Class<?> cls) {
-        if(cls==null) { return ""; }
-        return getFqcnAbbreviator().apply(cls.getName());
-    }
-
-    public String simpleName(final @Nullable Class<?> cls) {
-        if(cls==null) { return ""; }
-        return simpleName(cls.getName());
-    }
-
-    // -- DEFAULTS
-
-    static String abbreviate(final String input) {
-        return (""+input)
-                .replace("org.apache.isis.core.metamodel.facets.", "».c.m.f.")
-                .replace("org.apache.isis.core.metamodel.", "».c.m.")
-                .replace("org.apache.isis.core.", "».c.")
-                .replace("org.apache.isis.applib.", "».a.")
-                .replace("org.apache.isis.", "».")
-                .replace("java.lang.", "");
-    }
-
-    static String simpleName(final String name) {
-        return _Strings.splitThenStream(""+name, ".")
-        .reduce((first, second) -> second) // get the last
-        .orElse("null");
-    }
 
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java
index e1f0e64c5f..46973befb7 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelServiceMenu.java
@@ -284,7 +284,7 @@ public class MetaModelServiceMenu {
             final boolean includeInterfaces,
             final List<String> namespaces) {
         var config = Config.builder()
-                .ignoreNoopFacets(true)
+                .ignoreFallbackFacets(true)
                 .ignoreAbstractClasses(true)
                 .ignoreBuiltInValueTypes(true)
                 .ignoreInterfaces(!includeInterfaces)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/inspect/Object_inspectMetamodel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/inspect/Object_inspectMetamodel.java
index 753f741780..e2d9dad4f2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/inspect/Object_inspectMetamodel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/inspect/Object_inspectMetamodel.java
@@ -77,7 +77,7 @@ public class Object_inspectMetamodel {
         final String namespace = logicalTypeIfAny.get().getNamespace();
 
         val config = Config.builder()
-                .ignoreNoopFacets(true)
+                .ignoreFallbackFacets(true)
                 .ignoreAbstractClasses(true)
                 .ignoreInterfaces(true)
                 .ignoreBuiltInValueTypes(true)
@@ -112,6 +112,6 @@ public class Object_inspectMetamodel {
     }
 
     @Inject private MetaModelService metaModelService;
-    @Inject MessageService messageService;
+    @Inject private MessageService messageService;
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelAnnotator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelAnnotator.java
new file mode 100644
index 0000000000..637d2e6fd5
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelAnnotator.java
@@ -0,0 +1,92 @@
+/*
+ *  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.services.metamodel;
+
+import org.springframework.lang.Nullable;
+
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.schema.metamodel.v2.Action;
+import org.apache.isis.schema.metamodel.v2.Collection;
+import org.apache.isis.schema.metamodel.v2.DomainClassDto;
+import org.apache.isis.schema.metamodel.v2.Facet;
+import org.apache.isis.schema.metamodel.v2.Param;
+import org.apache.isis.schema.metamodel.v2.Property;
+
+/**
+ * SPI that allows to add arbitrary meta data as
+ * {@link org.apache.isis.schema.metamodel.v2.Annotation}s
+ * to the metamodel schema. Like eg. node titles for rendering of the metamodel tree structure.
+ * <p>
+ * Particularly useful for metamodel export tools.
+ *
+ * @since 2.0 {@index}
+ */
+public interface MetaModelAnnotator {
+
+    ExporterConfig config();
+
+    void annotate(Facet facetType, org.apache.isis.core.metamodel.facetapi.Facet facet);
+
+    void annotate(DomainClassDto domainClass, ObjectSpecification specification);
+
+    void annotate(Action actionType, ObjectAction action);
+
+    void annotate(Param parameterType, ObjectActionParameter parameter);
+
+    void annotate(Property propertyType, OneToOneAssociation property);
+
+    void annotate(Collection collectionType, OneToManyAssociation collection);
+
+    public interface ExporterConfig {
+
+        default String abbrev(final @Nullable Class<?> cls) {
+            if(cls==null) { return ""; }
+            return abbreviate(cls.getName());
+        }
+
+        default String simpleName(final @Nullable Class<?> cls) {
+            if(cls==null) { return ""; }
+            return simpleName(cls.getName());
+        }
+
+        // -- DEFAULTS
+
+        static String abbreviate(final String input) {
+            return (""+input)
+                    .replace("org.apache.isis.core.metamodel.facets.", "».c.m.f.")
+                    .replace("org.apache.isis.core.metamodel.", "».c.m.")
+                    .replace("org.apache.isis.core.", "».c.")
+                    .replace("org.apache.isis.applib.", "».a.")
+                    .replace("org.apache.isis.", "».")
+                    .replace("java.lang.", "");
+        }
+
+        static String simpleName(final String name) {
+            return _Strings.splitThenStream(""+name, ".")
+            .reduce((first, second) -> second) // get the last
+            .orElse("null");
+        }
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
index 6e2b71f2d8..a1132ce04b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelExporter.java
@@ -30,6 +30,7 @@ import java.util.regex.Pattern;
 import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
 import org.apache.isis.applib.services.metamodel.Config;
 import org.apache.isis.applib.spec.Specification;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
@@ -60,10 +61,14 @@ import lombok.val;
 
 class MetaModelExporter {
 
-    SpecificationLoader specificationLookup;
+    private final SpecificationLoader specificationLookup;
+    private final Can<? extends MetaModelAnnotator> annotators;
 
-    public MetaModelExporter(final SpecificationLoader specificationLoader) {
+    public MetaModelExporter(
+            final SpecificationLoader specificationLoader,
+            final Iterable<? extends MetaModelAnnotator> annotators) {
         this.specificationLookup = specificationLoader;
+        this.annotators = Can.ofIterable(annotators);
     }
 
     /**
@@ -179,12 +184,11 @@ class MetaModelExporter {
             final ObjectSpecification specification, final Config config) {
 
         final DomainClassDto domainClass = new DomainClassDto();
-        _Util.titleAnnotation(domainClass, specification, config);
         domainClass.setId(specification.getFullIdentifier());
-
         if(specification.isInjectable()) {
             domainClass.setService(true);
         }
+        annotators.forEach(a->a.annotate(domainClass, specification));
         return domainClass;
     }
 
@@ -277,7 +281,6 @@ class MetaModelExporter {
             final Map<ObjectSpecification, DomainClassDto> domainClassByObjectSpec,
             final Config config) {
         Property propertyType = new Property();
-        _Util.titleAnnotation(propertyType, otoa, config);
         propertyType.setId(otoa.getId());
         propertyType.setMixedIn(otoa.isMixedIn());
         propertyType.setFacets(new org.apache.isis.schema.metamodel.v2.FacetHolder.Facets());
@@ -286,6 +289,7 @@ class MetaModelExporter {
         propertyType.setType(value);
 
         addFacets(otoa, propertyType.getFacets(), config);
+        annotators.forEach(a->a.annotate(propertyType, otoa));
         return propertyType;
     }
 
@@ -294,7 +298,6 @@ class MetaModelExporter {
             final Map<ObjectSpecification, DomainClassDto> domainClassByObjectSpec,
             final Config config) {
         Collection collectionType = new Collection();
-        _Util.titleAnnotation(collectionType, otoa, config);
         collectionType.setId(otoa.getId());
         collectionType.setMixedIn(otoa.isMixedIn());
         collectionType.setFacets(new org.apache.isis.schema.metamodel.v2.FacetHolder.Facets());
@@ -303,6 +306,7 @@ class MetaModelExporter {
         collectionType.setType(value);
 
         addFacets(otoa, collectionType.getFacets(), config);
+        annotators.forEach(a->a.annotate(collectionType, otoa));
         return collectionType;
     }
 
@@ -311,7 +315,6 @@ class MetaModelExporter {
             final Map<ObjectSpecification, DomainClassDto> domainClassByObjectSpec,
             final Config config) {
         Action actionType = new Action();
-        _Util.titleAnnotation(actionType, oa, config);
         actionType.setId(oa.getId());
         actionType.setMixedIn(oa.isMixedIn());
         actionType.setFacets(new org.apache.isis.schema.metamodel.v2.FacetHolder.Facets());
@@ -328,6 +331,7 @@ class MetaModelExporter {
         for (final ObjectActionParameter parameter : parameters) {
             params.add(asXsdType(parameter, domainClassByObjectSpec, config));
         }
+        annotators.forEach(a->a.annotate(actionType, oa));
         return actionType;
     }
 
@@ -352,7 +356,6 @@ class MetaModelExporter {
         Param parameterType = parameter instanceof OneToOneActionParameter
                     ? new ScalarParam()
                     : new VectorParam();
-        _Util.titleAnnotation(parameterType, parameter, config);
         parameterType.setId(parameter.getId());
         parameterType.setFacets(new org.apache.isis.schema.metamodel.v2.FacetHolder.Facets());
 
@@ -361,6 +364,7 @@ class MetaModelExporter {
         parameterType.setType(value);
 
         addFacets(parameter, parameterType.getFacets(), config);
+        annotators.forEach(a->a.annotate(parameterType, parameter));
         return parameterType;
     }
 
@@ -371,7 +375,9 @@ class MetaModelExporter {
 
         final List<org.apache.isis.schema.metamodel.v2.Facet> facetList = facets.getFacet();
         facetHolder.streamFacets()
-        .filter(facet -> !facet.getPrecedence().isFallback() || !config.isIgnoreNoopFacets())
+        .filter(facet -> ! (
+                config.isIgnoreFallbackFacets()
+                    && facet.getPrecedence().isFallback()))
         .map(facet -> asXsdType(facet, config))
         .forEach(facetList::add);
 
@@ -383,12 +389,11 @@ class MetaModelExporter {
             final Config config) {
         final org.apache.isis.schema.metamodel.v2.Facet facetType =
                 new org.apache.isis.schema.metamodel.v2.Facet();
-        _Util.titleAnnotation(facetType, facet, config);
         facetType.setId(facet.facetType().getCanonicalName());
         facetType.setFqcn(facet.getClass().getCanonicalName());
 
         addFacetAttributes(facet, facetType, config);
-
+        annotators.forEach(a->a.annotate(facetType, facet));
         return facetType;
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
index 9de2ade501..331ec60c34 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/MetaModelServiceDefault.java
@@ -42,11 +42,13 @@ import org.apache.isis.applib.services.metamodel.Config;
 import org.apache.isis.applib.services.metamodel.DomainMember;
 import org.apache.isis.applib.services.metamodel.DomainModel;
 import org.apache.isis.applib.services.metamodel.MetaModelService;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.IsisModuleCoreMetamodel;
 import org.apache.isis.core.metamodel.facets.members.publish.command.CommandPublishingFacet;
+import org.apache.isis.core.metamodel.services.metamodel.MetaModelAnnotator.ExporterConfig;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.MixedIn;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
@@ -87,8 +89,6 @@ public class MetaModelServiceDefault implements MetaModelService {
     }
 
 
-
-
     @Override
     public DomainModel getDomainModel() {
 
@@ -222,7 +222,16 @@ public class MetaModelServiceDefault implements MetaModelService {
 
     @Override
     public MetamodelDto exportMetaModel(final Config config) {
-        return new MetaModelExporter(specificationLoader).exportMetaModel(config);
+
+        /*TODO[ISIS-3206] refactor: ideally config would provide the list, but unfortunately
+         * MetaModelAnnotator type is not know to Config, which lives in applib.
+         */
+        val annotators = config.isIncludeTitleAnnotations()
+                ? (Iterable) Can.<MetaModelAnnotator>of(new TitleAnnotator(new ExporterConfig(){}))
+                : Can.empty();
+
+        return new MetaModelExporter(specificationLoader, annotators)
+                .exportMetaModel(config);
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/_Util.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/TitleAnnotator.java
similarity index 68%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/_Util.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/TitleAnnotator.java
index a7efe528a2..2357707368 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/_Util.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/metamodel/TitleAnnotator.java
@@ -18,7 +18,6 @@
  */
 package org.apache.isis.core.metamodel.services.metamodel;
 
-import org.apache.isis.applib.services.metamodel.Config;
 import org.apache.isis.core.config.progmodel.ProgrammingModelConstants.CollectionSemantics;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
@@ -35,92 +34,86 @@ import org.apache.isis.schema.metamodel.v2.MetamodelElement.Annotations;
 import org.apache.isis.schema.metamodel.v2.Param;
 import org.apache.isis.schema.metamodel.v2.Property;
 
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
 import lombok.val;
-import lombok.experimental.UtilityClass;
+import lombok.experimental.Accessors;
 
-@UtilityClass
-class _Util {
+@RequiredArgsConstructor
+public class TitleAnnotator implements MetaModelAnnotator {
 
-    String withSuffix(String fileName, String suffix) {
-        if(!suffix.startsWith(".")) {
-            suffix = "." + suffix;
-        }
-        if(!fileName.endsWith(suffix)) {
-            fileName += suffix;
-        }
-        return fileName;
-    }
+    @Getter(onMethod_={@Override}) @Accessors(fluent=true)
+    private final ExporterConfig config;
 
-    static void titleAnnotation(
+    @Override
+    public void annotate(
             final Facet facetType,
-            final org.apache.isis.core.metamodel.facetapi.Facet facet,
-            final Config config) {
-        if(!config.isIncludeTitleAnnotations()) return;
+            final org.apache.isis.core.metamodel.facetapi.Facet facet) {
         titleAnnotation(facetType,
                 String.format("%s: %s",
-                        config.simpleName(facet.facetType()),
-                        config.abbrev(facet.getClass())));
+                        config().simpleName(facet.facetType()),
+                        config().abbrev(facet.getClass())));
     }
 
-    static void titleAnnotation(
-            final DomainClassDto domainClass, final ObjectSpecification specification, final Config config) {
-        if(!config.isIncludeTitleAnnotations()) return;
+    @Override
+    public void annotate(
+            final DomainClassDto domainClass, final ObjectSpecification specification) {
         titleAnnotation(domainClass,
                 String.format("%s: %s",
                         specification.getLogicalTypeName(),
-                        config.abbrev(specification.getCorrespondingClass())));
+                        config().abbrev(specification.getCorrespondingClass())));
     }
 
-    static void titleAnnotation(
-            final Action actionType, final ObjectAction action, final Config config) {
-        if(!config.isIncludeTitleAnnotations()) return;
+    @Override
+    public void annotate(
+            final Action actionType, final ObjectAction action) {
         titleAnnotation(actionType,
             String.format("%s(...): %s%s",
                     action.getId(),
-                    config.abbrev(action.getReturnType().getCorrespondingClass()),
+                    config().abbrev(action.getReturnType().getCorrespondingClass()),
                     titleSuffix(action.isMixedIn())));
     }
 
-    static void titleAnnotation(
-            final Param parameterType, final ObjectActionParameter parameter, final Config config) {
-        if(!config.isIncludeTitleAnnotations()) return;
+    @Override
+    public void annotate(
+            final Param parameterType, final ObjectActionParameter parameter) {
         titleAnnotation(parameterType,
                 String.format("%s: %s",
                         parameter.getId(),
                         parameter.isScalar()
-                        ? config.abbrev(parameter.getElementType().getCorrespondingClass())
-                        : renderTypeOf((OneToManyFeature) parameter, config))
+                        ? config().abbrev(parameter.getElementType().getCorrespondingClass())
+                        : renderTypeOf((OneToManyFeature) parameter, config()))
                 );
     }
 
-    static void titleAnnotation(
-            final Property propertyType, final OneToOneAssociation property, final Config config) {
-        if(!config.isIncludeTitleAnnotations()) return;
+    @Override
+    public void annotate(
+            final Property propertyType, final OneToOneAssociation property) {
         titleAnnotation(propertyType,
                 String.format("%s: %s%s",
                         property.getId(),
-                        config.abbrev(property.getElementType().getCorrespondingClass()),
+                        config().abbrev(property.getElementType().getCorrespondingClass()),
                         titleSuffix(property.isMixedIn())));
     }
 
-    static void titleAnnotation(
-            final Collection collectionType, final OneToManyAssociation collection, final Config config) {
-        if(!config.isIncludeTitleAnnotations()) return;
+    @Override
+    public void annotate(
+            final Collection collectionType, final OneToManyAssociation collection) {
         titleAnnotation(collectionType,
                 String.format("%s: %s%s",
                         collection.getId(),
-                        renderTypeOf(collection, config),
+                        renderTypeOf(collection, config()),
                         titleSuffix(collection.isMixedIn())));
     }
 
     // -- HELPER
 
-    private String renderTypeOf(final OneToManyFeature nonScalarFeature, final Config config) {
+    private String renderTypeOf(final OneToManyFeature nonScalarFeature, final ExporterConfig exporterConfig) {
         val toac = nonScalarFeature.getTypeOfAnyCardinality();
         val containerType = toac.getCollectionSemantics()
                 .map(CollectionSemantics::getContainerType)
-                .map(config::simpleName).orElse("?");
-        val elementType = config.abbrev(toac.getElementType());
+                .map(exporterConfig::simpleName).orElse("?");
+        val elementType = exporterConfig.abbrev(toac.getElementType());
         return String.format("%s<%s>", containerType, elementType);
     }