]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add error message provider to provide translations
authorTeemu Suo-Anttila <teemusa@vaadin.com>
Tue, 1 Nov 2016 09:42:15 +0000 (11:42 +0200)
committerTeemu Suo-Anttila <teemusa@vaadin.com>
Tue, 1 Nov 2016 12:33:16 +0000 (14:33 +0200)
Change-Id: I657535d377c471369e8c77fa1db946c490023939

server/src/main/java/com/vaadin/data/Binder.java
server/src/main/java/com/vaadin/data/ErrorMessageProvider.java [new file with mode: 0644]
server/src/main/java/com/vaadin/data/Validator.java
server/src/test/java/com/vaadin/data/ValidatorTest.java
server/src/test/java/com/vaadin/data/validator/ValidatorTestBase.java

index f500ef9a0a920ed7dcfda9019b394aac0d8ce390..e139bcd5c8af31fa31e2158ae65767db4922e32f 100644 (file)
@@ -149,6 +149,9 @@ public class Binder<BEAN> implements Serializable {
          * property. If any validator returns a failure, the property value is
          * not updated.
          *
+         * @see #withValidator(SerializablePredicate, String)
+         * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
+         *
          * @param validator
          *            the validator to add, not null
          * @return this binding, for chaining
@@ -167,6 +170,7 @@ public class Binder<BEAN> implements Serializable {
          * failure, the property value is not updated.
          *
          * @see #withValidator(Validator)
+         * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
          * @see Validator#from(SerializablePredicate, String)
          *
          * @param predicate
@@ -183,6 +187,34 @@ public class Binder<BEAN> implements Serializable {
             return withValidator(Validator.from(predicate, message));
         }
 
+        /**
+         * A convenience method to add a validator to this binding using the
+         * {@link Validator#from(SerializablePredicate, ErrorMessageProvider)}
+         * factory method.
+         * <p>
+         * Validators are applied, in registration order, when the field value
+         * is written to the backing property. If any validator returns a
+         * failure, the property value is not updated.
+         *
+         * @see #withValidator(Validator)
+         * @see #withValidator(SerializablePredicate, String)
+         * @see Validator#from(SerializablePredicate, ErrorMessageProvider)
+         *
+         * @param predicate
+         *            the predicate performing validation, not null
+         * @param errorMessageProvider
+         *            the provider to generate error messages, not null
+         * @return this binding, for chaining
+         * @throws IllegalStateException
+         *             if {@code bind} has already been called
+         */
+        public default Binding<BEAN, FIELDVALUE, TARGET> withValidator(
+                SerializablePredicate<? super TARGET> predicate,
+                ErrorMessageProvider errorMessageProvider) {
+            return withValidator(
+                    Validator.from(predicate, errorMessageProvider));
+        }
+
         /**
          * Maps the binding to another data type using the given
          * {@link Converter}.
@@ -1062,6 +1094,8 @@ public class Binder<BEAN> implements Serializable {
      *
      * @see #writeBean(Object)
      * @see #writeBeanIfValid(Object)
+     * @see #withValidator(SerializablePredicate, String)
+     * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
      *
      * @param validator
      *            the validator to add, not null
@@ -1081,8 +1115,10 @@ public class Binder<BEAN> implements Serializable {
      * updated. If the validators fail, the bean instance is reverted to its
      * previous state.
      *
-     * @see #save(Object)
-     * @see #saveIfValid(Object)
+     * @see #writeBean(Object)
+     * @see #writeBeanIfValid(Object)
+     * @see #withValidator(Validator)
+     * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
      *
      * @param predicate
      *            the predicate performing validation, not null
@@ -1095,6 +1131,31 @@ public class Binder<BEAN> implements Serializable {
         return withValidator(Validator.from(predicate, message));
     }
 
+    /**
+     * A convenience method to add a validator to this binder using the
+     * {@link Validator#from(SerializablePredicate, ErrorMessageProvider)}
+     * factory method.
+     * <p>
+     * Bean level validators are applied on the bean instance after the bean is
+     * updated. If the validators fail, the bean instance is reverted to its
+     * previous state.
+     *
+     * @see #writeBean(Object)
+     * @see #writeBeanIfValid(Object)
+     * @see #withValidator(Validator)
+     * @see #withValidator(SerializablePredicate, String)
+     *
+     * @param predicate
+     *            the predicate performing validation, not null
+     * @param errorMessageProvider
+     *            the provider to generate error messages, not null
+     * @return this binder, for chaining
+     */
+    public Binder<BEAN> withValidator(SerializablePredicate<BEAN> predicate,
+            ErrorMessageProvider errorMessageProvider) {
+        return withValidator(Validator.from(predicate, errorMessageProvider));
+    }
+
     /**
      * Validates the values of all bound fields and returns the validation
      * status.
diff --git a/server/src/main/java/com/vaadin/data/ErrorMessageProvider.java b/server/src/main/java/com/vaadin/data/ErrorMessageProvider.java
new file mode 100644 (file)
index 0000000..71a6723
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed 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 com.vaadin.data;
+
+import com.vaadin.data.util.converter.ValueContext;
+import com.vaadin.server.SerializableFunction;
+
+/**
+ * Provider interface for generating localizable error messages using
+ * {@link ValueContext}.
+ *
+ * @since
+ * @author Vaadin Ltd.
+ */
+@FunctionalInterface
+public interface ErrorMessageProvider
+        extends SerializableFunction<ValueContext, String> {
+
+    /**
+     * Returns a generated error message for given {@code ValueContext}.
+     *
+     * @param context
+     *            the value context
+     *
+     * @return generated error message
+     */
+    @Override
+    public String apply(ValueContext context);
+}
index 6f1d5827ec1fd37fdecfb70638af4e4caab2e88a..45a88bc7f3d106472db788798ba4755d7bf2d7ec 100644 (file)
@@ -101,15 +101,37 @@ public interface Validator<T>
             String errorMessage) {
         Objects.requireNonNull(guard, "guard cannot be null");
         Objects.requireNonNull(errorMessage, "errorMessage cannot be null");
+        return from(guard, ctx -> errorMessage);
+    }
+
+    /**
+     * Builds a validator out of a conditional function and an error message
+     * provider. If the function returns true, the validator returns
+     * {@code Result.ok()}; if it returns false or throws an exception,
+     * {@code Result.error()} is returned with the message from the provider.
+     *
+     * @param <T>
+     *            the value type
+     * @param guard
+     *            the function used to validate, not null
+     * @param errorMessageProvider
+     *            the provider to generate error messages, not null
+     * @return the new validator using the function
+     */
+    public static <T> Validator<T> from(SerializablePredicate<T> guard,
+            ErrorMessageProvider errorMessageProvider) {
+        Objects.requireNonNull(guard, "guard cannot be null");
+        Objects.requireNonNull(errorMessageProvider,
+                "errorMessageProvider cannot be null");
         return (value, context) -> {
             try {
                 if (guard.test(value)) {
                     return Result.ok(value);
                 } else {
-                    return Result.error(errorMessage);
+                    return Result.error(errorMessageProvider.apply(context));
                 }
             } catch (Exception e) {
-                return Result.error(errorMessage);
+                return Result.error(errorMessageProvider.apply(context));
             }
         };
     }
index ead9991b4c122348dda14a1a8e494e69fe6480c0..fa533c5010696fcefb12255c932214533b26b4a8 100644 (file)
  */
 package com.vaadin.data;
 
+import java.util.Locale;
 import java.util.Objects;
 
 import org.junit.Assert;
 import org.junit.Test;
 
 import com.vaadin.data.util.converter.ValueContext;
+import com.vaadin.data.validator.ValidatorTestBase;
 
 /**
  * @author Vaadin Ltd
  *
  */
-public class ValidatorTest {
+public class ValidatorTest extends ValidatorTestBase {
 
     @Test
     public void alwaysPass() {
@@ -47,4 +49,30 @@ public class ValidatorTest {
         result = validator.apply("", new ValueContext());
         Assert.assertFalse(result.isError());
     }
+
+    @Test
+    public void withValidator_customErrorMessageProvider() {
+        String finnishError = "Käyttäjän tulee olla täysi-ikäinen";
+        String englishError = "The user must be an adult";
+        String notTranslatableError = "NOT TRANSLATABLE";
+
+        Validator<Integer> ageValidator = Validator.from(age -> age >= 18,
+                ctx -> {
+                    Locale locale = ctx.getLocale().orElse(Locale.ENGLISH);
+
+                    if (locale.getLanguage().equals("fi")) {
+                        return finnishError;
+                    } else if (locale.getLanguage().equals("en")) {
+                        return englishError;
+                    }
+                    return notTranslatableError;
+                });
+
+        setLocale(Locale.ENGLISH);
+        assertFails(17, englishError, ageValidator);
+        setLocale(new Locale("fi", "FI"));
+        assertFails(17, finnishError, ageValidator);
+        setLocale(Locale.GERMAN);
+        assertFails(17, notTranslatableError, ageValidator);
+    }
 }
index 3d6cd2afe1604ed2f4a818843794ca027167d924..5a7198f33c4c132038d361236a0aef70f84e1ed1 100644 (file)
@@ -1,12 +1,23 @@
 package com.vaadin.data.validator;
 
+import java.util.Locale;
+
 import org.junit.Assert;
+import org.junit.Before;
 
 import com.vaadin.data.Validator;
 import com.vaadin.data.util.converter.ValueContext;
+import com.vaadin.ui.Label;
 
 public class ValidatorTestBase {
 
+    private Label localeContext;
+
+    @Before
+    public void setUp() {
+        localeContext = new Label();
+    }
+
     protected <T> void assertPasses(T value, Validator<? super T> v) {
         v.apply(value, new ValueContext())
                 .handle(val -> Assert.assertEquals(value, val), err -> Assert
@@ -15,7 +26,7 @@ public class ValidatorTestBase {
 
     protected <T> void assertFails(T value, String errorMessage,
             Validator<? super T> v) {
-        v.apply(value, new ValueContext()).handle(
+        v.apply(value, new ValueContext(localeContext)).handle(
                 val -> Assert.fail(value + " should fail " + v),
                 err -> Assert.assertEquals(errorMessage, err));
     }
@@ -23,4 +34,8 @@ public class ValidatorTestBase {
     protected <T> void assertFails(T value, AbstractValidator<? super T> v) {
         assertFails(value, v.getMessage(value), v);
     }
-}
+
+    protected void setLocale(Locale locale) {
+        localeContext.setLocale(locale);
+    }
+}
\ No newline at end of file