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        return new BigDecimal(String.valueOf(num));
066    }
067
068    /**
069     * Creates a {@link BigDecimal} from the given {@link Number} doing the
070     * valid conversion depending the type given.
071     *
072     * @param num the number type
073     * @return the corresponding {@link BigDecimal}
074     */
075    public static BigDecimal getBigDecimal(Number num){
076        return ConvertBigDecimal.of(num);
077    }
078
079    /**
080     * Creates a {@link BigDecimal} from the given {@link Number} doing the
081     * valid conversion depending the type given, if a {@link MonetaryContext}
082     * is given, it is applied to the number returned.
083     *
084     * @param num the number type
085     * @return the corresponding {@link BigDecimal}
086     */
087    public static BigDecimal getBigDecimal(Number num, MonetaryContext moneyContext){
088        BigDecimal bd = getBigDecimal(num);
089        if(Objects.nonNull(moneyContext)){
090            return new BigDecimal(bd.toString(), getMathContext(moneyContext, RoundingMode.HALF_EVEN));
091        }
092        return bd;
093    }
094
095    /**
096     * Evaluates the {@link MathContext} from the given {@link MonetaryContext}.
097     *
098     * @param monetaryContext the {@link MonetaryContext}
099     * @param defaultMode     the default {@link RoundingMode}, to be used if no one is set
100     *                        in {@link MonetaryContext}.
101     * @return the corresponding {@link MathContext}
102     */
103    public static MathContext getMathContext(MonetaryContext monetaryContext, RoundingMode defaultMode){
104        MathContext ctx = monetaryContext.get(MathContext.class);
105        if(Objects.nonNull(ctx)){
106            return ctx;
107        }
108        if(Objects.nonNull(defaultMode)){
109            return new MathContext(monetaryContext.getPrecision(),
110                                   monetaryContext.get(RoundingMode.class, defaultMode)
111            );
112        }
113        return new MathContext(monetaryContext.getPrecision(),
114                               monetaryContext.get(RoundingMode.class, RoundingMode.HALF_EVEN)
115        );
116    }
117
118    /**
119     * Method to check if a currency is compatible with this amount instance.
120     *
121     * @param amount       The monetary amount to be compared to, never null.
122     * @param currencyUnit the currency unit to compare, never null.
123     * @throws MonetaryException If the amount is null, or the amount's {@link CurrencyUnit} is not
124     *                           compatible, meaning has a different value of
125     *                           {@link CurrencyUnit#getCurrencyCode()}).
126     */
127    public static void checkAmountParameter(MonetaryAmount amount, CurrencyUnit currencyUnit){
128        Objects.requireNonNull(amount, "Amount must not be null.");
129        final CurrencyUnit amountCurrency = amount.getCurrency();
130        if(!(currencyUnit.getCurrencyCode().equals(amountCurrency.getCurrencyCode()))){
131            throw new MonetaryException("Currency mismatch: " + currencyUnit + '/' + amountCurrency);
132        }
133    }
134
135    /**
136     * Internal method to check for correct number parameter.
137     *
138     * @param number the number to be checked.
139     * @throws IllegalArgumentException If the number is null
140     */
141    public static void checkNumberParameter(Number number){
142        Objects.requireNonNull(number, "Number is required.");
143    }
144
145}