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 c3fc4ed  ISIS-2340: filter parsing failures during type conversion 
(wip)
c3fc4ed is described below

commit c3fc4ed4e9b48805e0df714f989b8b4b6e57dd59
Author: Andi Huber <[email protected]>
AuthorDate: Mon Aug 17 11:48:52 2020 +0200

    ISIS-2340: filter parsing failures during type conversion (wip)
    
    eg. when UI Text component is bound to Number value, we need to
    intercept invalid number formats, but these are not processed by the
    validation mechanics, since this one assumes types to be properly
    converted from eg. String
---
 .../vsp/ValueSemanticsProviderAndFacetAbstract.java     | 15 ++++++++++++++-
 .../integer/IntValueSemanticsProviderAbstract.java      |  2 +-
 .../javafx/ui/components/number/NumberFieldFactory.java | 17 +++++++++++++++++
 .../javafx/ui/components/text/TextFieldFactory.java     |  7 +++++++
 .../viewer/common/model/binding/BindingConverter.java   |  6 ++++++
 .../binding/NumberConverterForStringComponent.java      | 14 ++++++++++++++
 .../binding/TemporalConverterForLocalDateComponent.java | 12 +++++++-----
 7 files changed, 66 insertions(+), 7 deletions(-)

diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/value/vsp/ValueSemanticsProviderAndFacetAbstract.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/value/vsp/ValueSemanticsProviderAndFacetAbstract.java
index 3fd077c..ef4049e 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/value/vsp/ValueSemanticsProviderAndFacetAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/value/vsp/ValueSemanticsProviderAndFacetAbstract.java
@@ -21,12 +21,14 @@ package 
org.apache.isis.core.metamodel.facets.object.value.vsp;
 
 import java.text.Format;
 import java.util.Map;
+import java.util.Optional;
 
 import org.apache.isis.applib.adapters.DefaultsProvider;
 import org.apache.isis.applib.adapters.EncoderDecoder;
 import org.apache.isis.applib.adapters.Parser;
 import org.apache.isis.applib.adapters.ValueSemanticsProvider;
 import org.apache.isis.core.commons.exceptions.UnknownTypeException;
+import org.apache.isis.core.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
@@ -147,7 +149,7 @@ implements ValueSemanticsProvider<T>, EncoderDecoder<T>, 
Parser<T>, DefaultsProv
         if (entry == null) {
             throw new IllegalArgumentException();
         }
-        if (entry.trim().equals("")) {
+        if (entry.trim().isEmpty()) {
             if (mustHaveEntry()) {
                 throw new InvalidEntryException("An entry is required");
             } else {
@@ -156,6 +158,15 @@ implements ValueSemanticsProvider<T>, EncoderDecoder<T>, 
Parser<T>, DefaultsProv
         }
         return doParse(context, entry);
     }
+    
+    public Optional<Exception> tryParseTextEntry(final Object context, final 
String entry) {
+        try {
+            parseTextEntry(context, entry);
+        } catch (Exception e) {
+            return Optional.of(e);
+        }
+        return Optional.empty();
+    }
 
     /**
      * @param context
@@ -289,4 +300,6 @@ implements ValueSemanticsProvider<T>, EncoderDecoder<T>, 
Parser<T>, DefaultsProv
         attributeMap.put("defaultValue", this.defaultValue);
     }
 
+
+
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/integer/IntValueSemanticsProviderAbstract.java
 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/integer/IntValueSemanticsProviderAbstract.java
index 8819760..a5a6320 100644
--- 
a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/integer/IntValueSemanticsProviderAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/integer/IntValueSemanticsProviderAbstract.java
@@ -68,7 +68,7 @@ public abstract class IntValueSemanticsProviderAbstract 
extends ValueSemanticsPr
         try {
             return Integer.valueOf(format.parse(entry).intValue());
         } catch (final ParseException e) {
-            throw new TextEntryParseException("Not an whole number " + entry, 
e);
+            throw new TextEntryParseException("Not a whole number " + entry, 
e);
         }
     }
 
diff --git 
a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
 
b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
index 890e6f2..16388ec 100644
--- 
a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
+++ 
b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/number/NumberFieldFactory.java
@@ -23,6 +23,7 @@ import javax.inject.Inject;
 import org.springframework.core.annotation.Order;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.core.commons.internal.base._Strings;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
 import org.apache.isis.incubator.viewer.javafx.model.binding.BindingsFx;
@@ -33,13 +34,16 @@ import 
org.apache.isis.viewer.common.model.components.UiComponentFactory.Compone
 
 import javafx.scene.Node;
 import javafx.scene.control.TextField;
+import javafx.scene.control.TextFormatter;
 import javafx.scene.layout.VBox;
 import lombok.RequiredArgsConstructor;
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 @org.springframework.stereotype.Component
 @Order(OrderPrecedence.MIDPOINT)
 @RequiredArgsConstructor(onConstructor_ = {@Inject})
+@Log4j2
 public class NumberFieldFactory implements UiComponentHandlerFx {
 
     @Override
@@ -56,6 +60,19 @@ public class NumberFieldFactory implements 
UiComponentHandlerFx {
         val valueSpec = request.getFeatureTypeSpec();
         val converter = new NumberConverterForStringComponent(valueSpec);
         
+        // ensure user can only type text that is also parse-able by the value 
facet (parser)
+        // however, not every phase of text entering produces parse-able text
+        uiField.setTextFormatter(new TextFormatter<String>(change->{
+            val input = change.getText();
+            
+            val parsingError = converter.tryParse(_Strings.suffix(input, "0"));
+            if (parsingError.isPresent()) {
+                log.warn("Failed to parse UI input '{}': {}", input, 
parsingError.get());
+                return null; // veto change
+            }
+            return change; // allow change 
+        }));
+        
         if(request.getManagedFeature() instanceof ManagedParameter) {
 
             val managedParameter = 
(ManagedParameter)request.getManagedFeature();
diff --git 
a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
 
b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
index 633096b..a3e37d8 100644
--- 
a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
+++ 
b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.incubator.viewer.javafx.ui.components.text;
 
+import java.util.Optional;
+
 import javax.inject.Inject;
 
 import org.springframework.core.annotation.Order;
@@ -100,6 +102,11 @@ public class TextFieldFactory implements 
UiComponentHandlerFx {
         public String fromString(String stringifiedValue) {
             return stringifiedValue; // identity
         }
+
+        @Override
+        public Optional<String> tryParse(String stringifiedValue) {
+            return Optional.empty(); // always ok 
+        }
         
     }
 
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/BindingConverter.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/BindingConverter.java
index 5b4b237..ac9352c 100644
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/BindingConverter.java
+++ 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/BindingConverter.java
@@ -60,6 +60,12 @@ public interface BindingConverter<T> {
 
     T fromString(String stringifiedValue);
     
+    /**
+     * @param stringifiedValue
+     * @return optionally an error message, based on whether fails to parse 
{@code stringifiedValue} 
+     */
+    Optional<String> tryParse(String stringifiedValue);
+    
 }
 
 
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/NumberConverterForStringComponent.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/NumberConverterForStringComponent.java
index d827101..86acdd3 100644
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/NumberConverterForStringComponent.java
+++ 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/NumberConverterForStringComponent.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.viewer.common.model.binding;
 
