001package org.javamoney.moneta.spi; 002 003import javax.money.*; 004import java.math.BigDecimal; 005import java.math.BigInteger; 006import java.util.Objects; 007import java.util.concurrent.atomic.AtomicLong; 008 009/** 010 * Basic implementation of {@link MonetaryAmountFactory}, which simplifies development of the SPI interface. 011 * 012 * @param <T> the target class implementing {@link javax.money.MonetaryAmount}. 013 */ 014public abstract class AbstractAmountFactory<T extends MonetaryAmount> implements MonetaryAmountFactory<T>{ 015 016 /** 017 * The default {@link MonetaryContext} applied, if not set explicitly on creation. 018 */ 019 private MonetaryContext DEFAULT_MONETARY_CONTEXT = loadDefaultMonetaryContext(); 020 021 /** 022 * The default {@link MonetaryContext} applied, if not set explicitly on creation. 023 */ 024 private MonetaryContext MAX_MONETARY_CONTEXT = loadMaxMonetaryContext(); 025 026 private CurrencyUnit currency; 027 private Number number; 028 private MonetaryContext monetaryContext = DEFAULT_MONETARY_CONTEXT; 029 030 /** 031 * Creates a new instance of {@link MonetaryAmount}, using the default {@link MonetaryContext}. 032 * 033 * @return a {@code MonetaryAmount} combining the numeric value and currency unit. 034 * @throws ArithmeticException If the number exceeds the capabilities of the default {@link MonetaryContext} 035 * used. 036 */ 037 @Override 038 public T create(){ 039 return create(currency, number, monetaryContext); 040 } 041 042 protected abstract T create(CurrencyUnit currency, Number number, MonetaryContext monetaryContext); 043 044 protected abstract MonetaryContext loadDefaultMonetaryContext(); 045 046 protected abstract MonetaryContext loadMaxMonetaryContext(); 047 048 049 /* 050 * (non-Javadoc) 051 * @see javax.money.MonetaryAmountFactory#withCurrency(javax.money.CurrencyUnit) 052 */ 053 @Override 054 public MonetaryAmountFactory<T> setCurrency(CurrencyUnit currency){ 055 Objects.requireNonNull(currency); 056 this.currency = currency; 057 return this; 058 } 059 060 /* 061 * (non-Javadoc) 062 * @see javax.money.MonetaryAmountFactory#with(java.lang.Number) 063 */ 064 @Override 065 public MonetaryAmountFactory<T> setNumber(Number number){ 066 this.number = getBigDecimal(number); 067 return this; 068 } 069 070 /* 071 * (non-Javadoc) 072 * @see javax.money.MonetaryAmountFactory#withCurrency(java.lang.String) 073 */ 074 @Override 075 public MonetaryAmountFactory<T> setCurrency(String currencyCode){ 076 this.currency = MonetaryCurrencies.getCurrency(currencyCode); 077 return this; 078 } 079 080 /** 081 * Creates a new instance of {@link MonetaryAmounts}, using the default {@link MonetaryContext}. 082 * 083 * @param number numeric value. 084 * @return a {@code Money} combining the numeric value and currency unit. 085 * @throws ArithmeticException If the number exceeds the capabilities of the default {@link MonetaryContext} 086 * used. 087 * @throws UnknownCurrencyException if the currency code can not be resolved to {@link CurrencyUnit}. 088 */ 089 @Override 090 public MonetaryAmountFactory<T> setNumber(double number){ 091 this.number = new BigDecimal(String.valueOf(number)); 092 return this; 093 } 094 095 /* 096 * (non-Javadoc) 097 * @see javax.money.MonetaryAmountFactory#with(long) 098 */ 099 @Override 100 public MonetaryAmountFactory<T> setNumber(long number){ 101 this.number = BigDecimal.valueOf(number); 102 return this; 103 } 104 105 /* 106 * (non-Javadoc) 107 * @see javax.money.MonetaryAmountFactory#with(javax.money.MonetaryContext) 108 */ 109 @Override 110 public MonetaryAmountFactory<T> setContext(MonetaryContext monetaryContext){ 111 Objects.requireNonNull(monetaryContext); 112 this.monetaryContext = monetaryContext; 113 return this; 114 } 115 116 /** 117 * Returns the default {@link MonetaryContext} used, when no {@link MonetaryContext} is 118 * provided. 119 * 120 * @return the default {@link MonetaryContext}, never {@code null}. 121 */ 122 @Override 123 public MonetaryContext getDefaultMonetaryContext(){ 124 return DEFAULT_MONETARY_CONTEXT; 125 } 126 127 /** 128 * Returns the maximal {@link MonetaryContext} supported. 129 * 130 * @return the maximal {@link MonetaryContext}, never {@code null}. 131 */ 132 @Override 133 public MonetaryContext getMaximalMonetaryContext(){ 134 return MAX_MONETARY_CONTEXT; 135 } 136 137 /** 138 * Converts (if necessary) the given {@link MonetaryAmount} to a new {@link MonetaryAmount} 139 * instance, hereby supporting the {@link MonetaryContext} given. 140 * 141 * @param amt the amount to be converted, if necessary. 142 * @return an according Money instance. 143 */ 144 @Override 145 public MonetaryAmountFactory<T> setAmount(MonetaryAmount amt){ 146 this.currency = amt.getCurrency(); 147 this.number = amt.getNumber().numberValue(BigDecimal.class); 148 this.monetaryContext = new MonetaryContext.Builder(amt.getMonetaryContext()) 149 .setAmountType(DEFAULT_MONETARY_CONTEXT.getAmountType()).create(); 150 return this; 151 } 152 153 /** 154 * Creates a {@link BigDecimal} from the given {@link Number} doing the valid conversion 155 * depending the type given. 156 * 157 * @param num the number type 158 * @return the corresponding {@link BigDecimal} 159 */ 160 protected static BigDecimal getBigDecimal(Number num){ 161 // try fast equality check first (delegates to identity!) 162 if(BigDecimal.class.equals(num.getClass())){ 163 return (BigDecimal) num; 164 } 165 if(Long.class.equals(num.getClass()) || Integer.class.equals(num.getClass()) || 166 Short.class.equals(num.getClass()) || Byte.class.equals(num.getClass()) || 167 AtomicLong.class.equals(num.getClass())){ 168 return BigDecimal.valueOf(num.longValue()); 169 } 170 if(Float.class.equals(num.getClass()) || Double.class.equals(num.getClass())){ 171 return new BigDecimal(num.toString()); 172 } 173 // try instance of (slower) 174 if(num instanceof BigDecimal){ 175 return (BigDecimal) num; 176 } 177 if(num instanceof BigInteger){ 178 return new BigDecimal((BigInteger) num); 179 } 180 try{ 181 // Avoid imprecise conversion to double value if at all possible 182 return new BigDecimal(num.toString()); 183 } 184 catch(NumberFormatException e){ 185 } 186 return BigDecimal.valueOf(num.doubleValue()); 187 } 188 189}