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