You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/09/02 13:57:55 UTC

[isis] 02/02: ISIS-2774: adds support for enforcing member annotations

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

commit 9bd81d798eec9d40314fa0b1830195d24942c4b1
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Sep 2 15:57:40 2021 +0200

    ISIS-2774: adds support for enforcing member annotations
    
    fixes iconName facet not being imperative
    
    some fixes to imperative orphaned method checking: ignore the event rank
    when enumerating imperative facets
---
 .../isis/core/metamodel/facetapi/FacetHolder.java  |  7 +--
 .../metamodel/facetapi/FacetHolderAbstract.java    |  5 ++
 .../core/metamodel/facetapi/HasFacetHolder.java    | 13 +++--
 .../choices/enums/EnumValueSemanticsProvider.java  |  2 +-
 .../method/CssClassFacetMethodFactory.java         |  2 +-
 .../object/icon/method/IconFacetMethodFactory.java | 22 ++++----
 ...Method.java => IconFacetViaIconNameMethod.java} | 29 ++++++++--
 .../facets/object/layout/LayoutFacetFactory.java   |  2 +-
 .../annotation/TitleAnnotationFacetFactory.java    | 47 ++++++++--------
 .../title/methods/TitleFacetViaMethodsFactory.java |  2 +-
 ...tionEnforcesMetamodelContributionValidator.java |  6 ++-
 .../metamodel/methods/MethodFinderOptions.java     | 63 +++++++++++++---------
 .../core/metamodel/methods/MethodFinderUtils.java  | 25 ++++-----
 .../metamodel/methods/MethodLiteralConstants.java  | 19 +++++++
 .../ident/icon/IconFacetMethodFactoryTest.java     |  4 +-
 .../object/ident/icon/IconFacetMethodTest.java     |  6 +--
 .../DomainModelTest_usingGoodDomain.java           | 17 ++++++
 .../good/ProperMemberInheritanceAbstract.java      |  2 +
 .../good/ProperMemberInheritanceInterface.java     |  6 ++-
 .../model/good/ProperMemberSupport_action.java     | 15 +++---
 .../model/good/ProperMemberSupport_action2.java    | 13 ++---
 .../model/good/ProperMemberSupport_action3.java    | 13 ++---
 ...lWithAnnotationOptionalUsingPrivateSupport.java | 12 ++++-
 23 files changed, 217 insertions(+), 115 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolder.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolder.java
