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                        return new BigDecimal(num.toString());
045                }
046        },
047    /** Conversion from BigInteger. */
048    BIGINTEGER {
049                @Override
050                BigDecimal getDecimal(Number num) {
051                        return new BigDecimal((BigInteger) num);
052                }
053        },
054    /** Conversion from NumberValue. */
055    NUMBERVALUE {
056                @Override
057                BigDecimal getDecimal(Number num) {
058                        BigDecimal result = ((NumberValue)num).numberValue(BigDecimal.class);
059                        return isScaleZero(result);
060                }
061        },
062    /** Conversion from BigDecimal. */
063    BIGDECIMAL {
064                @Override
065                BigDecimal getDecimal(Number num) {
066                        BigDecimal result = ((BigDecimal)num);
067                        return isScaleZero(result);
068                }
069        },
070    /** COnversion from BigDecimal, extended. */
071    BIGDECIMAL_EXTENDS {
072                @Override
073                BigDecimal getDecimal(Number num) {
074                        BigDecimal result = ((BigDecimal)num).stripTrailingZeros();
075                        return isScaleZero(result);
076                }
077        },
078    /** Default conversion based on String, if everything else ffailed. */
079        DEFAULT {
080                @Override
081                BigDecimal getDecimal(Number num) {
082                        BigDecimal result = null;
083                        try {
084                                result = new BigDecimal(num.toString());
085                        } catch (NumberFormatException ignored) {
086                        }
087                        result = Optional.ofNullable(result).orElse(
088                                        BigDecimal.valueOf(num.doubleValue()));
089                        return isScaleZero(result);
090                }
091        };
092        
093        
094        abstract BigDecimal getDecimal(Number num);
095        
096        static BigDecimal of(Number num) {
097                Objects.requireNonNull(num, "Number is required.");
098                return factory(num).getDecimal(num);
099        }
100
101        private static ConvertBigDecimal factory(Number num) {
102                if (INSTEGERS.contains(num.getClass())) {
103                        return INTEGER;
104                }
105                if (FLOATINGS.contains(num.getClass())) {
106                        return FLUCTUAGE;
107                }
108                if (num instanceof NumberValue) {
109                        return NUMBERVALUE;
110                }
111                if (BigDecimal.class.equals(num.getClass())) {
112                        return BIGDECIMAL;
113                }
114        if (num instanceof BigInteger) {
115            return BIGINTEGER;
116        }
117                if (num instanceof BigDecimal) {
118                        return BIGDECIMAL_EXTENDS;
119                }
120                return DEFAULT;
121        }
122        
123        private static List<Class<? extends Number>> INSTEGERS = Arrays.asList(
124                        Long.class, Integer.class, Short.class, Byte.class,
125                        AtomicLong.class, AtomicInteger.class);
126        
127        private static List<Class<? extends Number>> FLOATINGS = Arrays.asList(
128                        Float.class, Double.class);
129        
130        private static BigDecimal isScaleZero(BigDecimal result) {
131                if (result.signum() == 0) {
132                        return BigDecimal.ZERO;
133                }
134                if (result.scale() > 0) {
135                        return result.stripTrailingZeros();
136                }
137                return result;
138        }
139}