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/04/12 18:46:05 UTC
[isis] branch master updated: ISIS-2604: implement new mm
validators and provide tests
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 45cf097 ISIS-2604: implement new mm validators and provide tests
45cf097 is described below
commit 45cf097b464b1630f48661660cc6ca61bc872d79
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Apr 12 20:45:49 2021 +0200
ISIS-2604: implement new mm validators and provide tests
---
.../isis/core/metamodel/facets/FacetFactory.java | 41 ++++----
.../action/ActionAnnotationFacetFactory.java | 24 ++++-
.../actions/layout/ActionLayoutFacetFactory.java | 22 +++-
.../CollectionAnnotationFacetFactory.java | 39 ++++---
.../layout/CollectionLayoutFacetFactory.java | 21 +++-
.../property/PropertyAnnotationFacetFactory.java | 23 +++--
.../propertylayout/PropertyLayoutFacetFactory.java | 21 +++-
...ModelValidatorForAmbiguousMixinAnnotations.java | 47 +++++++++
...etaModelValidatorForConflictingOptionality.java | 2 +
.../model/bad/AmbiguousMixinAnnotations.java | 109 +++++++++++++++++++
.../DomainModelTest_usingBadDomain.java | 115 +++++++++++++--------
...nModelTest_usingBadDomain_noActionEnforced.java | 4 +-
.../applib/validate/DomainModelValidator.java | 18 ++--
13 files changed, 385 insertions(+), 101 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/FacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/FacetFactory.java
index ea558f4..9eaa73b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/FacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/FacetFactory.java
@@ -25,7 +25,6 @@ import java.lang.reflect.Parameter;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.function.Supplier;
import org.apache.isis.commons.collections.ImmutableEnumSet;
import org.apache.isis.commons.internal.exceptions._Exceptions;
@@ -36,6 +35,8 @@ import org.apache.isis.core.metamodel.facetapi.FeatureType;
import org.apache.isis.core.metamodel.facetapi.MethodRemover;
import lombok.Getter;
+import lombok.NonNull;
+import lombok.val;
public interface FacetFactory {
@@ -239,22 +240,28 @@ public interface FacetFactory {
/**
* Annotation lookup on this context's method, if not found, extends search to type in case
* the predicate {@link #isMixinMain} evaluates {@code true}.
+ * <p>
+ * As of [ISIS-2604] we also make sure the annotation type does not appear in both places
+ * (method and type). Hence the 2nd parameter is a callback that fires if the annotation
+ * is found in both places.
+ *
* @since 2.0
*/
- public <A extends Annotation> Optional<A> synthesizeOnMethodOrMixinType(Class<A> annotationType) {
- return computeIfAbsent(synthesizeOnMethod(annotationType),
- ()-> isMixinMain()
- ? synthesizeOnType(annotationType)
- : Optional.empty() ) ;
- }
-
- private static <T> Optional<T> computeIfAbsent(
- Optional<T> optional,
- Supplier<Optional<T>> supplier) {
+ public <A extends Annotation> Optional<A> synthesizeOnMethodOrMixinType(
+ final @NonNull Class<A> annotationType,
+ final @NonNull Runnable onAmbiguity) {
+
+
+ val onMethod = synthesizeOnMethod(annotationType);
+ val onType = synthesizeOnType(annotationType);
- return optional.isPresent() ?
- optional
- : supplier.get();
+ if(onMethod.isPresent()) {
+ if(onType.isPresent()) {
+ onAmbiguity.run();
+ }
+ return onMethod;
+ }
+ return onType;
}
}
@@ -265,11 +272,7 @@ public interface FacetFactory {
*/
void process(ProcessMethodContext processMethodContext);
-
-
- // //////////////////////////////////////
- // process param
- // //////////////////////////////////////
+ // -- PROCESS PARAM
public static class ProcessParameterContext
extends AbstractProcessWithMethodContext<FacetedMethodParameter> {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
index 5b5dbba..0950722 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java
@@ -27,6 +27,7 @@ import org.apache.isis.applib.mixins.system.HasInteractionId;
import org.apache.isis.commons.internal.base._Strings;
import org.apache.isis.commons.internal.collections._Collections;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
import org.apache.isis.core.metamodel.facets.actions.action.associateWith.AssociatedWithFacetForActionAnnotation;
@@ -46,13 +47,21 @@ import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFac
import org.apache.isis.core.metamodel.facets.members.publish.command.CommandPublishingFacetForActionAnnotation;
import org.apache.isis.core.metamodel.facets.members.publish.execution.ExecutionPublishingActionFacetForActionAnnotation;
import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import org.apache.isis.core.metamodel.util.EventUtil;
import lombok.val;
-public class ActionAnnotationFacetFactory extends FacetFactoryAbstract {
+public class ActionAnnotationFacetFactory
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
+ private final MetaModelValidatorForAmbiguousMixinAnnotations ambiguousMixinAnnotationsValidator
+ = new MetaModelValidatorForAmbiguousMixinAnnotations();
+
+
public ActionAnnotationFacetFactory() {
super(FeatureType.ACTIONS_ONLY);
}
@@ -60,7 +69,11 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract {
@Override
public void process(final ProcessMethodContext processMethodContext) {
- val actionIfAny = processMethodContext.synthesizeOnMethodOrMixinType(Action.class);
+ val actionIfAny = processMethodContext
+ .synthesizeOnMethodOrMixinType(
+ Action.class,
+ () -> ambiguousMixinAnnotationsValidator
+ .addValidationFailure(processMethodContext.getFacetHolder(), Action.class));
processExplicit(processMethodContext, actionIfAny);
processInvocation(processMethodContext, actionIfAny);
@@ -285,4 +298,11 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract {
super.addFacet(facet);
}
+ // -- METAMODEL REFINER
+
+ @Override
+ public void refineProgrammingModel(ProgrammingModel programmingModel) {
+ programmingModel.addValidator(ambiguousMixinAnnotationsValidator);
+ }
+
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
index 0e978ff..1b38452 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/layout/ActionLayoutFacetFactory.java
@@ -20,6 +20,7 @@ package org.apache.isis.core.metamodel.facets.actions.layout;
import org.apache.isis.applib.annotation.ActionLayout;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
import org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacet;
import org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacetFallback;
@@ -36,11 +37,17 @@ import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFac
import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetFromActionLayoutAnnotation;
import org.apache.isis.core.metamodel.facets.object.bookmarkpolicy.BookmarkPolicyFacet;
import org.apache.isis.core.metamodel.facets.object.promptStyle.PromptStyleFacet;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import lombok.val;
public class ActionLayoutFacetFactory
-extends FacetFactoryAbstract {
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
+
+ private final MetaModelValidatorForAmbiguousMixinAnnotations ambiguousMixinAnnotationsValidator
+ = new MetaModelValidatorForAmbiguousMixinAnnotations();
public ActionLayoutFacetFactory() {
super(FeatureType.ACTIONS_ONLY);
@@ -50,7 +57,11 @@ extends FacetFactoryAbstract {
public void process(final ProcessMethodContext processMethodContext) {
val facetHolder = processMethodContext.getFacetHolder();
- val actionLayoutIfAny = processMethodContext.synthesizeOnMethodOrMixinType(ActionLayout.class);
+ val actionLayoutIfAny = processMethodContext
+ .synthesizeOnMethodOrMixinType(
+ ActionLayout.class,
+ () -> ambiguousMixinAnnotationsValidator
+ .addValidationFailure(processMethodContext.getFacetHolder(), ActionLayout.class));
// bookmarkable
BookmarkPolicyFacet bookmarkableFacet = BookmarkPolicyFacetForActionLayoutAnnotation
@@ -108,4 +119,11 @@ extends FacetFactoryAbstract {
}
+ // -- METAMODEL REFINER
+
+ @Override
+ public void refineProgrammingModel(ProgrammingModel programmingModel) {
+ programmingModel.addValidator(ambiguousMixinAnnotationsValidator);
+ }
+
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
index 568dfec..dfc31f1 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java
@@ -30,6 +30,7 @@ import org.apache.isis.applib.events.domain.CollectionDomainEvent;
import org.apache.isis.commons.internal.collections._Collections;
import org.apache.isis.core.metamodel.facetapi.FacetUtil;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetInferredFromArray;
@@ -44,12 +45,19 @@ import org.apache.isis.core.metamodel.facets.collections.collection.modify.Colle
import org.apache.isis.core.metamodel.facets.collections.collection.typeof.TypeOfFacetOnCollectionFromCollectionAnnotation;
import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation;
import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import org.apache.isis.core.metamodel.util.EventUtil;
import lombok.val;
-public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract {
+public class CollectionAnnotationFacetFactory
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
+
+ private final MetaModelValidatorForAmbiguousMixinAnnotations ambiguousMixinAnnotationsValidator
+ = new MetaModelValidatorForAmbiguousMixinAnnotations();
public CollectionAnnotationFacetFactory() {
super(FeatureType.COLLECTIONS_AND_ACTIONS);
@@ -58,7 +66,11 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract {
@Override
public void process(final ProcessMethodContext processMethodContext) {
- val collectionIfAny = processMethodContext.synthesizeOnMethodOrMixinType(Collection.class);
+ val collectionIfAny = processMethodContext
+ .synthesizeOnMethodOrMixinType(
+ Collection.class,
+ () -> ambiguousMixinAnnotationsValidator
+ .addValidationFailure(processMethodContext.getFacetHolder(), Collection.class));
inferIntentWhenOnTypeLevel(processMethodContext, collectionIfAny);
@@ -96,8 +108,6 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract {
return;
}
-
-
// following only runs for regular collections, not for mixins.
// those are tackled in the post-processing, when more of the metamodel is available to us
@@ -105,19 +115,17 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract {
// Set up CollectionDomainEventFacet, which will act as the hiding/disabling/validating advisor
//
-
// search for @Collection(domainEvent=...)
val collectionDomainEventFacet = collectionIfAny
- .map(Collection::domainEvent)
- .filter(domainEvent -> domainEvent != CollectionDomainEvent.Default.class)
- .map(domainEvent ->
+ .map(Collection::domainEvent)
+ .filter(domainEvent -> domainEvent != CollectionDomainEvent.Default.class)
+ .map(domainEvent ->
(CollectionDomainEventFacetAbstract)
new CollectionDomainEventFacetForCollectionAnnotation(
defaultFromDomainObjectIfRequired(typeSpec, domainEvent), holder))
- .orElse(
- new CollectionDomainEventFacetDefault(
- defaultFromDomainObjectIfRequired(typeSpec, CollectionDomainEvent.Default.class), holder)
- );
+ .orElse(
+ new CollectionDomainEventFacetDefault(
+ defaultFromDomainObjectIfRequired(typeSpec, CollectionDomainEvent.Default.class), holder));
if(!CollectionDomainEvent.Noop.class.isAssignableFrom(collectionDomainEventFacet.getEventType())) {
super.addFacet(collectionDomainEventFacet);
}
@@ -219,5 +227,12 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract {
return null;
}
+ // -- METAMODEL REFINER
+
+ @Override
+ public void refineProgrammingModel(ProgrammingModel programmingModel) {
+ programmingModel.addValidator(ambiguousMixinAnnotationsValidator);
+ }
+
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/layout/CollectionLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/layout/CollectionLayoutFacetFactory.java
index 2fdcef2..fba354c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/layout/CollectionLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/layout/CollectionLayoutFacetFactory.java
@@ -20,14 +20,21 @@ package org.apache.isis.core.metamodel.facets.collections.layout;
import org.apache.isis.applib.annotation.CollectionLayout;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetFromCollectionLayoutAnnotation;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import lombok.val;
public class CollectionLayoutFacetFactory
-extends FacetFactoryAbstract {
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
+ private final MetaModelValidatorForAmbiguousMixinAnnotations ambiguousMixinAnnotationsValidator
+ = new MetaModelValidatorForAmbiguousMixinAnnotations();
+
public CollectionLayoutFacetFactory() {
super(FeatureType.COLLECTIONS_AND_ACTIONS);
}
@@ -36,7 +43,11 @@ extends FacetFactoryAbstract {
public void process(final ProcessMethodContext processMethodContext) {
val facetHolder = processMethodContext.getFacetHolder();
- val collectionLayoutIfAny = processMethodContext.synthesizeOnMethodOrMixinType(CollectionLayout.class);
+ val collectionLayoutIfAny = processMethodContext
+ .synthesizeOnMethodOrMixinType(
+ CollectionLayout.class,
+ () -> ambiguousMixinAnnotationsValidator
+ .addValidationFailure(processMethodContext.getFacetHolder(), CollectionLayout.class));
val cssClassFacet = CssClassFacetForCollectionLayoutAnnotation
.create(collectionLayoutIfAny, facetHolder);
@@ -71,6 +82,12 @@ extends FacetFactoryAbstract {
super.addFacet(sortedByFacet);
}
+ // -- METAMODEL REFINER
+
+ @Override
+ public void refineProgrammingModel(ProgrammingModel programmingModel) {
+ programmingModel.addValidator(ambiguousMixinAnnotationsValidator);
+ }
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
index fe2ded9..c2a9579 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
@@ -33,8 +33,8 @@ import org.apache.isis.core.metamodel.facetapi.FacetUtil;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
-import org.apache.isis.core.metamodel.facets.actions.contributing.ContributingFacetAbstract;
import org.apache.isis.core.metamodel.facets.actions.contributing.ContributingFacet.Contributing;
+import org.apache.isis.core.metamodel.facets.actions.contributing.ContributingFacetAbstract;
import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacetAbstract;
import org.apache.isis.core.metamodel.facets.members.publish.command.CommandPublishingFacetForPropertyAnnotation;
import org.apache.isis.core.metamodel.facets.members.publish.execution.ExecutionPublishingPropertyFacetForPropertyAnnotation;
@@ -62,16 +62,21 @@ import org.apache.isis.core.metamodel.facets.properties.update.clear.PropertyCle
import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacet;
import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForConflictingOptionality;
import org.apache.isis.core.metamodel.util.EventUtil;
import lombok.val;
-public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract
+public class PropertyAnnotationFacetFactory
+extends FacetFactoryAbstract
implements MetaModelRefiner {
- private final MetaModelValidatorForConflictingOptionality conflictingOptionalityValidator =
- new MetaModelValidatorForConflictingOptionality();
+ private final MetaModelValidatorForConflictingOptionality conflictingOptionalityValidator
+ = new MetaModelValidatorForConflictingOptionality();
+
+ private final MetaModelValidatorForAmbiguousMixinAnnotations ambiguousMixinAnnotationsValidator
+ = new MetaModelValidatorForAmbiguousMixinAnnotations();
public PropertyAnnotationFacetFactory() {
super(FeatureType.PROPERTIES_AND_ACTIONS);
@@ -86,7 +91,11 @@ implements MetaModelRefiner {
@Override
public void process(final ProcessMethodContext processMethodContext) {
- val propertyIfAny = processMethodContext.synthesizeOnMethodOrMixinType(Property.class);
+ val propertyIfAny = processMethodContext
+ .synthesizeOnMethodOrMixinType(
+ Property.class,
+ () -> ambiguousMixinAnnotationsValidator
+ .addValidationFailure(processMethodContext.getFacetHolder(), Property.class));
inferIntentWhenOnTypeLevel(processMethodContext, propertyIfAny);
@@ -104,7 +113,6 @@ implements MetaModelRefiner {
processFileAccept(processMethodContext, propertyIfAny);
}
-
void inferIntentWhenOnTypeLevel(ProcessMethodContext processMethodContext, Optional<Property> propertyIfAny) {
if(!processMethodContext.isMixinMain() || !propertyIfAny.isPresent()) {
return; // no @Property found neither type nor method
@@ -371,11 +379,12 @@ implements MetaModelRefiner {
super.addFacet(facet);
}
- // //////////////////////////////////////
+ // -- METAMODEL REFINER
@Override
public void refineProgrammingModel(ProgrammingModel programmingModel) {
programmingModel.addValidator(conflictingOptionalityValidator);
+ programmingModel.addValidator(ambiguousMixinAnnotationsValidator);
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/propertylayout/PropertyLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/propertylayout/PropertyLayoutFacetFactory.java
index 609b6ae..1706647 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/propertylayout/PropertyLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/propertylayout/PropertyLayoutFacetFactory.java
@@ -21,14 +21,21 @@ package org.apache.isis.core.metamodel.facets.properties.propertylayout;
import org.apache.isis.applib.annotation.PropertyLayout;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelRefiner;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
import org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacetFromPropertyLayoutAnnotation;
import org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacetFromPropertyLayoutAnnotation;
+import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import lombok.val;
public class PropertyLayoutFacetFactory
-extends FacetFactoryAbstract {
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
+
+ private final MetaModelValidatorForAmbiguousMixinAnnotations ambiguousMixinAnnotationsValidator
+ = new MetaModelValidatorForAmbiguousMixinAnnotations();
public PropertyLayoutFacetFactory() {
super(FeatureType.PROPERTIES_AND_ACTIONS);
@@ -39,7 +46,10 @@ extends FacetFactoryAbstract {
val facetHolder = processMethodContext.getFacetHolder();
val propertyLayoutIfAny = processMethodContext
- .synthesizeOnMethodOrMixinType(PropertyLayout.class);
+ .synthesizeOnMethodOrMixinType(
+ PropertyLayout.class,
+ () -> ambiguousMixinAnnotationsValidator
+ .addValidationFailure(processMethodContext.getFacetHolder(), PropertyLayout.class));
val cssClassFacet = CssClassFacetForPropertyLayoutAnnotation
.create(propertyLayoutIfAny, facetHolder);
@@ -90,5 +100,12 @@ extends FacetFactoryAbstract {
super.addFacet(unchangingFacet);
}
+
+ // -- METAMODEL REFINER
+
+ @Override
+ public void refineProgrammingModel(ProgrammingModel programmingModel) {
+ programmingModel.addValidator(ambiguousMixinAnnotationsValidator);
+ }
}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForAmbiguousMixinAnnotations.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForAmbiguousMixinAnnotations.java
new file mode 100644
index 0000000..ce26fad
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForAmbiguousMixinAnnotations.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.metamodel.specloader.validator;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.isis.core.metamodel.facets.FacetedMethod;
+
+import lombok.val;
+
+public class MetaModelValidatorForAmbiguousMixinAnnotations
+extends MetaModelValidatorAbstract {
+
+ public <A extends Annotation> void addValidationFailure(
+ final FacetedMethod holder,
+ final Class<A> annotationType) {
+
+ final String annotationLiteral = "@" + annotationType.getSimpleName();
+ val identifier = holder.getIdentifier();
+
+ super.onFailure(
+ holder,
+ identifier,
+ "Annotation %s on both method and type level is not allowed, "
+ + "it must be one or the other. Found with mixin: %s",
+ annotationLiteral,
+ identifier.getFullIdentityString());
+ }
+
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java
index d449c57..35d0604 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorForConflictingOptionality.java
@@ -33,6 +33,8 @@ public class MetaModelValidatorForConflictingOptionality extends MetaModelValida
}
return facet;
}
+
+ // -- HELPER
private Facet addFailure(final Facet facet, final String message) {
if(facet != null) {
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/bad/AmbiguousMixinAnnotations.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/bad/AmbiguousMixinAnnotations.java
new file mode 100644
index 0000000..5f40bcf
--- /dev/null
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/model/bad/AmbiguousMixinAnnotations.java
@@ -0,0 +1,109 @@
+/*
+ * 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.testdomain.model.bad;
+
+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.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+
+import lombok.RequiredArgsConstructor;
+
+@SuppressWarnings("unused")
+public class AmbiguousMixinAnnotations {
+
+ @DomainObject(nature = Nature.VIEW_MODEL)
+ public static class Mixee {
+
+ }
+
+ // -- SHOULD FAIL VALIDATION
+
+ @Property
+ @RequiredArgsConstructor
+ public static class InvalidMixinP {
+ private final Mixee mixee;
+
+ @Property
+ public String prop() {
+ return null;
+ }
+ }
+
+ @PropertyLayout
+ @RequiredArgsConstructor
+ public static class InvalidMixinPL {
+ private final Mixee mixee;
+
+ @PropertyLayout
+ public String prop() {
+ return null;
+ }
+ }
+
+ @Collection
+ @RequiredArgsConstructor
+ public static class InvalidMixinC {
+ private final Mixee mixee;
+
+ @Collection
+ public String coll() {
+ return null;
+ }
+ }
+
+ @CollectionLayout
+ @RequiredArgsConstructor
+ public static class InvalidMixinCL {
+ private final Mixee mixee;
+
+ @CollectionLayout
+ public String coll() {
+ return null;
+ }
+ }
+
+ @Action
+ @RequiredArgsConstructor
+ public static class InvalidMixinA {
+ private final Mixee mixee;
+
+ @Action
+ public String act() {
+ return null;
+ }
+ }
+
+ @ActionLayout
+ @RequiredArgsConstructor
+ public static class InvalidMixinAL {
+ private final Mixee mixee;
+
+ @ActionLayout
+ public String act() {
+ return null;
+ }
+ }
+
+
+}
diff --git a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain.java b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain.java
index 7c5cec6..083a140 100644
--- a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain.java
+++ b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain.java
@@ -18,23 +18,39 @@
*/
package org.apache.isis.testdomain.domainmodel;
+import java.lang.annotation.Annotation;
+import java.util.stream.Stream;
+
import javax.inject.Inject;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.isis.applib.Identifier;
+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.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
import org.apache.isis.applib.exceptions.unrecoverable.DomainModelException;
+import org.apache.isis.applib.id.LogicalType;
import org.apache.isis.core.config.IsisConfiguration;
import org.apache.isis.core.config.environment.IsisSystemEnvironment;
import org.apache.isis.core.config.metamodel.specloader.IntrospectionMode;
import org.apache.isis.core.config.presets.IsisPresets;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.testdomain.conf.Configuration_headless;
+import org.apache.isis.testdomain.model.bad.AmbiguousMixinAnnotations;
import org.apache.isis.testdomain.model.bad.AmbiguousTitle;
import org.apache.isis.testdomain.model.bad.Configuration_usingInvalidDomain;
import org.apache.isis.testdomain.model.bad.InvalidOrphanedActionSupport;
@@ -43,8 +59,6 @@ import org.apache.isis.testdomain.model.bad.InvalidOrphanedPropertySupport;
import org.apache.isis.testdomain.model.bad.InvalidPropertyAnnotationOnAction;
import org.apache.isis.testing.integtestsupport.applib.validate.DomainModelValidator;
-import lombok.val;
-
@SpringBootTest(
classes = {
Configuration_headless.class,
@@ -65,6 +79,15 @@ class DomainModelTest_usingBadDomain {
@Inject private IsisSystemEnvironment isisSystemEnvironment;
@Inject private SpecificationLoader specificationLoader;
+ private DomainModelValidator validator;
+
+ @BeforeEach
+ void setup() {
+ validator = new DomainModelValidator(specificationLoader, configuration, isisSystemEnvironment);
+ assertThrows(DomainModelException.class, validator::throwIfInvalid);
+ }
+
+
@Test
void fullIntrospection_shouldBeEnabledByThisTestClass() {
assertTrue(IntrospectionMode.isFullIntrospect(configuration, isisSystemEnvironment));
@@ -72,71 +95,73 @@ class DomainModelTest_usingBadDomain {
@Test
void ambiguousTitle_shouldFail() {
-
- val validator = new DomainModelValidator(specificationLoader, configuration, isisSystemEnvironment);
-
- assertThrows(DomainModelException.class, validator::throwIfInvalid);
assertTrue(validator.anyMatchesContaining(
- AmbiguousTitle.class,
+ Identifier.classIdentifier(LogicalType.fqcn(AmbiguousTitle.class)),
"conflict for determining a strategy for retrieval of title"));
}
@Test
void orphanedActionSupport_shouldFail() {
-
- val validateDomainModel = new DomainModelValidator(specificationLoader, configuration, isisSystemEnvironment);
-
- assertThrows(DomainModelException.class, validateDomainModel::throwIfInvalid);
- assertTrue(validateDomainModel.anyMatchesContaining(
- InvalidOrphanedActionSupport.class,
+ assertTrue(validator.anyMatchesContaining(
+ Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedActionSupport.class)),
"is assumed to support"));
}
-
-// @Test
-// void orphanedActionSupportNotEnforced_shouldFail() {
-//
-// val validateDomainModel = new DomainModelValidator();
-//
-// assertThrows(DomainModelException.class, validateDomainModel::run);
-// assertTrue(validateDomainModel.anyMatchesContaining(
-// OrphanedPrefixedAction.class,
-// "is assumed to support"));
-// }
@Test
void orphanedPropertySupport_shouldFail() {
-
- val validateDomainModel = new DomainModelValidator(specificationLoader, configuration, isisSystemEnvironment);
-
- assertThrows(DomainModelException.class, validateDomainModel::throwIfInvalid);
- assertTrue(validateDomainModel.anyMatchesContaining(
- InvalidOrphanedPropertySupport.class,
+ assertTrue(validator.anyMatchesContaining(
+ Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedPropertySupport.class)),
"is assumed to support"));
}
@Test
void orphanedCollectionSupport_shouldFail() {
-
- val validateDomainModel = new DomainModelValidator(specificationLoader, configuration, isisSystemEnvironment);
-
- assertThrows(DomainModelException.class, validateDomainModel::throwIfInvalid);
- assertTrue(validateDomainModel.anyMatchesContaining(
- InvalidOrphanedCollectionSupport.class,
+ assertTrue(validator.anyMatchesContaining(
+ Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedCollectionSupport.class)),
"is assumed to support"));
}
+ @ParameterizedTest
+ @MethodSource("provideAmbiguousMixins")
+ void ambiguousMixinAnnotions_shouldFailValidation(
+ final Class<?> mixinClass,
+ final Class<? extends Annotation> annotationType,
+ final String mixinMethodName) {
+
+ final String annotationLiteral = "@" + annotationType.getSimpleName();
+ assertTrue(validator.anyMatchesContaining(
+ Identifier.propertyOrCollectionIdentifier(LogicalType.fqcn(mixinClass), mixinMethodName),
+ String.format("Annotation %s on both method and type level is not allowed", annotationLiteral)));
+ }
+
+ private static Stream<Arguments> provideAmbiguousMixins() {
+ return Stream.of(
+ Arguments.of(AmbiguousMixinAnnotations.InvalidMixinA.class, Action.class, "act"),
+ Arguments.of(AmbiguousMixinAnnotations.InvalidMixinAL.class, ActionLayout.class, "act"),
+ Arguments.of(AmbiguousMixinAnnotations.InvalidMixinP.class, Property.class, "prop"),
+ Arguments.of(AmbiguousMixinAnnotations.InvalidMixinPL.class, PropertyLayout.class, "prop"),
+ Arguments.of(AmbiguousMixinAnnotations.InvalidMixinC.class, Collection.class, "coll"),
+ Arguments.of(AmbiguousMixinAnnotations.InvalidMixinCL.class, CollectionLayout.class, "coll")
+ );
+ }
+
+ // -- INCUBATING
+
@Test @Disabled("this case has no vaildation refiner yet")
void invalidPropertyAnnotationOnAction_shouldFail() {
-
- val validateDomainModel = new DomainModelValidator(specificationLoader, configuration, isisSystemEnvironment);
-
- assertThrows(DomainModelException.class, validateDomainModel::throwIfInvalid);
- assertTrue(validateDomainModel.anyMatchesContaining(
- InvalidPropertyAnnotationOnAction.class,
+ assertTrue(validator.anyMatchesContaining(
+ Identifier.classIdentifier(LogicalType.fqcn(InvalidPropertyAnnotationOnAction.class)),
"TODO"));
}
-
-
-
+// @Test
+// void orphanedActionSupportNotEnforced_shouldFail() {
+//
+// val validateDomainModel = new DomainModelValidator();
+//
+// assertThrows(DomainModelException.class, validateDomainModel::run);
+// assertTrue(validateDomainModel.anyMatchesContaining(
+// OrphanedPrefixedAction.class,
+// "is assumed to support"));
+// }
}
diff --git a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain_noActionEnforced.java b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain_noActionEnforced.java
index fe170c7..63ec852 100644
--- a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain_noActionEnforced.java
+++ b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingBadDomain_noActionEnforced.java
@@ -28,7 +28,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.isis.applib.Identifier;
import org.apache.isis.applib.exceptions.unrecoverable.DomainModelException;
+import org.apache.isis.applib.id.LogicalType;
import org.apache.isis.core.config.IsisConfiguration;
import org.apache.isis.core.config.environment.IsisSystemEnvironment;
import org.apache.isis.core.config.metamodel.specloader.IntrospectionMode;
@@ -80,7 +82,7 @@ class DomainModelTest_usingBadDomain_noActionEnforced {
assertThrows(DomainModelException.class, validateDomainModel::throwIfInvalid);
assertTrue(validateDomainModel.anyMatchesContaining(
- InvalidOrphanedActionSupportNoActionEnforced.class,
+ Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedActionSupportNoActionEnforced.class)),
"is assumed to support"));
}
diff --git a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/validate/DomainModelValidator.java b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/validate/DomainModelValidator.java
index 854fd00..7da2d20 100644
--- a/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/validate/DomainModelValidator.java
+++ b/testing/integtestsupport/applib/src/main/java/org/apache/isis/testing/integtestsupport/applib/validate/DomainModelValidator.java
@@ -32,17 +32,15 @@ import org.junit.jupiter.api.Assertions;
import org.apache.isis.applib.Identifier;
import org.apache.isis.applib.exceptions.unrecoverable.DomainModelException;
-import org.apache.isis.applib.services.inject.ServiceInjector;
import org.apache.isis.applib.services.registry.ServiceRegistry;
-import org.apache.isis.core.config.environment.IsisSystemEnvironment;
import org.apache.isis.core.config.IsisConfiguration;
+import org.apache.isis.core.config.environment.IsisSystemEnvironment;
import org.apache.isis.core.config.metamodel.specloader.IntrospectionMode;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.metamodel.specloader.validator.ValidationFailure;
import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
-import static org.apache.isis.commons.internal.base._With.requires;
-
+import lombok.NonNull;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@@ -133,9 +131,9 @@ public class DomainModelValidator {
.filter(failure->filter.test(failure.getOrigin()));
}
- public Stream<ValidationFailure> streamFailuresMatchingOriginatingClass(Class<?> cls) {
- requires(cls, "cls");
- return streamFailures(origin->origin.getClassName().equals(cls.getName()));
+ public Stream<ValidationFailure> streamFailuresMatchingOriginatingIdentifier(
+ final @NonNull Identifier identifier) {
+ return streamFailures(id->id.equals(identifier));
}
@@ -144,8 +142,10 @@ public class DomainModelValidator {
/**
* primarily used for testing
*/
- public boolean anyMatchesContaining(Class<?> cls, String messageSnippet) {
- return streamFailuresMatchingOriginatingClass(cls)
+ public boolean anyMatchesContaining(
+ final @NonNull Identifier identifier,
+ final @NonNull String messageSnippet) {
+ return streamFailuresMatchingOriginatingIdentifier(identifier)
.anyMatch(failure->
failure.getMessage().contains(messageSnippet));
}