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 isScaleZero(result);
065                }
066        },
067    /** Conversion from BigDecimal. */
068    BIGDECIMAL {
069                @Override
070                BigDecimal getDecimal(Number num) {
071                        BigDecimal result = ((BigDecimal)num);
072                        return isScaleZero(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 isScaleZero(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 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 List<Class<? extends Number>> INSTEGERS = Arrays.asList(
129                        Long.class, Integer.class, Short.class, Byte.class,
130                        AtomicLong.class, AtomicInteger.class);
131        
132        private static List<Class<? extends Number>> FLOATINGS = Arrays.asList(
133                        Float.class, Double.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}