001/*
002 * Copyright (c) 2012, 2013, Credit Suisse (Anatole Tresch), Werner Keil. Licensed under the Apache
003 * License, Version 2.0 (the "License"); you may not use this file except in compliance with the
004 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
005 * Unless required by applicable law or agreed to in writing, software distributed under the License
006 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
007 * or implied. See the License for the specific language governing permissions and limitations under
008 * the License. Contributors: Anatole Tresch - initial implementation Werner Keil - extensions and
009 * adaptions.
010 */
011package org.javamoney.moneta;
012
013import org.javamoney.moneta.internal.FastMoneyAmountFactory;
014import org.javamoney.moneta.spi.AbstractMoney;
015import org.javamoney.moneta.spi.DefaultNumberValue;
016
017import javax.money.*;
018import java.io.Serializable;
019import java.math.BigDecimal;
020import java.util.Objects;
021
022// github.com/JavaMoney/jsr354-ri.git
023
024/**
025 * <type>long</type> based implementation of {@link MonetaryAmount}. This class internally uses a
026 * single long number as numeric representation, which basically is interpreted as minor units.<br/>
027 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money},
028 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision.
029 * As an example performing the following calculation one million times, results in slightly
030 * different results:
031 * <p/>
032 * <pre>
033 * Money money1 = money1.add(Money.of(EURO, 1234567.3444));
034 * money1 = money1.subtract(Money.of(EURO, 232323));
035 * money1 = money1.multiply(3.4);
036 * money1 = money1.divide(5.456);
037 * </pre>
038 * <p/>
039 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in
040 * 3680 ms, or roughly 3ns/loop.
041 * <p/>
042 * whrereas
043 * <p/>
044 * <pre>
045 * FastMoney money1 = money1.add(FastMoney.of(EURO, 1234567.3444));
046 * money1 = money1.subtract(FastMoney.of(EURO, 232323));
047 * money1 = money1.multiply(3.4);
048 * money1 = money1.divide(5.456);
049 * </pre>
050 * <p/>
051 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms,
052 * which is less than 1ns/loop.
053 * <p/>
054 * Also note than mixing up types my drastically change the performance behavior. E.g. replacing the
055 * code above with the following: *
056 * <p/>
057 * <pre>
058 * FastMoney money1 = money1.add(Money.of(EURO, 1234567.3444));
059 * money1 = money1.subtract(FastMoney.of(EURO, 232323));
060 * money1 = money1.multiply(3.4);
061 * money1 = money1.divide(5.456);
062 * </pre>
063 * <p/>
064 * executed one million (1000000) times may execute significantly longer, since monetary amount type
065 * conversion is involved.
066 * <p/>
067 * Basically, when mixing amount implementations, the performance of the amount, on which most of
068 * the operations are operated, has the most significant impact on the overall performance behavior.
069 *
070 * @author Anatole Tresch
071 * @author Werner Keil
072 * @version 0.5.2
073 */
074public final class FastMoney extends AbstractMoney implements Comparable<MonetaryAmount>, Serializable{
075
076    private static final long serialVersionUID = 1L;
077
078    /**
079     * The numeric part of this amount.
080     */
081    private long number;
082
083    /**
084     * The number value.
085     */
086    private transient NumberValue numberValue;
087
088    /**
089     * The current scale represented by the number.
090     */
091    private static final int SCALE = 5;
092
093    /**
094     * the {@link MonetaryContext} used by this instance, e.g. on division.
095     */
096    private static final MonetaryContext MONETARY_CONTEXT =
097            new MonetaryContext.Builder(FastMoney.class).setMaxScale(SCALE).setFixedScale(true)
098                    .setPrecision(String.valueOf(Integer.MAX_VALUE).length()).create();
099
100    /**
101     * Required for deserialization only.
102     */
103    private FastMoney(){
104    }
105
106    /**
107     * Creates a new instance os {@link FastMoney}.
108     *
109     * @param currency the currency, not null.
110     * @param number   the amount, not null.
111     */
112    private FastMoney(CurrencyUnit currency, Number number){
113        super(currency, MONETARY_CONTEXT);
114        Objects.requireNonNull(number, "Number is required.");
115        this.number = getInternalNumber(number);
116        this.numberValue = new DefaultNumberValue(number);
117    }
118
119    /**
120     * Creates a new instance os {@link FastMoney}.
121     *
122     * @param currency the currency, not null.
123     * @param numberBinding   the amount, not null.
124     */
125    private FastMoney(CurrencyUnit currency, NumberValue numberBinding){
126        super(currency, MONETARY_CONTEXT);
127        Objects.requireNonNull(numberBinding, "Number is required.");
128        this.number = getInternalNumber(numberBinding.numberValue(BigDecimal.class));
129    }
130
131    private long getInternalNumber(Number number){
132        BigDecimal bd = getBigDecimal(number);
133        return bd.movePointRight(SCALE).longValue();
134    }
135
136    private FastMoney(CurrencyUnit currency, long number){
137        super(currency, MONETARY_CONTEXT);
138        Objects.requireNonNull(currency, "Currency is required.");
139        this.currency = currency;
140        this.number = number;
141    }
142
143    /**
144     * Static factory method for creating a new instance of {@link FastMoney}.
145     *
146     * @param currency      The target currency, not null.
147     * @param numberBinding The numeric part, not null.
148     * @return A new instance of {@link FastMoney}.
149     */
150    public static FastMoney of(CurrencyUnit currency, NumberValue numberBinding){
151        return new FastMoney(currency, numberBinding);
152    }
153
154    /**
155     * Static factory method for creating a new instance of {@link FastMoney}.
156     *
157     * @param currency The target currency, not null.
158     * @param number   The numeric part, not null.
159     * @return A new instance of {@link FastMoney}.
160     */
161    public static FastMoney of(CurrencyUnit currency, Number number){
162        return new FastMoney(currency, number);
163    }
164
165    /**
166     * Static factory method for creating a new instance of {@link FastMoney}.
167     *
168     * @param currencyCode The target currency as currency code.
169     * @param number       The numeric part, not null.
170     * @return A new instance of {@link FastMoney}.
171     */
172    public static FastMoney of(String currencyCode, Number number){
173        CurrencyUnit currency = MonetaryCurrencies.getCurrency(currencyCode);
174        return of(currency, number);
175    }
176
177    /*
178     * @see java.lang.Comparable#compareTo(java.lang.Object)
179     */
180    public int compareTo(MonetaryAmount o){
181        int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
182        if(compare == 0){
183            compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class));
184        }
185        return compare;
186    }
187
188    /*
189     * (non-Javadoc)
190     * @see java.lang.Object#hashCode()
191     */
192    @Override
193    public int hashCode(){
194        final int prime = 31;
195        int result = 1;
196        result = prime * result + ((currency == null) ? 0 : currency.hashCode());
197        result = prime * result + (int) number;
198        return result;
199    }
200
201    /*
202     * (non-Javadoc)
203     * @see java.lang.Object#equals(java.lang.Object)
204     */
205    @Override
206    public boolean equals(Object obj){
207        if(this == obj)
208            return true;
209        if(obj == null)
210            return false;
211        if(getClass() != obj.getClass())
212            return false;
213        FastMoney other = (FastMoney) obj;
214        if(currency == null){
215            if(other.getCurrency() != null)
216                return false;
217        }else if(!currency.equals(other.getCurrency()))
218            return false;
219        if(number != other.number)
220            return false;
221        return true;
222    }
223
224    /*
225     * (non-Javadoc)
226     * @see javax.money.MonetaryAmount#getCurrency()
227     */
228    public CurrencyUnit getCurrency(){
229        return currency;
230    }
231
232    /*
233     * (non-Javadoc)
234     * @see javax.money.MonetaryAmount#abs()
235     */
236    public FastMoney abs(){
237        if(this.isPositiveOrZero()){
238            return this;
239        }
240        return this.negate();
241    }
242
243    // Arithmetic Operations
244
245    /*
246     * (non-Javadoc)
247     * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount)
248     */
249    public FastMoney add(MonetaryAmount amount){
250        checkAmountParameter(amount);
251        return new FastMoney(getCurrency(), this.number + getInternalNumber(amount.getNumber()));
252    }
253
254    /*
255     * (non-Javadoc)
256     * @see javax.money.MonetaryAmount#divide(java.lang.Number)
257     */
258    public FastMoney divide(Number divisor){
259        checkNumber(divisor);
260        return new FastMoney(getCurrency(), Math.round(this.number / divisor.doubleValue()));
261    }
262
263    /*
264     * (non-Javadoc)
265     * @see javax.money.MonetaryAmount#divideAndRemainder(java.lang.Number)
266     */
267    public FastMoney[] divideAndRemainder(Number divisor){
268        checkNumber(divisor);
269        BigDecimal div = getBigDecimal(divisor);
270        BigDecimal[] res = getBigDecimal().divideAndRemainder(div);
271        return new FastMoney[]{new FastMoney(getCurrency(), res[0]), new FastMoney(getCurrency(), res[1])};
272    }
273
274    /*
275     * (non-Javadoc)
276     * @see javax.money.MonetaryAmount#divideToIntegralValue(java.lang.Number)
277     */
278    public FastMoney divideToIntegralValue(Number divisor){
279        checkNumber(divisor);
280        BigDecimal div = getBigDecimal(divisor);
281        return new FastMoney(getCurrency(), getBigDecimal().divideToIntegralValue(div));
282    }
283
284    public FastMoney multiply(Number multiplicand){
285        checkNumber(multiplicand);
286        double multiplicandNum = multiplicand.doubleValue();
287        return new FastMoney(getCurrency(), Math.round(this.number * multiplicandNum));
288    }
289
290    /*
291     * (non-Javadoc)
292     * @see javax.money.MonetaryAmount#negate()
293     */
294    public FastMoney negate(){
295        return new FastMoney(getCurrency(), this.number * -1);
296    }
297
298    /*
299     * (non-Javadoc)
300     * @see javax.money.MonetaryAmount#plus()
301     */
302    public FastMoney plus(){
303        if(this.number >= 0){
304            return this;
305        }
306        return new FastMoney(getCurrency(), this.number * -1);
307    }
308
309    /*
310     * (non-Javadoc)
311     * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount)
312     */
313    public FastMoney subtract(MonetaryAmount subtrahend){
314        checkAmountParameter(subtrahend);
315        if(subtrahend.isZero()){
316            return this;
317        }
318        return new FastMoney(getCurrency(), this.number - getInternalNumber(subtrahend.getNumber()));
319    }
320
321    /*
322     * (non-Javadoc)
323     * @see javax.money.MonetaryAmount#remainder(java.lang.Number)
324     */
325    public FastMoney remainder(Number divisor){
326        checkNumber(divisor);
327        return new FastMoney(getCurrency(), this.number % getInternalNumber(divisor));
328    }
329
330    /*
331     * (non-Javadoc)
332     * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int)
333     */
334    public FastMoney scaleByPowerOfTen(int n){
335        return new FastMoney(getCurrency(), getBigDecimal().scaleByPowerOfTen(n));
336    }
337
338    /*
339     * (non-Javadoc)
340     * @see javax.money.MonetaryAmount#isZero()
341     */
342    public boolean isZero(){
343        return this.number == 0L;
344    }
345
346    /*
347     * (non-Javadoc)
348     * @see javax.money.MonetaryAmount#isPositive()
349     */
350    public boolean isPositive(){
351        return this.number > 0L;
352    }
353
354    /*
355     * (non-Javadoc)
356     * @see javax.money.MonetaryAmount#isPositiveOrZero()
357     */
358    public boolean isPositiveOrZero(){
359        return this.number >= 0L;
360    }
361
362    /*
363     * (non-Javadoc)
364     * @see javax.money.MonetaryAmount#isNegative()
365     */
366    public boolean isNegative(){
367        return this.number < 0L;
368    }
369
370    /*
371     * (non-Javadoc)
372     * @see javax.money.MonetaryAmount#isNegativeOrZero()
373     */
374    public boolean isNegativeOrZero(){
375        return this.number <= 0L;
376    }
377
378    /*
379     * (non-Javadoc)
380     * @see javax.money.MonetaryAmount#getScale()
381     */
382    public int getScale(){
383        return FastMoney.SCALE;
384    }
385
386    /*
387     * (non-Javadoc)
388     * @see javax.money.MonetaryAmount#getPrecision()
389     */
390    public int getPrecision(){
391        return getNumber().numberValue(BigDecimal.class).precision();
392    }
393
394        /*
395     * (non-Javadoc)
396         * @see javax.money.MonetaryAmount#signum()
397         */
398
399    public int signum(){
400        if(this.number < 0){
401            return -1;
402        }
403        if(this.number == 0){
404            return 0;
405        }
406        return 1;
407    }
408
409    /*
410     * (non-Javadoc)
411     * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount)
412     */
413    public boolean isLessThan(MonetaryAmount amount){
414        checkAmountParameter(amount);
415        return this.number < getInternalNumber(amount.getNumber());
416    }
417
418    /*
419     * (non-Javadoc)
420     * @see javax.money.MonetaryAmount#lessThan(java.lang.Number)
421     */
422    public boolean isLessThan(Number number){
423        checkNumber(number);
424        return this.number < getInternalNumber(number);
425    }
426
427    /*
428     * (non-Javadoc)
429     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount)
430     */
431    public boolean isLessThanOrEqualTo(MonetaryAmount amount){
432        checkAmountParameter(amount);
433        return this.number <= getInternalNumber(amount.getNumber());
434    }
435
436    /*
437     * (non-Javadoc)
438     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(java.lang.Number)
439     */
440    public boolean isLessThanOrEqualTo(Number number){
441        checkNumber(number);
442        return this.number <= getInternalNumber(number);
443    }
444
445    /*
446     * (non-Javadoc)
447     * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount)
448     */
449    public boolean isGreaterThan(MonetaryAmount amount){
450        checkAmountParameter(amount);
451        return this.number > getInternalNumber(amount.getNumber());
452    }
453
454    /*
455     * (non-Javadoc)
456     * @see javax.money.MonetaryAmount#greaterThan(java.lang.Number)
457     */
458    public boolean isGreaterThan(Number number){
459        checkNumber(number);
460        return this.number > getInternalNumber(number);
461    }
462
463    /*
464     * (non-Javadoc)
465     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see
466     */
467    public boolean isGreaterThanOrEqualTo(MonetaryAmount amount){
468        checkAmountParameter(amount);
469        return this.number >= getInternalNumber(amount.getNumber());
470    }
471
472    /*
473     * (non-Javadoc)
474     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(java.lang.Number)
475     */
476    public boolean isGreaterThanOrEqualTo(Number number){
477        checkNumber(number);
478        return this.number >= getInternalNumber(number);
479    }
480
481    /*
482     * (non-Javadoc)
483     * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount)
484     */
485    public boolean isEqualTo(MonetaryAmount amount){
486        checkAmountParameter(amount);
487        return this.number == getInternalNumber(amount.getNumber());
488    }
489
490    /*
491     * (non-Javadoc)
492     * @see javax.money.MonetaryAmount#hasSameNumberAs(java.lang.Number)
493     */
494    public boolean hasSameNumberAs(Number number){
495        checkNumber(number);
496        return this.number == getInternalNumber(number);
497    }
498
499    /*
500     * (non-Javadoc)
501     * @see javax.money.MonetaryAmount#isNotEqualTo(javax.money.MonetaryAmount)
502     */
503    public boolean isNotEqualTo(MonetaryAmount amount){
504        checkAmountParameter(amount);
505        return this.number != getInternalNumber(amount.getNumber());
506    }
507
508    /*
509     * (non-Javadoc)
510     * @see javax.money.MonetaryAmount#isNotEqualTo(java.lang.Number)
511     */
512    public boolean isNotEqualTo(Number number){
513        checkNumber(number);
514        return this.number != getInternalNumber(number);
515    }
516
517    /**
518     * Gets the number representation of the numeric value of this item.
519     *
520     * @return The {@link Number} represention matching best.
521     */
522    @Override
523    public NumberValue getNumber(){
524        if(numberValue == null){
525            numberValue = new DefaultNumberValue(getBigDecimal());
526        }
527        return numberValue;
528    }
529
530    // Static Factory Methods
531
532    /*
533     * (non-Javadoc)
534     * @see java.lang.Object#toString()
535     */
536    @Override
537    public String toString(){
538        return currency.toString() + ' ' + getBigDecimal();
539    }
540
541    // Internal helper methods
542
543    /**
544     * Internal method to check for correct number parameter.
545     *
546     * @param number
547     * @throws IllegalArgumentException If the number is null
548     */
549    private void checkNumber(Number number){
550        Objects.requireNonNull(number, "Number is required.");
551    }
552
553    /*
554     * }(non-Javadoc)
555     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
556     */
557    @Override
558    public FastMoney with(MonetaryOperator operator){
559        Objects.requireNonNull(operator);
560        try{
561            return FastMoney.class.cast(operator.apply(this));
562        }
563        catch(Exception e){
564            throw new MonetaryException("Operator failed: " + operator, e);
565        }
566    }
567
568    @Override
569    public <R> R query(MonetaryQuery<R> query){
570        Objects.requireNonNull(query);
571        try{
572            return query.queryFrom(this);
573        }
574        catch(Exception e){
575            throw new MonetaryException("Query failed: " + query, e);
576        }
577    }
578
579    public static FastMoney from(MonetaryAmount amount){
580        if(FastMoney.class == amount.getClass()){
581            return (FastMoney) amount;
582        }else if(Money.class == amount.getClass()){
583            return new FastMoney(amount.getCurrency(), amount.getNumber());
584        }
585        return new FastMoney(amount.getCurrency(), amount.getNumber());
586    }
587
588    private BigDecimal getBigDecimal(){
589        return BigDecimal.valueOf(this.number).movePointLeft(SCALE);
590    }
591
592    @Override
593    public MonetaryContext getMonetaryContext(){
594        return MONETARY_CONTEXT;
595    }
596
597    @Override
598    public FastMoney multiply(double amount){
599        if(amount == 1.0){
600            return this;
601        }
602        if(amount == 0.0){
603            return new FastMoney(this.currency, 0);
604        }
605        return new FastMoney(this.currency, Math.round(this.number * amount));
606    }
607
608    @Override
609    public FastMoney divide(long amount){
610        if(amount == 1){
611            return this;
612        }
613        return new FastMoney(this.currency, this.number / amount);
614    }
615
616    @Override
617    public FastMoney divide(double number){
618        return new FastMoney(getCurrency(), Math.round(this.number / number));
619    }
620
621    @Override
622    public FastMoney remainder(long number){
623        return remainder(BigDecimal.valueOf(number));
624    }
625
626    @Override
627    public FastMoney remainder(double amount){
628        return remainder(new BigDecimal(String.valueOf(amount)));
629    }
630
631    @Override
632    public FastMoney[] divideAndRemainder(long amount){
633        return divideAndRemainder(BigDecimal.valueOf(amount));
634    }
635
636    @Override
637    public FastMoney[] divideAndRemainder(double amount){
638        return divideAndRemainder(new BigDecimal(String.valueOf(amount)));
639    }
640
641    @Override
642    public FastMoney stripTrailingZeros(){
643        return this;
644    }
645
646    @Override
647    public FastMoney multiply(long multiplicand){
648        if(multiplicand == 1){
649            return this;
650        }
651        if(multiplicand == 0){
652            return new FastMoney(this.currency, 0L);
653        }
654        return new FastMoney(this.currency, multiplicand * this.number);
655    }
656
657    @Override
658    public FastMoney divideToIntegralValue(long divisor){
659        if(divisor == 1){
660            return this;
661        }
662        return divideToIntegralValue(getBigDecimal(divisor));
663    }
664
665    @Override
666    public FastMoney divideToIntegralValue(double divisor){
667        if(divisor == 1.0){
668            return this;
669        }
670        return divideToIntegralValue(getBigDecimal(divisor));
671    }
672
673    @Override
674    protected MonetaryContext getDefaultMonetaryContext(){
675        return MONETARY_CONTEXT;
676    }
677
678    @Override
679    public MonetaryAmountFactory<FastMoney> getFactory(){
680        return new FastMoneyAmountFactory().setAmount(this);
681    }
682
683}