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