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