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 javax.money.CurrencyUnit;
019import javax.money.MonetaryAmount;
020import javax.money.MonetaryContext;
021import javax.money.MonetaryException;
022import java.math.BigDecimal;
023import java.math.MathContext;
024import java.math.RoundingMode;
025import java.util.Objects;
026
027/**
028 * Platform RI: This utility class simplifies implementing {@link MonetaryAmount},
029 * by providing the common functionality. The different explicitly typed methods
030 * are all reduced to methods using {@link BigDecimal} as input, hereby
031 * performing any conversion to {@link BigDecimal} as needed. Obviosly this
032 * takes some time, so implementors that want to avoid this overhead should
033 * implement {@link MonetaryAmount} directly.
034 *
035 * @author Anatole Tresch
036 */
037public final class MoneyUtils {
038
039
040    private MoneyUtils() {
041    }
042
043
044    // Supporting methods
045
046    /**
047     * Creates a {@link BigDecimal} from the given {@link Number} doing the
048     * valid conversion depending the type given.
049     *
050     * @param num the number type
051     * @return the corresponding {@link BigDecimal}
052     */
053    public static BigDecimal getBigDecimal(long num) {
054        return BigDecimal.valueOf(num);
055    }
056
057    /**
058     * Creates a {@link BigDecimal} from the given {@link Number} doing the
059     * valid conversion depending the type given.
060     *
061     * @param num the number type
062     * @return the corresponding {@link BigDecimal}
063     */
064    public static BigDecimal getBigDecimal(double num) {
065        if (Double.isNaN(num)) {
066            throw new ArithmeticException("Invalid input Double.NaN.");
067        } else if(Double.isInfinite(num)) {
068            throw new ArithmeticException("Invalid input Double.xxx_INFINITY.");
069        }
070        return new BigDecimal(String.valueOf(num));
071    }
072
073    /**
074     * Creates a {@link BigDecimal} from the given {@link Number} doing the
075     * valid conversion depending the type given.
076     *
077     * @param num the number type
078     * @return the corresponding {@link BigDecimal}
079     */
080    public static BigDecimal getBigDecimal(Number num) {
081        return ConvertBigDecimal.of(num);
082    }
083
084    /**
085     * Creates a {@link BigDecimal} from the given {@link Number} doing the
086     * valid conversion depending the type given, if a {@link MonetaryContext}
087     * is given, it is applied to the number returned.
088     *
089     * @param num the number type
090     * @return the corresponding {@link BigDecimal}
091     */
092    public static BigDecimal getBigDecimal(Number num, MonetaryContext moneyContext) {
093        BigDecimal bd = getBigDecimal(num);
094        if (moneyContext!=null) {
095            return new BigDecimal(bd.toString(), getMathContext(moneyContext, RoundingMode.HALF_EVEN));
096        }
097        return bd;
098    }
099
100    /**
101     * Evaluates the {@link MathContext} from the given {@link MonetaryContext}.
102     *
103     * @param monetaryContext the {@link MonetaryContext}
104     * @param defaultMode     the default {@link RoundingMode}, to be used if no one is set
105     *                        in {@link MonetaryContext}.
106     * @return the corresponding {@link MathContext}
107     */
108    public static MathContext getMathContext(MonetaryContext monetaryContext, RoundingMode defaultMode) {
109        MathContext ctx = monetaryContext.get(MathContext.class);
110        if (ctx!=null) {
111            return ctx;
112        }
113        RoundingMode roundingMode = monetaryContext.get(RoundingMode.class);
114        if (roundingMode == null) {
115            roundingMode = defaultMode;
116        }
117        if (roundingMode == null) {
118            roundingMode = RoundingMode.HALF_EVEN;
119        }
120        return new MathContext(monetaryContext.getPrecision(), roundingMode);
121    }
122
123    /**
124     * Method to check if a currency is compatible with this amount instance.
125     *
126     * @param amount       The monetary amount to be compared to, never null.
127     * @param currencyUnit the currency unit to compare, never null.
128     * @throws MonetaryException If the amount is null, or the amount's {@link CurrencyUnit} is not
129     *                           compatible, meaning has a different value of
130     *                           {@link CurrencyUnit#getCurrencyCode()}).
131     */
132    public static void checkAmountParameter(MonetaryAmount amount, CurrencyUnit currencyUnit) {
133        Objects.requireNonNull(amount, "Amount must not be null.");
134        final CurrencyUnit amountCurrency = amount.getCurrency();
135        if (!(currencyUnit.getCurrencyCode().equals(amountCurrency.getCurrencyCode()))) {
136            throw new MonetaryException("Currency mismatch: " + currencyUnit + '/' + amountCurrency);
137        }
138    }
139
140    /**
141     * Internal method to check for correct number parameter.
142     *
143     * @param number the number to be checked.
144     * @throws IllegalArgumentException If the number is null
145     */
146    public static void checkNumberParameter(Number number) {
147        Objects.requireNonNull(number, "Number is required.");
148    }
149
150}