From 1ca43402712ea664eb8c9b8153d3212d58be34ec Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Mar 2016 18:51:41 +0100 Subject: [PATCH] NumberUtils consistently raises overflow exception for BigInteger/BigDecimal input Issue: SPR-14041 --- .../org/springframework/util/NumberUtils.java | 48 ++++++++++++------- .../util/NumberUtilsTests.java | 19 ++++---- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/NumberUtils.java b/spring-core/src/main/java/org/springframework/util/NumberUtils.java index 9df023c36b7..46b3f868b75 100644 --- a/spring-core/src/main/java/org/springframework/util/NumberUtils.java +++ b/spring-core/src/main/java/org/springframework/util/NumberUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,39 +87,29 @@ public abstract class NumberUtils { return (T) number; } else if (Byte.class == targetClass) { - long value = number.longValue(); + long value = checkedLongValue(number, targetClass); if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { raiseOverflowException(number, targetClass); } return (T) Byte.valueOf(number.byteValue()); } else if (Short.class == targetClass) { - long value = number.longValue(); + long value = checkedLongValue(number, targetClass); if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { raiseOverflowException(number, targetClass); } return (T) Short.valueOf(number.shortValue()); } else if (Integer.class == targetClass) { - long value = number.longValue(); + long value = checkedLongValue(number, targetClass); if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { raiseOverflowException(number, targetClass); } return (T) Integer.valueOf(number.intValue()); } else if (Long.class == targetClass) { - BigInteger bigInt = null; - if (number instanceof BigInteger) { - bigInt = (BigInteger) number; - } - else if (number instanceof BigDecimal) { - bigInt = ((BigDecimal) number).toBigInteger(); - } - // Effectively analogous to JDK 8's BigInteger.longValueExact() - if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) { - raiseOverflowException(number, targetClass); - } - return (T) Long.valueOf(number.longValue()); + long value = checkedLongValue(number, targetClass); + return (T) Long.valueOf(value); } else if (BigInteger.class == targetClass) { if (number instanceof BigDecimal) { @@ -148,11 +138,35 @@ public abstract class NumberUtils { } } + /** + * Check for a {@code BigInteger}/{@code BigDecimal} long overflow + * before returning the given number as a long value. + * @param number the number to convert + * @param targetClass the target class to convert to + * @return the long value, if convertible without overflow + * @throws IllegalArgumentException if there is an overflow + * @see #raiseOverflowException + */ + private static long checkedLongValue(Number number, Class targetClass) { + BigInteger bigInt = null; + if (number instanceof BigInteger) { + bigInt = (BigInteger) number; + } + else if (number instanceof BigDecimal) { + bigInt = ((BigDecimal) number).toBigInteger(); + } + // Effectively analogous to JDK 8's BigInteger.longValueExact() + if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) { + raiseOverflowException(number, targetClass); + } + return number.longValue(); + } + /** * Raise an overflow exception for the given number and target class. * @param number the number we tried to convert * @param targetClass the target class we tried to convert to - * @throws IllegalArgumentException + * @throws IllegalArgumentException if there is an overflow */ private static void raiseOverflowException(Number number, Class targetClass) { throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" + diff --git a/spring-core/src/test/java/org/springframework/util/NumberUtilsTests.java b/spring-core/src/test/java/org/springframework/util/NumberUtilsTests.java index d6fbc9b0bd9..2584cf42082 100644 --- a/spring-core/src/test/java/org/springframework/util/NumberUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/NumberUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -328,8 +328,6 @@ public class NumberUtilsTests { assertEquals(Integer.valueOf(Integer.MIN_VALUE), NumberUtils.convertNumberToTargetClass(BigInteger.valueOf(Integer.MAX_VALUE + 1), Integer.class)); assertEquals(Integer.valueOf(Integer.MIN_VALUE), NumberUtils.convertNumberToTargetClass(BigInteger.valueOf(Integer.MIN_VALUE), Integer.class)); assertEquals(Integer.valueOf(Integer.MAX_VALUE), NumberUtils.convertNumberToTargetClass(BigInteger.valueOf(Integer.MIN_VALUE - 1), Integer.class)); - assertToNumberOverflow(BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.ONE), Integer.class); - assertToNumberOverflow(BigInteger.valueOf(Integer.MIN_VALUE).subtract(BigInteger.ONE), Integer.class); assertEquals(Integer.valueOf(Integer.valueOf(-1)), NumberUtils.convertNumberToTargetClass(Long.valueOf(-1), Integer.class)); assertEquals(Integer.valueOf(Integer.valueOf(0)), NumberUtils.convertNumberToTargetClass(Long.valueOf(0), Integer.class)); @@ -338,8 +336,6 @@ public class NumberUtilsTests { assertEquals(Integer.valueOf(Integer.MIN_VALUE), NumberUtils.convertNumberToTargetClass(Long.valueOf(Integer.MAX_VALUE + 1), Integer.class)); assertEquals(Integer.valueOf(Integer.MIN_VALUE), NumberUtils.convertNumberToTargetClass(Long.valueOf(Integer.MIN_VALUE), Integer.class)); assertEquals(Integer.valueOf(Integer.MAX_VALUE), NumberUtils.convertNumberToTargetClass(Long.valueOf(Integer.MIN_VALUE - 1), Integer.class)); - assertToNumberOverflow(Long.valueOf(Long.MAX_VALUE + 1), Integer.class); - assertToNumberOverflow(Long.valueOf(Long.MIN_VALUE - 1), Integer.class); assertEquals(Integer.valueOf(Integer.valueOf(-1)), NumberUtils.convertNumberToTargetClass(Integer.valueOf(-1), Integer.class)); assertEquals(Integer.valueOf(Integer.valueOf(0)), NumberUtils.convertNumberToTargetClass(Integer.valueOf(0), Integer.class)); @@ -364,6 +360,12 @@ public class NumberUtilsTests { assertEquals(Integer.valueOf(Byte.MIN_VALUE), NumberUtils.convertNumberToTargetClass(Byte.valueOf((byte) (Byte.MAX_VALUE + 1)), Integer.class)); assertEquals(Integer.valueOf(Byte.MIN_VALUE), NumberUtils.convertNumberToTargetClass(Byte.valueOf(Byte.MIN_VALUE), Integer.class)); assertEquals(Integer.valueOf(Byte.MAX_VALUE), NumberUtils.convertNumberToTargetClass(Byte.valueOf((byte) (Byte.MIN_VALUE - 1)), Integer.class)); + + assertToNumberOverflow(Long.valueOf(Long.MAX_VALUE + 1), Integer.class); + assertToNumberOverflow(Long.valueOf(Long.MIN_VALUE - 1), Integer.class); + assertToNumberOverflow(BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.ONE), Integer.class); + assertToNumberOverflow(BigInteger.valueOf(Integer.MIN_VALUE).subtract(BigInteger.ONE), Integer.class); + assertToNumberOverflow(new BigDecimal("18446744073709551611"), Integer.class); } @Test @@ -376,9 +378,6 @@ public class NumberUtilsTests { assertEquals(Long.valueOf(Long.MIN_VALUE), NumberUtils.convertNumberToTargetClass(BigInteger.valueOf(Long.MIN_VALUE), Long.class)); assertEquals(Long.valueOf(Long.MAX_VALUE), NumberUtils.convertNumberToTargetClass(BigInteger.valueOf(Long.MIN_VALUE - 1), Long.class)); - assertToNumberOverflow(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), Long.class); - assertToNumberOverflow(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE), Long.class); - assertEquals(Long.valueOf(Long.valueOf(-1)), NumberUtils.convertNumberToTargetClass(Long.valueOf(-1), Long.class)); assertEquals(Long.valueOf(Long.valueOf(0)), NumberUtils.convertNumberToTargetClass(Long.valueOf(0), Long.class)); assertEquals(Long.valueOf(Long.valueOf(1)), NumberUtils.convertNumberToTargetClass(Long.valueOf(1), Long.class)); @@ -410,6 +409,10 @@ public class NumberUtilsTests { assertEquals(Long.valueOf(Byte.MIN_VALUE), NumberUtils.convertNumberToTargetClass(Byte.valueOf((byte) (Byte.MAX_VALUE + 1)), Long.class)); assertEquals(Long.valueOf(Byte.MIN_VALUE), NumberUtils.convertNumberToTargetClass(Byte.valueOf(Byte.MIN_VALUE), Long.class)); assertEquals(Long.valueOf(Byte.MAX_VALUE), NumberUtils.convertNumberToTargetClass(Byte.valueOf((byte) (Byte.MIN_VALUE - 1)), Long.class)); + + assertToNumberOverflow(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), Long.class); + assertToNumberOverflow(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE), Long.class); + assertToNumberOverflow(new BigDecimal("18446744073709551611"), Long.class); }