001/** 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package org.javamoney.moneta.spi; 017 018import java.math.BigDecimal; 019import java.math.BigInteger; 020import java.util.Arrays; 021import java.util.List; 022import java.util.Objects; 023import java.util.Optional; 024import java.util.concurrent.atomic.AtomicInteger; 025import java.util.concurrent.atomic.AtomicLong; 026 027import javax.money.NumberValue; 028 029/** 030 * This enumeration provides general utility functions supporting conversion of number types to BigDecimal. 031 */ 032public enum ConvertBigDecimal { 033 /** Conversion from integral numeric types, short, int, long. */ 034 INTEGER { 035 @Override 036 BigDecimal getDecimal(Number num) { 037 return BigDecimal.valueOf(num.longValue()); 038 } 039 }, 040 /** Conversion for floating point numbers. */ 041 FLUCTUAGE { 042 @Override 043 BigDecimal getDecimal(Number num) { 044 double d = num.doubleValue(); 045 if (Double.isNaN(d)|| Double.isInfinite(d)) { 046 throw new ArithmeticException("NaN, POSITIVE_INFINITY and NEGATIVE_INFINITY cannot be used as " + 047 "parameters for monetary operations."); 048 } 049 return new BigDecimal(num.toString()); 050 } 051 }, 052 /** Conversion from BigInteger. */ 053 BIGINTEGER { 054 @Override 055 BigDecimal getDecimal(Number num) { 056 return new BigDecimal((BigInteger) num); 057 } 058 }, 059 /** Conversion from NumberValue. */ 060 NUMBERVALUE { 061 @Override 062 BigDecimal getDecimal(Number num) { 063 BigDecimal result = ((NumberValue)num).numberValue(BigDecimal.class); 064 return stripScalingZeroes(result); 065 } 066 }, 067 /** Conversion from BigDecimal. */ 068 BIGDECIMAL { 069 @Override 070 BigDecimal getDecimal(Number num) { 071 BigDecimal result = ((BigDecimal)num); 072 return stripScalingZeroes(result); 073 } 074 }, 075 /** Conversion from BigDecimal, extended. */ 076 BIGDECIMAL_EXTENDS { 077 @Override 078 BigDecimal getDecimal(Number num) { 079 BigDecimal result = ((BigDecimal)num).stripTrailingZeros(); 080 return stripScalingZeroes(result); 081 } 082 }, 083 /** Default conversion based on String, if everything else failed. */ 084 DEFAULT { 085 @Override 086 BigDecimal getDecimal(Number num) { 087 BigDecimal result = null; 088 try { 089 result = new BigDecimal(num.toString()); 090 } catch (NumberFormatException ignored) { 091 } 092 result = Optional.ofNullable(result).orElse( 093 BigDecimal.valueOf(num.doubleValue())); 094 return stripScalingZeroes(result); 095 } 096 }; 097 098 099 abstract BigDecimal getDecimal(Number num); 100 101 static BigDecimal of(Number num) { 102 Objects.requireNonNull(num, "Number is required."); 103 return factory(num).getDecimal(num); 104 } 105 106 private static ConvertBigDecimal factory(Number num) { 107 if (INTEGERS.contains(num.getClass())) { 108 return INTEGER; 109 } 110 if (FLOATINGS.contains(num.getClass())) { 111 return FLUCTUAGE; 112 } 113 if (num instanceof NumberValue) { 114 return NUMBERVALUE; 115 } 116 if (BigDecimal.class.equals(num.getClass())) { 117 return BIGDECIMAL; 118 } 119 if (num instanceof BigInteger) { 120 return BIGINTEGER; 121 } 122 if (num instanceof BigDecimal) { 123 return BIGDECIMAL_EXTENDS; 124 } 125 return DEFAULT; 126 } 127 128 private static final List<Class<? extends Number>> INTEGERS = Arrays.asList( 129 Long.class, Integer.class, Short.class, Byte.class, 130 AtomicLong.class, AtomicInteger.class); 131 132 private static final List<Class<? extends Number>> FLOATINGS = Arrays.asList( 133 Float.class, Double.class); 134 135 private static BigDecimal stripScalingZeroes(BigDecimal result) { 136 if (result.signum() == 0) { 137 return BigDecimal.ZERO; 138 } 139 if (result.scale() > 0) { 140 return result.stripTrailingZeros(); 141 } 142 return result; 143 } 144}