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 2020/04/19 10:09:26 UTC

[isis] branch master updated: ISIS-2340: binding presentation to domain model (yet missing validation)

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 ea4c1bc  ISIS-2340: binding presentation to domain model (yet missing validation)
ea4c1bc is described below

commit ea4c1bc6eb2b3a58a3206f12995887a056a03ca5
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sun Apr 19 12:09:12 2020 +0200

    ISIS-2340: binding presentation to domain model (yet missing validation)
---
 .../viewer/vaadin/ui/binding/BinderUtil.java       | 114 +++++++++++++++++++++
 .../ui/components/object/ObjectFormView.java       |  14 ++-
 .../vaadin/ui/components/temporal/DateField.java   |  18 ++--
 .../components/temporal/TemporalFieldFactory.java  |  49 +++++----
 .../common/model/binding/UiComponentFactory.java   |  15 ++-
 5 files changed, 180 insertions(+), 30 deletions(-)

diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
new file mode 100644
index 0000000..a60c0cf
--- /dev/null
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
@@ -0,0 +1,114 @@
+/*
+ *  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.incubator.viewer.vaadin.ui.binding;
+
+import java.time.LocalDate;
+import java.util.Locale;
+
+import com.vaadin.flow.component.HasValue;
+import com.vaadin.flow.data.binder.Binder;
+import com.vaadin.flow.data.binder.ValueContext;
+import com.vaadin.flow.data.converter.Converter;
+import com.vaadin.flow.data.converter.DateToSqlDateConverter;
+import com.vaadin.flow.data.converter.LocalDateToDateConverter;
+
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public final class BinderUtil {
+
+    public static <T> Binder<Request> requestBinder(
+            final HasValue<?, T> uiField,
+            final Class<T> fieldValueType) {
+        
+        final Binder<Request> binder = new Binder<>();
+        binder.forField(uiField)
+                .bind(
+                        request->request.getPojo(fieldValueType).orElse(null), 
+                        (request, newValue)->request.getPropagator(fieldValueType).apply(newValue));
+        return binder;
+    }
+    
+    /**
+     * 
+     * @param <T> field value type (presentation type)
+     * @param <U> model value type
+     * @param uiField
+     * @param modelValueType
+     * @param converter
+     * @return
+     */
+    public static <T, U> Binder<Request> requestBinderWithConverter(
+            final HasValue<?, T> uiField,
+            final Class<U> modelValueType,
+            final Converter<T, U> converter) {
+        
+        final ValueContext valueContext = new ValueContext(Locale.getDefault());
+        
+        final Binder<Request> binder = new Binder<>();
+        
+        binder.forField(uiField)
+        .bind(
+                request->request
+                    .getPojo(modelValueType)
+                    .map(modelValue->converter.convertToPresentation(modelValue, valueContext))
+                    .orElse(null), 
+                (request, newValue)->request
+                    .getPropagator(modelValueType)
+                    .apply(converter
+                            .convertToModel(newValue, valueContext)
+                            .getOrThrow(message-> {
+                                throw _Exceptions.illegalArgument("cannot convert due %s", message);   
+                            }))
+        );
+        return binder;
+    }
+
+    // -- SHORTCUTS
+    
+    public static enum DateBinder {
+        
+        JAVA_TIME_LOCAL_DATE{
+
+            @Override
+            public Binder<Request> bind(HasValue<?, LocalDate> uiField) {
+                return BinderUtil.requestBinder(uiField, LocalDate.class);
+            }
+            
+        },
+        JAVA_SQL_DATE{
+
+            @Override
+            public Binder<Request> bind(HasValue<?, LocalDate> uiField) {
+                return BinderUtil.requestBinderWithConverter(uiField, java.sql.Date.class, 
+                        new LocalDateToDateConverter().chain(new DateToSqlDateConverter()));
+            }
+            
+        };
+        
+        public abstract Binder<Request> bind(final HasValue<?, LocalDate> uiField);
+        
+    }
+
+            
+    
+}
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
index 8884ba9..bef8517 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
@@ -20,6 +20,7 @@ package org.apache.isis.incubator.viewer.vaadin.ui.components.object;
 
 import java.util.Collection;
 import java.util.Optional;
