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.io.Serializable; 019import java.math.BigDecimal; 020import java.math.MathContext; 021import java.math.RoundingMode; 022import java.util.Objects; 023 024import javax.money.CurrencyUnit; 025import javax.money.MonetaryAmount; 026import javax.money.MonetaryContext; 027import javax.money.MonetaryException; 028 029/** 030 * Platform RI: This base class simplifies implementing {@link MonetaryAmount}, 031 * by providing the common functionality. The different explicitly typed methods 032 * are all reduced to methods using {@link BigDecimal} as input, hereby 033 * performing any conversion to {@link BigDecimal} as needed. Obviosly this 034 * takes some time, so implementors that want to avoid this overhead should 035 * implement {@link MonetaryAmount} directly. 036 * 037 * @author Anatole Tresch 038 */ 039public abstract class AbstractMoney implements 040 MonetaryAmount, Serializable { 041 042 /** 043 * serialVersionUID. 044 */ 045 private static final long serialVersionUID = 1L; 046 047 /** The currency of this amount. */ 048 protected CurrencyUnit currency; 049 050 /** the {@link MonetaryContext} used by this instance, e.g. on division. */ 051 protected MonetaryContext monetaryContext; 052 053 /** 054 * Required for deserialization. 055 */ 056 protected AbstractMoney() { 057 } 058 059 /** 060 * Constructor of {@link AbstractMoney}. 061 * 062 * @param currency 063 * the currency, not {@code null}. 064 */ 065 protected AbstractMoney(CurrencyUnit currency) { 066 this(currency, null); 067 } 068 069 /** 070 * Creates a new instance os {@link AbstractMoney}. 071 * 072 * @param currency 073 * the currency, not {@code null}. 074 * @param monetaryContext 075 * the {@link MonetaryContext}, not {@code null}. 076 */ 077 protected AbstractMoney(CurrencyUnit currency, 078 MonetaryContext monetaryContext) { 079 Objects.requireNonNull(currency, "Currency is required."); 080 this.currency = currency; 081 if (Objects.nonNull(monetaryContext)) { 082 this.monetaryContext = monetaryContext; 083 } 084 else { 085 this.monetaryContext = getDefaultMonetaryContext(); 086 } 087 Objects.requireNonNull(this.monetaryContext); 088 } 089 090 /** 091 * Method to be implemented by superclasses to provide the default 092 * {@link MonetaryContext}, when not explicit {@link MonetaryContext} is 093 * available. 094 * 095 * @return the default {@link MonetaryContext}, never {@code null}. 096 */ 097 protected abstract MonetaryContext getDefaultMonetaryContext(); 098 099 /** 100 * Returns the amount’s currency, modelled as {@link CurrencyUnit}. 101 * Implementations may co-variantly change the return type to a more 102 * specific implementation of {@link CurrencyUnit} if desired. 103 * 104 * @return the currency, never {@code null} 105 * @see javax.money.MonetaryAmount#getCurrency() 106 */ 107 @Override 108 public CurrencyUnit getCurrency() { 109 return currency; 110 } 111 112 /** 113 * Access the {@link MonetaryContext} used by this instance. 114 * 115 * @return the {@link MonetaryContext} used, never null. 116 * @see javax.money.MonetaryAmount#getMonetaryContext() 117 */ 118 @Override 119 public MonetaryContext getMonetaryContext() { 120 return this.monetaryContext; 121 } 122 123 // Supporting methods 124 125 /** 126 * Creates a {@link BigDecimal} from the given {@link Number} doing the 127 * valid conversion depending the type given. 128 * 129 * @param num 130 * the number type 131 * @return the corresponding {@link BigDecimal} 132 */ 133 protected static BigDecimal getBigDecimal(long num) { 134 return BigDecimal.valueOf(num); 135 } 136 137 /** 138 * Creates a {@link BigDecimal} from the given {@link Number} doing the 139 * valid conversion depending the type given. 140 * 141 * @param num 142 * the number type 143 * @return the corresponding {@link BigDecimal} 144 */ 145 protected static BigDecimal getBigDecimal(double num) { 146 return new BigDecimal(String.valueOf(num)); 147 } 148 149 /** 150 * Creates a {@link BigDecimal} from the given {@link Number} doing the 151 * valid conversion depending the type given. 152 * 153 * @param num 154 * the number type 155 * @return the corresponding {@link BigDecimal} 156 */ 157 protected static BigDecimal getBigDecimal(Number num) { 158 return ConvertBigDecimal.of(num); 159 } 160 161 /** 162 * Creates a {@link BigDecimal} from the given {@link Number} doing the 163 * valid conversion depending the type given, if a {@link MonetaryContext} 164 * is given, it is applied to the number returned. 165 * 166 * @param num 167 * the number type 168 * @return the corresponding {@link BigDecimal} 169 */ 170 protected static BigDecimal getBigDecimal(Number num, 171 MonetaryContext moneyContext) { 172 BigDecimal bd = getBigDecimal(num); 173 if (Objects.nonNull(moneyContext)) { 174 return new BigDecimal(bd.toString(), 175 getMathContext(moneyContext, RoundingMode.HALF_EVEN)); 176 } 177 return bd; 178 } 179 180 /** 181 * Evaluates the {@link MathContext} from the given {@link MonetaryContext}. 182 * 183 * @param monetaryContext 184 * the {@link MonetaryContext} 185 * @param defaultMode 186 * the default {@link RoundingMode}, to be used if no one is set 187 * in {@link MonetaryContext}. 188 * @return the corresponding {@link MathContext} 189 */ 190 protected static MathContext getMathContext( 191 MonetaryContext monetaryContext, 192 RoundingMode defaultMode) { 193 MathContext ctx = monetaryContext.getAttribute(MathContext.class); 194 if (Objects.nonNull(ctx)) { 195 return ctx; 196 } 197 if (Objects.nonNull(defaultMode)) { 198 return new MathContext(monetaryContext.getPrecision(), 199 monetaryContext.getAttribute(RoundingMode.class, 200 defaultMode)); 201 } 202 return new MathContext(monetaryContext.getPrecision(), 203 monetaryContext.getAttribute(RoundingMode.class, 204 RoundingMode.HALF_EVEN)); 205 } 206 207 /** 208 * Method to check if a currency is compatible with this amount instance. 209 * 210 * @param amount 211 * The monetary amount to be compared to, never null. 212 * @throws MonetaryException 213 * If the amount is null, or the amount's {@link CurrencyUnit} is not 214 * compatible, meaning has a different value of 215 * {@link CurrencyUnit#getCurrencyCode()}). 216 */ 217 protected void checkAmountParameter(MonetaryAmount amount) { 218 Objects.requireNonNull(amount, "Amount must not be null."); 219 final CurrencyUnit amountCurrency = amount.getCurrency(); 220 if (!(this.currency 221 .getCurrencyCode().equals(amountCurrency.getCurrencyCode()))) { 222 throw new MonetaryException("Currency mismatch: " 223 + this.currency + '/' + amountCurrency); 224 } 225 } 226 227 /** 228 * Internal method to check for correct number parameter. 229 * 230 * @param number 231 * @throws IllegalArgumentException 232 * If the number is null 233 */ 234 protected static void checkNumberParameter(Number number) { 235 Objects.requireNonNull(number, "Number is required."); 236 } 237 238}