import java.text.ParsePosition;
import java.util.Locale;
+import com.vaadin.data.Result;
+
/**
* A converter that converts from the number type T to {@link String} and back.
* Uses the given locale and {@link NumberFormat} for formatting and parsing.
public abstract class AbstractStringToNumberConverter<T>
implements Converter<String, T> {
+ private final String errorMessage;
+
+ /**
+ * Creates a new converter instance with the given error message.
+ *
+ * @param errorMessage
+ * the error message to use if conversion fails
+ */
+ protected AbstractStringToNumberConverter(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
/**
* Returns the format used by {@link #convertToPresentation(Object, Locale)}
* and {@link #convertToModel(Object, Locale)}.
* The locale to use for conversion
* @return The converted value
*/
- protected Number convertToNumber(String value, Locale locale) {
+ protected Result<Number> convertToNumber(String value, Locale locale) {
if (value == null) {
- return null;
+ return Result.ok(null);
}
// Remove leading and trailing white space
ParsePosition parsePosition = new ParsePosition(0);
Number parsedValue = getFormat(locale).parse(value, parsePosition);
if (parsePosition.getIndex() != value.length()) {
- throw new IllegalArgumentException(
- "Could not convert '" + value + "'");
+ return Result.error(getErrorMessage());
}
if (parsedValue == null) {
// Convert "" to null
- return null;
+ return Result.ok(null);
}
- return parsedValue;
+ return Result.ok(parsedValue);
+ }
+
+ /**
+ * Gets the error message to use when conversion fails.
+ *
+ * @return the error message
+ */
+ protected String getErrorMessage() {
+ return errorMessage;
}
@Override
*/
public class StringToBigDecimalConverter
extends AbstractStringToNumberConverter<BigDecimal> {
+
+ /**
+ * Creates a new converter instance with the given error message.
+ *
+ * @param errorMessage
+ * the error message to use if conversion fails
+ */
+ public StringToBigDecimalConverter(String errorMessage) {
+ super(errorMessage);
+ }
+
@Override
protected NumberFormat getFormat(Locale locale) {
NumberFormat numberFormat = super.getFormat(locale);
@Override
public Result<BigDecimal> convertToModel(String value, Locale locale) {
- return Result.ok((BigDecimal) convertToNumber(value, locale));
+ return convertToNumber(value, locale)
+ .map(number -> (BigDecimal) number);
}
}
*/
public class StringToBigIntegerConverter
extends AbstractStringToNumberConverter<BigInteger> {
+ /**
+ * Creates a new converter instance with the given error message.
+ *
+ * @param errorMessage
+ * the error message to use if conversion fails
+ */
+ public StringToBigIntegerConverter(String errorMessage) {
+ super(errorMessage);
+ }
@Override
protected NumberFormat getFormat(Locale locale) {
@Override
public Result<BigInteger> convertToModel(String value, Locale locale) {
-
- BigDecimal bigDecimalValue = (BigDecimal) convertToNumber(value,
- locale);
-
- return (bigDecimalValue != null)
- ? Result.ok(bigDecimalValue.toBigInteger()) : Result.ok(null);
+ return convertToNumber(value, locale).map(number -> {
+ if (number == null) {
+ return null;
+ } else {
+ return ((BigDecimal) number).toBigInteger();
+ }
+ });
}
}
private final String falseString;
+ private String errorMessage;
+
/**
* Creates converter with default string representations - "true" and
* "false".
*
+ * @param errorMessage
+ * the error message to use if conversion fails
*/
- public StringToBooleanConverter() {
- this(Boolean.TRUE.toString(), Boolean.FALSE.toString());
+ public StringToBooleanConverter(String errorMessage) {
+ this(errorMessage, Boolean.TRUE.toString(), Boolean.FALSE.toString());
}
/**
* Creates converter with custom string representation.
*
+ * @param errorMessage
+ * the error message to use if conversion fails
* @param falseString
* string representation for <code>false</code>
* @param trueString
* string representation for <code>true</code>
*/
- public StringToBooleanConverter(String trueString, String falseString) {
+ public StringToBooleanConverter(String errorMessage, String trueString,
+ String falseString) {
+ this.errorMessage = errorMessage;
this.trueString = trueString;
this.falseString = falseString;
}
@Override
public Result<Boolean> convertToModel(String value, Locale locale) {
- if (value == null || value.isEmpty()) {
+ if (value == null) {
return Result.ok(null);
}
return Result.ok(true);
} else if (getFalseString(locale).equals(value)) {
return Result.ok(false);
+ } else if (value.isEmpty()) {
+ return Result.ok(null);
} else {
- throw new IllegalArgumentException("Cannot convert " + value);
+ return Result.error(errorMessage);
}
}
public class StringToDoubleConverter
extends AbstractStringToNumberConverter<Double> {
+ /**
+ * Creates a new converter instance with the given error message.
+ *
+ * @param errorMessage
+ * the error message to use if conversion fails
+ */
+ public StringToDoubleConverter(String errorMessage) {
+ super(errorMessage);
+ }
+
@Override
public Result<Double> convertToModel(String value, Locale locale) {
- Number n = convertToNumber(value, locale);
- return n == null ? Result.ok(null) : Result.ok(n.doubleValue());
+ Result<Number> n = convertToNumber(value, locale);
+
+ return n.map(number -> {
+ if (number == null) {
+ return null;
+ } else {
+ return number.doubleValue();
+ }
+ });
}
}
* parsing.
* <p>
* Leading and trailing white spaces are ignored when converting from a String.
- * </p>
* <p>
* Override and overwrite {@link #getFormat(Locale)} to use a different format.
- * </p>
*
* @author Vaadin Ltd
* @since 8.0
public class StringToFloatConverter
extends AbstractStringToNumberConverter<Float> {
+ /**
+ * Creates a new converter instance with the given error message.
+ *
+ * @param errorMessage
+ * the error message to use if conversion fails
+ */
+ public StringToFloatConverter(String errorMessage) {
+ super(errorMessage);
+ }
+
@Override
public Result<Float> convertToModel(String value, Locale locale) {
- Number n = convertToNumber(value, locale);
- return n == null ? Result.ok(null) : Result.ok(n.floatValue());
+ Result<Number> n = convertToNumber(value, locale);
+
+ return n.map(number -> {
+ if (number == null) {
+ return null;
+ } else {
+ return number.floatValue();
+ }
+ });
}
}
public class StringToIntegerConverter
extends AbstractStringToNumberConverter<Integer> {
- private final String errorMessage;
-
/**
* Creates a new converter instance with the given error message.
*
* the error message to use if conversion fails
*/
public StringToIntegerConverter(String errorMessage) {
- this.errorMessage = errorMessage;
+ super(errorMessage);
}
/**
@Override
public Result<Integer> convertToModel(String value, Locale locale) {
- Number n = convertToNumber(value, locale);
-
- if (n == null) {
- return Result.ok(null);
- }
-
- int intValue = n.intValue();
- if (intValue == n.longValue()) {
- // If the value of n is outside the range of long, the return value
- // of longValue() is either Long.MIN_VALUE or Long.MAX_VALUE. The
- // above comparison promotes int to long and thus does not need to
- // consider wrap-around.
- return Result.ok(intValue);
- } else {
- return Result.error(errorMessage);
- }
+ Result<Number> n = convertToNumber(value, locale);
+ return n.flatMap(number -> {
+ if (number == null) {
+ return Result.ok(null);
+ } else {
+ int intValue = number.intValue();
+ if (intValue == number.longValue()) {
+ // If the value of n is outside the range of long, the
+ // return value of longValue() is either Long.MIN_VALUE or
+ // Long.MAX_VALUE. The/ above comparison promotes int to
+ // long and thus does not need to consider wrap-around.
+ return Result.ok(intValue);
+ } else {
+ return Result.error(getErrorMessage());
+ }
+ }
+ });
}
}
public class StringToLongConverter
extends AbstractStringToNumberConverter<Long> {
+ /**
+ * Creates a new converter instance with the given error message.
+ *
+ * @param errorMessage
+ * the error message to use if conversion fails
+ */
+ public StringToLongConverter(String errorMessage) {
+ super(errorMessage);
+ }
+
/**
* Returns the format used by {@link #convertToPresentation(Long, Locale)}
* and {@link #convertToModel(String, Locale)}.
@Override
public Result<Long> convertToModel(String value, Locale locale) {
- Number n = convertToNumber(value, locale);
- return n == null ? Result.ok(null) : Result.ok(n.longValue());
-
+ Result<Number> n = convertToNumber(value, locale);
+ return n.map(number -> {
+ if (number == null) {
+ return null;
+ } else {
+ return number.longValue();
+ }
+ });
}
}
final DecimalFormat fmt = new DecimalFormat("0.###", symbols);
fmt.setGroupingUsed(false);
- Converter<String, ?> floatConverter = new StringToFloatConverter() {
+ Converter<String, ?> floatConverter = new StringToFloatConverter(
+ "Error converting value") {
@Override
protected NumberFormat getFormat(Locale locale) {
return fmt;
converterMap.put(Float.class, floatConverter);
converterMap.put(float.class, floatConverter);
- Converter<String, ?> doubleConverter = new StringToDoubleConverter() {
+ Converter<String, ?> doubleConverter = new StringToDoubleConverter(
+ "Error converting value") {
@Override
protected NumberFormat getFormat(Locale locale) {
return fmt;
final DecimalFormat bigDecimalFmt = new DecimalFormat("0.###", symbols);
bigDecimalFmt.setGroupingUsed(false);
bigDecimalFmt.setParseBigDecimal(true);
- converterMap.put(BigDecimal.class, new StringToBigDecimalConverter() {
- @Override
- protected NumberFormat getFormat(Locale locale) {
- return bigDecimalFmt;
- };
- });
+ converterMap.put(BigDecimal.class,
+ new StringToBigDecimalConverter("Error converting value") {
+ @Override
+ protected NumberFormat getFormat(Locale locale) {
+ return bigDecimalFmt;
+ };
+ });
// strings do nothing
converterMap.put(String.class, new Converter<String, String>() {
Assert.assertEquals("foo@bar.com", person.getEmail());
}
+ @Test
+ public void manyConvertersAndValidators() throws ValidationException {
+ TextField yearOfBirthField = new TextField();
+ binder.forField(yearOfBirthField)
+ // Validator will be run with the String value of the field
+ .withValidator(text -> text.length() == 4,
+ "Doesn't look like a year")
+ // Converter will only be run for strings with 4 characters
+ .withConverter(
+ new StringToIntegerConverter("Must enter a number"))
+ // Validator will be run with the converted value
+ .withValidator(year -> year >= 1900 && year <= 2000,
+ "Person must be born in the 20th century")
+ .bind(BookPerson::getYearOfBirth, BookPerson::setYearOfBirth);
+
+ yearOfBirthField.setValue("abc");
+ Assert.assertEquals("Doesn't look like a year",
+ binder.validate().get(0).getMessage());
+ yearOfBirthField.setValue("abcd");
+ Assert.assertEquals("Must enter a number",
+ binder.validate().get(0).getMessage());
+ yearOfBirthField.setValue("1200");
+ Assert.assertEquals("Person must be born in the 20th century",
+ binder.validate().get(0).getMessage());
+
+ yearOfBirthField.setValue("1950");
+ Assert.assertTrue(binder.validate().isEmpty());
+ BookPerson person = new BookPerson(1500, 12);
+ binder.save(person);
+ Assert.assertEquals(1950, person.getYearOfBirth());
+ }
+
}
@Test
public void testNullConversion() {
- assertResult(null, getConverter().convertToModel(null, null));
+ assertValue(null, getConverter().convertToModel(null, null));
}
protected abstract Converter<?, ?> getConverter();
- protected void assertResult(Object object, Result<?> result) {
- assertResult(null, object, result);
+ protected void assertValue(Object object, Result<?> result) {
+ assertValue(null, object, result);
}
- protected void assertResult(String error, Object object, Result<?> result) {
+ protected void assertValue(String error, Object object, Result<?> result) {
Assert.assertNotNull("Result should never be null", result);
Assert.assertFalse("Result is not ok", result.isError());
Assert.assertEquals(object,
error != null ? error : message)));
}
+ protected void assertError(String expected, Result<?> result) {
+ Assert.assertNotNull("Result should never be null", result);
+ Assert.assertTrue("Result should be an error", result.isError());
+ Assert.assertEquals(expected, result.getMessage().get());
+ }
+
+ protected String getErrorMessage() {
+ return "conversion failed";
+ }
+
}
--- /dev/null
+package com.vaadin.tests.data.converter;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.data.Result;
+import com.vaadin.data.util.converter.Converter;
+
+public abstract class AbstractStringConverterTest
+ extends AbstractConverterTest {
+
+ @Override
+ protected abstract Converter<String, ?> getConverter();
+
+ @Test
+ public void testEmptyStringConversion() {
+ assertValue("Null value was converted incorrectly", null,
+ getConverter().convertToModel("", null));
+ }
+
+ @Test
+ public void testErrorMessage() {
+ Result<?> result = getConverter().convertToModel("abc", null);
+ Assert.assertTrue(result.isError());
+ Assert.assertEquals(getErrorMessage(), result.getMessage().get());
+ }
+
+ protected String getErrorMessage() {
+ return "conversion failed";
+ }
+
+}
@Override
@Test
public void testNullConversion() {
- assertResult(null, getConverter().convertToModel(null, null));
+ assertValue(null, getConverter().convertToModel(null, null));
}
@Test
public void testValueConversion() {
Date d = new Date(100, 0, 1);
- assertResult(
+ assertValue(
Long.valueOf(946677600000l
+ (d.getTimezoneOffset() + 120) * 60 * 1000L),
getConverter().convertToModel(d, null));
public void testValueConversion() {
Date testDate = new Date(100, 0, 1);
long time = testDate.getTime();
- assertResult(testDate, getConverter()
+ assertValue(testDate, getConverter()
.convertToModel(new java.sql.Date(time), Locale.ENGLISH));
}
}
import com.vaadin.data.Result;
import com.vaadin.data.util.converter.StringToBigDecimalConverter;
-public class StringToBigDecimalConverterTest extends AbstractConverterTest {
+public class StringToBigDecimalConverterTest
+ extends AbstractStringConverterTest {
@Override
protected StringToBigDecimalConverter getConverter() {
- return new StringToBigDecimalConverter();
- }
-
- @Test
- public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
+ return new StringToBigDecimalConverter(getErrorMessage());
}
@Test
Result<BigDecimal> converted = getConverter().convertToModel("10",
null);
BigDecimal expected = new BigDecimal(10);
- assertResult(expected, converted);
+ assertValue(expected, converted);
}
@Test
import java.math.BigInteger;
import java.util.Locale;
+import org.junit.Assert;
import org.junit.Test;
import com.vaadin.data.Result;
import com.vaadin.data.util.converter.StringToBigIntegerConverter;
-import junit.framework.Assert;
-
-public class StringToBigIntegerConverterTest extends AbstractConverterTest {
+public class StringToBigIntegerConverterTest
+ extends AbstractStringConverterTest {
@Override
protected StringToBigIntegerConverter getConverter() {
- return new StringToBigIntegerConverter();
- }
-
- @Test
- public void testEmptyStringConversion() {
- assertResult("Empty value was converted incorrectly", null,
- getConverter().convertToModel("", null));
+ return new StringToBigIntegerConverter(getErrorMessage());
}
@Test
Result<BigInteger> converted = getConverter().convertToModel(bigInt,
null);
BigInteger expected = new BigInteger(bigInt);
- assertResult("Value bigger than max long was converted incorrectly",
+ assertValue("Value bigger than max long was converted incorrectly",
expected, converted);
}
import com.vaadin.data.util.converter.StringToBooleanConverter;
-public class StringToBooleanConverterTest extends AbstractConverterTest {
+public class StringToBooleanConverterTest extends AbstractStringConverterTest {
@Override
protected StringToBooleanConverter getConverter() {
- return new StringToBooleanConverter();
+ return new StringToBooleanConverter(getErrorMessage());
}
- StringToBooleanConverter yesNoConverter = new StringToBooleanConverter(
- "yes", "no");
- StringToBooleanConverter localeConverter = new StringToBooleanConverter() {
+ private StringToBooleanConverter yesNoConverter = new StringToBooleanConverter(
+ getErrorMessage(), "yes", "no");
+ private StringToBooleanConverter emptyTrueConverter = new StringToBooleanConverter(
+ getErrorMessage(), "", "ABSENT");
+ private StringToBooleanConverter localeConverter = new StringToBooleanConverter(
+ getErrorMessage()) {
@Override
public String getFalseString(Locale locale) {
Date d = new Date(3000000000000L);
}
};
- @Test
- public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
- }
-
@Test
public void testValueConversion() {
- assertResult(true, getConverter().convertToModel("true", null));
- assertResult(false, getConverter().convertToModel("false", null));
+ assertValue(true, getConverter().convertToModel("true", null));
+ assertValue(false, getConverter().convertToModel("false", null));
}
@Test
public void testYesNoValueConversion() {
- assertResult(true, yesNoConverter.convertToModel("yes", null));
- assertResult(false, yesNoConverter.convertToModel("no", null));
+ assertValue(true, yesNoConverter.convertToModel("yes", null));
+ assertValue(false, yesNoConverter.convertToModel("no", null));
Assert.assertEquals("yes",
yesNoConverter.convertToPresentation(true, null));
yesNoConverter.convertToPresentation(false, null));
}
+ @Test
+ public void testEmptyTrueValueConversion() {
+ assertValue(true, emptyTrueConverter.convertToModel("", null));
+ assertValue(false, emptyTrueConverter.convertToModel("ABSENT", null));
+
+ Assert.assertEquals("",
+ emptyTrueConverter.convertToPresentation(true, null));
+ Assert.assertEquals("ABSENT",
+ emptyTrueConverter.convertToPresentation(false, null));
+ }
+
@Test
public void testLocale() {
Assert.assertEquals("May 18, 2033",
@Test
public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
+ assertValue(null, getConverter().convertToModel("", null));
}
@Test
public void testValueConversion() {
- assertResult(new Date(100, 0, 1), getConverter()
+ assertValue(new Date(100, 0, 1), getConverter()
.convertToModel("Jan 1, 2000 12:00:00 AM", Locale.ENGLISH));
}
}
package com.vaadin.tests.data.converter;
+import org.junit.Assert;
import org.junit.Test;
import com.vaadin.data.Result;
@Override
protected StringToDoubleConverter getConverter() {
- return new StringToDoubleConverter();
+ return new StringToDoubleConverter("Failed");
}
@Test
public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
+ assertValue(null, getConverter().convertToModel("", null));
}
@Test
public void testValueConversion() {
Result<Double> value = getConverter().convertToModel("10", null);
- assertResult(10.0d, value);
+ assertValue(10.0d, value);
}
+
+ @Test
+ public void testErrorMessage() {
+ Result<Double> result = getConverter().convertToModel("abc", null);
+ Assert.assertTrue(result.isError());
+ Assert.assertEquals("Failed", result.getMessage().get());
+ }
+
}
import com.vaadin.data.util.converter.StringToFloatConverter;
-public class StringToFloatConverterTest extends AbstractConverterTest {
+public class StringToFloatConverterTest extends AbstractStringConverterTest {
@Override
protected StringToFloatConverter getConverter() {
- return new StringToFloatConverter();
+ return new StringToFloatConverter(getErrorMessage());
}
@Override
@Test
public void testNullConversion() {
- assertResult(null, getConverter().convertToModel(null, null));
+ assertValue(null, getConverter().convertToModel(null, null));
}
+ @Override
@Test
public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
+ assertValue(null, getConverter().convertToModel("", null));
}
@Test
public void testValueConversion() {
- assertResult(Float.valueOf(10),
+ assertValue(Float.valueOf(10),
getConverter().convertToModel("10", null));
}
}
import org.junit.Assert;
import org.junit.Test;
+import com.vaadin.data.Result;
import com.vaadin.data.util.converter.StringToIntegerConverter;
public class StringToIntegerConverterTest extends AbstractConverterTest {
@Test
public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
+ assertValue(null, getConverter().convertToModel("", null));
}
@Test
@Test
public void testValueConversion() {
- assertResult(Integer.valueOf(10),
+ assertValue(Integer.valueOf(10),
getConverter().convertToModel("10", null));
}
+
+ @Test
+ public void testErrorMessage() {
+ Result<Integer> result = getConverter().convertToModel("abc", null);
+ Assert.assertTrue(result.isError());
+ Assert.assertEquals("Failed", result.getMessage().get());
+ }
}
import com.vaadin.data.Result;
import com.vaadin.data.util.converter.StringToLongConverter;
-public class StringToLongConverterTest extends AbstractConverterTest {
+public class StringToLongConverterTest extends AbstractStringConverterTest {
@Override
protected StringToLongConverter getConverter() {
- return new StringToLongConverter();
+ return new StringToLongConverter(getErrorMessage());
}
+ @Override
@Test
public void testEmptyStringConversion() {
- assertResult(null, getConverter().convertToModel("", null));
+ assertValue(null, getConverter().convertToModel("", null));
}
@Test
public void testValueConversion() {
- assertResult(Long.valueOf(10),
+ assertValue(Long.valueOf(10),
getConverter().convertToModel("10", null));
}
public void testExtremeLongValueConversion() {
Result<Long> l = getConverter().convertToModel("9223372036854775807",
null);
- assertResult(Long.MAX_VALUE, l);
+ assertValue(Long.MAX_VALUE, l);
l = getConverter().convertToModel("-9223372036854775808", null);
- assertResult(Long.MIN_VALUE, l);
+ assertValue(Long.MIN_VALUE, l);
}
@Test
// Long.MAX_VALUE+1 is converted to Long.MAX_VALUE
Result<Long> l = getConverter().convertToModel("9223372036854775808",
null);
- assertResult(Long.MAX_VALUE, l);
+ assertValue(Long.MAX_VALUE, l);
// Long.MIN_VALUE-1 is converted to Long.MIN_VALUE
l = getConverter().convertToModel("-9223372036854775809", null);
- assertResult(Long.MIN_VALUE, l);
+ assertValue(Long.MIN_VALUE, l);
}
}