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/11/30 14:33:00 UTC

[isis] branch master updated: ISIS-2903: fixes mm-validation logic for annotation @XmlJavaTypeAdapter

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 b39f584  ISIS-2903: fixes mm-validation logic for annotation @XmlJavaTypeAdapter
b39f584 is described below

commit b39f58407695b1e7b8157a9968a55477ae53d3ca
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Nov 30 15:30:01 2021 +0100

    ISIS-2903: fixes mm-validation logic for annotation @XmlJavaTypeAdapter
    
    - fixes [Demo] Nature Stateful Refs Entity, such that can now click on
    the title
    
    - however, choices, when removing a child, are broken (detached
    entities)
---
 .../metamodel/facets/jaxb/JaxbFacetFactory.java    | 142 ++++++++++-----------
 .../jaxbrefentity/StatefulVmJaxbRefsEntity.java    |   7 +-
 .../domainmodel/jdo/DomainModelTest.java           |   3 +
 .../isis/testdomain/jpa/JpaInventoryJaxbVm.java    |  18 +++
 4 files changed, 97 insertions(+), 73 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/jaxb/JaxbFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/jaxb/JaxbFacetFactory.java
index 3d49dbb..a8a53c4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/jaxb/JaxbFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/jaxb/JaxbFacetFactory.java
@@ -47,12 +47,13 @@ import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySe
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 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.OneToOneAssociation;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.specloader.validator.ValidationFailure;
 
 import static org.apache.isis.commons.internal.reflection._Reflect.Filter.isPublic;
 import static org.apache.isis.commons.internal.reflection._Reflect.Filter.paramCount;
 
