Rework thrown exceptions for DecimalType, PercentType and QuantityType (#2374)
Throw NumberFormatException when numbers are invalid and cannot be parsed to BigDecimals. Add JavaDocs to document when which exception is thrown. Add more unit tests to cover the thrown exceptions. Signed-off-by: Wouter Born <github@maindrain.net>pull/2376/head
parent
3c30177269
commit
696ebacdc7
|
@ -56,17 +56,33 @@ public class DecimalType extends Number implements PrimitiveType, State, Command
|
|||
this.value = BigDecimal.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DecimalType} with the given value.
|
||||
* The English locale is used to determine (decimal/grouping) separator characters.
|
||||
*
|
||||
* @param value the non null value representing a number
|
||||
*
|
||||
* @throws NumberFormatException when the number could not be parsed to a {@link BigDecimal}
|
||||
*/
|
||||
public DecimalType(String value) {
|
||||
this(value, Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DecimalType} with the given value.
|
||||
*
|
||||
* @param value the non null value representing a number
|
||||
* @param locale the locale used to determine (decimal/grouping) separator characters
|
||||
*
|
||||
* @throws NumberFormatException when the number could not be parsed to a {@link BigDecimal}
|
||||
*/
|
||||
public DecimalType(String value, Locale locale) {
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(locale);
|
||||
df.setParseBigDecimal(true);
|
||||
ParsePosition position = new ParsePosition(0);
|
||||
BigDecimal parsedValue = (BigDecimal) df.parseObject(value, position);
|
||||
if (parsedValue == null || position.getErrorIndex() != -1 || position.getIndex() < value.length()) {
|
||||
throw new IllegalArgumentException("Invalid BigDecimal value: " + value);
|
||||
throw new NumberFormatException("Invalid BigDecimal value: " + value);
|
||||
}
|
||||
this.value = parsedValue;
|
||||
}
|
||||
|
@ -81,6 +97,14 @@ public class DecimalType extends Number implements PrimitiveType, State, Command
|
|||
return value.toPlainString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static access to {@link DecimalType#DecimalType(String)}.
|
||||
*
|
||||
* @param value the non null value representing a number
|
||||
* @return a new {@link DecimalType}
|
||||
*
|
||||
* @throws NumberFormatException when the number could not be parsed to a {@link BigDecimal}
|
||||
*/
|
||||
public static DecimalType valueOf(String value) {
|
||||
return new DecimalType(value);
|
||||
}
|
||||
|
|
|
@ -34,24 +34,59 @@ public class PercentType extends DecimalType {
|
|||
public static final PercentType ZERO = new PercentType(0);
|
||||
public static final PercentType HUNDRED = new PercentType(100);
|
||||
|
||||
/**
|
||||
* Creates a new {@link PercentType} with 0 as value.
|
||||
*/
|
||||
public PercentType() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PercentType} with the given value.
|
||||
*
|
||||
* @param value the value representing a percentage
|
||||
*
|
||||
* @throws IllegalArgumentException when the value is not between 0 and 100
|
||||
*/
|
||||
public PercentType(int value) {
|
||||
super(value);
|
||||
validateValue(this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PercentType} with the given value.
|
||||
* The English locale is used to determine (decimal/grouping) separator characters.
|
||||
*
|
||||
* @param value the non null value representing a percentage
|
||||
*
|
||||
* @throws NumberFormatException when the number could not be parsed to a {@link BigDecimal}
|
||||
* @throws IllegalArgumentException when the value is not between 0 and 100
|
||||
*/
|
||||
public PercentType(String value) {
|
||||
this(value, Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PercentType} with the given value.
|
||||
*
|
||||
* @param value the non null value representing a percentage
|
||||
* @param locale the locale used to determine (decimal/grouping) separator characters
|
||||
*
|
||||
* @throws NumberFormatException when the number could not be parsed to a {@link BigDecimal}
|
||||
* @throws IllegalArgumentException when the value is not between 0 and 100
|
||||
*/
|
||||
public PercentType(String value, Locale locale) {
|
||||
super(value, locale);
|
||||
validateValue(this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PercentType} with the given value.
|
||||
*
|
||||
* @param value the value representing a percentage.
|
||||
*
|
||||
* @throws IllegalArgumentException when the value is not between 0 and 100
|
||||
*/
|
||||
public PercentType(BigDecimal value) {
|
||||
super(value);
|
||||
validateValue(this.value);
|
||||
|
@ -63,6 +98,15 @@ public class PercentType extends DecimalType {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static access to {@link PercentType#PercentType(String)}.
|
||||
*
|
||||
* @param value the non null value representing a percentage
|
||||
* @return new {@link PercentType}
|
||||
*
|
||||
* @throws NumberFormatException when the number could not be parsed to a {@link BigDecimal}
|
||||
* @throws IllegalArgumentException when the value is not between 0 and 100
|
||||
*/
|
||||
public static PercentType valueOf(String value) {
|
||||
return new PercentType(value);
|
||||
}
|
||||
|
|
|
@ -99,6 +99,9 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
|||
* The English locale is used to determine (decimal/grouping) separator characters.
|
||||
*
|
||||
* @param value the non null value representing a quantity with an optional unit.
|
||||
*
|
||||
* @throws NumberFormatException when a quantity without a unit could not be parsed
|
||||
* @throws IllegalArgumentException when a quantity with a unit could not be parsed
|
||||
*/
|
||||
public QuantityType(String value) {
|
||||
this(value, Locale.ENGLISH);
|
||||
|
@ -110,6 +113,9 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
|||
*
|
||||
* @param value the non null value representing a quantity with an optional unit.
|
||||
* @param locale the locale used to determine (decimal/grouping) separator characters.
|
||||
*
|
||||
* @throws NumberFormatException when a quantity without a unit could not be parsed
|
||||
* @throws IllegalArgumentException when a quantity with a unit could not be parsed
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public QuantityType(String value, Locale locale) {
|
||||
|
@ -123,7 +129,7 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
|||
ParsePosition position = new ParsePosition(0);
|
||||
BigDecimal parsedValue = (BigDecimal) df.parseObject(value, position);
|
||||
if (parsedValue == null || position.getErrorIndex() != -1 || position.getIndex() < value.length()) {
|
||||
throw new IllegalArgumentException("Invalid BigDecimal value: " + value);
|
||||
throw new NumberFormatException("Invalid BigDecimal value: " + value);
|
||||
}
|
||||
quantity = (Quantity<T>) Quantities.getQuantity(parsedValue, AbstractUnit.ONE, Scale.RELATIVE);
|
||||
} else {
|
||||
|
@ -190,6 +196,15 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
|||
return toFullString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static access to {@link QuantityType#QuantityType(String)}.
|
||||
*
|
||||
* @param value the non null value representing a quantity with an optional unit
|
||||
* @return a new {@link QuantityType}
|
||||
*
|
||||
* @throws NumberFormatException when a quantity without a unit could not be parsed
|
||||
* @throws IllegalArgumentException when a quantity with a unit could not be parsed
|
||||
*/
|
||||
public static QuantityType<? extends Quantity<?>> valueOf(String value) {
|
||||
return new QuantityType<>(value);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
package org.openhab.core.library.types;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.text.DecimalFormatSymbols;
|
||||
|
@ -45,6 +46,38 @@ public class DecimalTypeTest {
|
|||
Locale.forLanguageTag("en-US"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testKnownInvalidConstructors(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123 Hello World"));
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType(""));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("."));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("1 2"));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123..56"));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123abc56"));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123.123,56"));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123٬123٫56"));
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType(".", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("1 2", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123..56", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123abc56", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123.123,56", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123٬123٫56", Locale.ENGLISH));
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType(",", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("1 2", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123,,56", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123abc56", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123,123.56", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new DecimalType("123٬123٫56", Locale.GERMAN));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "-2,000.5", "-2.5", "-2", "-0", "0", "2", "2.5", "2,000.5", "-10E3", "-10E-3", "-0E-22",
|
||||
"-0E0", "-0E-0", "0E0", "0E-22", "10E-3", "10E3" })
|
||||
|
|
|
@ -44,6 +44,32 @@ public class PercentTypeTest {
|
|||
Locale.forLanguageTag("en-US"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testKnownInvalidConstructors(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("123 Hello World"));
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType(""));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("."));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1 2"));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1..56"));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1abc"));
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType(".", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1 2", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1..56", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1abc", Locale.ENGLISH));
|
||||
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType(",", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1 2", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1,,56", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new PercentType("1abc", Locale.GERMAN));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "0", "0.000", "0.001", "2", "2.5", "0E0", "0E-22", "10E-3", "1E2" })
|
||||
public void testValidConstructors(String value) {
|
||||
|
|
|
@ -84,7 +84,8 @@ public class QuantityTypeTest {
|
|||
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123 Hello World"));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("°C"));
|
||||
assertThrows(NumberFormatException.class, () -> new QuantityType<>("abc"));
|
||||
assertThrows(NumberFormatException.class, () -> new QuantityType<>("°C"));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>(". °C"));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("1 2 °C"));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123..56 °C"));
|
||||
|
@ -92,7 +93,8 @@ public class QuantityTypeTest {
|
|||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123.123,56 °C"));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123٬123٫56 °C"));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("°C", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new QuantityType<>("abc", Locale.ENGLISH));
|
||||
assertThrows(NumberFormatException.class, () -> new QuantityType<>("°C", Locale.ENGLISH));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>(". °C", Locale.ENGLISH));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("1 2 °C", Locale.ENGLISH));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123..56 °C", Locale.ENGLISH));
|
||||
|
@ -100,7 +102,8 @@ public class QuantityTypeTest {
|
|||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123.123,56 °C", Locale.ENGLISH));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123٬123٫56 °C", Locale.ENGLISH));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("°C", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new QuantityType<>("abc", Locale.GERMAN));
|
||||
assertThrows(NumberFormatException.class, () -> new QuantityType<>("°C", Locale.GERMAN));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>(", °C", Locale.GERMAN));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("1 2 °C", Locale.GERMAN));
|
||||
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("123,,56 °C", Locale.GERMAN));
|
||||
|
|
Loading…
Reference in New Issue