org.jopendocument.util.convertor.NumberConvertorTest Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU
* General Public License Version 3 only ("GPL").
* You may not use this file except in compliance with the License.
* You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
* See the License for the specific language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*
*/
package org.jopendocument.util.convertor;
import org.jopendocument.util.NumberUtils;
import org.jopendocument.util.convertor.NumberConvertor.OverflowException;
import org.jopendocument.util.convertor.NumberConvertor.RoundingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import junit.framework.TestCase;
public class NumberConvertorTest extends TestCase {
private static final List> numberClasses;
private static final Map, Number> fives = new HashMap, Number>();
private static final Map, Number> minValues = new HashMap, Number>();
private static final Map, Number> maxValues = new HashMap, Number>();
static {
numberClasses = new ArrayList>();
numberClasses.add(Byte.class);
numberClasses.add(Short.class);
numberClasses.add(AtomicInteger.class);
numberClasses.add(Integer.class);
numberClasses.add(AtomicLong.class);
numberClasses.add(Long.class);
numberClasses.add(BigInteger.class);
numberClasses.add(Float.class);
numberClasses.add(Double.class);
numberClasses.add(BigDecimal.class);
fives.put(Byte.class, Byte.valueOf((byte) 5));
fives.put(Short.class, Short.valueOf((short) 5));
fives.put(Integer.class, Integer.valueOf(5));
fives.put(AtomicInteger.class, new AtomicInteger(5));
fives.put(Long.class, Long.valueOf(5));
fives.put(AtomicLong.class, new AtomicLong(5));
fives.put(BigInteger.class, BigInteger.valueOf(5));
fives.put(BigDecimal.class, BigDecimal.valueOf(5));
fives.put(Float.class, 5f);
fives.put(Double.class, 5d);
}
private static void fillValues() throws IllegalAccessException, NoSuchFieldException {
for (final Class> c : new Class>[] { Byte.class, Short.class, Integer.class, Long.class }) {
minValues.put(c.asSubclass(Number.class), (Number) c.getField("MIN_VALUE").get(null));
maxValues.put(c.asSubclass(Number.class), (Number) c.getField("MAX_VALUE").get(null));
}
minValues.put(AtomicInteger.class, new AtomicInteger((Integer) minValues.get(Integer.class)));
minValues.put(AtomicLong.class, new AtomicLong((Long) minValues.get(Long.class)));
maxValues.put(AtomicInteger.class, new AtomicInteger((Integer) maxValues.get(Integer.class)));
maxValues.put(AtomicLong.class, new AtomicLong((Long) maxValues.get(Long.class)));
maxValues.put(Float.class, Float.MAX_VALUE);
minValues.put(Float.class, -Float.MAX_VALUE);
maxValues.put(Double.class, Double.MAX_VALUE);
minValues.put(Double.class, -Double.MAX_VALUE);
// BigInteger and BigDecimal have no limits
}
public static Map, Number> getMinValues() throws IllegalAccessException, NoSuchFieldException {
if (minValues.isEmpty()) {
fillValues();
}
return minValues;
}
public static Map, Number> getMaxValues() throws IllegalAccessException, NoSuchFieldException {
if (maxValues.isEmpty()) {
fillValues();
}
return maxValues;
}
public static boolean isFloat(Number n) {
return n instanceof Float || n instanceof Double;
}
public void testConvert() throws Exception {
for (final Class extends Number> nClass : numberClasses) {
final Number ourFive = fives.get(nClass);
assert ourFive.getClass() == nClass;
for (final Number five : fives.values()) {
// inexact
{
final Number converted = NumberConvertor.convert(five, nClass);
assertTrue(NumberUtils.areEqual(ourFive, converted));
// and back
assertTrue(NumberUtils.areEqual(five, NumberConvertor.convert(converted, five.getClass())));
}
// exact
{
assertConvertExactAndBack(five, nClass, ourFive);
}
}
assertConvertExactAtLimits(getMinValues().get(nClass), nClass);
assertConvertExactAtLimits(getMaxValues().get(nClass), nClass);
}
}
private void assertConvertExactAndBack(final Number n, final Class extends Number> destClass, final Number expected) {
final Number converted = assertConvertExact(n, destClass, expected, false);
// if we could convert it one way, try the opposite
if (converted != null)
assertConvertExact(converted, n.getClass(), n, false);
}
private Number assertConvertExact(final Number n, final Class extends Number> destClass, final Number expected, final boolean offLimits) {
// convert() never tries to round floats to integers
// BigDecimal is arbitrary-precision so it never has to round
if (isFloat(n) && !isFloat(expected) && !(expected instanceof BigDecimal)) {
try {
NumberConvertor.convertExact(n, destClass);
// overflow is a bigger problem than rounding
if (offLimits)
fail("Should have thrown a OverflowException from " + n.getClass() + " to " + destClass);
else
fail("Should have thrown a RoundingException from " + n.getClass() + " to " + destClass);
} catch (RuntimeException e) {
// OK
if (!(offLimits && e instanceof OverflowException) && !(!offLimits && e instanceof RoundingException))
throw e;
}
return null;
} else if (offLimits) {
try {
NumberConvertor.convertExact(n, destClass);
fail("Should have thrown an OverflowException");
} catch (OverflowException e) {
// OK
}
return null;
} else {
final Number converted = NumberConvertor.convertExact(n, destClass);
assertEquals(destClass, converted.getClass());
assertTrue(NumberUtils.areEqual(expected, converted));
return converted;
}
}
private void assertConvertExactAtLimits(final Number limitValue, final Class extends Number> nClass) {
if (limitValue != null) {
assertEquals(nClass, limitValue.getClass());
// BigDecimal has no limits
final BigDecimal bd = NumberConvertor.toBigDecimal(limitValue);
// the limit is OK
assertConvertExactAndBack(bd, nClass, limitValue);
// beyond is not
final BigDecimal beyondLimit;
if (isFloat(limitValue))
beyondLimit = bd.multiply(BigDecimal.valueOf(2));
else
beyondLimit = bd.signum() > 0 ? bd.add(BigDecimal.ONE) : bd.subtract(BigDecimal.ONE);
assertBeyondLimit(bd, beyondLimit);
assertConvertExact(beyondLimit, nClass, limitValue, true);
// test that when trying to convert a float it first checks for overflow, except for
// Double since beyondLimit.doubleValue() would be brought back inside the limits
if (nClass != Double.class) {
assertBeyondLimit(bd, BigDecimal.valueOf(beyondLimit.doubleValue()));
assertConvertExact(beyondLimit.doubleValue(), nClass, limitValue, true);
}
}
}
private void assertBeyondLimit(final BigDecimal limit, final BigDecimal beyondLimit) {
assertEquals(limit.signum(), beyondLimit.signum());
assertTrue(beyondLimit.abs().compareTo(limit.abs()) > 0);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy