|  | 
| 1 | 1 | package org.javamoney.moneta.spi; | 
| 2 | 2 | 
 | 
|  | 3 | +import javax.money.*; | 
| 3 | 4 | import java.math.BigDecimal; | 
| 4 | 5 | import java.math.BigInteger; | 
| 5 | 6 | import java.util.Objects; | 
| 6 | 7 | import java.util.concurrent.atomic.AtomicLong; | 
| 7 | 8 | 
 | 
| 8 |  | -import javax.money.CurrencyUnit; | 
| 9 |  | -import javax.money.MonetaryAmount; | 
| 10 |  | -import javax.money.MonetaryAmountFactory; | 
| 11 |  | -import javax.money.MonetaryAmounts; | 
| 12 |  | -import javax.money.MonetaryContext; | 
| 13 |  | -import javax.money.MonetaryCurrencies; | 
| 14 |  | -import javax.money.UnknownCurrencyException; | 
| 15 |  | - | 
| 16 |  | -public abstract class AbstractAmountFactory<T extends MonetaryAmount> | 
| 17 |  | -		implements MonetaryAmountFactory<T> { | 
| 18 |  | - | 
| 19 |  | -	/** | 
| 20 |  | -	 * The default {@link MonetaryContext} applied, if not set explicitly on creation. | 
| 21 |  | -	 */ | 
| 22 |  | -	private MonetaryContext DEFAULT_MONETARY_CONTEXT = loadDefaultMonetaryContext(); | 
| 23 |  | - | 
| 24 |  | -	/** | 
| 25 |  | -	 * The default {@link MonetaryContext} applied, if not set explicitly on creation. | 
| 26 |  | -	 */ | 
| 27 |  | -	private MonetaryContext MAX_MONETARY_CONTEXT = loadMaxMonetaryContext(); | 
| 28 |  | - | 
| 29 |  | -	private CurrencyUnit currency; | 
| 30 |  | -	private Number number; | 
| 31 |  | -	private MonetaryContext monetaryContext = DEFAULT_MONETARY_CONTEXT; | 
| 32 |  | - | 
| 33 |  | -	/** | 
| 34 |  | -	 * Creates a new instance of {@link MonetaryAmount}, using the default {@link MonetaryContext}. | 
| 35 |  | -	 *  | 
| 36 |  | -	 * @param number | 
| 37 |  | -	 *            numeric value, not {@code null}. | 
| 38 |  | -	 * @param currency | 
| 39 |  | -	 *            currency unit, not {@code null}. | 
| 40 |  | -	 * @return a {@code MonetaryAmount} combining the numeric value and currency unit. | 
| 41 |  | -	 * @throws ArithmeticException | 
| 42 |  | -	 *             If the number exceeds the capabilities of the default {@link MonetaryContext} | 
| 43 |  | -	 *             used. | 
| 44 |  | -	 */ | 
| 45 |  | -	@Override | 
| 46 |  | -	public T create() { | 
| 47 |  | -		return create(currency, number, monetaryContext); | 
| 48 |  | -	} | 
| 49 |  | - | 
| 50 |  | -	protected abstract T create(CurrencyUnit currency, | 
| 51 |  | -			Number number, | 
| 52 |  | -			MonetaryContext monetaryContext); | 
| 53 |  | - | 
| 54 |  | -	protected abstract MonetaryContext loadDefaultMonetaryContext(); | 
| 55 |  | - | 
| 56 |  | -	protected abstract MonetaryContext loadMaxMonetaryContext(); | 
| 57 |  | -	 | 
| 58 |  | - | 
| 59 |  | -	/* | 
| 60 |  | -	 * (non-Javadoc) | 
| 61 |  | -	 * @see javax.money.MonetaryAmountFactory#withCurrency(javax.money.CurrencyUnit) | 
| 62 |  | -	 */ | 
| 63 |  | -	@Override | 
| 64 |  | -	public MonetaryAmountFactory<T> setCurrency(CurrencyUnit currency) { | 
| 65 |  | -		Objects.requireNonNull(currency); | 
| 66 |  | -		this.currency = currency; | 
| 67 |  | -		return this; | 
| 68 |  | -	} | 
| 69 |  | - | 
| 70 |  | -	/* | 
| 71 |  | -	 * (non-Javadoc) | 
| 72 |  | -	 * @see javax.money.MonetaryAmountFactory#with(java.lang.Number) | 
| 73 |  | -	 */ | 
| 74 |  | -	@Override | 
| 75 |  | -	public MonetaryAmountFactory<T> setNumber(Number number) { | 
| 76 |  | -		this.number = getBigDecimal(number); | 
| 77 |  | -		return this; | 
| 78 |  | -	} | 
| 79 |  | - | 
| 80 |  | -	/* | 
| 81 |  | -	 * (non-Javadoc) | 
| 82 |  | -	 * @see javax.money.MonetaryAmountFactory#withCurrency(java.lang.String) | 
| 83 |  | -	 */ | 
| 84 |  | -	@Override | 
| 85 |  | -	public MonetaryAmountFactory<T> setCurrency(String currencyCode) { | 
| 86 |  | -		this.currency = MonetaryCurrencies.getCurrency(currencyCode); | 
| 87 |  | -		return this; | 
| 88 |  | -	} | 
| 89 |  | - | 
| 90 |  | -	/** | 
| 91 |  | -	 * Creates a new instance of {@link MonetaryAmounts}, using the default {@link MonetaryContext}. | 
| 92 |  | -	 *  | 
| 93 |  | -	 * @param number | 
| 94 |  | -	 *            numeric value, not {@code null}. | 
| 95 |  | -	 * @param currencyCode | 
| 96 |  | -	 *            currency code, not {@code null}. | 
| 97 |  | -	 * @return a {@code Money} combining the numeric value and currency unit. | 
| 98 |  | -	 * @throws ArithmeticException | 
| 99 |  | -	 *             If the number exceeds the capabilities of the default {@link MonetaryContext} | 
| 100 |  | -	 *             used. | 
| 101 |  | -	 * @throws UnknownCurrencyException | 
| 102 |  | -	 *             if the currency code can not be resolved to {@link CurrencyUnit}. | 
| 103 |  | -	 */ | 
| 104 |  | -	@Override | 
| 105 |  | -	public MonetaryAmountFactory<T> setNumber(double number) { | 
| 106 |  | -		this.number = new BigDecimal(String.valueOf(number)); | 
| 107 |  | -		return this; | 
| 108 |  | -	} | 
| 109 |  | - | 
| 110 |  | -	/* | 
| 111 |  | -	 * (non-Javadoc) | 
| 112 |  | -	 * @see javax.money.MonetaryAmountFactory#with(long) | 
| 113 |  | -	 */ | 
| 114 |  | -	@Override | 
| 115 |  | -	public MonetaryAmountFactory<T> setNumber(long number) { | 
| 116 |  | -		this.number = BigDecimal.valueOf(number); | 
| 117 |  | -		return this; | 
| 118 |  | -	} | 
| 119 |  | - | 
| 120 |  | -	/* | 
| 121 |  | -	 * (non-Javadoc) | 
| 122 |  | -	 * @see javax.money.MonetaryAmountFactory#with(javax.money.MonetaryContext) | 
| 123 |  | -	 */ | 
| 124 |  | -	@Override | 
| 125 |  | -	public MonetaryAmountFactory<T> setContext(MonetaryContext monetaryContext) { | 
| 126 |  | -		Objects.requireNonNull(monetaryContext); | 
| 127 |  | -		this.monetaryContext = monetaryContext; | 
| 128 |  | -		return this; | 
| 129 |  | -	} | 
| 130 |  | - | 
| 131 |  | -	/** | 
| 132 |  | -	 * Returns the default {@link MonetaryContext} used, when no {@link MonetaryContext} is | 
| 133 |  | -	 * provided. | 
| 134 |  | -	 *  | 
| 135 |  | -	 * @return the default {@link MonetaryContext}, never {@code null}. | 
| 136 |  | -	 */ | 
| 137 |  | -	@Override | 
| 138 |  | -	public MonetaryContext getDefaultMonetaryContext() { | 
| 139 |  | -		return DEFAULT_MONETARY_CONTEXT; | 
| 140 |  | -	} | 
| 141 |  | - | 
| 142 |  | -	/** | 
| 143 |  | -	 * Returns the maximal {@link MonetaryContext} supported. | 
| 144 |  | -	 *  | 
| 145 |  | -	 * @return the maximal {@link MonetaryContext}, never {@code null}. | 
| 146 |  | -	 */ | 
| 147 |  | -	@Override | 
| 148 |  | -	public MonetaryContext getMaximalMonetaryContext() { | 
| 149 |  | -		return MAX_MONETARY_CONTEXT; | 
| 150 |  | -	} | 
| 151 |  | - | 
| 152 |  | -	/** | 
| 153 |  | -	 * Converts (if necessary) the given {@link MonetaryAmount} to a new {@link MonetaryAmount} | 
| 154 |  | -	 * instance, hereby supporting the {@link MonetaryContext} given. | 
| 155 |  | -	 *  | 
| 156 |  | -	 * @param amt | 
| 157 |  | -	 *            the amount to be converted, if necessary. | 
| 158 |  | -	 * @param monetaryContext | 
| 159 |  | -	 *            the {@link MonetaryContext} to be used, if {@code null} the default | 
| 160 |  | -	 *            {@link MonetaryContext} is used. | 
| 161 |  | -	 * @return an according Money instance. | 
| 162 |  | -	 */ | 
| 163 |  | -	@Override | 
| 164 |  | -	public MonetaryAmountFactory<T> setAmount(MonetaryAmount amt) { | 
| 165 |  | -		this.currency = amt.getCurrency(); | 
| 166 |  | -		this.number = amt.getNumber().numberValue(BigDecimal.class); | 
| 167 |  | -		this.monetaryContext = new MonetaryContext.Builder( | 
| 168 |  | -				amt.getMonetaryContext()).setAmountType( | 
| 169 |  | -				DEFAULT_MONETARY_CONTEXT.getAmountType()).create(); | 
| 170 |  | -		return this; | 
| 171 |  | -	} | 
| 172 |  | - | 
| 173 |  | -	/** | 
| 174 |  | -	 * Creates a {@link BigDecimal} from the given {@link Number} doing the valid conversion | 
| 175 |  | -	 * depending the type given. | 
| 176 |  | -	 *  | 
| 177 |  | -	 * @param num | 
| 178 |  | -	 *            the number type | 
| 179 |  | -	 * @return the corresponding {@link BigDecimal} | 
| 180 |  | -	 */ | 
| 181 |  | -	protected static BigDecimal getBigDecimal(Number num) { | 
| 182 |  | -		// try fast equality check first (delegates to identity!) | 
| 183 |  | -		if (BigDecimal.class.equals(num.getClass())) { | 
| 184 |  | -			return (BigDecimal) num; | 
| 185 |  | -		} | 
| 186 |  | -		if (Long.class.equals(num.getClass()) | 
| 187 |  | -				|| Integer.class.equals(num.getClass()) | 
| 188 |  | -				|| Short.class.equals(num.getClass()) | 
| 189 |  | -				|| Byte.class.equals(num.getClass()) | 
| 190 |  | -				|| AtomicLong.class.equals(num.getClass())) { | 
| 191 |  | -			return BigDecimal.valueOf(num.longValue()); | 
| 192 |  | -		} | 
| 193 |  | -		if (Float.class.equals(num.getClass()) | 
| 194 |  | -				|| Double.class.equals(num.getClass())) { | 
| 195 |  | -			return new BigDecimal(num.toString()); | 
| 196 |  | -		} | 
| 197 |  | -		// try instance of (slower) | 
| 198 |  | -		if (num instanceof BigDecimal) { | 
| 199 |  | -			return (BigDecimal) num; | 
| 200 |  | -		} | 
| 201 |  | -		if (num instanceof BigInteger) { | 
| 202 |  | -			return new BigDecimal((BigInteger) num); | 
| 203 |  | -		} | 
| 204 |  | -		try { | 
| 205 |  | -			// Avoid imprecise conversion to double value if at all possible | 
| 206 |  | -			return new BigDecimal(num.toString()); | 
| 207 |  | -		} catch (NumberFormatException e) { | 
| 208 |  | -		} | 
| 209 |  | -		return BigDecimal.valueOf(num.doubleValue()); | 
| 210 |  | -	} | 
|  | 9 | +/** | 
|  | 10 | + * Basic implementation of {@link MonetaryAmountFactory}, which simplifies development of the SPI interface. | 
|  | 11 | + * | 
|  | 12 | + * @param <T> the target class implementing {@link javax.money.MonetaryAmount}. | 
|  | 13 | + */ | 
|  | 14 | +public abstract class AbstractAmountFactory<T extends MonetaryAmount> implements MonetaryAmountFactory<T>{ | 
|  | 15 | + | 
|  | 16 | +    /** | 
|  | 17 | +     * The default {@link MonetaryContext} applied, if not set explicitly on creation. | 
|  | 18 | +     */ | 
|  | 19 | +    private MonetaryContext DEFAULT_MONETARY_CONTEXT = loadDefaultMonetaryContext(); | 
|  | 20 | + | 
|  | 21 | +    /** | 
|  | 22 | +     * The default {@link MonetaryContext} applied, if not set explicitly on creation. | 
|  | 23 | +     */ | 
|  | 24 | +    private MonetaryContext MAX_MONETARY_CONTEXT = loadMaxMonetaryContext(); | 
|  | 25 | + | 
|  | 26 | +    private CurrencyUnit currency; | 
|  | 27 | +    private Number number; | 
|  | 28 | +    private MonetaryContext monetaryContext = DEFAULT_MONETARY_CONTEXT; | 
|  | 29 | + | 
|  | 30 | +    /** | 
|  | 31 | +     * Creates a new instance of {@link MonetaryAmount}, using the default {@link MonetaryContext}. | 
|  | 32 | +     * | 
|  | 33 | +     * @return a {@code MonetaryAmount} combining the numeric value and currency unit. | 
|  | 34 | +     * @throws ArithmeticException If the number exceeds the capabilities of the default {@link MonetaryContext} | 
|  | 35 | +     *                             used. | 
|  | 36 | +     */ | 
|  | 37 | +    @Override | 
|  | 38 | +    public T create(){ | 
|  | 39 | +        return create(currency, number, monetaryContext); | 
|  | 40 | +    } | 
|  | 41 | + | 
|  | 42 | +    protected abstract T create(CurrencyUnit currency, Number number, MonetaryContext monetaryContext); | 
|  | 43 | + | 
|  | 44 | +    protected abstract MonetaryContext loadDefaultMonetaryContext(); | 
|  | 45 | + | 
|  | 46 | +    protected abstract MonetaryContext loadMaxMonetaryContext(); | 
|  | 47 | + | 
|  | 48 | + | 
|  | 49 | +    /* | 
|  | 50 | +     * (non-Javadoc) | 
|  | 51 | +     * @see javax.money.MonetaryAmountFactory#withCurrency(javax.money.CurrencyUnit) | 
|  | 52 | +     */ | 
|  | 53 | +    @Override | 
|  | 54 | +    public MonetaryAmountFactory<T> setCurrency(CurrencyUnit currency){ | 
|  | 55 | +        Objects.requireNonNull(currency); | 
|  | 56 | +        this.currency = currency; | 
|  | 57 | +        return this; | 
|  | 58 | +    } | 
|  | 59 | + | 
|  | 60 | +    /* | 
|  | 61 | +     * (non-Javadoc) | 
|  | 62 | +     * @see javax.money.MonetaryAmountFactory#with(java.lang.Number) | 
|  | 63 | +     */ | 
|  | 64 | +    @Override | 
|  | 65 | +    public MonetaryAmountFactory<T> setNumber(Number number){ | 
|  | 66 | +        this.number = getBigDecimal(number); | 
|  | 67 | +        return this; | 
|  | 68 | +    } | 
|  | 69 | + | 
|  | 70 | +    /* | 
|  | 71 | +     * (non-Javadoc) | 
|  | 72 | +     * @see javax.money.MonetaryAmountFactory#withCurrency(java.lang.String) | 
|  | 73 | +     */ | 
|  | 74 | +    @Override | 
|  | 75 | +    public MonetaryAmountFactory<T> setCurrency(String currencyCode){ | 
|  | 76 | +        this.currency = MonetaryCurrencies.getCurrency(currencyCode); | 
|  | 77 | +        return this; | 
|  | 78 | +    } | 
|  | 79 | + | 
|  | 80 | +    /** | 
|  | 81 | +     * Creates a new instance of {@link MonetaryAmounts}, using the default {@link MonetaryContext}. | 
|  | 82 | +     * | 
|  | 83 | +     * @param number numeric value. | 
|  | 84 | +     * @return a {@code Money} combining the numeric value and currency unit. | 
|  | 85 | +     * @throws ArithmeticException      If the number exceeds the capabilities of the default {@link MonetaryContext} | 
|  | 86 | +     *                                  used. | 
|  | 87 | +     * @throws UnknownCurrencyException if the currency code can not be resolved to {@link CurrencyUnit}. | 
|  | 88 | +     */ | 
|  | 89 | +    @Override | 
|  | 90 | +    public MonetaryAmountFactory<T> setNumber(double number){ | 
|  | 91 | +        this.number = new BigDecimal(String.valueOf(number)); | 
|  | 92 | +        return this; | 
|  | 93 | +    } | 
|  | 94 | + | 
|  | 95 | +    /* | 
|  | 96 | +     * (non-Javadoc) | 
|  | 97 | +     * @see javax.money.MonetaryAmountFactory#with(long) | 
|  | 98 | +     */ | 
|  | 99 | +    @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 | +    } | 
| 211 | 188 | 
 | 
| 212 | 189 | } | 
0 commit comments