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;
032
033/**
034 * Platform RI: Default immutable implementation of {@link MonetaryAmount} based on
035 * {@link BigDecimal} for the numeric representation.
036 * <p>
037 * As required by {@link MonetaryAmount} this class is final, thread-safe, immutable and
038 * serializable.
039 *
040 * @author Anatole Tresch
041 * @author Werner Keil
042 * @version 0.6.1
043 */
044public final class RoundedMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable{
045
046    /**
047     * serialVersionUID.
048     */
049    private static final long serialVersionUID = 366517590511294389L;
050    /**
051     * The default {@link MonetaryContext} applied.
052     */
053    public static final MonetaryContext DEFAULT_MONETARY_CONTEXT = MonetaryContextBuilder.of(RoundedMoney.class)
054            .set("rounding", MonetaryRoundings.getDefaultRounding(), MonetaryOperator.class).
055
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("rounding", rounding, MonetaryOperator.class).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                        int scale = context.getInt("scale", 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("rounding", this.rounding, MonetaryOperator.class);
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 getMonetaryContext(){
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, this.monetaryContext.get(RoundingMode.class, RoundingMode.HALF_EVEN));
331        return new RoundedMoney(dec, this.currency, this.rounding).with(rounding);
332    }
333
334    /*
335     * (non-Javadoc)
336     * @see javax.money.MonetaryAmount#divideAndRemainder(javax.money.MonetaryAmount)
337     */
338    @Override
339        public RoundedMoney[] divideAndRemainder(Number divisor){
340        BigDecimal bd = MoneyUtils.getBigDecimal(divisor);
341        if(isOne(bd)){
342            return new RoundedMoney[]{this, new RoundedMoney(0L, getCurrency(), this.rounding)};
343        }
344        BigDecimal[] dec = this.number.divideAndRemainder(MoneyUtils.getBigDecimal(divisor), this.monetaryContext
345                .get(MathContext.class, MathContext.DECIMAL64));
346        return new RoundedMoney[]{new RoundedMoney(dec[0], this.currency, this.rounding),
347                new RoundedMoney(dec[1], this.currency, this.rounding).with(rounding)};
348    }
349
350    /*
351     * (non-Javadoc)
352     * @see javax.money.MonetaryAmount#divideToIntegralValue(Number) )D
353     */
354    @Override
355        public RoundedMoney divideToIntegralValue(Number divisor){
356        BigDecimal dec = this.number.divideToIntegralValue(MoneyUtils.getBigDecimal(divisor), this.monetaryContext
357                .get(MathContext.class, MathContext.DECIMAL64));
358        return new RoundedMoney(dec, this.currency, this.rounding);
359    }
360
361    /*
362     * (non-Javadoc)
363     * @see javax.money.MonetaryAmount#multiply(Number)
364     */
365    @Override
366        public RoundedMoney multiply(Number multiplicand){
367        BigDecimal bd = MoneyUtils.getBigDecimal(multiplicand);
368        if(isOne(bd)){
369            return this;
370        }
371        BigDecimal dec = this.number.multiply(bd, this.monetaryContext.get(MathContext.class, MathContext.DECIMAL64));
372        return new RoundedMoney(dec, this.currency, this.rounding).with(rounding);
373    }
374
375    /*
376     * (non-Javadoc)
377     * @see javax.money.MonetaryAmount#negate()
378     */
379    @Override
380        public RoundedMoney negate(){
381        return new RoundedMoney(this.number.negate(this.monetaryContext.get(MathContext.class, MathContext.DECIMAL64)),
382                                this.currency, this.rounding);
383    }
384
385    /*
386     * (non-Javadoc)
387     * @see javax.money.MonetaryAmount#plus()
388     */
389    @Override
390        public RoundedMoney plus(){
391        return new RoundedMoney(this.number.plus(this.monetaryContext.get(MathContext.class, MathContext.DECIMAL64)),
392                                this.currency, this.rounding);
393    }
394
395    /*
396     * (non-Javadoc)
397     * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount)
398     */
399    @Override
400        public RoundedMoney subtract(MonetaryAmount subtrahend){
401        MoneyUtils.checkAmountParameter(subtrahend, this.currency);
402        if(subtrahend.isZero()){
403            return this;
404        }
405        return new RoundedMoney(this.number.subtract(subtrahend.getNumber().numberValue(BigDecimal.class),
406                                                     this.monetaryContext
407                                                             .get(MathContext.class, MathContext.DECIMAL64)),
408                                this.currency, this.rounding);
409    }
410
411    /*
412     * (non-Javadoc)
413     * @see javax.money.MonetaryAmount#pow(int)
414     */
415    public RoundedMoney pow(int n){
416        return new RoundedMoney(this.number.pow(n, this.monetaryContext.get(MathContext.class, MathContext.DECIMAL64)),
417                                this.currency, this.rounding).with(rounding);
418    }
419
420    /*
421     * (non-Javadoc)
422     * @see javax.money.MonetaryAmount#ulp()
423     */
424    public RoundedMoney ulp(){
425        return new RoundedMoney(this.number.ulp(), this.currency, this.rounding);
426    }
427
428    /*
429     * (non-Javadoc)
430     * @see javax.money.MonetaryAmount#remainder(Number)
431     */
432    @Override
433        public RoundedMoney remainder(Number divisor){
434        return new RoundedMoney(this.number.remainder(MoneyUtils.getBigDecimal(divisor), this.monetaryContext
435                .get(MathContext.class, MathContext.DECIMAL64)), this.currency, this.rounding);
436    }
437
438    /*
439     * (non-Javadoc)
440     * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int)
441     */
442    @Override
443        public RoundedMoney scaleByPowerOfTen(int n){
444        return new RoundedMoney(this.number.scaleByPowerOfTen(n), this.currency, this.rounding);
445    }
446
447    /*
448     * (non-Javadoc)
449     * @see javax.money.MonetaryAmount#isZero()
450     */
451    @Override
452        public boolean isZero(){
453        return this.number.signum() == 0;
454    }
455
456    /*
457     * (non-Javadoc)
458     * @see javax.money.MonetaryAmount#isPositive()
459     */
460    @Override
461        public boolean isPositive(){
462        return signum() == 1;
463    }
464
465    /*
466     * (non-Javadoc)
467     * @see javax.money.MonetaryAmount#isPositiveOrZero()
468     */
469    @Override
470        public boolean isPositiveOrZero(){
471        return signum() >= 0;
472    }
473
474    /*
475     * (non-Javadoc)
476     * @see javax.money.MonetaryAmount#isNegative()
477     */
478    @Override
479        public boolean isNegative(){
480        return signum() == -1;
481    }
482
483    /*
484     * (non-Javadoc)
485     * @see javax.money.MonetaryAmount#isNegativeOrZero()
486     */
487    @Override
488        public boolean isNegativeOrZero(){
489        return signum() <= 0;
490    }
491
492    /*
493     * (non-Javadoc)
494     * @see javax.money.MonetaryAmount#with(java.lang.Number)
495     */
496    public RoundedMoney with(Number amount){
497        checkNumber(amount);
498        return new RoundedMoney(MoneyUtils.getBigDecimal(amount), this.currency, this.rounding);
499    }
500
501    /**
502     * Creates a new Money instance, by just replacing the {@link CurrencyUnit}.
503     *
504     * @param currency the currency unit to be replaced, not {@code null}
505     * @return the new amount with the same numeric value and {@link MathContext}, but the new
506     * {@link CurrencyUnit}.
507     */
508    public RoundedMoney with(CurrencyUnit currency){
509        Objects.requireNonNull(currency, "currency required");
510        return new RoundedMoney(asType(BigDecimal.class), currency, this.rounding);
511    }
512
513    /*
514     * (non-Javadoc)
515     * @see javax.money.MonetaryAmount#with(CurrencyUnit, java.lang.Number)
516     */
517    public RoundedMoney with(CurrencyUnit currency, Number amount){
518        checkNumber(amount);
519        return new RoundedMoney(MoneyUtils.getBigDecimal(amount), currency, this.rounding);
520    }
521
522    /*
523     * (non-Javadoc)
524     * @see javax.money.MonetaryAmount#getScale()
525     */
526    public int getScale(){
527        return this.number.scale();
528    }
529
530    /*
531     * (non-Javadoc)
532     * @see javax.money.MonetaryAmount#getPrecision()
533     */
534    public int getPrecision(){
535        return this.number.precision();
536    }
537
538        /*
539     * (non-Javadoc)
540         * @see javax.money.MonetaryAmount#signum()
541         */
542
543    @Override
544        public int signum(){
545        return this.number.signum();
546    }
547
548    /*
549     * (non-Javadoc)
550     * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount)
551     */
552    @Override
553        public boolean isLessThan(MonetaryAmount amount){
554        MoneyUtils.checkAmountParameter(amount, this.currency);
555        return number.stripTrailingZeros()
556                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) < 0;
557    }
558
559    /*
560     * (non-Javadoc)
561     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount)
562     */
563    @Override
564        public boolean isLessThanOrEqualTo(MonetaryAmount amount){
565        MoneyUtils.checkAmountParameter(amount, this.currency);
566        return number.stripTrailingZeros()
567                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) <= 0;
568    }
569
570    /*
571     * (non-Javadoc)
572     * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount)
573     */
574    @Override
575        public boolean isGreaterThan(MonetaryAmount amount){
576        MoneyUtils.checkAmountParameter(amount, this.currency);
577        return number.stripTrailingZeros()
578                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) > 0;
579    }
580
581    /*
582     * (non-Javadoc)
583     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see
584     */
585    @Override
586        public boolean isGreaterThanOrEqualTo(MonetaryAmount amount){
587        MoneyUtils.checkAmountParameter(amount, this.currency);
588        return number.stripTrailingZeros()
589                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) >= 0;
590    }
591
592    /*
593     * (non-Javadoc)
594     * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount)
595     */
596    @Override
597        public boolean isEqualTo(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 javax.money.MonetaryAmount#isNotEqualTo(javax.money.MonetaryAmount)
606     */
607    public boolean isNotEqualTo(MonetaryAmount amount){
608        MoneyUtils.checkAmountParameter(amount, this.currency);
609        return number.stripTrailingZeros()
610                .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) != 0;
611    }
612
613    /*
614     * }(non-Javadoc)
615     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
616     */
617    @Override
618    public RoundedMoney with(MonetaryOperator operator){
619        Objects.requireNonNull(operator);
620        try{
621            return RoundedMoney.from(operator.apply(this));
622        }
623        catch(MonetaryException | ArithmeticException e){
624            throw e;
625        }
626        catch(Exception e){
627            throw new MonetaryException("Query failed: " + operator, e);
628        }
629    }
630
631    public static RoundedMoney from(MonetaryAmount amt){
632        if(amt.getClass() == RoundedMoney.class){
633            return (RoundedMoney) amt;
634        }
635        if(amt.getClass() == FastMoney.class){
636            return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency());
637        }else if(amt.getClass() == Money.class){
638            return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency());
639        }
640        return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency());
641    }
642
643        /**
644         * Obtains an instance of RoundedMoney from a text string such as 'EUR
645         * 25.25'.
646         * @param text
647         * @return RoundedMoney instance
648         * @throws NullPointerException
649         * @throws NumberFormatException
650         * @throws UnknownCurrencyException
651         */
652        public static RoundedMoney parse(CharSequence text) {
653                return parse(text, DEFAULT_FORMATTER);
654        }
655        /**
656         * Obtains an instance of FastMoney from a text using specific formatter.
657         * @param text  the text to parse not null
658         * @param formatter the formatter to use not null
659         * @return RoundedMoney instance
660         */
661        public static RoundedMoney parse(CharSequence text, MonetaryAmountFormat formatter) {
662                return from(formatter.parse(text));
663        }
664
665        private static ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
666                        .of(ToStringMonetaryAmountFormatStyle.ROUNDED_MONEY);
667    /*
668     * }(non-Javadoc)
669     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
670     */
671    @Override
672    public <T> T query(MonetaryQuery<T> query){
673        Objects.requireNonNull(query);
674        try{
675            return query.queryFrom(this);
676        }
677        catch(MonetaryException | ArithmeticException e){
678            throw e;
679        }
680        catch(Exception e){
681            throw new MonetaryException("Query failed: " + query, e);
682        }
683    }
684
685    /*
686     * @see javax.money.MonetaryAmount#asType(java.lang.Class)
687     */
688    @SuppressWarnings("unchecked")
689    public <T> T asType(Class<T> type){
690        if(BigDecimal.class.equals(type)){
691            return (T) this.number;
692        }
693        if(Number.class.equals(type)){
694            return (T) this.number;
695        }
696        if(Double.class.equals(type)){
697            return (T) Double.valueOf(this.number.doubleValue());
698        }
699        if(Float.class.equals(type)){
700            return (T) Float.valueOf(this.number.floatValue());
701        }
702        if(Long.class.equals(type)){
703            return (T) Long.valueOf(this.number.longValue());
704        }
705        if(Integer.class.equals(type)){
706            return (T) Integer.valueOf(this.number.intValue());
707        }
708        if(Short.class.equals(type)){
709            return (T) Short.valueOf(this.number.shortValue());
710        }
711        if(Byte.class.equals(type)){
712            return (T) Byte.valueOf(this.number.byteValue());
713        }
714        if(BigInteger.class.equals(type)){
715            return (T) this.number.toBigInteger();
716        }
717        throw new IllegalArgumentException("Unsupported representation type: " + type);
718    }
719
720    /*
721     * }(non-Javadoc)
722     * @see javax.money.MonetaryAmount#asType(java.lang.Class, javax.money.Rounding)
723     */
724    public <T> T asType(Class<T> type, MonetaryOperator adjuster){
725        RoundedMoney amount = (RoundedMoney) adjuster.apply(this);
726        return amount.asType(type);
727    }
728
729    /*
730     * (non-Javadoc)
731     * @see java.lang.Object#toString()
732     */
733    @Override
734    public String toString(){
735        return currency.getCurrencyCode() + ' ' + number;
736    }
737
738    /*
739     * (non-Javadoc)
740     * @see java.lang.Object#hashCode()
741     */
742    @Override
743    public int hashCode(){
744        return Objects.hash(currency, asNumberStripped());
745    }
746
747    /*
748     * (non-Javadoc)
749     * @see java.lang.Object#equals(java.lang.Object)
750     */
751    @Override
752    public boolean equals(Object obj){
753        if(obj == this){
754            return true;
755        }
756        if(obj instanceof RoundedMoney){
757            RoundedMoney other = (RoundedMoney) obj;
758            return Objects.equals(currency, other.currency) &&
759                    Objects.equals(asNumberStripped(), other.asNumberStripped());
760        }
761        return false;
762    }
763
764    /*
765     * @see java.lang.Comparable#compareTo(java.lang.Object)
766     */
767    @Override
768        public int compareTo(MonetaryAmount o){
769        Objects.requireNonNull(o);
770        int compare;
771        if(this.currency.equals(o.getCurrency())){
772            compare = asNumberStripped().compareTo(RoundedMoney.from(o).asNumberStripped());
773        }else{
774            compare = this.currency.getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
775        }
776        return compare;
777    }
778
779    /*
780     * (non-Javadoc)
781     * @see javax.money.MonetaryAmount#getNumber()
782     */
783    @Override
784        public NumberValue getNumber(){
785        return new DefaultNumberValue(number);
786    }
787
788    /**
789     * Method that returns BigDecimal.ZERO, if {@link #isZero()}, and #number
790     * {@link #stripTrailingZeros()} in all other cases.
791     *
792     * @return the stripped number value.
793     */
794    public BigDecimal asNumberStripped(){
795        if(isZero()){
796            return BigDecimal.ZERO;
797        }
798        return this.number.stripTrailingZeros();
799    }
800
801    /**
802     * Internal method to check for correct number parameter.
803     *
804     * @param number the number to check.
805     * @throws IllegalArgumentException If the number is null
806     */
807    private void checkNumber(Number number){
808        Objects.requireNonNull(number, "Number is required.");
809    }
810
811    @Override
812    public RoundedMoney multiply(long amount){
813        if(amount == 1L){
814            return this;
815        }
816        return multiply(MoneyUtils.getBigDecimal(amount));
817    }
818
819    @Override
820    public RoundedMoney multiply(double amount){
821        if(amount == 1.0d){
822            return this;
823        }
824        return multiply(MoneyUtils.getBigDecimal(amount));
825    }
826
827    @Override
828    public RoundedMoney divide(long amount){
829        if(amount == 1L){
830            return this;
831        }
832        return divide(MoneyUtils.getBigDecimal(amount));
833    }
834
835    @Override
836    public RoundedMoney divide(double amount){
837
838        if(amount == 1.0d){
839            return this;
840        }
841        return divide(MoneyUtils.getBigDecimal(amount));
842    }
843
844    @Override
845    public RoundedMoney remainder(long amount){
846        return remainder(MoneyUtils.getBigDecimal(amount));
847    }
848
849    @Override
850    public RoundedMoney remainder(double amount){
851        return remainder(MoneyUtils.getBigDecimal(amount));
852    }
853
854    @Override
855    public RoundedMoney[] divideAndRemainder(long amount){
856        return divideAndRemainder(MoneyUtils.getBigDecimal(amount));
857    }
858
859    @Override
860    public RoundedMoney[] divideAndRemainder(double amount){
861        return divideAndRemainder(MoneyUtils.getBigDecimal(amount));
862    }
863
864    @Override
865    public RoundedMoney stripTrailingZeros(){
866        if(isZero()){
867            return of(BigDecimal.ZERO, getCurrency());
868        }
869        return of(this.number.stripTrailingZeros(), getCurrency());
870    }
871
872    @Override
873    public RoundedMoney divideToIntegralValue(long divisor){
874        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
875    }
876
877    @Override
878    public RoundedMoney divideToIntegralValue(double divisor){
879        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
880    }
881
882    @Override
883    public MonetaryAmountFactory<RoundedMoney> getFactory(){
884        return new RoundedMoneyAmountBuilder().setAmount(this);
885    }
886
887    private boolean isOne(Number number){
888        BigDecimal bd = MoneyUtils.getBigDecimal(number);
889        try{
890            return bd.scale() == 0 && bd.longValueExact() == 1L;
891        }
892        catch(Exception e){
893            // The only way to end up here is that longValueExact throws an ArithmeticException,
894            // so the amount is definitively not equal to 1.
895            return false;
896        }
897    }
898}