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