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