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;
017
018import org.javamoney.moneta.ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle;
019import org.javamoney.moneta.internal.RoundedMoneyAmountBuilder;
020import org.javamoney.moneta.spi.DefaultNumberValue;
021import org.javamoney.moneta.spi.MoneyUtils;
022
023import javax.money.*;
024import javax.money.format.MonetaryAmountFormat;
025
026import java.io.Serializable;
027import java.math.BigDecimal;
028import java.math.BigInteger;
029import java.math.MathContext;
030import java.math.RoundingMode;
031import java.util.Objects;
032import java.util.Optional;
033
034/**
035 * Platform RI: Default immutable implementation of {@link MonetaryAmount} based on
036 * {@link BigDecimal} for the numeric representation.
037 * <p>
038 * As required by {@link MonetaryAmount} this class is final, thread-safe, immutable and
039 * serializable.
040 *
041 * @author Anatole Tresch
042 * @author Werner Keil
043 * @version 0.6.1
044 */
045public final class RoundedMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable {
046
047    /**
048     * serialVersionUID.
049     */
050    private static final long serialVersionUID = 366517590511294389L;
051    /**
052     * The default {@link MonetaryContext} applied.
053     */
054    public static final MonetaryContext DEFAULT_MONETARY_CONTEXT = MonetaryContextBuilder.of(RoundedMoney.class)
055            .set("MonetaryRounding", Monetary.getDefaultRounding()).
056                    build();
057
058    /**
059     * The currency of this amount.
060     */
061    private final CurrencyUnit currency;
062
063    /**
064     * the {@link MonetaryContext} used by this instance, e.g. on division.
065     */
066    private final MonetaryContext monetaryContext;
067
068    /**
069     * The numeric part of this amount.
070     */
071    private final BigDecimal number;
072
073    /**
074     * The rounding to be done.
075     */
076    private final MonetaryOperator rounding;
077
078
079    /**
080     * Creates a new instance os {@link RoundedMoney}.
081     *
082     * @param currency the currency, not null.
083     * @param number   the amount, not null.
084     */
085    public RoundedMoney(Number number, CurrencyUnit currency, MonetaryOperator rounding) {
086        this(number, currency, null, rounding);
087    }
088
089    public RoundedMoney(Number number, CurrencyUnit currency, MathContext mathContext) {
090        Objects.requireNonNull(currency, "Currency is required.");
091        this.currency = currency;
092        this.rounding = Monetary.getRounding(RoundingQueryBuilder.of().set(mathContext).build());
093        this.monetaryContext =
094                DEFAULT_MONETARY_CONTEXT.toBuilder().set("MonetaryRounding", rounding).set(mathContext)
095                        .build();
096        Objects.requireNonNull(number, "Number is required.");
097        checkNumber(number);
098        this.number = MoneyUtils.getBigDecimal(number, monetaryContext);
099    }
100
101    public RoundedMoney(Number number, CurrencyUnit currency, MonetaryContext context, MonetaryOperator rounding) {
102        Objects.requireNonNull(currency, "Currency is required.");
103        this.currency = currency;
104        Objects.requireNonNull(number, "Number is required.");
105        checkNumber(number);
106
107        MonetaryContextBuilder b = DEFAULT_MONETARY_CONTEXT.toBuilder();
108        if (Objects.nonNull(rounding)) {
109            this.rounding = rounding;
110        } else {
111            if (context != null) {
112                MathContext mc = context.get(MathContext.class);
113                if (mc == null) {
114                    RoundingMode rm = context.get(RoundingMode.class);
115                    if (rm != null) {
116                        Integer scale = Optional.ofNullable(context.getInt("scale")).orElse(2);
117                        b.set(rm);
118                        b.set("scale", scale);
119                        this.rounding = Monetary
120                                .getRounding(RoundingQueryBuilder.of().setScale(scale).set(rm).build());
121                    }
122                    else{
123                        this.rounding = Monetary.getDefaultRounding();
124                    }
125                } else {
126                    b.set(mc.getRoundingMode());
127                    b.set("scale", 2);
128                    this.rounding =
129                            Monetary.getRounding(RoundingQueryBuilder.of().set(mc).setScale(2).build());
130                }
131            }
132            else{
133                this.rounding = Monetary.getDefaultRounding();
134            }
135        }
136        b.set("MonetaryRounding", this.rounding);
137        if (context != null) {
138            b.importContext(context);
139        }
140        this.monetaryContext = b.build();
141        this.number = MoneyUtils.getBigDecimal(number, monetaryContext);
142    }
143
144    // Static Factory Methods
145
146    /**
147     * Translates a {@code BigDecimal} value and a {@code CurrencyUnit} currency into a
148     * {@code Money}.
149     *
150     * @param number   numeric value of the {@code Money}.
151     * @param currency currency unit of the {@code Money}.
152     * @return a {@code Money} combining the numeric value and currency unit.
153     */
154    public static RoundedMoney of(BigDecimal number, CurrencyUnit currency) {
155        return new RoundedMoney(number, currency, Monetary.getDefaultRounding());
156    }
157
158    /**
159     * Translates a {@code BigDecimal} value and a {@code CurrencyUnit} currency into a
160     * {@code Money}.
161     *
162     * @param number   numeric value of the {@code Money}.
163     * @param currency currency unit of the {@code Money}.
164     * @param rounding The rounding to be applied.
165     * @return a {@code Money} combining the numeric value and currency unit.
166     */
167    public static RoundedMoney of(BigDecimal number, CurrencyUnit currency, MonetaryOperator rounding) {
168        return new RoundedMoney(number, currency, rounding);
169    }
170
171    /**
172     * Translates a {@code BigDecimal} value and a {@code CurrencyUnit} currency into a
173     * {@code Money}.
174     *
175     * @param number      numeric value of the {@code Money}.
176     * @param currency    currency unit of the {@code Money}.
177     * @param mathContext the {@link MathContext} to be used.
178     * @return a {@code Money} combining the numeric value and currency unit.
179     */
180    public static RoundedMoney of(BigDecimal number, CurrencyUnit currency, MathContext mathContext) {
181        return new RoundedMoney(number, currency, mathContext);
182    }
183
184    /**
185     * Static factory method for creating a new instance of {@link RoundedMoney} .
186     *
187     * @param currency The target currency, not null.
188     * @param number   The numeric part, not null.
189     * @return A new instance of {@link RoundedMoney}.
190     */
191    public static RoundedMoney of(Number number, CurrencyUnit currency) {
192        return new RoundedMoney(number, currency, (MonetaryOperator) null);
193    }
194
195    /**
196     * Static factory method for creating a new instance of {@link RoundedMoney} .
197     *
198     * @param currency The target currency, not null.
199     * @param number   The numeric part, not null.
200     * @param rounding The rounding to be applied.
201     * @return A new instance of {@link RoundedMoney}.
202     */
203    public static RoundedMoney of(Number number, CurrencyUnit currency, MonetaryOperator rounding) {
204        return new RoundedMoney(number, currency, rounding);
205    }
206
207    /**
208     * Static factory method for creating a new instance of {@link RoundedMoney} .
209     *
210     * @param currency The target currency, not null.
211     * @param number   The numeric part, not null.
212     * @return A new instance of {@link RoundedMoney}.
213     */
214    public static RoundedMoney of(Number number, CurrencyUnit currency, MonetaryContext monetaryContext) {
215        return new RoundedMoney(number, currency,
216                DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), null);
217    }
218
219    /**
220     * Static factory method for creating a new instance of {@link RoundedMoney} .
221     *
222     * @param currency        The target currency, not null.
223     * @param number          The numeric part, not null.
224     * @param monetaryContext the {@link MonetaryContext} to be used.
225     * @param rounding        The rounding to be applied.
226     * @return A new instance of {@link RoundedMoney}.
227     */
228    public static RoundedMoney of(CurrencyUnit currency, Number number, MonetaryContext monetaryContext,
229                                  MonetaryOperator rounding) {
230        return new RoundedMoney(number, currency,
231                DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), rounding);
232    }
233
234    /**
235     * Static factory method for creating a new instance of {@link RoundedMoney} .
236     *
237     * @param currencyCode The target currency as ISO currency code.
238     * @param number       The numeric part, not null.
239     * @return A new instance of {@link RoundedMoney}.
240     */
241    public static RoundedMoney of(Number number, String currencyCode) {
242        return new RoundedMoney(number, Monetary.getCurrency(currencyCode),
243                Monetary.getDefaultRounding());
244    }
245
246    /**
247     * Static factory method for creating a new instance of {@link RoundedMoney} .
248     *
249     * @param currencyCode The target currency as ISO currency code.
250     * @param number       The numeric part, not null.
251     * @param rounding     The rounding to be applied.
252     * @return A new instance of {@link RoundedMoney}.
253     */
254    public static RoundedMoney of(Number number, String currencyCode, MonetaryOperator rounding) {
255        return new RoundedMoney(number, Monetary.getCurrency(currencyCode), rounding);
256    }
257
258    /**
259     * Static factory method for creating a new instance of {@link RoundedMoney} .
260     *
261     * @param currencyCode The target currency as ISO currency code.
262     * @param number       The numeric part, not null.
263     * @return A new instance of {@link RoundedMoney}.
264     */
265    public static RoundedMoney of(Number number, String currencyCode, MonetaryContext monetaryContext) {
266        return new RoundedMoney(number, Monetary.getCurrency(currencyCode),
267                DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), null);
268    }
269
270    /**
271     * Static factory method for creating a new instance of {@link RoundedMoney} .
272     *
273     * @param currencyCode The target currency as ISO currency code.
274     * @param number       The numeric part, not null.
275     * @param rounding     The rounding to be applied.
276     * @return A new instance of {@link RoundedMoney}.
277     */
278    public static RoundedMoney of(String currencyCode, Number number, MonetaryContext monetaryContext,
279                                  MonetaryOperator rounding) {
280        return new RoundedMoney(number, Monetary.getCurrency(currencyCode),
281                DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), rounding);
282    }
283
284    /*
285     * (non-Javadoc)
286     * @see javax.money.MonetaryAmount#getCurrency()
287     */
288    @Override
289    public CurrencyUnit getCurrency() {
290        return currency;
291    }
292
293    /**
294     * Access the {@link MathContext} used by this instance.
295     *
296     * @return the {@link MathContext} used, never null.
297     */
298    @Override
299    public MonetaryContext getContext() {
300        return this.monetaryContext;
301    }
302
303    @Override
304    public RoundedMoney abs() {
305        if (this.isPositiveOrZero()) {
306            return this;
307        }
308        return this.negate();
309    }
310
311    // Arithmetic Operations
312
313    @Override
314    public RoundedMoney add(MonetaryAmount amount) {
315        MoneyUtils.checkAmountParameter(amount, this.currency);
316        if (amount.isZero()) {
317            return this;
318        }
319        return new RoundedMoney(this.number.add(amount.getNumber().numberValue(BigDecimal.class)), this.currency,
320                this.rounding).with(rounding);
321    }
322
323    /*
324     * (non-Javadoc)
325     * @see javax.money.MonetaryAmount#divide(javax.money.MonetaryAmount)
326     */
327    @Override
328    public RoundedMoney divide(Number divisor) {
329        BigDecimal bd = MoneyUtils.getBigDecimal(divisor);
330        if (isOne(bd)) {
331            return this;
332        }
333        BigDecimal dec = this.number.divide(bd, Optional.ofNullable(this.monetaryContext.get(RoundingMode.class)).
334                orElse(RoundingMode.HALF_EVEN));
335        return new RoundedMoney(dec, this.currency, this.rounding).with(rounding);
336    }
337
338    /*
339     * (non-Javadoc)
340     * @see javax.money.MonetaryAmount#divideAndRemainder(javax.money.MonetaryAmount)
341     */
342    @Override
343    public RoundedMoney[] divideAndRemainder(Number divisor) {
344        BigDecimal bd = MoneyUtils.getBigDecimal(divisor);
345        if (isOne(bd)) {
346            return new RoundedMoney[]{this, new RoundedMoney(0L, getCurrency(), this.rounding)};
347        }
348        BigDecimal[] dec = this.number.divideAndRemainder(MoneyUtils.getBigDecimal(divisor), Optional.ofNullable(
349                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64));
350        return new RoundedMoney[]{new RoundedMoney(dec[0], this.currency, this.rounding),
351                new RoundedMoney(dec[1], this.currency, this.rounding).with(rounding)};
352    }
353
354    /*
355     * (non-Javadoc)
356     * @see javax.money.MonetaryAmount#divideToIntegralValue(Number) )D
357     */
358    @Override
359    public RoundedMoney divideToIntegralValue(Number divisor) {
360        BigDecimal dec = this.number.divideToIntegralValue(MoneyUtils.getBigDecimal(divisor), Optional.ofNullable(
361                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64));
362        return new RoundedMoney(dec, this.currency, this.rounding);
363    }
364
365    /*
366     * (non-Javadoc)
367     * @see javax.money.MonetaryAmount#multiply(Number)
368     */
369    @Override
370    public RoundedMoney multiply(Number multiplicand) {
371        BigDecimal bd = MoneyUtils.getBigDecimal(multiplicand);
372        if (isOne(bd)) {
373            return this;
374        }
375        BigDecimal dec = this.number.multiply(bd, Optional.ofNullable(
376                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64));
377        return new RoundedMoney(dec, this.currency, this.rounding).with(rounding);
378    }
379
380    /*
381     * (non-Javadoc)
382     * @see javax.money.MonetaryAmount#negate()
383     */
384    @Override
385    public RoundedMoney negate() {
386        return new RoundedMoney(this.number.negate(Optional.ofNullable(
387                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64)),
388                this.currency, this.rounding);
389    }
390
391    /*
392     * (non-Javadoc)
393     * @see javax.money.MonetaryAmount#plus()
394     */
395    @Override
396    public RoundedMoney plus() {
397        return new RoundedMoney(this.number.plus(Optional.ofNullable(
398                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64)),
399                this.currency, this.rounding);
400    }
401
402    /*
403     * (non-Javadoc)
404     * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount)
405     */
406    @Override
407    public RoundedMoney subtract(MonetaryAmount amount) {
408        MoneyUtils.checkAmountParameter(amount, this.currency);
409        if (amount.isZero()) {
410            return this;
411        }
412        return new RoundedMoney(this.number.subtract(amount.getNumber().numberValue(BigDecimal.class),
413                Optional.ofNullable(
414                        this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64)),
415                this.currency, this.rounding);
416    }
417
418    /*
419     * (non-Javadoc)
420     * @see javax.money.MonetaryAmount#pow(int)
421     */
422    public RoundedMoney pow(int n) {
423        return new RoundedMoney(this.number.pow(n, Optional.ofNullable(
424                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64)),
425                this.currency, this.rounding).with(rounding);
426    }
427
428    /*
429     * (non-Javadoc)
430     * @see javax.money.MonetaryAmount#ulp()
431     */
432    public RoundedMoney ulp() {
433        return new RoundedMoney(this.number.ulp(), this.currency, this.rounding);
434    }
435
436    /*
437     * (non-Javadoc)
438     * @see javax.money.MonetaryAmount#remainder(Number)
439     */
440    @Override
441    public RoundedMoney remainder(Number divisor) {
442        return new RoundedMoney(this.number.remainder(MoneyUtils.getBigDecimal(divisor), Optional.ofNullable(
443                this.monetaryContext.get(MathContext.class)).orElse(MathContext.DECIMAL64)),
444                this.currency, this.rounding);
445    }
446
447    /*
448     * (non-Javadoc)
449     * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int)
450     */
451    @Override
452    public RoundedMoney scaleByPowerOfTen(int power) {
453        return new RoundedMoney(this.number.scaleByPowerOfTen(power), this.currency, this.rounding);
454    }
455
456    /*
457     * (non-Javadoc)
458     * @see javax.money.MonetaryAmount#isZero()
459     */
460    @Override
461    public boolean isZero() {
462        return this.number.signum() == 0;
463    }
464
465    /*
466     * (non-Javadoc)
467     * @see javax.money.MonetaryAmount#isPositive()
468     */
469    @Override
470    public boolean isPositive() {
471        return signum() == 1;
472    }
473
474    /*
475     * (non-Javadoc)
476     * @see javax.money.MonetaryAmount#isPositiveOrZero()
477     */
478    @Override
479    public boolean isPositiveOrZero() {
480        return signum() >= 0;
481    }
482
483    /*
484     * (non-Javadoc)
485     * @see javax.money.MonetaryAmount#isNegative()
486     */
487    @Override
488    public boolean isNegative() {
489        return signum() == -1;
490    }
491
492    /*
493     * (non-Javadoc)
494     * @see javax.money.MonetaryAmount#isNegativeOrZero()
495     */
496    @Override
497    public boolean isNegativeOrZero() {
498        return signum() <= 0;
499    }
500
501    /*
502     * (non-Javadoc)
503     * @see javax.money.MonetaryAmount#with(java.lang.Number)
504     */
505    public RoundedMoney with(Number amount) {
506        checkNumber(amount);
507        return new RoundedMoney(MoneyUtils.getBigDecimal(amount), this.currency, this.rounding);
508    }
509
510    /**
511     * Creates a new Money instance, by just replacing the {@link CurrencyUnit}.
512     *
513     * @param currency the currency unit to be replaced, not {@code null}
514     * @return the new amount with the same numeric value and {@link MathContext}, but the new
515     * {@link CurrencyUnit}.
516     */
517    public RoundedMoney with(CurrencyUnit currency) {
518        Objects.requireNonNull(currency, "currency required");
519        return new RoundedMoney(asType(BigDecimal.class), currency, this.rounding);
520    }
521
522    /*
523     * (non-Javadoc)
524     * @see javax.money.MonetaryAmount#with(CurrencyUnit, java.lang.Number)
525     */
526    public RoundedMoney with(CurrencyUnit currency, Number amount) {
527        checkNumber(amount);
528        return new RoundedMoney(MoneyUtils.getBigDecimal(amount), currency, this.rounding);
529    }
530
531    /*
532     * (non-Javadoc)
533     * @see javax.money.MonetaryAmount#getScale()
534     */
535    public int getScale() {
536        return this.number.scale();
537    }
538
539    /*
540     * (non-Javadoc)
541     * @see javax.money.MonetaryAmount#getPrecision()
542     */
543    public int getPrecision() {
544        return this.number.precision();
545    }
546
547        /*
548     * (non-Javadoc)
549         * @see javax.money.MonetaryAmount#signum()
550         */
551
552    @Override
553    public int signum() {
554        return this.number.signum();
555    }
556
557    /*
558     * (non-Javadoc)
559     * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount)
560     */
561    @Override
562    public boolean isLessThan(MonetaryAmount amount) {
563        MoneyUtils.checkAmountParameter(amount, this.currency);
564        return number.stripTrailingZeros()
565                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) < 0;
566    }
567
568    /*
569     * (non-Javadoc)
570     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount)
571     */
572    @Override
573    public boolean isLessThanOrEqualTo(MonetaryAmount amount) {
574        MoneyUtils.checkAmountParameter(amount, this.currency);
575        return number.stripTrailingZeros()
576                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) <= 0;
577    }
578
579    /*
580     * (non-Javadoc)
581     * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount)
582     */
583    @Override
584    public boolean isGreaterThan(MonetaryAmount amount) {
585        MoneyUtils.checkAmountParameter(amount, this.currency);
586        return number.stripTrailingZeros()
587                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) > 0;
588    }
589
590    /*
591     * (non-Javadoc)
592     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see
593     */
594    @Override
595    public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) {
596        MoneyUtils.checkAmountParameter(amount, this.currency);
597        return number.stripTrailingZeros()
598                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) >= 0;
599    }
600
601    /*
602     * (non-Javadoc)
603     * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount)
604     */
605    @Override
606    public boolean isEqualTo(MonetaryAmount amount) {
607        MoneyUtils.checkAmountParameter(amount, this.currency);
608        return number.stripTrailingZeros()
609                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) == 0;
610    }
611
612    /*
613     * (non-Javadoc)
614     * @see javax.money.MonetaryAmount#isNotEqualTo(javax.money.MonetaryAmount)
615     */
616    public boolean isNotEqualTo(MonetaryAmount amount) {
617        MoneyUtils.checkAmountParameter(amount, this.currency);
618        return number.stripTrailingZeros()
619                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) != 0;
620    }
621
622    /*
623     * }(non-Javadoc)
624     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
625     */
626    @Override
627    public RoundedMoney with(MonetaryOperator operator) {
628        Objects.requireNonNull(operator);
629        try {
630            return RoundedMoney.from(operator.apply(this));
631        } catch (MonetaryException | ArithmeticException e) {
632            throw e;
633        } catch (Exception e) {
634            throw new MonetaryException("Query failed: " + operator, e);
635        }
636    }
637
638    public static RoundedMoney from(MonetaryAmount amt) {
639        if (amt.getClass() == RoundedMoney.class) {
640            return (RoundedMoney) amt;
641        }
642        if (amt.getClass() == FastMoney.class) {
643            return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency());
644        } else if (amt.getClass() == Money.class) {
645            return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency());
646        }
647        return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency());
648    }
649
650    /**
651     * Obtains an instance of RoundedMoney from a text string such as 'EUR
652     * 25.25'.
653     *
654     * @param text the input text, not null.
655     * @return RoundedMoney instance
656     * @throws NullPointerException
657     * @throws NumberFormatException
658     * @throws UnknownCurrencyException
659     */
660    public static RoundedMoney parse(CharSequence text) {
661        return parse(text, DEFAULT_FORMATTER);
662    }
663
664    /**
665     * Obtains an instance of FastMoney from a text using specific formatter.
666     *
667     * @param text      the text to parse not null
668     * @param formatter the formatter to use not null
669     * @return RoundedMoney instance
670     */
671    public static RoundedMoney parse(CharSequence text, MonetaryAmountFormat formatter) {
672        return from(formatter.parse(text));
673    }
674
675    private static final ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
676            .of(ToStringMonetaryAmountFormatStyle.ROUNDED_MONEY);
677
678    /*
679     * }(non-Javadoc)
680     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
681     */
682    @Override
683    public <T> T query(MonetaryQuery<T> query) {
684        Objects.requireNonNull(query);
685        try {
686            return query.queryFrom(this);
687        } catch (MonetaryException | ArithmeticException e) {
688            throw e;
689        } catch (Exception e) {
690            throw new MonetaryException("Query failed: " + query, e);
691        }
692    }
693
694    /*
695     * @see javax.money.MonetaryAmount#asType(java.lang.Class)
696     */
697    @SuppressWarnings("unchecked")
698    public <T> T asType(Class<T> type) {
699        if (BigDecimal.class.equals(type)) {
700            return (T) this.number;
701        }
702        if (Number.class.equals(type)) {
703            return (T) this.number;
704        }
705        if (Double.class.equals(type)) {
706            return (T) Double.valueOf(this.number.doubleValue());
707        }
708        if (Float.class.equals(type)) {
709            return (T) Float.valueOf(this.number.floatValue());
710        }
711        if (Long.class.equals(type)) {
712            return (T) Long.valueOf(this.number.longValue());
713        }
714        if (Integer.class.equals(type)) {
715            return (T) Integer.valueOf(this.number.intValue());
716        }
717        if (Short.class.equals(type)) {
718            return (T) Short.valueOf(this.number.shortValue());
719        }
720        if (Byte.class.equals(type)) {
721            return (T) Byte.valueOf(this.number.byteValue());
722        }
723        if (BigInteger.class.equals(type)) {
724            return (T) this.number.toBigInteger();
725        }
726        throw new IllegalArgumentException("Unsupported representation type: " + type);
727    }
728
729    /*
730     * }(non-Javadoc)
731     * @see javax.money.MonetaryAmount#asType(java.lang.Class, javax.money.Rounding)
732     */
733    public <T> T asType(Class<T> type, MonetaryOperator adjuster) {
734        RoundedMoney amount = (RoundedMoney) adjuster.apply(this);
735        return amount.asType(type);
736    }
737
738    /*
739     * (non-Javadoc)
740     * @see java.lang.Object#toString()
741     */
742    @Override
743    public String toString() {
744        return currency.getCurrencyCode() + ' ' + number;
745    }
746
747    /*
748     * (non-Javadoc)
749     * @see java.lang.Object#hashCode()
750     */
751    @Override
752    public int hashCode() {
753        return Objects.hash(currency, asNumberStripped());
754    }
755
756    /*
757     * (non-Javadoc)
758     * @see java.lang.Object#equals(java.lang.Object)
759     */
760    @Override
761    public boolean equals(Object obj) {
762        if (obj == this) {
763            return true;
764        }
765        if (obj instanceof RoundedMoney) {
766            RoundedMoney other = (RoundedMoney) obj;
767            return Objects.equals(currency, other.currency) &&
768                    Objects.equals(asNumberStripped(), other.asNumberStripped());
769        }
770        return false;
771    }
772
773    /*
774     * @see java.lang.Comparable#compareTo(java.lang.Object)
775     */
776    @Override
777    public int compareTo(MonetaryAmount o) {
778        Objects.requireNonNull(o);
779        int compare;
780        if (this.currency.equals(o.getCurrency())) {
781            compare = asNumberStripped().compareTo(RoundedMoney.from(o).asNumberStripped());
782        } else {
783            compare = this.currency.getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
784        }
785        return compare;
786    }
787
788    /*
789     * (non-Javadoc)
790     * @see javax.money.MonetaryAmount#getNumber()
791     */
792    @Override
793    public NumberValue getNumber() {
794        return new DefaultNumberValue(number);
795    }
796
797    /**
798     * Method that returns BigDecimal.ZERO, if {@link #isZero()}, and #number
799     * {@link #stripTrailingZeros()} in all other cases.
800     *
801     * @return the stripped number value.
802     */
803    public BigDecimal asNumberStripped() {
804        if (isZero()) {
805            return BigDecimal.ZERO;
806        }
807        return this.number.stripTrailingZeros();
808    }
809
810    /**
811     * Internal method to check for correct number parameter.
812     *
813     * @param number the number to check.
814     * @throws IllegalArgumentException If the number is null
815     */
816    private void checkNumber(Number number) {
817        Objects.requireNonNull(number, "Number is required.");
818    }
819
820    @Override
821    public RoundedMoney multiply(long multiplicand) {
822        if (multiplicand == 1L) {
823            return this;
824        }
825        return multiply(MoneyUtils.getBigDecimal(multiplicand));
826    }
827
828    @Override
829    public RoundedMoney multiply(double multiplicand) {
830        Money.checkNoInfinityOrNaN(multiplicand);
831        if (multiplicand == 1.0d) {
832            return this;
833        }
834        return multiply(MoneyUtils.getBigDecimal(multiplicand));
835    }
836
837    @Override
838    public RoundedMoney divide(long divisor) {
839        if (divisor == 1L) {
840            return this;
841        }
842        return divide(MoneyUtils.getBigDecimal(divisor));
843    }
844
845    @Override
846    public RoundedMoney divide(double divisor) {
847        if (Money.isInfinityAndNotNaN(divisor)) {
848            return new RoundedMoney(0L, getCurrency(), this.monetaryContext, this.rounding);
849        }
850        if (divisor == 1.0d) {
851            return this;
852        }
853        return divide(MoneyUtils.getBigDecimal(divisor));
854    }
855
856    @Override
857    public RoundedMoney remainder(long divisor) {
858        return remainder(MoneyUtils.getBigDecimal(divisor));
859    }
860
861    @Override
862    public RoundedMoney remainder(double divisor) {
863        if (Money.isInfinityAndNotNaN(divisor)) {
864            return new RoundedMoney(0L, getCurrency(), this.monetaryContext, this.rounding);
865        }
866        return remainder(MoneyUtils.getBigDecimal(divisor));
867    }
868
869    @Override
870    public RoundedMoney[] divideAndRemainder(long divisor) {
871        return divideAndRemainder(MoneyUtils.getBigDecimal(divisor));
872    }
873
874    @Override
875    public RoundedMoney[] divideAndRemainder(double divisor) {
876        if (Money.isInfinityAndNotNaN(divisor)) {
877            RoundedMoney zero = new RoundedMoney(0L, getCurrency(), this.monetaryContext, this.rounding);
878            return new RoundedMoney[]{zero, zero};
879        }
880        return divideAndRemainder(MoneyUtils.getBigDecimal(divisor));
881    }
882
883    @Override
884    public RoundedMoney stripTrailingZeros() {
885        if (isZero()) {
886            return of(BigDecimal.ZERO, getCurrency());
887        }
888        return of(this.number.stripTrailingZeros(), getCurrency());
889    }
890
891    @Override
892    public RoundedMoney divideToIntegralValue(long divisor) {
893        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
894    }
895
896    @Override
897    public RoundedMoney divideToIntegralValue(double divisor) {
898        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
899    }
900
901    @Override
902    public MonetaryAmountFactory<RoundedMoney> getFactory() {
903        return new RoundedMoneyAmountBuilder().setAmount(this);
904    }
905
906    private boolean isOne(Number number) {
907        BigDecimal bd = MoneyUtils.getBigDecimal(number);
908        try {
909            return bd.scale() == 0 && bd.longValueExact() == 1L;
910        } catch (Exception e) {
911            // The only way to end up here is that longValueExact throws an ArithmeticException,
912            // so the amount is definitively not equal to 1.
913            return false;
914        }
915    }
916}