+import java.util.Optional;
+
 import org.apache.isis.core.commons.collections.Can;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -54,6 +56,12 @@ public final class NumberConverterForStringComponent 
implements BindingConverter
 
     @Override
     public ManagedObject wrap(String stringifiedNumber) {
+        
+        if(tryParse(stringifiedNumber).isPresent()) {
+            // return an intermediate placeholder
+            return ManagedObject.empty(getValueSpecification());
+        }
+        
         val number = valueFacet.parseTextEntry(null, stringifiedNumber);
         return ManagedObject.of(getValueSpecification(), number);
     }
@@ -87,4 +95,10 @@ public final class NumberConverterForStringComponent 
implements BindingConverter
         return stringifiedValue; // identity
     }
 
+    @Override
+    public Optional<String> tryParse(String stringifiedValue) {
+        return valueFacet.tryParseTextEntry(null, stringifiedValue)
+                .map(Exception::getMessage); // TODO should be passed through 
the ExceptionRecognizer
+    }
+
 }
\ No newline at end of file
diff --git 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/TemporalConverterForLocalDateComponent.java
 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/TemporalConverterForLocalDateComponent.java
index d151426..7cf2a90 100644
--- 
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/TemporalConverterForLocalDateComponent.java
+++ 
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/TemporalConverterForLocalDateComponent.java
@@ -19,6 +19,7 @@
 package org.apache.isis.viewer.common.model.binding;
 
 import java.time.LocalDate;
+import java.util.Optional;
 
 import org.apache.isis.core.commons.internal.base._Casts;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
@@ -55,11 +56,6 @@ public class TemporalConverterForLocalDateComponent 
implements BindingConverter<
         return localDate;
     }
 
-//    // for performance reasons in order of likelihood (just guessing)
-//    @Getter
-//    private final static Can<Class<? extends Facet>> supportedFacets = 
Can.of(
-//            TemporalValueFacet.class);
-
     @Override
     public String toString(LocalDate value) {
         return valueFacet.parseableTitleOf(value);
@@ -78,5 +74,11 @@ public class TemporalConverterForLocalDateComponent 
implements BindingConverter<
         throw _Exceptions.unmatchedCase(value.getClass());  
     }
 
+    @Override
+    public Optional<String> tryParse(String stringifiedValue) {
+        return valueFacet.tryParseTextEntry(null, stringifiedValue)
+                .map(Exception::getMessage); // TODO should be passed through 
the ExceptionRecognizer
+    }
+
 
 }
\ No newline at end of file

Reply via email to