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