index 04f4731..25eaa64 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolder.java
@@ -80,7 +80,7 @@ public interface FacetHolder extends HasMetaModelContext {
      * Convenience; saves having to {@link #getFacet(Class)} and then check if
      * <tt>null</tt> and not a fallback.
      */
-    default boolean containsNonFallbackFacet(Class<? extends Facet> facetType) {
+    default boolean containsNonFallbackFacet(final Class<? extends Facet> facetType) {
         val facet = getFacet(facetType);
         return facet != null
                 && !facet.getPrecedence().isFallback();
@@ -90,7 +90,7 @@ public interface FacetHolder extends HasMetaModelContext {
      * As {@link #containsNonFallbackFacet(Class)}, with additional requirement, that the
      * facet is <i>explicit</i>, not {@link Facet.Precedence#isInferred() inferred}.
      */
-    default boolean containsExplicitNonFallbackFacet(Class<? extends Facet> facetType) {
+    default boolean containsExplicitNonFallbackFacet(final Class<? extends Facet> facetType) {
         val facet = getFacet(facetType);
         return facet != null
                 && !facet.getPrecedence().isFallback()
@@ -99,7 +99,7 @@ public interface FacetHolder extends HasMetaModelContext {
 
     Stream<Facet> streamFacets();
 
-    default <F extends Facet> Stream<F> streamFacets(Class<F> requiredType) {
+    default <F extends Facet> Stream<F> streamFacets(final Class<F> requiredType) {
         return streamFacets()
                 .filter(facet->requiredType.isAssignableFrom(facet.getClass()))
                 .map(requiredType::cast);
@@ -117,6 +117,7 @@ public interface FacetHolder extends HasMetaModelContext {
 
     // -- VALIDATION SUPPORT
 
+    Stream<FacetRanking> streamFacetRankings();
     Optional<FacetRanking> getFacetRanking(Class<? extends Facet> facetType);
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolderAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolderAbstract.java
index fec986b..b577e11 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolderAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/FacetHolderAbstract.java
@@ -109,6 +109,11 @@ implements FacetHolder {
     // -- VALIDATION SUPPORT
 
     @Override
+    public Stream<FacetRanking> streamFacetRankings() {
+        return rankingByType.values().stream();
+    }
+
+    @Override
     public Optional<FacetRanking> getFacetRanking(final Class<? extends Facet> facetType) {
         return Optional.ofNullable(rankingByType.get(facetType));
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/HasFacetHolder.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/HasFacetHolder.java
index e9db430..4240743 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/HasFacetHolder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facetapi/HasFacetHolder.java
@@ -35,12 +35,12 @@ public interface HasFacetHolder extends FacetHolder {
     }
 
     @Override
-    default <T extends Facet> T getFacet(Class<T> cls) {
+    default <T extends Facet> T getFacet(final Class<T> cls) {
         return getFacetHolder().getFacet(cls);
     }
 
     @Override
-    default boolean containsFacet(Class<? extends Facet> facetType) {
+    default boolean containsFacet(final Class<? extends Facet> facetType) {
         return getFacetHolder().containsFacet(facetType);
     }
 
@@ -50,12 +50,17 @@ public interface HasFacetHolder extends FacetHolder {
     }
 
     @Override
-    default void addFacet(Facet facet) {
+    default void addFacet(final Facet facet) {
         getFacetHolder().addFacet(facet);
     }
 
     @Override
-    default Optional<FacetRanking> getFacetRanking(Class<? extends Facet> facetType) {
+    default Stream<FacetRanking> streamFacetRankings() {
+        return getFacetHolder().streamFacetRankings();
+    }
+
+    @Override
+    default Optional<FacetRanking> getFacetRanking(final Class<? extends Facet> facetType) {
         return getFacetHolder().getFacetRanking(facetType);
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/choices/enums/EnumValueSemanticsProvider.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/choices/enums/EnumValueSemanticsProvider.java
index fd912c0..08ddb3d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/choices/enums/EnumValueSemanticsProvider.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/choices/enums/EnumValueSemanticsProvider.java
@@ -83,7 +83,7 @@ implements EnumFacet {
                 defaultFor(adaptedClass));
 
         titleMethod = MethodFinderUtils.findMethod_returningText(
-                MethodFinderOptions.layoutSupport(introspectionPolicy),
+                MethodFinderOptions.objectSupport(introspectionPolicy),
                 getAdaptedClass(),
                 TITLE,
                 null);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/cssclass/method/CssClassFacetMethodFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/cssclass/method/CssClassFacetMethodFactory.java
index bd66e0b..5001ed3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/cssclass/method/CssClassFacetMethodFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/cssclass/method/CssClassFacetMethodFactory.java
@@ -48,7 +48,7 @@ extends MethodPrefixBasedFacetFactoryAbstract {
 
         final Method method = MethodFinderUtils.findMethod(
                 MethodFinderOptions
-                .layoutSupport(processClassContext.getIntrospectionPolicy()),
+                .objectSupport(processClassContext.getIntrospectionPolicy()),
                 cls, PREFIX, String.class, NO_ARG);
         if (method == null) {
             return;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethodFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethodFactory.java
index 1eb9ef6..0b42c28 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethodFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethodFactory.java
@@ -19,43 +19,41 @@
 
 package org.apache.isis.core.metamodel.facets.object.icon.method;
 
-import java.lang.reflect.Method;
-
 import javax.inject.Inject;
 
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facetapi.FacetUtil;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.methods.MethodFinderOptions;
 import org.apache.isis.core.metamodel.methods.MethodFinderUtils;
 import org.apache.isis.core.metamodel.methods.MethodLiteralConstants;
 import org.apache.isis.core.metamodel.methods.MethodPrefixBasedFacetFactoryAbstract;
 
+import lombok.val;
+
 public class IconFacetMethodFactory
 extends MethodPrefixBasedFacetFactoryAbstract {
 
-    private static final String PREFIX = MethodLiteralConstants.ICON_NAME;
+    private static final String METHOD_NAME = MethodLiteralConstants.ICON_NAME;
 
     @Inject
     public IconFacetMethodFactory(final MetaModelContext mmc) {
-        super(mmc, FeatureType.OBJECTS_ONLY, OrphanValidation.VALIDATE, Can.ofSingleton(PREFIX));
+        super(mmc, FeatureType.OBJECTS_ONLY, OrphanValidation.VALIDATE, Can.ofSingleton(METHOD_NAME));
     }
 
     @Override
     public void process(final ProcessClassContext processClassContext) {
-        final Class<?> cls = processClassContext.getCls();
-        final FacetHolder facetHolder = processClassContext.getFacetHolder();
+        val cls = processClassContext.getCls();
+        val facetHolder = processClassContext.getFacetHolder();
 
-        final Method method = MethodFinderUtils.findMethod(
+        val method = MethodFinderUtils.findMethod(
                 MethodFinderOptions
-                .layoutSupport(processClassContext.getIntrospectionPolicy()),
-                cls, PREFIX, String.class, NO_ARG);
+                .objectSupport(processClassContext.getIntrospectionPolicy()),
+                cls, METHOD_NAME, String.class, NO_ARG);
         if (method == null) {
             return;
         }
         processClassContext.removeMethod(method);
-        FacetUtil.addFacet(new IconFacetMethod(method, facetHolder));
+        addFacet(new IconFacetViaIconNameMethod(method, facetHolder));
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetViaIconNameMethod.java
similarity index 66%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethod.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetViaIconNameMethod.java
index 7bb3802..8c83d44 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetMethod.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/icon/method/IconFacetViaIconNameMethod.java
@@ -22,26 +22,32 @@ package org.apache.isis.core.metamodel.facets.object.icon.method;
 import java.lang.reflect.Method;
 import java.util.function.BiConsumer;
 
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
 import org.apache.isis.core.metamodel.facets.object.icon.IconFacetAbstract;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 
+import lombok.Getter;
 import lombok.NonNull;
+import lombok.val;
 
-public class IconFacetMethod
-extends IconFacetAbstract {
+public class IconFacetViaIconNameMethod
+extends IconFacetAbstract
+implements ImperativeFacet {
 
-    private final @NonNull Method method;
+    @Getter(onMethod_ = {@Override}) private final @NonNull Can<Method> methods;
 
-    public IconFacetMethod(final Method method, final FacetHolder holder) {
+    public IconFacetViaIconNameMethod(final Method method, final FacetHolder holder) {
         super(holder);
-        this.method = method;
+        this.methods = ImperativeFacet.singleMethod(method);
     }
 
     @Override
     public String iconName(final ManagedObject owningAdapter) {
         try {
+            val method = methods.getFirstOrFail();
             return (String) ManagedObjects.InvokeUtil.invoke(method, owningAdapter);
         } catch (final RuntimeException ex) {
             return null;
@@ -49,8 +55,21 @@ extends IconFacetAbstract {
     }
 
     @Override
+    public Intent getIntent(final Method method) {
+        return Intent.UI_HINT;
+    }
+
+    @Override
+    protected String toStringValues() {
+        val method = methods.getFirstOrFail();
+        return "method=" + method;
+    }
+
+    @Override
     public void visitAttributes(final BiConsumer<String, Object> visitor) {
+        val method = methods.getFirstOrFail();
         super.visitAttributes(visitor);
+        ImperativeFacet.visitAttributes(this, visitor);
         visitor.accept("method", method);
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/layout/LayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/layout/LayoutFacetFactory.java
index 15ca351..c12f40b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/layout/LayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/layout/LayoutFacetFactory.java
@@ -46,7 +46,7 @@ extends MethodPrefixBasedFacetFactoryAbstract {
 
         final Method method = MethodFinderUtils.findMethod(
                 MethodFinderOptions
-                .layoutSupport(processClassContext.getIntrospectionPolicy()),
+                .objectSupport(processClassContext.getIntrospectionPolicy()),
                 cls, PREFIX, String.class, NO_ARG);
 
         final LayoutFacet layoutFacet;
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
index 02d8bbc..dc74a6f 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
@@ -19,7 +19,6 @@
 
 package org.apache.isis.core.metamodel.facets.object.title.annotation;
 
-import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -42,6 +41,7 @@ import org.apache.isis.core.metamodel.facets.fallback.FallbackFacetFactory;
 import org.apache.isis.core.metamodel.facets.object.title.methods.TitleFacetViaMethodsFactory;
 import org.apache.isis.core.metamodel.methods.MethodFinderOptions;
 import org.apache.isis.core.metamodel.methods.MethodFinderUtils;
+import org.apache.isis.core.metamodel.methods.MethodLiteralConstants;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.specimpl.ObjectSpecificationAbstract;
@@ -147,43 +147,46 @@ implements MetaModelRefiner {
 
 
     /**
-     * Violation if there is a class that has both a <tt>title()</tt> method and also any non-inherited method
-     * annotated with <tt>@Title</tt>.
-     *
+     * Violation if there is a class that has both a <tt>title()</tt> method
+     * and also any declared (non-inherited) method annotated with <tt>@Title</tt>.
      * <p>
-     * If there are only inherited methods annotated with <tt>@Title</tt> then this is <i>not</i> a violation; but
-     * (from the implementation of {@link TitleFacetViaMethodsFactory} the imperative <tt>title()</tt> method will take
+     * If there are only inherited methods annotated with <tt>@Title</tt>
+     * then this is <i>not</i> a violation;
+     * but (from the implementation of {@link TitleFacetViaMethodsFactory}
+     * the imperative <tt>title()</tt> method will take
      * precedence.
      */
     @Override
     public void refineProgrammingModel(final ProgrammingModel programmingModel) {
 
-        programmingModel.addVisitingValidatorSkipManagedBeans(objectSpec -> {
+        programmingModel.addVisitingValidatorSkipManagedBeans(_objectSpec -> {
 
-            final Class<?> cls = objectSpec.getCorrespondingClass();
-            final var introspectionPolicy = ((ObjectSpecificationAbstract)objectSpec).getIntrospectionPolicy();
+            final var objectSpec = (ObjectSpecificationAbstract)_objectSpec;
+            final var cls = objectSpec.getCorrespondingClass();
+            final var introspectionPolicy = objectSpec.getIntrospectionPolicy();
 
-            final Method titleMethod = MethodFinderUtils.findMethod(
-                    MethodFinderOptions.layoutSupport(introspectionPolicy),
+            final var titleMethod = MethodFinderUtils.findMethod(
+                    MethodFinderOptions.objectSupport(introspectionPolicy),
                     cls, TITLE_METHOD_NAME, String.class, null);
             if (titleMethod == null) {
                 return;
             }
 
             // determine if cls contains an @Title annotated method, not inherited from superclass
-            final ObjectSpecification supClass = objectSpec.superclass();
-            if (supClass == null) {
+            final ObjectSpecification superSpec = objectSpec.superclass();
+            if (superSpec == null) {
                 return;
             }
 
-            final var superIntrospectionPolicy = ((ObjectSpecificationAbstract)supClass).getIntrospectionPolicy();
+            final var superIntrospectionPolicy = ((ObjectSpecificationAbstract)superSpec).getIntrospectionPolicy();
+            final var superCls = superSpec.getCorrespondingClass();
 
-            final List<Method> methods = methodsWithTitleAnnotation(superIntrospectionPolicy, cls);
-            final List<Method> superClassMethods = methodsWithTitleAnnotation(superIntrospectionPolicy, supClass.getCorrespondingClass());
-            if (methods.size() > superClassMethods.size()) {
+            if (countMethodsWithTitleAnnotation(introspectionPolicy, cls)
+                    > countMethodsWithTitleAnnotation(superIntrospectionPolicy, superCls)) {
                 ValidationFailure.raiseFormatted(
                         objectSpec,
-                        "%s: conflict for determining a strategy for retrieval of title for class, contains a method '%s' and an annotation '@%s'",
+                        "%s: conflict for determining a strategy for retrieval of title for class, "
+                        + "contains a method '%s' and an annotation '@%s'",
                         objectSpec.getFeatureIdentifier().getClassName(),
                         TITLE_METHOD_NAME,
                         Title.class.getName());
@@ -192,11 +195,13 @@ implements MetaModelRefiner {
         });
     }
 
-    private static List<Method> methodsWithTitleAnnotation(
+    private static long countMethodsWithTitleAnnotation(
             final IntrospectionPolicy introspectionPolicy,
             final Class<?> cls) {
-        return MethodFinderUtils.findMethodsWithAnnotation(
-                MethodFinderOptions.layoutSupport(introspectionPolicy), cls, Title.class);
+        return MethodFinderUtils.streamMethodsWithAnnotation(
+                MethodFinderOptions.objectSupport(introspectionPolicy), cls, Title.class)
+                .filter(method->!method.getName().equals(MethodLiteralConstants.TITLE))
+                .count();
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/methods/TitleFacetViaMethodsFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/methods/TitleFacetViaMethodsFactory.java
index dcd98ae..29888c2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/methods/TitleFacetViaMethodsFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/title/methods/TitleFacetViaMethodsFactory.java
@@ -70,7 +70,7 @@ extends MethodPrefixBasedFacetFactoryAbstract {
 
         val titleMethod = MethodFinderUtils.findMethod_returningText(
                 MethodFinderOptions
-                .layoutSupport(processClassContext.getIntrospectionPolicy()),
+                .objectSupport(processClassContext.getIntrospectionPolicy()),
                 cls,
                 TITLE,
                 NO_ARG);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/DomainIncludeAnnotationEnforcesMetamodelContributionValidator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/DomainIncludeAnnotationEnforcesMetamodelContributionValidator.java
index a6420ac..d68313a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/DomainIncludeAnnotationEnforcesMetamodelContributionValidator.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/DomainIncludeAnnotationEnforcesMetamodelContributionValidator.java
@@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.methods;
 import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
 
@@ -93,7 +94,10 @@ extends MetaModelVisitingValidatorAbstract {
 
         spec
         .streamFacetHolders()
-        .flatMap(FacetHolder::streamFacets)
+        .flatMap(FacetHolder::streamFacetRankings)
+        .map(facetRanking->facetRanking.getWinnerNonEvent(facetRanking.facetType()))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
         .filter(ImperativeFacet.class::isInstance)
         .map(ImperativeFacet.class::cast)
         .map(ImperativeFacet::getMethods)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderOptions.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderOptions.java
index e3f29bd..656ff50 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderOptions.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderOptions.java
@@ -25,11 +25,14 @@ import java.util.function.Predicate;
 import org.apache.isis.applib.annotation.Domain;
 import org.apache.isis.applib.annotation.Introspection.EncapsulationPolicy;
 import org.apache.isis.applib.annotation.Introspection.IntrospectionPolicy;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.functions._Predicates;
 import org.apache.isis.commons.internal.reflection._Annotations;
 import org.apache.isis.commons.internal.reflection._Reflect;
 
+import lombok.NonNull;
 import lombok.Value;
+import lombok.val;
 
 @Value(staticConstructor = "of")
 public class MethodFinderOptions {
@@ -53,32 +56,32 @@ public class MethodFinderOptions {
         return havingAnyOrNoAnnotation(memberIntrospectionPolicy);
     }
 
-    public static MethodFinderOptions memberSupport(
-            final IntrospectionPolicy memberIntrospectionPolicy) {
-        return havingAnnotationIfEnforcedByPolicyOrAccessibility(
-                memberIntrospectionPolicy, Domain.Include.class);
-    }
-
     public static MethodFinderOptions objectSupport(
             final IntrospectionPolicy memberIntrospectionPolicy) {
         return havingAnnotationIfEnforcedByPolicyOrAccessibility(
-                memberIntrospectionPolicy, Domain.Include.class);
+                memberIntrospectionPolicy,
+                Domain.Include.class,
+                MethodLiteralConstants.ConflictingAnnotations.OBJECT_SUPPORT.getProhibits());
     }
 
     public static MethodFinderOptions livecycleCallback(
             final IntrospectionPolicy memberIntrospectionPolicy) {
         return havingAnnotationIfEnforcedByPolicyOrAccessibility(
-                memberIntrospectionPolicy, Domain.Include.class);
+                memberIntrospectionPolicy,
+                Domain.Include.class,
+                MethodLiteralConstants.ConflictingAnnotations.OBJECT_LIFECYCLE.getProhibits());
     }
 
-    public static MethodFinderOptions layoutSupport(
+    public static MethodFinderOptions memberSupport(
             final IntrospectionPolicy memberIntrospectionPolicy) {
         return havingAnnotationIfEnforcedByPolicyOrAccessibility(
-                memberIntrospectionPolicy, Domain.Include.class);
+                memberIntrospectionPolicy,
+                Domain.Include.class,
+                MethodLiteralConstants.ConflictingAnnotations.MEMBER_SUPPORT.getProhibits());
     }
 
-    private final EncapsulationPolicy encapsulationPolicy;
-    private final Predicate<Method> mustSatisfy;
+    private final @NonNull EncapsulationPolicy encapsulationPolicy;
+    private final @NonNull Predicate<Method> mustSatisfy;
 
     // -- HELPER
 
@@ -91,7 +94,8 @@ public class MethodFinderOptions {
 
     private static MethodFinderOptions havingAnnotationIfEnforcedByPolicyOrAccessibility(
             final IntrospectionPolicy memberIntrospectionPolicy,
-            final Class<? extends Annotation> annotationType) {
+            final Class<? extends Annotation> annotationType,
+            final Can<Class<? extends Annotation>> conflictingAnnotations) {
 
         //MemberAnnotationPolicy
         //  when REQUIRED -> annot. on support also required
@@ -100,22 +104,33 @@ public class MethodFinderOptions {
         return of(
                 EncapsulationPolicy.ENCAPSULATED_MEMBERS_SUPPORTED, // support methods are always allowed private
                 memberIntrospectionPolicy.getMemberAnnotationPolicy().isMemberAnnotationsRequired()
-                    ? method->_Annotations.synthesizeInherited(method, annotationType).isPresent()
+                    ? method->havingAnnotation(method, annotationType, conflictingAnnotations)
                     : method-> !_Reflect.isAccessible(method)
-                            ? _Annotations.synthesizeInherited(method, annotationType).isPresent()
+                            ? havingAnnotation(method, annotationType, conflictingAnnotations)
                             : true);
 
     }
 
+    //FIXME[ISIS-2774] if annotation appears on an abstract method that was inherited with given method,
+    // its not detected here
+    private static boolean havingAnnotation(
+            final Method method,
+            final Class<? extends Annotation> annotationType,
+            final Can<Class<? extends Annotation>> conflictingAnnotations) {
+
+        val isMarkerAnnotationPresent = _Annotations.synthesizeInherited(method, annotationType).isPresent();
+        if(isMarkerAnnotationPresent) {
+
+            val isConflictingAnnotationPresent = conflictingAnnotations
+            .stream()
+            .anyMatch(conflictingAnnotationType->
+                    _Annotations.synthesizeInherited(method, conflictingAnnotationType).isPresent());
+
+            // do not pickup this method if conflicting - so meta-model validation will fail later on
+            return !isConflictingAnnotationPresent;
+        }
+        return isMarkerAnnotationPresent;
+    }
 
-//    private static MethodFinderOptions havingAnnotation(
-//            final IntrospectionPolicy memberIntrospectionPolicy,
-//            final Class<? extends Annotation> associatedAnnotationType) {
-//        return of(
-//                memberIntrospectionPolicy.getEncapsulationPolicy(),
-//                memberIntrospectionPolicy.getMemberAnnotationPolicy().isMemberAnnotationsRequired()
-//                    ? method->_Annotations.synthesizeInherited(method, associatedAnnotationType).isPresent()
-//                    : _Predicates.alwaysTrue());
-//    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderUtils.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderUtils.java
index 83c7d3f..82575d3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderUtils.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodFinderUtils.java
@@ -23,10 +23,8 @@ import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.isis.applib.services.i18n.TranslatableString;
@@ -108,6 +106,10 @@ public final class MethodFinderUtils {
             }
         }
 
+        if(!options.getMustSatisfy().test(method)) {
+            return null;
+        }
+
         return method;
     }
 
@@ -165,21 +167,15 @@ public final class MethodFinderUtils {
         }
     }
 
-    public static List<Method> findMethodsWithAnnotation(
-            final MethodFinderOptions options,
-            final Class<?> type,
-            final Class<? extends Annotation> annotationClass) {
-
-        // Validate arguments
-        if (type == null || annotationClass == null) {
-            throw new IllegalArgumentException("One or more arguments are 'null' valued");
-        }
+    public static Stream<Method> streamMethodsWithAnnotation(
+            final @NonNull MethodFinderOptions options,
+            final @NonNull Class<?> type,
+            final @NonNull Class<? extends Annotation> annotationClass) {
 
         // Find methods annotated with the specified annotation
         return streamMethods(options, type)
                 .filter(method -> !MethodUtil.isStatic(method))
-                .filter(method -> method.isAnnotationPresent(annotationClass))
-                .collect(Collectors.toList());
+                .filter(method -> method.isAnnotationPresent(annotationClass));
     }
 
     public static void removeMethod(final MethodRemover methodRemover, final Method method) {
@@ -368,7 +364,8 @@ public final class MethodFinderUtils {
         val methodCache = _MethodCache.getInstance();
         return options.getEncapsulationPolicy().isEncapsulatedMembersSupported()
                 ? methodCache.streamPublicOrDeclaredMethods(type)
-                : methodCache.streamPublicMethods(type);
+                : methodCache.streamPublicMethods(type)
+                .filter(options.getMustSatisfy()::test);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodLiteralConstants.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodLiteralConstants.java
index 531fa0d..9ea4df7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodLiteralConstants.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/methods/MethodLiteralConstants.java
@@ -18,15 +18,22 @@
  */
 package org.apache.isis.core.metamodel.methods;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.util.function.IntFunction;
 
 import org.springframework.lang.Nullable;
 
+import org.apache.isis.applib.annotation.MemberSupport;
+import org.apache.isis.applib.annotation.ObjectLifecycle;
+import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.commons.StringExtensions;
 
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
 public final class MethodLiteralConstants {
 
     // -- ACCESSORS
@@ -77,6 +84,18 @@ public final class MethodLiteralConstants {
 
     public static final String TO_STRING = "toString";
 
+    // -- CONFLICTING MARKER ANNOTATIONS
+
+    @RequiredArgsConstructor @Getter
+    public static enum ConflictingAnnotations {
+        OBJECT_SUPPORT(Can.of(ObjectLifecycle.class, MemberSupport.class)),
+        OBJECT_LIFECYCLE(Can.of(ObjectSupport.class, MemberSupport.class)),
+        MEMBER_SUPPORT(Can.of(ObjectSupport.class, ObjectLifecycle.class));
+        final Can<Class<? extends Annotation>> prohibits;
+    }
+
+    // -- METHOD NAMING CONVENTIONS
+
     @FunctionalInterface
     public static interface SupportingMethodNameProviderForAction {
         @Nullable String getActionSupportingMethodName(Method actionMethod, String prefix, boolean isMixin);
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
index 382b575..fbcfebd 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodFactoryTest.java
@@ -30,7 +30,7 @@ import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facets.AbstractFacetFactoryTest;
 import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessClassContext;
 import org.apache.isis.core.metamodel.facets.object.icon.IconFacet;
-import org.apache.isis.core.metamodel.facets.object.icon.method.IconFacetMethod;
+import org.apache.isis.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod;
 import org.apache.isis.core.metamodel.facets.object.icon.method.IconFacetMethodFactory;
 
 public class IconFacetMethodFactoryTest extends AbstractFacetFactoryTest {
@@ -64,7 +64,7 @@ public class IconFacetMethodFactoryTest extends AbstractFacetFactoryTest {
 
         final Facet facet = facetedMethod.getFacet(IconFacet.class);
         assertThat(facet, is(notNullValue()));
-        assertThat(facet, is(instanceOf(IconFacetMethod.class)));
+        assertThat(facet, is(instanceOf(IconFacetViaIconNameMethod.class)));
 
         assertTrue(methodRemover.getRemovedMethodMethodCalls().contains(iconNameMethod));
     }
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
index bd156f2..8a4ccf3 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/icon/IconFacetMethodTest.java
@@ -29,7 +29,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.object.icon.method.IconFacetMethod;
+import org.apache.isis.core.metamodel.facets.object.icon.method.IconFacetViaIconNameMethod;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -40,7 +40,7 @@ public class IconFacetMethodTest {
 
     private final Mockery mockery = new JUnit4Mockery();
 
-    private IconFacetMethod facet;
+    private IconFacetViaIconNameMethod facet;
     private FacetHolder mockFacetHolder;
 
     private ManagedObject mockOwningAdapter;
@@ -60,7 +60,7 @@ public class IconFacetMethodTest {
         mockFacetHolder = mockery.mock(FacetHolder.class);
         mockOwningAdapter = mockery.mock(ManagedObject.class);
         final Method iconNameMethod = DomainObjectWithProblemInIconNameMethod.class.getMethod("iconName");
-        facet = new IconFacetMethod(iconNameMethod, mockFacetHolder);
+        facet = new IconFacetViaIconNameMethod(iconNameMethod, mockFacetHolder);
 
         mockery.checking(new Expectations() {
             {
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
index a5d79f7..e8a0912 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java
@@ -489,6 +489,23 @@ class DomainModelTest_usingGoodDomain {
         coll.assertCollectionElements(List.of("Foo"));
     }
 
+//    @Test
+//    void quicktest() {
+//        val testerFactory = new DomainObjectTesterFactory(serviceInjector);
+//        val prop = testerFactory.propertyTester(DomainObjectList.class, "elementTypeFqcn");
+//        prop.assertExists(true);
+//
+//        val objectSpec = specificationLoader.specForTypeElseFail(DomainObjectList.class);
+//
+//        val titleFacet = objectSpec.getFacetRanking(TitleFacet.class).get().getWinnerNonEvent(TitleFacet.class).get();
+//        assertNotNull(titleFacet);
+//        assertTrue(titleFacet instanceof ImperativeFacet, "no imperative: " + titleFacet.getClass());
+//
+//        ((ImperativeFacet)titleFacet).getMethods()
+//        .forEach(System.err::println);
+//
+//    }
+
     @Test
     void viewmodelWithAnnotationOptional_usingPrivateSupport() {
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceAbstract.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceAbstract.java
index 2c2b879..873431a 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceAbstract.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceAbstract.java
@@ -32,10 +32,12 @@ import lombok.Setter;
 
 abstract class ProperMemberInheritanceAbstract {
 
+    //@Title //FIXME[ISIS-2774] either not picked up, or validation fails when abstract
     public String title() {
         return "inherited title";
     }
 
+    //@ObjectSupport //FIXME[ISIS-2774] either not picked up, or validation fails when abstract
     public String iconName() {
         return "inherited icon";
     }
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceInterface.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceInterface.java
index 2f2a1ed..1680ed6 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceInterface.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberInheritanceInterface.java
@@ -24,15 +24,19 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
+import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Title;
 
 public interface ProperMemberInheritanceInterface {
 
+    @Title
     default String title() {
         return "inherited title";
     }
 
+    @ObjectSupport
     default String iconName() {
         return "inherited icon";
     }
@@ -63,7 +67,7 @@ public interface ProperMemberInheritanceInterface {
 
     @Action
     @ActionLayout(named = "foo", describedAs = "bar")
-    default void sampleActionOverrideWithParam(String x) {
+    default void sampleActionOverrideWithParam(final String x) {
     }
 
     @Property
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action.java
index 4f93a318..404611b 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action.java
@@ -37,7 +37,8 @@ public class ProperMemberSupport_action {
 
     // proper mix-in action
     //@Action // <-- inferred by annotation on type above
-    public ProperMemberSupport act(String p0, String p1) {
+    @MemberSupport
+    public ProperMemberSupport act(final String p0, final String p1) {
         return holder;
     }
 
@@ -52,17 +53,17 @@ public class ProperMemberSupport_action {
     }
 
     @MemberSupport
-    public String validateAct(String p0, String p1) {
+    public String validateAct(final String p0, final String p1) {
         return null;
     }
 
     @MemberSupport
-    public Set<String> autoComplete0Act(@MinLength(3) String search) {
+    public Set<String> autoComplete0Act(@MinLength(3) final String search) {
         return null;
     }
 
     @MemberSupport
-    public Set<String> autoComplete1Act(@MinLength(3) String search) {
+    public Set<String> autoComplete1Act(@MinLength(3) final String search) {
         return null;
     }
 
@@ -77,7 +78,7 @@ public class ProperMemberSupport_action {
     }
 
     @MemberSupport
-    public Set<String> choices1Act(String p0) {
+    public Set<String> choices1Act(final String p0) {
         return null;
     }
 
@@ -92,12 +93,12 @@ public class ProperMemberSupport_action {
     }
 
     @MemberSupport
-    public String validate0Act(String p0) {
+    public String validate0Act(final String p0) {
         return null;
     }
 
     @MemberSupport
-    public String validate1Act(String p1) {
+    public String validate1Act(final String p1) {
         return null;
     }
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action2.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action2.java
index 03158aa..29b3adf 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action2.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action2.java
@@ -35,7 +35,8 @@ public class ProperMemberSupport_action2 {
 
     // proper mix-in action
 
-    public ProperMemberSupport act(String p0, String p1) {
+    @MemberSupport
+    public ProperMemberSupport act(final String p0, final String p1) {
         return holder;
     }
 
@@ -50,17 +51,17 @@ public class ProperMemberSupport_action2 {
     }
 
     @MemberSupport
-    public String validateAct(String p0, String p1) {
+    public String validateAct(final String p0, final String p1) {
         return null;
     }
 
     @MemberSupport
-    public Set<String> autoComplete0Act(@MinLength(3) String search) {
+    public Set<String> autoComplete0Act(@MinLength(3) final String search) {
         return null;
     }
 
     @MemberSupport
-    public Set<String> autoComplete1Act(@MinLength(3) String search) {
+    public Set<String> autoComplete1Act(@MinLength(3) final String search) {
         return null;
     }
 
@@ -97,12 +98,12 @@ public class ProperMemberSupport_action2 {
     }
 
     @MemberSupport
-    public String validate0Act(String p0) {
+    public String validate0Act(final String p0) {
         return null;
     }
 
     @MemberSupport
-    public String validate1Act(String p1) {
+    public String validate1Act(final String p1) {
         return null;
     }
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action3.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action3.java
index 7bad058..48fca58 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action3.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ProperMemberSupport_action3.java
@@ -36,7 +36,8 @@ public class ProperMemberSupport_action3 {
 
     // proper mix-in action
 
-    public ProperMemberSupport act(List<String> p0, List<String> p1) {
+    @MemberSupport
+    public ProperMemberSupport act(final List<String> p0, final List<String> p1) {
         return holder;
     }
 
@@ -51,17 +52,17 @@ public class ProperMemberSupport_action3 {
     }
 
     @MemberSupport
-    public String validateAct(List<String> p0, List<String> p1) {
+    public String validateAct(final List<String> p0, final List<String> p1) {
         return null;
     }
 
     @MemberSupport
-    public Set<String> autoComplete0Act(@MinLength(3) String search) {
+    public Set<String> autoComplete0Act(@MinLength(3) final String search) {
         return null;
     }
 
     @MemberSupport
-    public Set<String> autoComplete1Act(@MinLength(3) String search) {
+    public Set<String> autoComplete1Act(@MinLength(3) final String search) {
         return null;
     }
 
@@ -98,12 +99,12 @@ public class ProperMemberSupport_action3 {
     }
 
     @MemberSupport
-    public String validate0Act(List<String> p0) {
+    public String validate0Act(final List<String> p0) {
         return null;
     }
 
     @MemberSupport
-    public String validate1Act(List<String> p1) {
+    public String validate1Act(final List<String> p1) {
         return null;
     }
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ViewModelWithAnnotationOptionalUsingPrivateSupport.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ViewModelWithAnnotationOptionalUsingPrivateSupport.java
index d0ba85e..33c9e91 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ViewModelWithAnnotationOptionalUsingPrivateSupport.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/good/ViewModelWithAnnotationOptionalUsingPrivateSupport.java
@@ -27,7 +27,9 @@ import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Introspection;
 import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.Title;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -46,6 +48,12 @@ implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
+    // allowed to be private since 2.0.0-M7
+    @Title
+    private String title() {
+        return "view-model with annotation optional using private support";
+    }
+
     // -- PUBLIC ACTION WITH PRIVATE SUPPORT
 
     @Action
@@ -86,13 +94,13 @@ implements Serializable {
     // -- PRIVATE OBJECT SUPPORT
 
     // allowed to be private since 2.0.0-M7
-    @MemberSupport//FIXME[ISIS-2774] be more specific, don't allow @MemberSupport
+    @ObjectSupport
     private String disabled() {
         return "object disabled for testing purposes";
     }
 
     // allowed to be private since 2.0.0-M7
-    @MemberSupport//FIXME[ISIS-2774] be more specific, don't allow @MemberSupport
+    @ObjectSupport
     private boolean hidden() {
         return false;
     }