+import lombok.RequiredArgsConstructor;
 import lombok.val;
 
 /**
@@ -149,7 +150,7 @@ implements MetaModelRefiner {
     public void refineProgrammingModel(final ProgrammingModel programmingModel) {
 
         final List<TypeValidator> typeValidators = getTypeValidators(getConfiguration());
-        final List<PropertyValidator> propertyValidators = getPropertyValidators(getConfiguration());
+        final List<AssociationValidator> associationValidators = getAssociationValidators(getConfiguration());
 
         programmingModel.addVisitingValidatorSkipManagedBeans(objectSpec->{
 
@@ -167,15 +168,15 @@ implements MetaModelRefiner {
                 typeValidator.validate(objectSpec);
             }
 
-            final Stream<OneToOneAssociation> properties = objectSpec
-                    .streamProperties(MixedIn.EXCLUDED);
+            final Stream<ObjectAssociation> associations = objectSpec
+                    .streamAssociations(MixedIn.EXCLUDED);
 
-            properties
+            associations
             // ignore derived
-            .filter(property->property.containsNonFallbackFacet(PropertySetterFacet.class))
-            .forEach(property->{
-                for (final PropertyValidator adapterValidator : propertyValidators) {
-                    adapterValidator.validate(objectSpec, property);
+            .filter(association->association.containsNonFallbackFacet(PropertySetterFacet.class))
+            .forEach(association->{
+                for (final AssociationValidator adapterValidator : associationValidators) {
+                    adapterValidator.validate(objectSpec, association);
                 }
             });
 
@@ -198,24 +199,24 @@ implements MetaModelRefiner {
         return typeValidators;
     }
 
-    private List<PropertyValidator> getPropertyValidators(final IsisConfiguration configuration) {
-        final List<PropertyValidator> propertyValidators = _Lists.newArrayList();
+    private List<AssociationValidator> getAssociationValidators(final IsisConfiguration configuration) {
+        final List<AssociationValidator> associationValidators = _Lists.newArrayList();
         if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isReferenceTypeAdapter()) {
-            propertyValidators.add(new PropertyValidatorForReferenceTypes());
+            associationValidators.add(new PropertyValidatorForReferenceTypes());
         }
         if(configuration.getCore().getMetaModel().getValidator().getJaxbViewModel().isDateTimeTypeAdapter()) {
-            propertyValidators.add(new PropertyValidatorForDateTypes(java.sql.Timestamp.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(ZonedDateTime.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(OffsetDateTime.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(LocalDate.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(LocalDateTime.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(LocalTime.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(org.joda.time.DateTime.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(org.joda.time.LocalDate.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(org.joda.time.LocalDateTime.class));
-            propertyValidators.add(new PropertyValidatorForDateTypes(org.joda.time.LocalTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(java.sql.Timestamp.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(ZonedDateTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(OffsetDateTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(LocalDate.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(LocalDateTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(LocalTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(org.joda.time.DateTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(org.joda.time.LocalDate.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(org.joda.time.LocalDateTime.class));
+            associationValidators.add(new PropertyValidatorForDateTypes(org.joda.time.LocalTime.class));
         }
-        return propertyValidators;
+        return associationValidators;
     }
 
     private static abstract class TypeValidator {
@@ -223,78 +224,77 @@ implements MetaModelRefiner {
 
     }
 
-    private static abstract class PropertyValidator {
+    private static abstract class AssociationValidator {
         abstract void validate(
                 ObjectSpecification objectSpec,
-                OneToOneAssociation property);
+                ObjectAssociation propertyOrCollection);
 
     }
 
-    private static class PropertyValidatorForReferenceTypes extends PropertyValidator {
+    private static class PropertyValidatorForReferenceTypes extends AssociationValidator {
 
         @Override
         void validate(
                 final ObjectSpecification objectSpec,
-                final OneToOneAssociation property) {
+                final ObjectAssociation propertyOrCollection) {
 
-            final ObjectSpecification propertyTypeSpec = property.getElementType();
-            if (!propertyTypeSpec.isEntity()) {
-                return;
-            }
+            val elementTypeSpec = propertyOrCollection.getElementType();
+            if (elementTypeSpec.isEntity()
+                    && !propertyOrCollection.containsFacet(XmlJavaTypeAdapterFacet.class)
+                    && !propertyOrCollection.containsFacet(XmlTransientFacet.class)) {
 
-            final XmlJavaTypeAdapterFacet xmlJavaTypeAdapterFacet =
-                    propertyTypeSpec.getFacet(XmlJavaTypeAdapterFacet.class);
-            if(xmlJavaTypeAdapterFacet != null) {
-                return;
+                val elementType = elementTypeSpec.getCorrespondingClass();
+                ValidationFailure.raiseFormatted(
+                        propertyOrCollection,
+                        "JAXB view model '%s' %s '%s' is of entity type '%s', "
+                        + "but is not annotated with @XmlJavaTypeAdapter. "
+                        + "The referenced entity types must be annotated with "
+                        + "@XmlJavaTypeAdapter(org.apache.isis.applib.jaxb.%s.class) or equivalent.",
+                        objectSpec.getFullIdentifier(),
+                        elementTypeSpec.isNotCollection()
+                            ? "@Property"
+                            : "@Collection",
+                        propertyOrCollection.getId(),
+                        elementType.getName(),
+                        elementTypeSpec.isNotCollection()
+                            ? "PersistentEntityAdapter"
+                            : "PersistentEntitiesAdapter");
             }
-            final Class<?> propertyType = propertyTypeSpec.getCorrespondingClass();
-            ValidationFailure.raiseFormatted(
-                    property,
-                    "JAXB view model '%s' property '%s' is of type '%s' but that type is not annotated with @XmlJavaTypeAdapter.  The type must be annotated with @XmlJavaTypeAdapter(org.apache.isis.applib.jaxb.PersistentEntityAdapter.class) or equivalent.",
-                    objectSpec.getFullIdentifier(),
-                    property.getId(),
-                    propertyType.getName());
 
         }
     }
 
-    private static class PropertyValidatorForDateTypes extends PropertyValidator {
-        private final Class<?> jodaType;
-
-        private PropertyValidatorForDateTypes(final Class<?> jodaType) {
-            this.jodaType = jodaType;
-        }
+    @RequiredArgsConstructor
+    private static class PropertyValidatorForDateTypes extends AssociationValidator {
+        private final Class<?> dateType;
 
         @Override
         void validate(
                 final ObjectSpecification objectSpec,
-                final OneToOneAssociation property) {
-
-            final ObjectSpecification propertyTypeSpec = property.getElementType();
-            final Class<?> propertyType = propertyTypeSpec.getCorrespondingClass();
+                final ObjectAssociation propertyOrCollection) {
 
-            if (!jodaType.isAssignableFrom(propertyType)) {
-                return;
-            }
+            val elementTypeSpec = propertyOrCollection.getElementType();
+            val elementType = elementTypeSpec.getCorrespondingClass();
+            if (dateType.isAssignableFrom(elementType)
+                    && !propertyOrCollection.containsFacet(XmlJavaTypeAdapterFacet.class)
+                    && !propertyOrCollection.containsFacet(XmlTransientFacet.class)) {
 
-            final XmlJavaTypeAdapterFacet xmlJavaTypeAdapterFacet = property.getFacet(XmlJavaTypeAdapterFacet.class);
-            if (xmlJavaTypeAdapterFacet != null) {
-                return;
-            }
+                ValidationFailure.raiseFormatted(
+                        propertyOrCollection,
+                        "JAXB view model '%s' %s '%s' is of type '%s', "
+                        + "but is not annotated with @XmlJavaTypeAdapter. "
+                        + "The field/method must be annotated with "
+                        + "@XmlJavaTypeAdapter(org.apache.isis.schema.utils.jaxbadapters.XxxAdapter.ForJaxb.class) "
+                        + "or equivalent, "
+                        + "or be ignored by being annotated with @XmlTransient.",
+                        objectSpec.getFullIdentifier(),
+                        elementTypeSpec.isNotCollection()
+                            ? "@Property"
+                            : "@Collection",
+                        propertyOrCollection.getId(),
+                        dateType.getName());
 
-            final XmlTransientFacet xmlTransientFacet =
-                    property.getFacet(XmlTransientFacet.class);
-            if(xmlTransientFacet != null) {
-                return;
             }
-
-            // else
-            ValidationFailure.raiseFormatted(
-                    property,
-                    "JAXB view model '%s' property '%s' is of type '%s' but is not annotated with @XmlJavaTypeAdapter.  The field/method must be annotated with @XmlJavaTypeAdapter(org.apache.isis.schema.utils.jaxbadapters.XxxAdapter.ForJaxb.class) or equivalent, or be ignored by being annotated with @XmlTransient.",
-                    objectSpec.getFullIdentifier(),
-                    property.getId(),
-                    jodaType.getName());
         }
     }
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObject/nature/viewmodels/jaxbrefentity/StatefulVmJaxbRefsEntity.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObject/nature/viewmodels/jaxbrefentity/StatefulVmJaxbRefsEntity.java
index e8fb467..dedc492 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObject/nature/viewmodels/jaxbrefentity/StatefulVmJaxbRefsEntity.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/DomainObject/nature/viewmodels/jaxbrefentity/StatefulVmJaxbRefsEntity.java
@@ -26,10 +26,10 @@ import javax.inject.Inject;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlTransient;
 import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
@@ -42,6 +42,8 @@ import org.apache.isis.applib.annotation.ObjectSupport;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.jaxb.PersistentEntitiesAdapter;
+import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -77,6 +79,7 @@ public class StatefulVmJaxbRefsEntity implements HasAsciiDocDescription {
     @Getter @Setter
     @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
     @XmlElement(required = false)
+    @XmlJavaTypeAdapter(PersistentEntityAdapter.class)
     private JaxbRefEntity favoriteChild = null;
 
     @Action(semantics = SemanticsOf.IDEMPOTENT)
@@ -132,8 +135,8 @@ public class StatefulVmJaxbRefsEntity implements HasAsciiDocDescription {
 //tag::class[]
     @Getter @Setter
     @Collection
-    @XmlElementWrapper(name = "children")
     @XmlElement(name = "child")
+    @XmlJavaTypeAdapter(PersistentEntitiesAdapter.class)
     private List<JaxbRefEntity> children = new ArrayList<>();
 
     @Action(choicesFrom = "children", semantics = SemanticsOf.NON_IDEMPOTENT)
diff --git a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java
index 010ecea..de7f9e7 100644
--- a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java
+++ b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java
@@ -73,6 +73,9 @@ class DomainModelTest {
         assertFalse(specificationLoader.snapshotSpecifications().isEmpty());
 
         val validateDomainModel = new DomainModelValidator(serviceRegistry);
+
+        validateDomainModel.getFailures().forEach(f->System.err.printf("%s:%s%n", f.getOrigin(), f.getMessage()));
+
         validateDomainModel.throwIfInvalid(); // should not throw
     }
 
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryJaxbVm.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryJaxbVm.java
index d4225b0..02e224a 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryJaxbVm.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryJaxbVm.java
@@ -1,3 +1,21 @@
+/*
+ *  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.jpa;
 
 import java.util.ArrayList;