org.jopendocument.util.convertor.NumberConvertor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jOpenDocument Show documentation
Show all versions of jOpenDocument Show documentation
jOpenDocument is a free library for developers looking to use
Open Document files without OpenOffice.org.
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2013 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 java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public abstract class NumberConvertor implements ValueConvertor {
public static final NumberConvertor INT_TO_LONG = new NumberConvertor() {
@Override
public Long convert(Integer o) {
return o.longValue();
}
@Override
public Integer unconvert(Long o) {
return o.intValue();
}
};
public static final NumberConvertor SHORT_TO_INT = new NumberConvertor() {
@Override
public Integer convert(Short o) {
return o.intValue();
}
@Override
public Short unconvert(Integer o) {
return o.shortValue();
}
};
public static final NumberConvertor create(final Class c1, final Class c2, final boolean exact) {
return new NumberConvertor() {
@Override
public N2 convert(N1 o) {
return exact ? convertExact(o, c2) : convert(o, c2);
}
@Override
public N1 unconvert(N2 o) {
return exact ? convertExact(o, c1) : convert(o, c1);
}
};
}
/**
* Convert from one class of {@link Number} to another. Necessary since new Integer(123) isn't
* equal to new Long(123).
*
* @param type of desired Number.
* @param n the instance to convert, e.g. new Integer(123).
* @param clazz desired class of Number, e.g. Long.class.
* @return n
as an instance of clazz
, e.g. new Long(123).
*/
public static N convert(Number n, Class clazz) {
final Number res;
if (n == null || n.getClass() == clazz || clazz == Number.class) {
res = n;
} else if (clazz == Short.class) {
res = n.shortValue();
} else if (clazz == Integer.class) {
res = n.intValue();
} else if (clazz == Long.class) {
res = n.longValue();
} else if (clazz == Byte.class) {
res = n.byteValue();
} else if (clazz == BigInteger.class) {
if (n instanceof BigDecimal)
res = ((BigDecimal) n).toBigInteger();
else if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong)
res = BigInteger.valueOf(n.longValue());
else if (n instanceof Float || n instanceof Double)
res = BigDecimal.valueOf(n.doubleValue()).toBigInteger();
else
res = new BigInteger(n.toString());
} else if (clazz == AtomicInteger.class) {
res = new AtomicInteger(n.intValue());
} else if (clazz == AtomicLong.class) {
res = new AtomicLong(n.longValue());
} else if (clazz == BigDecimal.class) {
res = toBigDecimal(n);
} else if (clazz == Double.class) {
res = n.doubleValue();
} else if (clazz == Float.class) {
res = n.floatValue();
} else {
throw new IllegalArgumentException("unknown class: " + clazz);
}
return clazz.cast(res);
}
public static BigDecimal toBigDecimal(final Number n) {
final BigDecimal res;
if (n == null || n instanceof BigDecimal) {
res = (BigDecimal) n;
} else if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong) {
res = BigDecimal.valueOf(n.longValue());
} else if (n instanceof Float || n instanceof Double) {
res = BigDecimal.valueOf(n.doubleValue());
} else if (n instanceof BigInteger) {
res = new BigDecimal((BigInteger) n);
} else {
res = new BigDecimal(n.toString());
}
return res;
}
public static class OverflowException extends ArithmeticException {
public OverflowException(Number n, Class clazz) {
super("Cannot convert to " + clazz + " : " + n);
}
@Override
public OverflowException initCause(Throwable cause) {
super.initCause(cause);
return this;
}
}
public static class RoundingException extends ArithmeticException {
public RoundingException(Number n, Class clazz) {
super("Rounding necessary for " + clazz + " : " + n + " (" + n.getClass() + ")");
}
}
public static final BigDecimal MAX_FLOAT = BigDecimal.valueOf(Float.MAX_VALUE);
public static final BigDecimal MIN_FLOAT = MAX_FLOAT.negate();
public static final BigDecimal MAX_DOUBLE = BigDecimal.valueOf(Double.MAX_VALUE);
public static final BigDecimal MIN_DOUBLE = MAX_DOUBLE.negate();
public static N convertExact(Number n, Class clazz) throws OverflowException, RoundingException {
final Number res;
if (n == null || n.getClass() == clazz || clazz == Number.class) {
res = n;
} else if (n instanceof BigDecimal) {
// cannot use *ValueExact() since we want to differentiate between overflow and rounding
final BigDecimal bd = (BigDecimal) n;
if (clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == AtomicInteger.class || clazz == Long.class || clazz == AtomicLong.class || clazz == BigInteger.class) {
final BigInteger bi;
try {
bi = bd.toBigIntegerExact();
} catch (ArithmeticException e) {
throw new RoundingException(n, clazz);
}
if (clazz == BigInteger.class) {
res = bi;
} else {
try {
res = convertExact(bi, clazz);
} catch (RoundingException e) {
// cannot be a rounding error since we pass a BigInteger
throw new IllegalStateException(e);
} catch (OverflowException e) {
throw new OverflowException(n, clazz).initCause(e);
}
}
} else if (clazz == Float.class) {
if (bd.compareTo(MAX_FLOAT) > 0 || bd.compareTo(MIN_FLOAT) < 0)
throw new OverflowException(n, clazz);
res = n.floatValue();
} else if (clazz == Double.class) {
if (bd.compareTo(MAX_DOUBLE) > 0 || bd.compareTo(MIN_DOUBLE) < 0)
throw new OverflowException(n, clazz);
res = n.doubleValue();
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
} else if (clazz == Byte.class) {
final byte value = n.byteValue();
if (n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong) {
if (value != n.longValue())
throw new OverflowException(n, clazz);
} else if (n instanceof BigInteger) {
if (((BigInteger) n).bitLength() >= Byte.SIZE)
throw new OverflowException(n, clazz);
} else if (n instanceof Float || n instanceof Double) {
if (n.doubleValue() > Byte.MAX_VALUE || n.doubleValue() < Byte.MIN_VALUE)
throw new OverflowException(n, clazz);
else
throw new RoundingException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = value;
} else if (clazz == Short.class) {
final short value = n.shortValue();
if (n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong) {
if (value != n.longValue())
throw new OverflowException(n, clazz);
} else if (n instanceof Byte) {
} else if (n instanceof BigInteger) {
if (((BigInteger) n).bitLength() >= Short.SIZE)
throw new OverflowException(n, clazz);
} else if (n instanceof Float || n instanceof Double) {
if (n.doubleValue() > Short.MAX_VALUE || n.doubleValue() < Short.MIN_VALUE)
throw new OverflowException(n, clazz);
else
throw new RoundingException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = value;
} else if (clazz == Integer.class || clazz == AtomicInteger.class) {
final int value = n.intValue();
if (n instanceof Long || n instanceof AtomicLong) {
if (value != n.longValue())
throw new OverflowException(n, clazz);
} else if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger) {
} else if (n instanceof BigInteger) {
if (((BigInteger) n).bitLength() >= Integer.SIZE)
throw new OverflowException(n, clazz);
} else if (n instanceof Float || n instanceof Double) {
if (n.doubleValue() > Integer.MAX_VALUE || n.doubleValue() < Integer.MIN_VALUE)
throw new OverflowException(n, clazz);
else
throw new RoundingException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = clazz == Integer.class ? Integer.valueOf(value) : new AtomicInteger(value);
} else if (clazz == Long.class || clazz == AtomicLong.class) {
final long value = n.longValue();
if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong) {
} else if (n instanceof BigInteger) {
if (((BigInteger) n).bitLength() >= Long.SIZE)
throw new OverflowException(n, clazz);
} else if (n instanceof Float || n instanceof Double) {
// at the limits of Long, Double have less than one unit of precision, so since both
// case of this if throw exceptions, be lenient to allow testing the limits
if (n.doubleValue() >= Long.MAX_VALUE || n.doubleValue() <= Long.MIN_VALUE)
throw new OverflowException(n, clazz);
else
throw new RoundingException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = clazz == Long.class ? Long.valueOf(value) : new AtomicLong(value);
} else if (clazz == BigInteger.class) {
if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong) {
} else if (n instanceof Float || n instanceof Double) {
throw new RoundingException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = BigInteger.valueOf(n.longValue());
} else if (clazz == BigDecimal.class) {
res = toBigDecimal(n);
} else if (clazz == Double.class) {
if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong || n instanceof Float) {
} else if (n instanceof BigInteger) {
final BigInteger bi = (BigInteger) n;
// can use toBigIntegerExact() since the precision is low at the limits
if (bi.compareTo(MAX_DOUBLE.toBigIntegerExact()) > 0 || bi.compareTo(MIN_DOUBLE.toBigIntegerExact()) < 0)
throw new OverflowException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = n.doubleValue();
} else if (clazz == Float.class) {
if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof AtomicInteger || n instanceof Long || n instanceof AtomicLong) {
} else if (n instanceof Double) {
if (Math.abs(n.doubleValue()) > Float.MAX_VALUE)
throw new OverflowException(n, clazz);
} else if (n instanceof BigInteger) {
final BigInteger bi = (BigInteger) n;
// can use toBigIntegerExact() since the precision is low at the limits
if (bi.compareTo(MAX_FLOAT.toBigIntegerExact()) > 0 || bi.compareTo(MIN_FLOAT.toBigIntegerExact()) < 0)
throw new OverflowException(n, clazz);
} else {
throw new IllegalStateException("Unknown class " + n.getClass());
}
res = n.floatValue();
} else {
throw new IllegalArgumentException("Unknown class: " + clazz);
}
return clazz.cast(res);
}
}