+import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
 
 import com.vaadin.flow.component.Component;
@@ -30,8 +31,10 @@ import com.vaadin.flow.component.html.Label;
 import com.vaadin.flow.component.orderedlayout.VerticalLayout;
 import com.vaadin.flow.component.textfield.TextField;
 
+import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.MutableCurrentHolder;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentFactoryVaa;
@@ -74,7 +77,16 @@ public class ObjectFormView extends VerticalLayout {
                 tablesLayout.add(new Label(objectAssociation.getName()));
                 tablesLayout.add(createCollectionComponent(objectAssociation, assocObject));
             } else {
-                val uiComponentCreateRequest = UiComponentFactory.Request.of(assocObject, objectAssociation);
+                
+                final UnaryOperator<ManagedObject> toDomainPropagator = proposedNewValue -> {
+                    MutableCurrentHolder property = (MutableCurrentHolder) objectAssociation;
+                    property.set(managedObject, proposedNewValue, InteractionInitiatedBy.USER);
+                    // TODO where is the rule checking part?
+                    return proposedNewValue;
+                };
+                
+                val uiComponentCreateRequest = UiComponentFactory.Request
+                        .of(assocObject, objectAssociation, toDomainPropagator);
                 val uiComponent = uiComponentFactory.componentFor(uiComponentCreateRequest);
                 formLayout.add(uiComponent);
             }
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/DateField.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/DateField.java
index 1efb183..fcdce50 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/DateField.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/DateField.java
@@ -30,8 +30,8 @@ public class DateField extends CustomField<LocalDate> {
     private static final long serialVersionUID = 1L;
     
     private final DatePicker datePicker = new DatePicker();
-    private LocalDate temporal;
-
+    private boolean isNull;
+    
     public DateField(String label) {
         super();
         setLabel(label);
@@ -40,20 +40,20 @@ public class DateField extends CustomField<LocalDate> {
     
     @Override
     protected LocalDate generateModelValue() {
-        return temporal;
+        return datePicker.getValue();
     }
 
     @Override
-    protected void setPresentationValue(@Nullable LocalDate temporal) {
-        this.temporal = temporal;
+    protected void setPresentationValue(@Nullable LocalDate newValue) {
         
-        if(temporal==null) {
+        this.isNull = newValue==null;
+        datePicker.setValue(newValue);
+        
+        if(this.isNull) {
             datePicker.setPlaceholder("no date");
-            return;
         }
         
-        datePicker.setValue(temporal);
-        
     }
+   
     
 }
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
index e5852ed..9048b4b 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
@@ -19,16 +19,17 @@
 package org.apache.isis.incubator.viewer.vaadin.ui.components.temporal;
 
 import java.time.LocalDate;
-import java.time.temporal.Temporal;
-import java.util.Optional;
 
 import com.vaadin.flow.component.Component;
+import com.vaadin.flow.data.binder.Binder;
 
 import org.springframework.core.annotation.Order;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facets.value.temporal.TemporalValueFacet;
+import org.apache.isis.core.metamodel.facets.value.temporal.TemporalValueFacet.TemporalCharacteristic;
+import org.apache.isis.incubator.viewer.vaadin.ui.binding.BinderUtil;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentHandlerVaa;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
 
@@ -40,24 +41,44 @@ public class TemporalFieldFactory implements UiComponentHandlerVaa {
 
     @Override
     public boolean isHandling(Request request) {
-        return request.hasFeatureFacet(TemporalValueFacet.class)
+        return request.isFeatureTypeEqualTo(java.sql.Date.class)
+            ||(request.hasFeatureFacet(TemporalValueFacet.class)
                 // TODO lift this restrictions, as we support more types
-                && request.isFeatureTypeEqualTo(LocalDate.class); 
+                && (
+                        request.isFeatureTypeEqualTo(LocalDate.class)
+//                        || request.isFeatureTypeEqualTo(java.sql.Date.class)
+                )); 
     }
 
     @Override
     public Component handle(Request request) {
 
-        val temporalFacet = request.getFeatureFacetElseFail(TemporalValueFacet.class);
-        val temporalCharacteristic = temporalFacet.getTemporalCharacteristic();
-        val offsetCharacteristic = temporalFacet.getOffsetCharacteristic();
-        val temporal = request.getPojo(Temporal.class);
+        val temporalFacet = request.getFeatureFacet(TemporalValueFacet.class);
+        val temporalCharacteristic = temporalFacet
+                .map(TemporalValueFacet::getTemporalCharacteristic)
+                .orElse(TemporalCharacteristic.DATE_ONLY);
+        //val offsetCharacteristic = temporalFacet.getOffsetCharacteristic();
         
         switch(temporalCharacteristic) {
         case DATE_ONLY:{
+
             val uiField = new DateField(request.getFeatureLabel());
-            uiField.setValue(toLocalDate(temporal).orElse(null));
+            
+            final Binder<Request> binder;
+            if(request.isFeatureTypeEqualTo(LocalDate.class)) {
+                binder = BinderUtil.DateBinder.JAVA_TIME_LOCAL_DATE.bind(uiField);
+            } else if(request.isFeatureTypeEqualTo(java.sql.Date.class)) {
+                binder = BinderUtil.DateBinder.JAVA_SQL_DATE.bind(uiField);
+            } else {
+                throw _Exceptions.unmatchedCase(request.getFeatureType());
+            }
+            
+            binder.setBean(request);
             return uiField;
+            
+            //val uiField = DateField.bind(request);
+            
+
             }    
         case TIME_ONLY:{
             // TODO ... 
@@ -71,14 +92,4 @@ public class TemporalFieldFactory implements UiComponentHandlerVaa {
         
     }
 
-    private Optional<LocalDate> toLocalDate(Optional<Temporal> temporal) {
-        return temporal.map(this::toLocalDate);
-    }
-    
-    private LocalDate toLocalDate(Temporal temporal) {
-        return (temporal instanceof LocalDate)
-                ? (LocalDate) temporal
-                : null;
-    }
-    
 }
diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
index 344158e..1f954eb 100644
--- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/UiComponentFactory.java
@@ -19,10 +19,12 @@
 package org.apache.isis.viewer.common.model.binding;
 
 import java.util.Optional;
+import java.util.function.UnaryOperator;
 
 import javax.annotation.Nullable;
 
 import org.apache.isis.core.commons.handler.ChainOfResponsibility;
+import org.apache.isis.core.commons.internal.base._Casts;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
@@ -31,6 +33,7 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
 
 import lombok.NonNull;
 import lombok.Value;
+import lombok.val;
 
 public interface UiComponentFactory<T> {
     
@@ -43,6 +46,7 @@ public interface UiComponentFactory<T> {
         /** not null but the wrapped pojo is allowed to be null*/
         @NonNull private final ManagedObject managedObject; 
         @NonNull private final ObjectFeature objectFeature;
+        @NonNull private final UnaryOperator<ManagedObject> toDomainPropagator;
         
         // -- SHORTCUTS
         
@@ -113,7 +117,16 @@ public interface UiComponentFactory<T> {
             return Optional.ofNullable(getManagedObject().getPojo())
                     .map(type::cast);
         }
-        
+
+        public <T> UnaryOperator<T> getPropagator(Class<T> pojoType) {
+            return (T newValueProposal)->{
+                val newValue = ManagedObject.of(getFeatureSpec(), newValueProposal);
+                return _Casts.uncheckedCast(
+                        toDomainPropagator
+                            .apply(newValue)
+                            .getPojo());
+            };
+        }
         
     }