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