class NumberUtils {

  @SuppressWarnings("unchecked")
  public static <T> T convert(final Number number, final Class<T> targetClass)
      throws IllegalArgumentException {

    assertNotNull(number, "Number must not be null");
    assertNotNull(targetClass, "Target class must not be null");

    if (targetClass.isInstance(number)) {
      return (T) number;
    } else if (targetClass.equals(Byte.class) || targetClass.equals(byte.class)) {
      final long value = number.longValue();
      if (value < Byte.MIN_VALUE || Byte.MAX_VALUE < value) {
        throwOverflowException(number, targetClass);
      }
      return (T) Byte.valueOf((byte) value);
    } else if (targetClass.equals(Short.class) || targetClass.equals(short.class)) {
      final long value = number.longValue();
      if (value < Short.MIN_VALUE || Short.MAX_VALUE < value) {
        throwOverflowException(number, targetClass);
      }
      return (T) Short.valueOf((short) value);
    } else if (targetClass.equals(Integer.class) || targetClass.equals(int.class)) {
      final long value = number.longValue();
      if (value < Integer.MIN_VALUE || Integer.MAX_VALUE < value) {
        throwOverflowException(number, targetClass);
      }
      return (T) Integer.valueOf((int) value);
    } else if (targetClass.equals(Long.class) || targetClass.equals(long.class)) {
      return (T) Long.valueOf(number.longValue());
    } else if (targetClass.equals(BigInteger.class)) {
      if (number instanceof BigDecimal) {
        // do not lose precision - use BigDecimal's own conversion
        return (T) ((BigDecimal) number).toBigInteger();
      } else {
        // original value is not a Big* number - use standard long conversion
        return (T) BigInteger.valueOf(number.longValue());
      }
    } else if (targetClass.equals(Float.class) || targetClass.equals(float.class)) {
      return (T) Float.valueOf(number.floatValue());
    } else if (targetClass.equals(Double.class) || targetClass.equals(double.class)) {
      return (T) Double.valueOf(number.doubleValue());
    } else if (targetClass.equals(BigDecimal.class)) {
      // always use BigDecimal(String) here to avoid unpredictability of BigDecimal(double)
      // (see BigDecimal javadoc for details)
      return (T) new BigDecimal(number.toString());
    }
    throw new IllegalArgumentException(
        "Could not convert number [" + number + "] of type [" + number.getClass().getName()
            + "] to unknown target class [" + targetClass.getName() + "]");
  }

}