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