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