]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add option to use error message provider for StringTo converters (#10711)
authorTeemu Suo-Anttila <tsuoanttila@users.noreply.github.com>
Mon, 19 Mar 2018 17:04:48 +0000 (19:04 +0200)
committerIlia Motornyi <elmot@vaadin.com>
Mon, 19 Mar 2018 17:04:48 +0000 (19:04 +0200)
server/src/main/java/com/vaadin/data/converter/AbstractStringToNumberConverter.java
server/src/main/java/com/vaadin/data/converter/StringToBigDecimalConverter.java
server/src/main/java/com/vaadin/data/converter/StringToBigIntegerConverter.java
server/src/main/java/com/vaadin/data/converter/StringToBooleanConverter.java
server/src/main/java/com/vaadin/data/converter/StringToDoubleConverter.java
server/src/main/java/com/vaadin/data/converter/StringToFloatConverter.java
server/src/main/java/com/vaadin/data/converter/StringToIntegerConverter.java
server/src/main/java/com/vaadin/data/converter/StringToLongConverter.java
server/src/test/java/com/vaadin/data/BinderTest.java

index b3aff7c0e7f3dc496d6791a142d49f364cef2645..3918dc54474a7992af8f51188a05f5d3e6b36bc5 100644 (file)
@@ -21,6 +21,7 @@ import java.text.ParsePosition;
 import java.util.Locale;
 
 import com.vaadin.data.Converter;
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -39,9 +40,27 @@ import com.vaadin.data.ValueContext;
 public abstract class AbstractStringToNumberConverter<T extends Number>
         implements Converter<String, T> {
 
-    private final String errorMessage;
+    private final ErrorMessageProvider errorMessageProvider;
     private T emptyValue;
 
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    protected AbstractStringToNumberConverter(T emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        this.emptyValue = emptyValue;
+        this.errorMessageProvider = errorMessageProvider;
+    }
+
     /**
      * Creates a new converter instance with the given empty string value and
      * error message.
@@ -54,8 +73,7 @@ public abstract class AbstractStringToNumberConverter<T extends Number>
      */
     protected AbstractStringToNumberConverter(T emptyValue,
             String errorMessage) {
-        this.emptyValue = emptyValue;
-        this.errorMessage = errorMessage;
+        this(emptyValue, ctx -> errorMessage);
     }
 
     /**
@@ -81,11 +99,12 @@ public abstract class AbstractStringToNumberConverter<T extends Number>
      *
      * @param value
      *            The value to convert
-     * @param locale
-     *            The locale to use for conversion
+     * @param context
+     *            The value context for conversion
      * @return The converted value
      */
-    protected Result<Number> convertToNumber(String value, Locale locale) {
+    protected Result<Number> convertToNumber(String value,
+            ValueContext context) {
         if (value == null) {
             return Result.ok(null);
         }
@@ -96,9 +115,10 @@ public abstract class AbstractStringToNumberConverter<T extends Number>
         // Parse and detect errors. If the full string was not used, it is
         // an error.
         ParsePosition parsePosition = new ParsePosition(0);
-        Number parsedValue = getFormat(locale).parse(value, parsePosition);
+        Number parsedValue = getFormat(context.getLocale().orElse(null))
+                .parse(value, parsePosition);
         if (parsePosition.getIndex() != value.length()) {
-            return Result.error(getErrorMessage());
+            return Result.error(getErrorMessage(context));
         }
 
         if (parsedValue == null) {
@@ -114,8 +134,8 @@ public abstract class AbstractStringToNumberConverter<T extends Number>
      *
      * @return the error message
      */
-    protected String getErrorMessage() {
-        return errorMessage;
+    protected String getErrorMessage(ValueContext context) {
+        return errorMessageProvider.apply(context);
     }
 
     @Override
index 9418e53e1f248dbe0c045f8c56884eb684251625..c3a55561bf7d2f1b2818dacedc64c3dac77d9f4c 100644 (file)
@@ -20,6 +20,7 @@ import java.text.DecimalFormat;
 import java.text.NumberFormat;
 import java.util.Locale;
 
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -66,6 +67,37 @@ public class StringToBigDecimalConverter
         super(emptyValue, errorMessage);
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToBigDecimalConverter(
+            ErrorMessageProvider errorMessageProvider) {
+        this(null, errorMessageProvider);
+    }
+
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToBigDecimalConverter(BigDecimal emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        super(emptyValue, errorMessageProvider);
+    }
+
     @Override
     protected NumberFormat getFormat(Locale locale) {
         NumberFormat numberFormat = super.getFormat(locale);
@@ -79,7 +111,7 @@ public class StringToBigDecimalConverter
     @Override
     public Result<BigDecimal> convertToModel(String value,
             ValueContext context) {
-        return convertToNumber(value, context.getLocale().orElse(null))
+        return convertToNumber(value, context)
                 .map(number -> (BigDecimal) number);
     }
 
index 840f6ae8c0d7a0c682afad1cdd2d8c47a08ce87c..0d3642cb948b750f9db9f79b3497b9862399f76c 100644 (file)
@@ -21,6 +21,7 @@ import java.text.DecimalFormat;
 import java.text.NumberFormat;
 import java.util.Locale;
 
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -67,6 +68,37 @@ public class StringToBigIntegerConverter
         super(emptyValue, errorMessage);
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToBigIntegerConverter(
+            ErrorMessageProvider errorMessageProvider) {
+        this(null, errorMessageProvider);
+    }
+
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToBigIntegerConverter(BigInteger emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        super(emptyValue, errorMessageProvider);
+    }
+
     @Override
     protected NumberFormat getFormat(Locale locale) {
         NumberFormat numberFormat = super.getFormat(locale);
@@ -80,17 +112,16 @@ public class StringToBigIntegerConverter
     @Override
     public Result<BigInteger> convertToModel(String value,
             ValueContext context) {
-        return convertToNumber(value, context.getLocale().orElse(null))
-                .map(number -> {
-                    if (number == null) {
-                        return null;
-                    }
-                    // Empty value will be a BigInteger
-                    if (number instanceof BigInteger) {
-                        return (BigInteger) number;
-                    }
-                    return ((BigDecimal) number).toBigInteger();
-                });
+        return convertToNumber(value, context).map(number -> {
+            if (number == null) {
+                return null;
+            }
+            // Empty value will be a BigInteger
+            if (number instanceof BigInteger) {
+                return (BigInteger) number;
+            }
+            return ((BigDecimal) number).toBigInteger();
+        });
     }
 
 }
index 49fa043a867ee79ca58cf15a0b56a6032c75ea1d..edb2908afad22a7f63325b9e3653119b4c2f062f 100644 (file)
@@ -19,13 +19,15 @@ package com.vaadin.data.converter;
 import java.util.Locale;
 
 import com.vaadin.data.Converter;
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
 /**
  * A converter that converts from {@link String} to {@link Boolean} and back.
  * The String representation is given by {@link Boolean#toString()} or provided
- * in constructor {@link StringToBooleanConverter#StringToBooleanConverter(String, String, String)}.
+ * in constructor
+ * {@link StringToBooleanConverter#StringToBooleanConverter(String, String, String)}.
  * <p>
  * Leading and trailing white spaces are ignored when converting from a String.
  * </p>
@@ -43,7 +45,7 @@ public class StringToBooleanConverter implements Converter<String, Boolean> {
 
     private final String falseString;
 
-    private String errorMessage;
+    private ErrorMessageProvider errorMessageProvider;
 
     /**
      * Creates converter with default string representations - "true" and
@@ -56,6 +58,20 @@ public class StringToBooleanConverter implements Converter<String, Boolean> {
         this(errorMessage, Boolean.TRUE.toString(), Boolean.FALSE.toString());
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToBooleanConverter(ErrorMessageProvider errorMessageProvider) {
+        this(Boolean.TRUE.toString(), Boolean.FALSE.toString(),
+                errorMessageProvider);
+    }
+
     /**
      * Creates converter with custom string representation.
      *
@@ -68,7 +84,24 @@ public class StringToBooleanConverter implements Converter<String, Boolean> {
      */
     public StringToBooleanConverter(String errorMessage, String trueString,
             String falseString) {
-        this.errorMessage = errorMessage;
+        this(trueString, falseString, ctx -> errorMessage);
+    }
+
+    /**
+     * Creates converter with custom string representation.
+     *
+     * @param falseString
+     *            string representation for <code>false</code>
+     * @param trueString
+     *            string representation for <code>true</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToBooleanConverter(String trueString, String falseString,
+            ErrorMessageProvider errorMessageProvider) {
+        this.errorMessageProvider = errorMessageProvider;
         this.trueString = trueString;
         this.falseString = falseString;
     }
@@ -90,7 +123,7 @@ public class StringToBooleanConverter implements Converter<String, Boolean> {
         } else if (value.isEmpty()) {
             return Result.ok(null);
         } else {
-            return Result.error(errorMessage);
+            return Result.error(errorMessageProvider.apply(context));
         }
     }
 
index 627775fbba818981a3afe5b2fd5faf04dd702400..b1b11fdd1db3c6feed40759489cfc111afd5828b 100644 (file)
@@ -19,6 +19,7 @@ package com.vaadin.data.converter;
 import java.text.NumberFormat;
 import java.util.Locale;
 
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -64,10 +65,39 @@ public class StringToDoubleConverter
         super(emptyValue, errorMessage);
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToDoubleConverter(ErrorMessageProvider errorMessageProvider) {
+        this(null, errorMessageProvider);
+    }
+
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToDoubleConverter(Double emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        super(emptyValue, errorMessageProvider);
+    }
+
     @Override
     public Result<Double> convertToModel(String value, ValueContext context) {
-        Result<Number> n = convertToNumber(value,
-                context.getLocale().orElse(null));
+        Result<Number> n = convertToNumber(value, context);
 
         return n.map(number -> {
             if (number == null) {
index 37e159f844608a31bc623a3d0f2d3f534e3cc3d2..62c211a2ad8e153c3ecbe08044e65d2882f85fb4 100644 (file)
@@ -19,6 +19,7 @@ package com.vaadin.data.converter;
 import java.text.NumberFormat;
 import java.util.Locale;
 
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -62,10 +63,39 @@ public class StringToFloatConverter
         super(emptyValue, errorMessage);
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToFloatConverter(ErrorMessageProvider errorMessageProvider) {
+        this(null, errorMessageProvider);
+    }
+
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToFloatConverter(Float emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        super(emptyValue, errorMessageProvider);
+    }
+
     @Override
     public Result<Float> convertToModel(String value, ValueContext context) {
-        Result<Number> n = convertToNumber(value,
-                context.getLocale().orElse(null));
+        Result<Number> n = convertToNumber(value, context);
 
         return n.map(number -> {
             if (number == null) {
index 88f2c2912611a2bc2eaa0bace83d53557d0a86a1..b0a9449a56b778155123d52ad0ea3a99b8a8d4cf 100644 (file)
@@ -19,6 +19,7 @@ package com.vaadin.data.converter;
 import java.text.NumberFormat;
 import java.util.Locale;
 
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -61,6 +62,36 @@ public class StringToIntegerConverter
         super(emptyValue, errorMessage);
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToIntegerConverter(ErrorMessageProvider errorMessageProvider) {
+        this(null, errorMessageProvider);
+    }
+
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToIntegerConverter(Integer emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        super(emptyValue, errorMessageProvider);
+    }
+
     /**
      * Returns the format used by
      * {@link #convertToPresentation(Object, ValueContext)} and
@@ -80,8 +111,7 @@ public class StringToIntegerConverter
 
     @Override
     public Result<Integer> convertToModel(String value, ValueContext context) {
-        Result<Number> n = convertToNumber(value,
-                context.getLocale().orElse(null));
+        Result<Number> n = convertToNumber(value, context);
         return n.flatMap(number -> {
             if (number == null) {
                 return Result.ok(null);
@@ -94,7 +124,7 @@ public class StringToIntegerConverter
                 // long and thus does not need to consider wrap-around.
                 return Result.ok(intValue);
             }
-            return Result.error(getErrorMessage());
+            return Result.error(getErrorMessage(context));
         });
     }
 
index 4e652963165abc4830391e17eb829fb548fae740..39ef8f777af6adcf8528a2c670bd43ac0769b0a1 100644 (file)
@@ -19,6 +19,7 @@ package com.vaadin.data.converter;
 import java.text.NumberFormat;
 import java.util.Locale;
 
+import com.vaadin.data.ErrorMessageProvider;
 import com.vaadin.data.Result;
 import com.vaadin.data.ValueContext;
 
@@ -61,6 +62,36 @@ public class StringToLongConverter
         super(emptyValue, errorMessage);
     }
 
+    /**
+     * Creates a new converter instance with the given error message provider.
+     * Empty strings are converted to <code>null</code>.
+     *
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToLongConverter(ErrorMessageProvider errorMessageProvider) {
+        this(null, errorMessageProvider);
+    }
+
+    /**
+     * Creates a new converter instance with the given empty string value and
+     * error message provider.
+     *
+     * @param emptyValue
+     *            the presentation value to return when converting an empty
+     *            string, may be <code>null</code>
+     * @param errorMessageProvider
+     *            the error message provider to use if conversion fails
+     *
+     * @since
+     */
+    public StringToLongConverter(Long emptyValue,
+            ErrorMessageProvider errorMessageProvider) {
+        super(emptyValue, errorMessageProvider);
+    }
+
     /**
      * Returns the format used by
      * {@link #convertToPresentation(Object, ValueContext)} and
@@ -80,8 +111,7 @@ public class StringToLongConverter
 
     @Override
     public Result<Long> convertToModel(String value, ValueContext context) {
-        Result<Number> n = convertToNumber(value,
-                context.getLocale().orElse(null));
+        Result<Number> n = convertToNumber(value, context);
         return n.map(number -> {
             if (number == null) {
                 return null;
index abbbe541ae6385de8ebb23823050004a0db96129..ef52ea8ef5ad8819044b191542af68f0510f451b 100644 (file)
@@ -1102,4 +1102,27 @@ public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
         assertTrue("Binding should be readonly", binding.isReadOnly());
         assertTrue("Name field should be readonly", nameField.isReadOnly());
     }
+
+    @Test
+    public void conversionWithLocaleBasedErrorMessage() {
+        String fiError = "VIRHE";
+        String otherError = "ERROR";
+
+        binder.forField(ageField).withConverter(new StringToIntegerConverter(
+                context -> context.getLocale().map(Locale::getLanguage)
+                        .orElse("en").equals("fi") ? fiError : otherError))
+                .bind(Person::getAge, Person::setAge);
+
+        binder.setBean(item);
+
+        ageField.setValue("not a number");
+
+        assertEquals(otherError,
+                ageField.getErrorMessage().getFormattedHtmlMessage());
+        ageField.setLocale(new Locale("fi"));
+        // Re-validate to get the error message with correct locale
+        binder.validate();
+        assertEquals(fiError,
+                ageField.getErrorMessage().getFormattedHtmlMessage());
+    }
 }