aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Dahlström <johannesd@vaadin.com>2016-07-29 00:51:43 +0300
committerIlia Motornyi <elmot@vaadin.com>2016-08-03 13:13:00 +0000
commit3b4753a85b8489ee4d86ec1ecb947bf836db246d (patch)
tree5aa656ce0500105e284e8ef2557c5bcb842c3403
parent895da2253630a9b44dc151400afe034c8589215d (diff)
downloadvaadin-framework-3b4753a85b8489ee4d86ec1ecb947bf836db246d.tar.gz
vaadin-framework-3b4753a85b8489ee4d86ec1ecb947bf836db246d.zip
Implement new String validators
Change-Id: I893af1f426d04674269860871fc42fb71e5a9188
-rw-r--r--server/src/main/java/com/vaadin/tokka/data/Validator.java4
-rw-r--r--server/src/main/java/com/vaadin/tokka/data/util/Result.java24
-rw-r--r--server/src/main/java/com/vaadin/tokka/data/validators/EmailValidator.java47
-rw-r--r--server/src/main/java/com/vaadin/tokka/data/validators/RegexpValidator.java111
-rw-r--r--server/src/main/java/com/vaadin/tokka/data/validators/StringLengthValidator.java101
-rw-r--r--server/src/test/java/com/vaadin/tokka/data/validators/BeanValidatorTest.java17
-rw-r--r--server/src/test/java/com/vaadin/tokka/data/validators/EmailValidatorTest.java98
-rw-r--r--server/src/test/java/com/vaadin/tokka/data/validators/RangeValidatorTest.java17
-rw-r--r--server/src/test/java/com/vaadin/tokka/data/validators/RegexpValidatorTest.java71
-rw-r--r--server/src/test/java/com/vaadin/tokka/data/validators/StringLengthValidatorTest.java41
-rw-r--r--server/src/test/java/com/vaadin/tokka/data/validators/ValidatorTestBase.java26
11 files changed, 523 insertions, 34 deletions
diff --git a/server/src/main/java/com/vaadin/tokka/data/Validator.java b/server/src/main/java/com/vaadin/tokka/data/Validator.java
index 3c6fc522dd..884737de11 100644
--- a/server/src/main/java/com/vaadin/tokka/data/Validator.java
+++ b/server/src/main/java/com/vaadin/tokka/data/Validator.java
@@ -93,7 +93,9 @@ public interface Validator<T> extends Function<T, Result<T>>, Serializable {
/**
* Validates the given value. Returns a {@code Result} instance representing
- * the outcome of the validation.
+ * the outcome of the validation: either {@link Result#ok(Object) Result.ok}
+ * if the value passed validation or {@link Result#error(String)
+ * Result.error} otherwise.
*
* @param value
* the input value to validate
diff --git a/server/src/main/java/com/vaadin/tokka/data/util/Result.java b/server/src/main/java/com/vaadin/tokka/data/util/Result.java
index 7792c447e5..4e6b880072 100644
--- a/server/src/main/java/com/vaadin/tokka/data/util/Result.java
+++ b/server/src/main/java/com/vaadin/tokka/data/util/Result.java
@@ -62,7 +62,7 @@ public interface Result<R> extends Serializable {
*/
public static <R> Result<R> error(String message) {
Objects.requireNonNull(message, "message cannot be null");
- return new ResultImpl<R>(null, message);
+ return new ResultImpl<>(null, message);
}
/**
@@ -141,16 +141,38 @@ public interface Result<R> extends Serializable {
*/
public void handle(Consumer<R> ifOk, Consumer<String> ifError);
+ /**
+ * If this result denotes a success, invokes the given consumer with the
+ * carried value, otherwise does nothing.
+ *
+ * @param consumer
+ * the function to call if success
+ */
public default void ifOk(Consumer<R> consumer) {
handle(consumer, error -> {
});
}
+ /**
+ * If this result denotes a failure, invokes the given consumer with the
+ * carried error message, otherwise does nothing.
+ *
+ * @param consumer
+ * the function to call if failure
+ */
public default void ifError(Consumer<String> consumer) {
handle(value -> {
}, consumer);
}
+ /**
+ * Returns whether this result represents a successful outcome.
+ *
+ * @return true if this result is successful, false otherwise
+ *
+ * @see #ok(Object)
+ * @see #ifOk(Consumer)
+ */
public boolean isOk();
/**
diff --git a/server/src/main/java/com/vaadin/tokka/data/validators/EmailValidator.java b/server/src/main/java/com/vaadin/tokka/data/validators/EmailValidator.java
new file mode 100644
index 0000000000..ffd6fb3a2e
--- /dev/null
+++ b/server/src/main/java/com/vaadin/tokka/data/validators/EmailValidator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2014 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.tokka.data.validators;
+
+/**
+ * A string validator for e-mail addresses. The e-mail address syntax is not
+ * complete according to RFC 822 but handles the vast majority of valid e-mail
+ * addresses correctly.
+ *
+ * @author Vaadin Ltd.
+ * @since
+ */
+@SuppressWarnings("serial")
+public class EmailValidator extends RegexpValidator {
+
+ private static final String PATTERN = "^"
+ + "([a-zA-Z0-9_\\.\\-+])+" // local
+ + "@"
+ + "[a-zA-Z0-9-.]+" // domain
+ + "\\."
+ + "[a-zA-Z0-9-]{2,}" // tld
+ + "$";
+
+ /**
+ * Creates a validator for checking that a string is a syntactically valid
+ * e-mail address.
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ */
+ public EmailValidator(String errorMessage) {
+ super(errorMessage, PATTERN, true);
+ }
+}
diff --git a/server/src/main/java/com/vaadin/tokka/data/validators/RegexpValidator.java b/server/src/main/java/com/vaadin/tokka/data/validators/RegexpValidator.java
new file mode 100644
index 0000000000..0350806720
--- /dev/null
+++ b/server/src/main/java/com/vaadin/tokka/data/validators/RegexpValidator.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2014 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.tokka.data.validators;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.vaadin.tokka.data.util.Result;
+
+/**
+ * A string validator comparing the string against a Java regular expression.
+ * Both complete matches and substring matches are supported.
+ * <p>
+ * For the Java regular expression syntax, see {@link java.util.regex.Pattern}.
+ *
+ * @author Vaadin Ltd.
+ * @since
+ */
+@SuppressWarnings("serial")
+public class RegexpValidator extends AbstractValidator<String> {
+
+ private Pattern pattern;
+ private boolean complete;
+ private transient Matcher matcher = null;
+
+ /**
+ * Creates a validator for checking that the regular expression matches the
+ * complete string to validate.
+ *
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ * @param regexp
+ * a Java regular expression
+ */
+ public RegexpValidator(String errorMessage, String regexp) {
+ this(errorMessage, regexp, true);
+ }
+
+ /**
+ * Creates a validator for checking that the regular expression matches the
+ * string to validate.
+ *
+ * @param regexp
+ * a Java regular expression
+ * @param complete
+ * true to use check for a complete match, false to look for a
+ * matching substring
+ * @param errorMessage
+ * the message to display in case the value does not validate.
+ */
+ public RegexpValidator(String errorMessage, String regexp,
+ boolean complete) {
+ super(errorMessage);
+ pattern = Pattern.compile(regexp);
+ this.complete = complete;
+ }
+
+ @Override
+ public Result<String> apply(String value) {
+ return toResult(value, isValid(value));
+ }
+
+ @Override
+ public String toString() {
+ return "RegexpValidator[" + pattern + "]";
+ }
+
+ /**
+ * Returns whether the given string matches the regular expression.
+ *
+ * @param value
+ * the string to match
+ * @return true if the string matched, false otherwise
+ */
+ protected boolean isValid(String value) {
+ if (complete) {
+ return getMatcher(value).matches();
+ } else {
+ return getMatcher(value).find();
+ }
+ }
+
+ /**
+ * Returns a new or reused matcher for the pattern.
+ *
+ * @param value
+ * the string to find matches in
+ * @return a matcher for the string
+ */
+ private Matcher getMatcher(String value) {
+ if (matcher == null) {
+ matcher = pattern.matcher(value);
+ } else {
+ matcher.reset(value);
+ }
+ return matcher;
+ }
+}
diff --git a/server/src/main/java/com/vaadin/tokka/data/validators/StringLengthValidator.java b/server/src/main/java/com/vaadin/tokka/data/validators/StringLengthValidator.java
new file mode 100644
index 0000000000..affb71fff8
--- /dev/null
+++ b/server/src/main/java/com/vaadin/tokka/data/validators/StringLengthValidator.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2000-2014 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.tokka.data.validators;
+
+import com.vaadin.tokka.data.util.Result;
+
+/**
+ * Verifies that the length of a string is within the given range.
+ *
+ * @author Vaadin Ltd.
+ * @since
+ */
+@SuppressWarnings("serial")
+public class StringLengthValidator extends AbstractValidator<String> {
+
+ private RangeValidator<Integer> validator;
+
+ /**
+ * Creates a new StringLengthValidator with a given error message and
+ * minimum and maximum length limits.
+ *
+ * @param errorMessage
+ * the error message to return if validation fails
+ * @param minLength
+ * the minimum permissible length of the string or null for no
+ * limit.
+ * @param maxLength
+ * the maximum permissible length of the string or null for no
+ * limit.
+ */
+ public StringLengthValidator(String errorMessage, Integer minLength,
+ Integer maxLength) {
+ super(errorMessage);
+ validator = RangeValidator.of(errorMessage, minLength, maxLength);
+ }
+
+ @Override
+ public Result<String> apply(String value) {
+ Result<?> lengthCheck = validator.apply(value.length());
+ return toResult(value, lengthCheck.isOk());
+ }
+
+ /**
+ * Gets the maximum permissible length of the string.
+ *
+ * @return the maximum length of the string or null if there is no limit
+ */
+ public Integer getMaxLength() {
+ return validator.getMaxValue();
+ }
+
+ /**
+ * Gets the minimum permissible length of the string.
+ *
+ * @return the minimum length of the string or null if there is no limit
+ */
+ public Integer getMinLength() {
+ return validator.getMinValue();
+ }
+
+ /**
+ * Sets the maximum permissible length of the string.
+ *
+ * @param maxLength
+ * the maximum length to accept or null for no limit
+ */
+ public void setMaxLength(Integer maxLength) {
+ validator.setMaxValue(maxLength);
+ }
+
+ /**
+ * Sets the minimum permissible length.
+ *
+ * @param minLength
+ * the minimum length to accept or null for no limit
+ */
+ public void setMinLength(Integer minLength) {
+ validator.setMaxValue(minLength);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[%d, %d]", getClass().getSimpleName(),
+ getMinLength(), getMaxLength());
+ }
+
+}
diff --git a/server/src/test/java/com/vaadin/tokka/data/validators/BeanValidatorTest.java b/server/src/test/java/com/vaadin/tokka/data/validators/BeanValidatorTest.java
index 0e144102b9..48931c3207 100644
--- a/server/src/test/java/com/vaadin/tokka/data/validators/BeanValidatorTest.java
+++ b/server/src/test/java/com/vaadin/tokka/data/validators/BeanValidatorTest.java
@@ -1,8 +1,5 @@
package com.vaadin.tokka.data.validators;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
import java.util.Calendar;
import java.util.Locale;
@@ -11,7 +8,7 @@ import org.junit.Test;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.BeanToValidate;
-public class BeanValidatorTest {
+public class BeanValidatorTest extends ValidatorTestBase {
@Test
public void testFirstNameNullFails() {
@@ -74,16 +71,4 @@ public class BeanValidatorTest {
private BeanValidator validator(String propertyName) {
return new BeanValidator(BeanToValidate.class, propertyName);
}
-
- private <T> void assertPasses(T value, BeanValidator v) {
- v.apply(value).handle(
- val -> assertEquals(value, val),
- err -> fail(value + " should pass " + v + " but got " + err));
- }
-
- private <T> void assertFails(T value, String error, BeanValidator v) {
- v.apply(value).handle(
- val -> fail(value + " should fail " + v),
- err -> assertEquals(error, err));
- }
}
diff --git a/server/src/test/java/com/vaadin/tokka/data/validators/EmailValidatorTest.java b/server/src/test/java/com/vaadin/tokka/data/validators/EmailValidatorTest.java
new file mode 100644
index 0000000000..e98b966cc8
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tokka/data/validators/EmailValidatorTest.java
@@ -0,0 +1,98 @@
+package com.vaadin.tokka.data.validators;
+
+import org.junit.Test;
+
+public class EmailValidatorTest extends ValidatorTestBase {
+
+ @Test(expected = NullPointerException.class)
+ public void testNullStringFails() {
+ validator("null should throw").apply(null);
+ }
+
+ @Test
+ public void testEmptyStringFails() {
+ assertFails("", validator("empty string not allowed"));
+ }
+
+ @Test
+ public void testStringWithoutAtSignFails() {
+ assertFails("johannesd.vaadin", validator("@ is required"));
+ }
+
+ @Test
+ public void testMissingLocalPartFails() {
+ RegexpValidator v = validator("local part is required");
+ assertFails("@localhost", v);
+ assertFails(" @localhost", v);
+ }
+
+ @Test
+ public void testNonAsciiEmailFails() {
+ RegexpValidator v = validator("accented letters not allowed");
+ assertFails("jöhännes@vaadin.com", v);
+ assertFails("johannes@váádìn.com", v);
+ assertFails("johannes@vaadin.cõm", v);
+ }
+
+ @Test
+ public void testLocalPartWithPunctuationPasses() {
+ RegexpValidator v = shouldNotFail();
+ assertPasses("johannesd+test@vaadin.com", v);
+ assertPasses("johannes.dahlstrom@vaadin.com", v);
+ assertPasses("johannes_d@vaadin.com", v);
+ }
+
+ @Test
+ public void testEmailWithoutDomainPartFails() {
+ assertFails("johannesd@", validator("domain part is required"));
+ }
+
+ @Test
+ public void testComplexDomainPasses() {
+ assertPasses("johannesd@foo.bar.baz.vaadin.com", shouldNotFail());
+ }
+
+ @Test
+ public void testDomainWithPunctuationPasses() {
+ assertPasses("johannesd@vaadin-dev.com", shouldNotFail());
+ }
+
+ @Test
+ public void testMissingTldFails() {
+ assertFails("johannesd@localhost", validator("tld is required"));
+ }
+
+ @Test
+ public void testOneLetterTldFails() {
+ assertFails("johannesd@vaadin.f",
+ validator("one-letter tld not allowed"));
+ }
+
+ @Test
+ public void testLongTldPasses() {
+ assertPasses("joonas@vaadin.management", shouldNotFail());
+ }
+
+ @Test
+ public void testIdnTldPasses() {
+ assertPasses("leif@vaadin.XN--VERMGENSBERATER-CTB", shouldNotFail());
+ }
+
+ @Test
+ public void testYelledEmailPasses() {
+ assertPasses("JOHANNESD@VAADIN.COM", shouldNotFail());
+ }
+
+ @Test
+ public void testEmailWithDigitsPasses() {
+ assertPasses("johannes84@v44d1n.com", shouldNotFail());
+ }
+
+ private EmailValidator validator(String errorMessage) {
+ return new EmailValidator(errorMessage);
+ }
+
+ private EmailValidator shouldNotFail() {
+ return validator("this should not fail");
+ }
+}
diff --git a/server/src/test/java/com/vaadin/tokka/data/validators/RangeValidatorTest.java b/server/src/test/java/com/vaadin/tokka/data/validators/RangeValidatorTest.java
index f5ac670472..02a1b1859c 100644
--- a/server/src/test/java/com/vaadin/tokka/data/validators/RangeValidatorTest.java
+++ b/server/src/test/java/com/vaadin/tokka/data/validators/RangeValidatorTest.java
@@ -1,13 +1,10 @@
package com.vaadin.tokka.data.validators;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
import java.time.LocalDate;
import org.junit.Test;
-public class RangeValidatorTest {
+public class RangeValidatorTest extends ValidatorTestBase {
@Test
public void testIntegerRangeValidIntPasses() {
@@ -104,16 +101,4 @@ public class RangeValidatorTest {
assertPasses(LocalDate.of(2016, 7, 31), v);
assertFails(LocalDate.ofEpochDay(1_000_000_000), v);
}
-
- private <T> void assertPasses(T value, RangeValidator<T> v) {
- v.apply(value).handle(
- val -> assertEquals(value, val),
- err -> fail(value + " should pass " + v + " but got " + err));
- }
-
- private <T> void assertFails(T value, RangeValidator<T> v) {
- v.apply(value).handle(
- val -> fail(value + " should fail " + v),
- err -> assertEquals(v.getMessage(value), err));
- }
}
diff --git a/server/src/test/java/com/vaadin/tokka/data/validators/RegexpValidatorTest.java b/server/src/test/java/com/vaadin/tokka/data/validators/RegexpValidatorTest.java
new file mode 100644
index 0000000000..1deca3f2f2
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tokka/data/validators/RegexpValidatorTest.java
@@ -0,0 +1,71 @@
+package com.vaadin.tokka.data.validators;
+
+import org.junit.Test;
+
+public class RegexpValidatorTest extends ValidatorTestBase {
+
+ @Test(expected = NullPointerException.class)
+ public void testNullStringFails() {
+ new RegexpValidator("Should be 'abc'", "abc").apply(null);
+ }
+
+ @Test
+ public void testEmptyPatternMatchesEmptyString() {
+ assertPasses("", new RegexpValidator("Should be empty", "", true));
+ }
+
+ @Test
+ public void testEmptyPatternDoesNotMatchNonEmptyString() {
+ assertFails("x", new RegexpValidator("Should be empty", "", true));
+ }
+
+ @Test
+ public void testPatternMatchesString() {
+ RegexpValidator v = new RegexpValidator(
+ "Should be foo and bar repeating", "(foo|bar)+", true);
+
+ assertPasses("foo", v);
+ assertPasses("barfoo", v);
+ assertPasses("foobarbarbarfoobarfoofoobarbarfoofoofoobar", v);
+ }
+
+ @Test
+ public void testPatternDoesNotMatchString() {
+ RegexpValidator v = new RegexpValidator(
+ "Should be foo and bar repeating", "(foo|bar)+", true);
+
+ assertFails("", v);
+ assertFails("barf", v);
+ assertFails(" bar", v);
+ assertFails("foobarbarbarfoobar.foofoobarbarfoofoofoobar", v);
+ }
+
+ @Test
+ public void testEmptyPatternFoundInAnyString() {
+ RegexpValidator v = new RegexpValidator("Should always pass", "",
+ false);
+
+ assertPasses("", v);
+ assertPasses(" ", v);
+ assertPasses("qwertyuiopasdfghjklzxcvbnm", v);
+ }
+
+ @Test
+ public void testPatternFoundInString() {
+ RegexpValidator v = new RegexpValidator("Should contain a number",
+ "\\d+", false);
+
+ assertPasses("0", v);
+ assertPasses(" 123 ", v);
+ assertPasses("qwerty9iop", v);
+ }
+
+ @Test
+ public void testPatternNotFoundInString() {
+ RegexpValidator v = new RegexpValidator("Should contain a number",
+ "\\d+", false);
+
+ assertFails("", v);
+ assertFails("qwertyiop", v);
+ }
+}
diff --git a/server/src/test/java/com/vaadin/tokka/data/validators/StringLengthValidatorTest.java b/server/src/test/java/com/vaadin/tokka/data/validators/StringLengthValidatorTest.java
new file mode 100644
index 0000000000..048277bbd7
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tokka/data/validators/StringLengthValidatorTest.java
@@ -0,0 +1,41 @@
+package com.vaadin.tokka.data.validators;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+
+public class StringLengthValidatorTest extends ValidatorTestBase {
+
+ private static String LONG_STRING = Stream.generate(() -> "x").limit(1000)
+ .collect(Collectors.joining());
+
+ @Test(expected = NullPointerException.class)
+ public void testNullStringFails() {
+ new StringLengthValidator("", 0, 10).apply(null);
+ }
+
+ @Test
+ public void testMaxLengthTooLongStringFails() {
+ assertFails(LONG_STRING, new StringLengthValidator(
+ "Should be at most 10", null, 10));
+ }
+
+ @Test
+ public void testMaxLengthStringPasses() {
+ assertPasses(LONG_STRING, new StringLengthValidator(
+ "Should be at most 1000", null, 1000));
+ }
+
+ @Test
+ public void testMinLengthEmptyStringFails() {
+ assertFails("", new StringLengthValidator("Should be at least 1", 1,
+ null));
+ }
+
+ @Test
+ public void testMinLengthStringPasses() {
+ assertPasses("å", new StringLengthValidator("Should be at least 1", 1,
+ null));
+ }
+}
diff --git a/server/src/test/java/com/vaadin/tokka/data/validators/ValidatorTestBase.java b/server/src/test/java/com/vaadin/tokka/data/validators/ValidatorTestBase.java
new file mode 100644
index 0000000000..6546dad76f
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tokka/data/validators/ValidatorTestBase.java
@@ -0,0 +1,26 @@
+package com.vaadin.tokka.data.validators;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.vaadin.tokka.data.Validator;
+
+public class ValidatorTestBase {
+
+ protected <T> void assertPasses(T value, Validator<? super T> v) {
+ v.apply(value).handle(
+ val -> assertEquals(value, val),
+ err -> fail(value + " should pass " + v + " but got " + err));
+ }
+
+ protected <T> void assertFails(T value, String errorMessage,
+ Validator<? super T> v) {
+ v.apply(value).handle(
+ val -> fail(value + " should fail " + v),
+ err -> assertEquals(errorMessage, err));
+ }
+
+ protected <T> void assertFails(T value, AbstractValidator<? super T> v) {
+ assertFails(value, v.getMessage(value), v);
+ }
+}