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.Monetary; 020import javax.money.MonetaryAmount; 021import javax.money.MonetaryAmountFactory; 022import javax.money.MonetaryContext; 023import javax.money.MonetaryContextBuilder; 024import javax.money.MonetaryException; 025 026import java.math.BigDecimal; 027import java.util.Objects; 028 029/** 030 * Basic implementation of {@link javax.money.MonetaryAmountFactory}, which simplifies development of the SPI interface. 031 * 032 * @param <T> the target class implementing {@link javax.money.MonetaryAmount}. 033 */ 034public abstract class AbstractAmountBuilder<T extends MonetaryAmount> implements MonetaryAmountFactory<T> { 035 036 /** 037 * The default {@link MonetaryContext} applied, if not set explicitly on creation. 038 */ 039 private final MonetaryContext DEFAULT_MONETARY_CONTEXT = loadDefaultMonetaryContext(); 040 041 /** 042 * The default {@link MonetaryContext} applied, if not set explicitly on creation. 043 */ 044 private final MonetaryContext MAX_MONETARY_CONTEXT = loadMaxMonetaryContext(); 045 046 private CurrencyUnit currency; 047 private Number number; 048 private MonetaryContext monetaryContext = DEFAULT_MONETARY_CONTEXT; 049 050 /** 051 * Creates a new instance of {@link MonetaryAmount}, using the default {@link MonetaryContext}. 052 * 053 * @return a {@code MonetaryAmount} combining the numeric value and currency unit. 054 * @throws ArithmeticException If the number exceeds the capabilities of the default {@link MonetaryContext} 055 * used. 056 */ 057 @Override 058 public T create() { 059 if (currency == null) { 060 throw new MonetaryException("Cannot create an instance of '"+this.getAmountType().getName()+"': missing currency."); 061 } 062 if (number == null) { 063 throw new MonetaryException("Cannot create an instance of '"+this.getAmountType().getName()+"': missing number."); 064 } 065 if (monetaryContext == null) { 066 throw new MonetaryException("Cannot create an instance of '"+this.getAmountType().getName()+"': missing context."); 067 } 068 return create(number, currency, monetaryContext); 069 } 070 071 protected abstract T create(Number number, CurrencyUnit currency, MonetaryContext monetaryContext); 072 073 protected abstract MonetaryContext loadDefaultMonetaryContext(); 074 075 protected abstract MonetaryContext loadMaxMonetaryContext(); 076 077 078 /* 079 * (non-Javadoc) 080 * @see MonetaryAmountFactory#withCurrency(CurrencyUnit) 081 */ 082 @Override 083 public MonetaryAmountFactory<T> setCurrency(CurrencyUnit currency) { 084 Objects.requireNonNull(currency); 085 this.currency = currency; 086 return this; 087 } 088 089 /* 090 * (non-Javadoc) 091 * @see MonetaryAmountFactory#with(java.lang.Number) 092 */ 093 @Override 094 public MonetaryAmountFactory<T> setNumber(Number number) { 095 this.number = getBigDecimal(number); 096 return this; 097 } 098 099 /* 100 * (non-Javadoc) 101 * @see MonetaryAmountFactory#withCurrency(java.lang.String) 102 */ 103 @Override 104 public MonetaryAmountFactory<T> setCurrency(String currencyCode) { 105 this.currency = Monetary.getCurrency(currencyCode); 106 return this; 107 } 108 109 /** 110 * Creates a new instance of {@link javax.money.Monetary}, using the default {@link MonetaryContext}. 111 * 112 * @param number numeric value. 113 * @return a {@code Money} combining the numeric value and currency unit. 114 * @throws ArithmeticException If the number exceeds the capabilities of the default {@link MonetaryContext} 115 * used. 116 * @throws javax.money.UnknownCurrencyException if the currency code can not be resolved to {@link CurrencyUnit}. 117 */ 118 @Override 119 public MonetaryAmountFactory<T> setNumber(double number) { 120 this.number = new BigDecimal(String.valueOf(number)); 121 return this; 122 } 123 124 /* 125 * (non-Javadoc) 126 * @see MonetaryAmountFactory#with(long) 127 */ 128 @Override 129 public MonetaryAmountFactory<T> setNumber(long number) { 130 this.number = BigDecimal.valueOf(number); 131 return this; 132 } 133 134 /* 135 * (non-Javadoc) 136 * @see MonetaryAmountFactory#with(MonetaryContext) 137 */ 138 @Override 139 public MonetaryAmountFactory<T> setContext(MonetaryContext monetaryContext) { 140 Objects.requireNonNull(monetaryContext); 141 int maxScale = getMaximalMonetaryContext().getMaxScale(); 142 if (maxScale != -1 && maxScale < monetaryContext.getMaxScale()) { 143 throw new MonetaryException( 144 "Context exceeds maximal capabilities (scale) of this type: " + monetaryContext); 145 } 146 int precision = getMaximalMonetaryContext().getPrecision(); 147 if (precision != 0 && precision < monetaryContext.getPrecision()) { 148 throw new MonetaryException( 149 "Contexts exceeds maximal capabilities (precision) of this type: " + monetaryContext); 150 } 151 this.monetaryContext = monetaryContext; 152 return this; 153 } 154 155 /** 156 * Returns the default {@link MonetaryContext} used, when no {@link MonetaryContext} is 157 * provided. 158 * 159 * @return the default {@link MonetaryContext}, never {@code null}. 160 */ 161 @Override 162 public MonetaryContext getDefaultMonetaryContext() { 163 return DEFAULT_MONETARY_CONTEXT; 164 } 165 166 /** 167 * Returns the maximal {@link MonetaryContext} supported. 168 * 169 * @return the maximal {@link MonetaryContext}, never {@code null}. 170 */ 171 @Override 172 public MonetaryContext getMaximalMonetaryContext() { 173 return MAX_MONETARY_CONTEXT; 174 } 175 176 /** 177 * Converts (if necessary) the given {@link MonetaryAmount} to a new {@link MonetaryAmount} 178 * instance, hereby supporting the {@link MonetaryContext} given. 179 * 180 * @param amt the amount to be converted, if necessary. 181 * @return an according Money instance. 182 */ 183 @Override 184 public MonetaryAmountFactory<T> setAmount(MonetaryAmount amt) { 185 this.currency = amt.getCurrency(); 186 this.number = amt.getNumber().numberValue(BigDecimal.class); 187 this.monetaryContext = MonetaryContextBuilder.of(DEFAULT_MONETARY_CONTEXT.getAmountType()) 188 .importContext(amt.getContext()).build(); 189 return this; 190 } 191 192 /** 193 * Creates a {@link BigDecimal} from the given {@link Number} doing the valid conversion 194 * depending the type given. 195 * 196 * @param num the number type 197 * @return the corresponding {@link BigDecimal} 198 */ 199 protected static BigDecimal getBigDecimal(Number num) { 200 return ConvertBigDecimal.of(num); 201 } 202 203}