001/**
002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta;
017
018import org.javamoney.moneta.ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle;
019import org.javamoney.moneta.internal.FastMoneyAmountBuilder;
020import org.javamoney.moneta.spi.DefaultNumberValue;
021import org.javamoney.moneta.spi.MonetaryConfig;
022import org.javamoney.moneta.spi.MoneyUtils;
023
024import javax.money.*;
025import javax.money.format.MonetaryAmountFormat;
026
027import java.io.Serializable;
028import java.math.BigDecimal;
029import java.util.Objects;
030import java.util.logging.Level;
031import java.util.logging.Logger;
032
033/**
034 * <code>long</code> based implementation of {@link MonetaryAmount}.This class internally uses a
035 * single long number as numeric representation, which basically is interpreted as minor units.<p>
036 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money},
037 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision.
038 * As an example performing the following calculation one million times, results in slightly
039 * different results:
040 * </p>
041 * <pre><code>
042 * Money money1 = money1.add(Money.of(1234567.3444, "EUR"));
043 * money1 = money1.subtract(Money.of(232323, "EUR"));
044 * money1 = money1.multiply(3.4);
045 * money1 = money1.divide(5.456);
046 * </code></pre>
047 * <p>
048 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in
049 * 3680 ms, or roughly 3ns/loop.
050 * <p>
051 * whereas
052 * </p>
053 * <pre><code>
054 * FastMoney money1 = money1.add(FastMoney.of(1234567.3444, "EUR"));
055 * money1 = money1.subtract(FastMoney.of(232323, "EUR"));
056 * money1 = money1.multiply(3.4);
057 * money1 = money1.divide(5.456);
058 * </code></pre>
059 * <p>
060 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms,
061 * which is less than 1ns/loop.
062 * </p><p>
063 * Also note than mixing up types my drastically change the performance behavior. E.g. replacing the
064 * code above with the following: *
065 * </p>
066 * <pre><code>
067 * FastMoney money1 = money1.add(Money.of(1234567.3444, "EUR"));
068 * money1 = money1.subtract(FastMoney.of(232323, "EUR"));
069 * money1 = money1.multiply(3.4);
070 * money1 = money1.divide(5.456);
071 * </code></pre>
072 * <p>
073 * executed one million (1000000) times may execute significantly longer, since monetary amount type
074 * conversion is involved.
075 * </p><p>
076 * Basically, when mixing amount implementations, the performance of the amount, on which most of
077 * the operations are operated, has the most significant impact on the overall performance behavior.
078 *
079 * @author Anatole Tresch
080 * @author Werner Keil
081 * @version 1.0
082 */
083public final class FastMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable {
084
085    private static final long serialVersionUID = 1L;
086
087    /**
088     * The logger used.
089     */
090    private static final Logger LOG = Logger.getLogger(FastMoney.class.getName());
091
092    /**
093     * The currency of this amount.
094     */
095    private final CurrencyUnit currency;
096
097    /**
098     * The numeric part of this amount.
099     */
100    private final long number;
101
102    /**
103     * The current scale represented by the number.
104     */
105    private static final int SCALE = 5;
106
107    /**
108     * the {@link MonetaryContext} used by this instance, e.g. on division.
109     */
110    private static final MonetaryContext MONETARY_CONTEXT =
111            MonetaryContextBuilder.of(FastMoney.class).setMaxScale(SCALE).setFixedScale(true).setPrecision(19).build();
112
113    /**
114     * Maximum possible value supported, using XX (no currency).
115     */
116    public static final FastMoney MAX_VALUE = new FastMoney(Long.MAX_VALUE, Monetary.getCurrency("XXX"));
117    /**
118     * Maximum possible numeric value supported.
119     */
120    private static final BigDecimal MAX_BD = MAX_VALUE.getBigDecimal();
121    /**
122     * Minimum possible value supported, using XX (no currency).
123     */
124    public static final FastMoney MIN_VALUE = new FastMoney(Long.MIN_VALUE, Monetary.getCurrency("XXX"));
125    /**
126     * Minimum possible numeric value supported.
127     */
128    private static final BigDecimal MIN_BD = MIN_VALUE.getBigDecimal();
129
130
131    /**
132     * Creates a new instance os {@link FastMoney}.
133     *
134     * @param currency the currency, not null.
135     * @param number   the amount, not null.
136     */
137    private FastMoney(Number number, CurrencyUnit currency, boolean allowInternalRounding) {
138        Objects.requireNonNull(currency, "Currency is required.");
139        this.currency = currency;
140        Objects.requireNonNull(number, "Number is required.");
141        this.number = getInternalNumber(number, allowInternalRounding);
142    }
143
144    /**
145     * Creates a new instance os {@link FastMoney}.
146     *
147     * @param currency    the currency, not null.
148     * @param numberValue the numeric value, not null.
149     */
150    private FastMoney(NumberValue numberValue, CurrencyUnit currency, boolean allowInternalRounding) {
151        Objects.requireNonNull(currency, "Currency is required.");
152        this.currency = currency;
153        Objects.requireNonNull(numberValue, "Number is required.");
154        this.number = getInternalNumber(numberValue.numberValue(BigDecimal.class), allowInternalRounding);
155    }
156
157    /**
158     * Creates a new instance os {@link FastMoney}.
159     *
160     * @param number   The format number value
161     * @param currency the currency, not null.
162     */
163    private FastMoney(long number, CurrencyUnit currency) {
164        Objects.requireNonNull(currency, "Currency is required.");
165        this.currency = currency;
166        this.number = number;
167    }
168
169    /**
170     * Returns the amount’s currency, modelled as {@link CurrencyUnit}.
171     * Implementations may co-variantly change the return type to a more
172     * specific implementation of {@link CurrencyUnit} if desired.
173     *
174     * @return the currency, never {@code null}
175     * @see javax.money.MonetaryAmount#getCurrency()
176     */
177    @Override
178    public CurrencyUnit getCurrency() {
179        return currency;
180    }
181
182    /**
183     * Access the {@link MonetaryContext} used by this instance.
184     *
185     * @return the {@link MonetaryContext} used, never null.
186     * @see javax.money.MonetaryAmount#getContext()
187     */
188    @Override
189    public MonetaryContext getContext() {
190        return MONETARY_CONTEXT;
191    }
192
193    private long getInternalNumber(Number number, boolean allowInternalRounding) {
194        BigDecimal bd = MoneyUtils.getBigDecimal(number);
195        if (!allowInternalRounding && bd.scale() > SCALE) {
196            throw new ArithmeticException(number + " can not be represented by this class, scale > " + SCALE);
197        }
198        if (bd.compareTo(MIN_BD) < 0) {
199            throw new ArithmeticException("Overflow: " + number + " < " + MIN_BD);
200        } else if (bd.compareTo(MAX_BD) > 0) {
201            throw new ArithmeticException("Overflow: " + number + " > " + MAX_BD);
202        }
203        return bd.movePointRight(SCALE).longValue();
204    }
205
206
207    /**
208     * Static factory method for creating a new instance of {@link FastMoney}.
209     *
210     * @param currency      The target currency, not null.
211     * @param numberBinding The numeric part, not null.
212     * @return A new instance of {@link FastMoney}.
213     */
214    public static FastMoney of(NumberValue numberBinding, CurrencyUnit currency) {
215        return new FastMoney(numberBinding, currency, false);
216    }
217
218    /**
219     * Static factory method for creating a new instance of {@link FastMoney}.
220     *
221     * @param currency The target currency, not null.
222     * @param number   The numeric part, not null.
223     * @return A new instance of {@link FastMoney}.
224     */
225    public static FastMoney of(Number number, CurrencyUnit currency) {
226        return new FastMoney(number, currency, false);
227    }
228
229    /**
230     * Static factory method for creating a new instance of {@link FastMoney}.
231     *
232     * @param currencyCode The target currency as currency code.
233     * @param number       The numeric part, not null.
234     * @return A new instance of {@link FastMoney}.
235     */
236    public static FastMoney of(Number number, String currencyCode) {
237        CurrencyUnit currency = Monetary.getCurrency(currencyCode);
238        return of(number, currency);
239    }
240
241    /*
242     * @see java.lang.Comparable#compareTo(java.lang.Object)
243     */
244    @Override
245    public int compareTo(MonetaryAmount o) {
246        Objects.requireNonNull(o);
247        int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
248        if (compare == 0) {
249            compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class));
250        }
251        return compare;
252    }
253
254    /*
255     * (non-Javadoc)
256     * @see java.lang.Object#hashCode()
257     */
258    @Override
259    public int hashCode() {
260        return Objects.hash(currency, number);
261    }
262
263    /*
264     * (non-Javadoc)
265     * @see java.lang.Object#equals(java.lang.Object)
266     */
267    @Override
268    public boolean equals(Object obj) {
269        if (obj == this) {
270            return true;
271        }
272        if (obj instanceof FastMoney) {
273            FastMoney other = (FastMoney) obj;
274            return Objects.equals(currency, other.currency) && Objects.equals(number, other.number);
275        }
276        return false;
277    }
278
279
280    /*
281     * (non-Javadoc)
282     * @see javax.money.MonetaryAmount#abs()
283     */
284    @Override
285    public FastMoney abs() {
286        if (this.isPositiveOrZero()) {
287            return this;
288        }
289        return this.negate();
290    }
291
292    // Arithmetic Operations
293
294    /*
295     * (non-Javadoc)
296     * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount)
297     */
298    @Override
299    public FastMoney add(MonetaryAmount amount) {
300        checkAmountParameter(amount);
301        if (amount.isZero()) {
302            return this;
303        }
304        return new FastMoney(Math.addExact(this.number, getInternalNumber(amount.getNumber(), false)), getCurrency());
305    }
306
307    private void checkAmountParameter(MonetaryAmount amount) {
308        MoneyUtils.checkAmountParameter(amount, this.currency);
309        // numeric check for overflow...
310        if (amount.getNumber().getScale() > SCALE) {
311            throw new ArithmeticException("Parameter exceeds maximal scale: " + SCALE);
312        }
313        if (amount.getNumber().getPrecision() > MAX_BD.precision()) {
314            throw new ArithmeticException("Parameter exceeds maximal precision: " + SCALE);
315        }
316    }
317
318
319    /*
320         * (non-Javadoc)
321         * @see javax.money.MonetaryAmount#divide(java.lang.Number)
322         */
323    @Override
324    public FastMoney divide(Number divisor) {
325        if (Money.isInfinityAndNotNaN(divisor)) {
326            return new FastMoney(0L, getCurrency());
327        }
328        checkNumber(divisor);
329        if (isOne(divisor)) {
330            return this;
331        }
332        return new FastMoney(Math.round(this.number / divisor.doubleValue()), getCurrency());
333    }
334
335    /*
336     * (non-Javadoc)
337     * @see javax.money.MonetaryAmount#divideAndRemainder(java.lang.Number)
338     */
339    @Override
340    public FastMoney[] divideAndRemainder(Number divisor) {
341        if (Money.isInfinityAndNotNaN(divisor)) {
342            FastMoney zero = new FastMoney(0L, getCurrency());
343            return new FastMoney[]{zero, zero};
344        }
345        checkNumber(divisor);
346        if (isOne(divisor)) {
347            return new FastMoney[]{this, FastMoney.of(0, getCurrency())};
348        }
349        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
350        BigDecimal[] res = getBigDecimal().divideAndRemainder(div);
351        return new FastMoney[]{new FastMoney(res[0], getCurrency(), true), new FastMoney(res[1], getCurrency(), true)};
352    }
353
354    /*
355     * (non-Javadoc)
356     * @see javax.money.MonetaryAmount#divideToIntegralValue(java.lang.Number)
357     */
358    @Override
359    public FastMoney divideToIntegralValue(Number divisor) {
360        if (Money.isInfinityAndNotNaN(divisor)) {
361            return new FastMoney(0L, getCurrency());
362        }
363        checkNumber(divisor);
364        if (isOne(divisor)) {
365            return this;
366        }
367        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
368        return new FastMoney(getBigDecimal().divideToIntegralValue(div), getCurrency(), false);
369    }
370
371    @Override
372    public FastMoney multiply(Number multiplicand) {
373        Money.checkNoInfinityOrNaN(multiplicand);
374        checkNumber(multiplicand);
375        if (isOne(multiplicand)) {
376            return this;
377        }
378        return new FastMoney(Math.multiplyExact(this.number, getInternalNumber(multiplicand, false)) / 100000L,
379                getCurrency());
380    }
381
382    /*
383     * (non-Javadoc)
384     * @see javax.money.MonetaryAmount#negate()
385     */
386    @Override
387    public FastMoney negate() {
388        return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency());
389    }
390
391    /*
392     * (non-Javadoc)
393     * @see javax.money.MonetaryAmount#plus()
394     */
395    @Override
396    public FastMoney plus() {
397        if (this.number >= 0) {
398            return this;
399        }
400        return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency());
401    }
402
403    /*
404     * (non-Javadoc)
405     * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount)
406     */
407    @Override
408    public FastMoney subtract(MonetaryAmount subtrahend) {
409        checkAmountParameter(subtrahend);
410        if (subtrahend.isZero()) {
411            return this;
412        }
413        return new FastMoney(Math.subtractExact(this.number, getInternalNumber(subtrahend.getNumber(), false)),
414                getCurrency());
415    }
416
417    /*
418     * (non-Javadoc)
419     * @see javax.money.MonetaryAmount#remainder(java.lang.Number)
420     */
421    @Override
422    public FastMoney remainder(Number divisor) {
423        checkNumber(divisor);
424        if (isOne(divisor)) {
425            return new FastMoney(0, getCurrency());
426        }
427        return new FastMoney(this.number % getInternalNumber(divisor, false), getCurrency());
428    }
429
430    private boolean isOne(Number number) {
431        BigDecimal bd = MoneyUtils.getBigDecimal(number);
432        try {
433            return bd.scale() == 0 && bd.longValueExact() == 1L;
434        } catch (Exception e) {
435            // The only way to end up here is that longValueExact throws an ArithmeticException,
436            // so the amount is definitively not equal to 1.
437            return false;
438        }
439    }
440
441    /*
442     * (non-Javadoc)
443     * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int)
444     */
445    @Override
446    public FastMoney scaleByPowerOfTen(int power) {
447        return new FastMoney(getNumber().numberValue(BigDecimal.class).scaleByPowerOfTen(power), getCurrency(), true);
448    }
449
450    /*
451     * (non-Javadoc)
452     * @see javax.money.MonetaryAmount#isZero()
453     */
454    @Override
455    public boolean isZero() {
456        return this.number == 0L;
457    }
458
459    /*
460     * (non-Javadoc)
461     * @see javax.money.MonetaryAmount#isPositive()
462     */
463    @Override
464    public boolean isPositive() {
465        return this.number > 0L;
466    }
467
468    /*
469     * (non-Javadoc)
470     * @see javax.money.MonetaryAmount#isPositiveOrZero()
471     */
472    @Override
473    public boolean isPositiveOrZero() {
474        return this.number >= 0L;
475    }
476
477    /*
478     * (non-Javadoc)
479     * @see javax.money.MonetaryAmount#isNegative()
480     */
481    @Override
482    public boolean isNegative() {
483        return this.number < 0L;
484    }
485
486    /*
487     * (non-Javadoc)
488     * @see javax.money.MonetaryAmount#isNegativeOrZero()
489     */
490    @Override
491    public boolean isNegativeOrZero() {
492        return this.number <= 0L;
493    }
494
495    /*
496     * (non-Javadoc)
497     * @see javax.money.MonetaryAmount#getScale()
498     */
499    public int getScale() {
500        return FastMoney.SCALE;
501    }
502
503    /*
504     * (non-Javadoc)
505     * @see javax.money.MonetaryAmount#getPrecision()
506     */
507    public int getPrecision() {
508        return getNumber().numberValue(BigDecimal.class).precision();
509    }
510
511        /*
512     * (non-Javadoc)
513         * @see javax.money.MonetaryAmount#signum()
514         */
515
516    @Override
517    public int signum() {
518        if (this.number < 0) {
519            return -1;
520        }
521        if (this.number == 0) {
522            return 0;
523        }
524        return 1;
525    }
526
527    /*
528     * (non-Javadoc)
529     * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount)
530     */
531    @Override
532    public boolean isLessThan(MonetaryAmount amount) {
533        checkAmountParameter(amount);
534        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) < 0;
535    }
536
537    /*
538     * (non-Javadoc)
539     * @see javax.money.MonetaryAmount#lessThan(java.lang.Number)
540     */
541    public boolean isLessThan(Number number) {
542        checkNumber(number);
543        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) < 0;
544    }
545
546    /*
547     * (non-Javadoc)
548     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount)
549     */
550    @Override
551    public boolean isLessThanOrEqualTo(MonetaryAmount amount) {
552        checkAmountParameter(amount);
553        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) <= 0;
554    }
555
556    /*
557     * (non-Javadoc)
558     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(java.lang.Number)
559     */
560    public boolean isLessThanOrEqualTo(Number number) {
561        checkNumber(number);
562        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) <= 0;
563    }
564
565    /*
566     * (non-Javadoc)
567     * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount)
568     */
569    @Override
570    public boolean isGreaterThan(MonetaryAmount amount) {
571        checkAmountParameter(amount);
572        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) > 0;
573    }
574
575    /*
576     * (non-Javadoc)
577     * @see javax.money.MonetaryAmount#greaterThan(java.lang.Number)
578     */
579    public boolean isGreaterThan(Number number) {
580        checkNumber(number);
581        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) > 0;
582    }
583
584    /*
585     * (non-Javadoc)
586     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see
587     */
588    @Override
589    public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) {
590        checkAmountParameter(amount);
591        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) >= 0;
592    }
593
594    /*
595     * (non-Javadoc)
596     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(java.lang.Number)
597     */
598    public boolean isGreaterThanOrEqualTo(Number number) {
599        checkNumber(number);
600        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) >= 0;
601    }
602
603    /*
604     * (non-Javadoc)
605     * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount)
606     */
607    @Override
608    public boolean isEqualTo(MonetaryAmount amount) {
609        checkAmountParameter(amount);
610        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) == 0;
611    }
612
613    /*
614     * (non-Javadoc)
615     * @see javax.money.MonetaryAmount#hasSameNumberAs(java.lang.Number)
616     */
617    public boolean hasSameNumberAs(Number number) {
618        checkNumber(number);
619        try {
620            return this.number == getInternalNumber(number, false);
621        } catch (ArithmeticException e) {
622            return false;
623        }
624    }
625
626
627    /**
628     * Gets the number representation of the numeric value of this item.
629     *
630     * @return The {@link Number} representation matching best.
631     */
632    @Override
633    public NumberValue getNumber() {
634        return new DefaultNumberValue(getBigDecimal());
635    }
636
637    @Override
638    public String toString() {
639        return currency.toString() + ' ' + getBigDecimal();
640    }
641
642    // Internal helper methods
643
644    /**
645     * Internal method to check for correct number parameter.
646     *
647     * @param number the number to be checked, including null..
648     * @throws NullPointerException          If the number is null
649     * @throws java.lang.ArithmeticException If the number exceeds the capabilities of this class.
650     */
651    private void checkNumber(Number number) {
652        Objects.requireNonNull(number, "Number is required.");
653        // numeric check for overflow...
654        if (number.longValue() > MAX_BD.longValue()) {
655            throw new ArithmeticException("Value exceeds maximal value: " + MAX_BD);
656        }
657        BigDecimal bd = MoneyUtils.getBigDecimal(number);
658        if (bd.precision() > MAX_BD.precision()) {
659            throw new ArithmeticException("Precision exceeds maximal precision: " + MAX_BD.precision());
660        }
661        if (bd.scale() > SCALE) {
662            if (Boolean.parseBoolean(MonetaryConfig.getConfig()
663                    .getOrDefault("org.javamoney.moneta.FastMoney.enforceScaleCompatibility",
664                            "false"))) {
665                throw new ArithmeticException("Scale of " + bd + " exceeds maximal scale: " + SCALE);
666            } else {
667                if (LOG.isLoggable(Level.FINEST)) {
668                    LOG.finest("Scale exceeds maximal scale of FastMoney (" + SCALE +
669                            "), implicit rounding will be applied to " + number);
670                }
671            }
672        }
673    }
674
675    /*
676     * }(non-Javadoc)
677     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
678     */
679    @Override
680    public FastMoney with(MonetaryOperator operator) {
681        Objects.requireNonNull(operator);
682        try {
683            return FastMoney.class.cast(operator.apply(this));
684        } catch (ArithmeticException e) {
685            throw e;
686        } catch (Exception e) {
687            throw new MonetaryException("Operator failed: " + operator, e);
688        }
689    }
690
691    @Override
692    public <R> R query(MonetaryQuery<R> query) {
693        Objects.requireNonNull(query);
694        try {
695            return query.queryFrom(this);
696        } catch (MonetaryException | ArithmeticException e) {
697            throw e;
698        } catch (Exception e) {
699            throw new MonetaryException("Query failed: " + query, e);
700        }
701    }
702
703    public static FastMoney from(MonetaryAmount amount) {
704        if (FastMoney.class.isInstance(amount)) {
705            return FastMoney.class.cast(amount);
706        }
707        return new FastMoney(amount.getNumber(), amount.getCurrency(), false);
708    }
709
710    /**
711     * Obtains an instance of FastMoney from a text string such as 'EUR 25.25'.
712     *
713     * @param text the text to parse not null
714     * @return FastMoney instance
715     * @throws NullPointerException
716     * @throws NumberFormatException
717     * @throws UnknownCurrencyException
718     */
719    public static FastMoney parse(CharSequence text) {
720        return parse(text, DEFAULT_FORMATTER);
721    }
722
723    /**
724     * Obtains an instance of FastMoney from a text using specific formatter.
725     *
726     * @param text      the text to parse not null
727     * @param formatter the formatter to use not null
728     * @return FastMoney instance
729     */
730    public static FastMoney parse(CharSequence text, MonetaryAmountFormat formatter) {
731        return from(formatter.parse(text));
732    }
733
734    private static final ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
735            .of(ToStringMonetaryAmountFormatStyle.FAST_MONEY);
736
737    private BigDecimal getBigDecimal() {
738        return BigDecimal.valueOf(this.number).movePointLeft(SCALE);
739    }
740
741    @Override
742    public FastMoney multiply(double multiplicand) {
743        Money.checkNoInfinityOrNaN(multiplicand);
744        if (multiplicand == 1.0) {
745            return this;
746        }
747        if (multiplicand == 0.0) {
748            return new FastMoney(0, this.currency);
749        }
750        return new FastMoney(Math.round(this.number * multiplicand), this.currency);
751    }
752
753    @Override
754    public FastMoney divide(long divisor) {
755        if (divisor == 1L) {
756            return this;
757        }
758        return new FastMoney(this.number / divisor, this.currency);
759    }
760
761    @Override
762    public FastMoney divide(double divisor) {
763        if (Money.isInfinityAndNotNaN(divisor)) {
764            return new FastMoney(0L, getCurrency());
765        }
766        if (divisor == 1.0d) {
767            return this;
768        }
769        return new FastMoney(Math.round(this.number / divisor), getCurrency());
770    }
771
772    @Override
773    public FastMoney remainder(long divisor) {
774        return remainder(BigDecimal.valueOf(divisor));
775    }
776
777    @Override
778    public FastMoney remainder(double divisor) {
779        if (Money.isInfinityAndNotNaN(divisor)) {
780            return new FastMoney(0L, getCurrency());
781        }
782        return remainder(new BigDecimal(String.valueOf(divisor)));
783    }
784
785    @Override
786    public FastMoney[] divideAndRemainder(long divisor) {
787        return divideAndRemainder(BigDecimal.valueOf(divisor));
788    }
789
790    @Override
791    public FastMoney[] divideAndRemainder(double divisor) {
792        if (Money.isInfinityAndNotNaN(divisor)) {
793            FastMoney zero = new FastMoney(0L, getCurrency());
794            return new FastMoney[]{zero, zero};
795        } else if (divisor == Double.NaN) {
796            throw new ArithmeticException("Not a number: NaN.");
797        }
798        return divideAndRemainder(new BigDecimal(String.valueOf(divisor)));
799    }
800
801    @Override
802    public FastMoney stripTrailingZeros() {
803        return this;
804    }
805
806    @Override
807    public FastMoney multiply(long multiplicand) {
808        if (multiplicand == 1) {
809            return this;
810        }
811        if (multiplicand == 0) {
812            return new FastMoney(0L, this.currency);
813        }
814        return new FastMoney(Math.multiplyExact(multiplicand, this.number), this.currency);
815    }
816
817    @Override
818    public FastMoney divideToIntegralValue(long divisor) {
819        if (divisor == 1) {
820            return this;
821        }
822        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
823    }
824
825    @Override
826    public FastMoney divideToIntegralValue(double divisor) {
827        if (Money.isInfinityAndNotNaN(divisor)) {
828            return new FastMoney(0L, getCurrency());
829        }
830        if (divisor == 1.0) {
831            return this;
832        }
833        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
834    }
835
836    @Override
837    public MonetaryAmountFactory<FastMoney> getFactory() {
838        return new FastMoneyAmountBuilder().setAmount(this);
839    }
840
841}