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