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.concurrent.atomic.AtomicInteger; 024import java.util.concurrent.atomic.AtomicLong; 025 026import javax.money.NumberValue; 027 028/** 029 * This enumeration provides general utility functions supporting conversion of number types to BigDecimal. 030 */ 031public enum ConvertBigDecimal { 032 /** Conversion from integral numeric types, short, int, long. */ 033 INTEGER { 034 @Override 035 BigDecimal getDecimal(Number num) { 036 return BigDecimal.valueOf(num.longValue()); 037 } 038 }, 039 /** Conversion for floating point numbers. */ 040 FLUCTUAGE { 041 @Override 042 BigDecimal getDecimal(Number num) { 043 double d = num.doubleValue(); 044 if (Double.isNaN(d)|| Double.isInfinite(d)) { 045 throw new ArithmeticException("NaN, POSITIVE_INFINITY and NEGATIVE_INFINITY cannot be used as " + 046 "parameters for monetary operations."); 047 } 048 return new BigDecimal(num.toString()); 049 } 050 }, 051 /** Conversion from BigInteger. */ 052 BIGINTEGER { 053 @Override 054 BigDecimal getDecimal(Number num) { 055 return new BigDecimal((BigInteger) num); 056 } 057 }, 058 /** Conversion from NumberValue. */ 059 NUMBERVALUE { 060 @Override 061 BigDecimal getDecimal(Number num) { 062 BigDecimal result = ((NumberValue)num).numberValue(BigDecimal.class); 063 return isScaleZero(result); 064 } 065 }, 066 /** Conversion from BigDecimal. */ 067 BIGDECIMAL { 068 @Override 069 BigDecimal getDecimal(Number num) { 070 BigDecimal result = ((BigDecimal)num); 071 return isScaleZero(result); 072 } 073 }, 074 /** COnversion from BigDecimal, extended. */ 075 BIGDECIMAL_EXTENDS { 076 @Override 077 BigDecimal getDecimal(Number num) { 078 BigDecimal result = ((BigDecimal)num).stripTrailingZeros(); 079 return isScaleZero(result); 080 } 081 }, 082 /** Default conversion based on String, if everything else failed. */ 083 DEFAULT { 084 @Override 085 BigDecimal getDecimal(Number num) { 086 BigDecimal result = null; 087 try { 088 result = new BigDecimal(num.toString()); 089 } catch (NumberFormatException ignored) { 090 } 091 if(result==null){ 092 result = BigDecimal.valueOf(num.doubleValue()); 093 } 094 return isScaleZero(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 (INSTEGERS.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> FLOATINGS = Arrays.asList( 129 (Class)Float.class, Double.class); 130 131 private static final List<Class> INSTEGERS = Arrays.asList( 132 (Class)Long.class, Integer.class, Short.class, Byte.class, 133 AtomicLong.class, AtomicInteger.class); 134 135 private static BigDecimal isScaleZero(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}