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; 026import java.util.Optional; 027 028/** 029 * Platform RI: This utility class simplifies implementing {@link MonetaryAmount}, 030 * by providing the common functionality. The different explicitly typed methods 031 * are all reduced to methods using {@link BigDecimal} as input, hereby 032 * performing any conversion to {@link BigDecimal} as needed. Obviously this 033 * takes some time, so implementors that want to avoid this overhead should 034 * implement {@link MonetaryAmount} directly. 035 * 036 * @author Anatole Tresch 037 */ 038public final class MoneyUtils { 039 040 041 private MoneyUtils() { 042 } 043 044 045 // Supporting methods 046 047 /** 048 * Creates a {@link BigDecimal} from the given {@link Number} doing the 049 * valid conversion depending the type given. 050 * 051 * @param num the number type 052 * @return the corresponding {@link BigDecimal} 053 */ 054 public static BigDecimal getBigDecimal(long num) { 055 return BigDecimal.valueOf(num); 056 } 057 058 /** 059 * Creates a {@link BigDecimal} from the given {@link Number} doing the 060 * valid conversion depending the type given. 061 * 062 * @param num the number type 063 * @return the corresponding {@link BigDecimal} 064 */ 065 public static BigDecimal getBigDecimal(double num) { 066 if (num == Double.NaN) { 067 throw new ArithmeticException("Invalid input Double.NaN."); 068 } else if (num == Double.POSITIVE_INFINITY) { 069 throw new ArithmeticException("Invalid input Double.POSITIVE_INFINITY."); 070 } else if (num == Double.NEGATIVE_INFINITY) { 071 throw new ArithmeticException("Invalid input Double.NEGATIVE_INFINITY."); 072 } 073 return new BigDecimal(String.valueOf(num)); 074 } 075 076 /** 077 * Creates a {@link BigDecimal} from the given {@link Number} doing the 078 * valid conversion depending the type given. 079 * 080 * @param num the number type 081 * @return the corresponding {@link BigDecimal} 082 */ 083 public static BigDecimal getBigDecimal(Number num) { 084 return ConvertBigDecimal.of(num); 085 } 086 087 /** 088 * Creates a {@link BigDecimal} from the given {@link Number} doing the 089 * valid conversion depending the type given, if a {@link MonetaryContext} 090 * is given, it is applied to the number returned. 091 * 092 * @param num the number type 093 * @return the corresponding {@link BigDecimal} 094 */ 095 public static BigDecimal getBigDecimal(Number num, MonetaryContext moneyContext) { 096 BigDecimal bd = getBigDecimal(num); 097 if (Objects.nonNull(moneyContext)) { 098 return new BigDecimal(bd.toString(), getMathContext(moneyContext, RoundingMode.HALF_EVEN)); 099 } 100 return bd; 101 } 102 103 /** 104 * Evaluates the {@link MathContext} from the given {@link MonetaryContext}. 105 * 106 * @param monetaryContext the {@link MonetaryContext} 107 * @param defaultMode the default {@link RoundingMode}, to be used if no one is set 108 * in {@link MonetaryContext}. 109 * @return the corresponding {@link MathContext} 110 */ 111 public static MathContext getMathContext(MonetaryContext monetaryContext, RoundingMode defaultMode) { 112 MathContext ctx = monetaryContext.get(MathContext.class); 113 if (Objects.nonNull(ctx)) { 114 return ctx; 115 } 116 RoundingMode roundingMode = monetaryContext.get(RoundingMode.class); 117 if (roundingMode == null) { 118 roundingMode = Optional.ofNullable(defaultMode).orElse(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}