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