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}