View Javadoc

1   /*
2    *  Copyright 2009-2013 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.joda.money;
17  
18  import java.io.InvalidObjectException;
19  import java.io.ObjectInputStream;
20  import java.io.Serializable;
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.math.RoundingMode;
24  import java.util.Arrays;
25  import java.util.Iterator;
26  import java.util.regex.Pattern;
27  
28  import org.joda.convert.FromString;
29  import org.joda.convert.ToString;
30  
31  /**
32   * An amount of money with unrestricted decimal place precision.
33   * <p>
34   * This class represents a quantity of money, stored as a {@code BigDecimal} amount
35   * in a single {@link CurrencyUnit currency}.
36   * <p>
37   * Every currency has a certain standard number of decimal places.
38   * This is typically 2 (Euro, British Pound, US Dollar) but might be
39   * 0 (Japanese Yen), 1 (Vietnamese Dong) or 3 (Bahrain Dinar).
40   * The {@code BigMoney} class is not restricted to the standard decimal places
41   * and can represent an amount to any precision that a {@code BigDecimal} can represent.
42   * <p>
43   * This class is immutable and thread-safe.
44   */
45  public final class BigMoney implements BigMoneyProvider, Comparable<BigMoneyProvider>, Serializable {
46  
47      /**
48       * The serialisation version.
49       */
50      private static final long serialVersionUID = 1L;
51      /**
52       * The regex for parsing.
53       */
54      private static final Pattern PARSE_REGEX = Pattern.compile("[+-]?[0-9]*[.]?[0-9]*");
55  
56      /**
57       * The currency, not null.
58       */
59      private final CurrencyUnit currency;
60      /**
61       * The amount, not null.
62       */
63      private final BigDecimal amount;
64  
65      //-----------------------------------------------------------------------
66      /**
67       * Obtains an instance of {@code BigMoney} from a {@code BigDecimal}.
68       * <p>
69       * This allows you to create an instance with a specific currency and amount.
70       * The scale of the money will be that of the BigDecimal.
71       *
72       * @param currency  the currency, not null
73       * @param amount  the amount of money, not null
74       * @return the new instance, never null
75       * @throws IllegalArgumentException if an invalid BigDecimal subclass has been used
76       */
77      public static BigMoney of(CurrencyUnit currency, BigDecimal amount) {
78          MoneyUtils.checkNotNull(currency, "Currency must not be null");
79          MoneyUtils.checkNotNull(amount, "Amount must not be null");
80          if (amount.getClass() != BigDecimal.class) {
81              BigInteger value = amount.unscaledValue();
82              if (value == null) {
83                  throw new IllegalArgumentException("Illegal BigDecimal subclass");
84              }
85              if (value.getClass() != BigInteger.class) {
86                  value = new BigInteger(value.toString());
87              }
88              amount = new BigDecimal(value, amount.scale());
89          }
90          return new BigMoney(currency, amount);
91      }
92  
93      /**
94       * Obtains an instance of {@code BigMoney} from a {@code double} using a well-defined conversion.
95       * <p>
96       * This allows you to create an instance with a specific currency and amount.
97       * <p>
98       * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
99       * the most expected answer for most programming scenarios.
100      * Any {@code double} literal in code will be converted to
101      * exactly the same BigDecimal with the same scale.
102      * For example, the literal '1.425d' will be converted to '1.425'.
103      * The scale of the money will be that of the BigDecimal produced.
104      *
105      * @param currency  the currency, not null
106      * @param amount  the amount of money, not null
107      * @return the new instance, never null
108      */
109     public static BigMoney of(CurrencyUnit currency, double amount) {
110         MoneyUtils.checkNotNull(currency, "Currency must not be null");
111         return BigMoney.of(currency, BigDecimal.valueOf(amount));
112     }
113 
114     //-----------------------------------------------------------------------
115     /**
116      * Obtains an instance of {@code BigMoney} from a {@code BigDecimal} at a specific scale.
117      * <p>
118      * This allows you to create an instance with a specific currency and amount.
119      * No rounding is performed on the amount, so it must have a
120      * scale less than or equal to the new scale.
121      *
122      * @param currency  the currency, not null
123      * @param amount  the amount of money, not null
124      * @param scale  the scale to use, zero or positive
125      * @return the new instance, never null
126      * @throws ArithmeticException if the scale exceeds the currency scale
127      */
128     public static BigMoney ofScale(CurrencyUnit currency, BigDecimal amount, int scale) {
129         return BigMoney.ofScale(currency, amount, scale, RoundingMode.UNNECESSARY);
130     }
131 
132     /**
133      * Obtains an instance of {@code BigMoney} from a {@code double} using a
134      * well-defined conversion, rounding as necessary.
135      * <p>
136      * This allows you to create an instance with a specific currency and amount.
137      * If the amount has a scale in excess of the scale of the currency then the excess
138      * fractional digits are rounded using the rounding mode.
139      *
140      * @param currency  the currency, not null
141      * @param amount  the amount of money, not null
142      * @param scale  the scale to use, zero or positive
143      * @param roundingMode  the rounding mode to use, not null
144      * @return the new instance, never null
145      * @throws ArithmeticException if the rounding fails
146      */
147     public static BigMoney ofScale(CurrencyUnit currency, BigDecimal amount, int scale, RoundingMode roundingMode) {
148         MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
149         MoneyUtils.checkNotNull(amount, "Amount must not be null");
150         MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
151         amount = amount.setScale(scale, roundingMode);
152         return BigMoney.of(currency, amount);
153     }
154 
155     /**
156      * Obtains an instance of {@code BigMoney} from a scaled amount.
157      * <p>
158      * This allows you to create an instance with a specific currency, amount and scale.
159      * The amount is defined in terms of the specified scale.
160      * <p>
161      * For example, {@code ofScale(USD, 234, 2)} creates the instance {@code USD 2.34}.
162      *
163      * @param currency  the currency, not null
164      * @param unscaledAmount  the unscaled amount of money
165      * @param scale  the scale to use
166      * @return the new instance, never null
167      */
168     public static BigMoney ofScale(CurrencyUnit currency, long unscaledAmount, int scale) {
169         MoneyUtils.checkNotNull(currency, "Currency must not be null");
170         return BigMoney.of(currency, BigDecimal.valueOf(unscaledAmount, scale));
171     }
172 
173     //-----------------------------------------------------------------------
174     /**
175      * Obtains an instance of {@code BigMoney} from an amount in major units.
176      * <p>
177      * This allows you to create an instance with a specific currency and amount.
178      * The scale of the money will be zero.
179      * <p>
180      * The amount is a whole number only. Thus you can initialise the value
181      * 'USD 20', but not the value 'USD 20.32'.
182      * For example, {@code ofMajor(USD, 25)} creates the instance {@code USD 25}.
183      *
184      * @param currency  the currency, not null
185      * @param amountMajor  the amount of money in the major division of the currency
186      * @return the new instance, never null
187      */
188     public static BigMoney ofMajor(CurrencyUnit currency, long amountMajor) {
189         MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
190         return BigMoney.of(currency, BigDecimal.valueOf(amountMajor));
191     }
192 
193     /**
194      * Obtains an instance of {@code BigMoney} from an amount in minor units.
195      * <p>
196      * This allows you to create an instance with a specific currency and amount
197      * expressed in terms of the minor unit.
198      * The scale of the money will be that of the currency, such as 2 for USD or 0 for JPY.
199      * <p>
200      * For example, if constructing US Dollars, the input to this method represents cents.
201      * Note that when a currency has zero decimal places, the major and minor units are the same.
202      * For example, {@code ofMajor(USD, 2595)} creates the instance {@code USD 25.95}.
203      *
204      * @param currency  the currency, not null
205      * @param amountMinor  the amount of money in the minor division of the currency
206      * @return the new instance, never null
207      */
208     public static BigMoney ofMinor(CurrencyUnit currency, long amountMinor) {
209         MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
210         return BigMoney.of(currency, BigDecimal.valueOf(amountMinor, currency.getDecimalPlaces()));
211     }
212 
213     //-----------------------------------------------------------------------
214     /**
215      * Obtains an instance of {@code BigMoney} representing zero.
216      * <p>
217      * The scale of the money will be zero.
218      * For example, {@code zero(USD)} creates the instance {@code USD 0}.
219      *
220      * @param currency  the currency, not null
221      * @return the instance representing zero, never null
222      */
223     public static BigMoney zero(CurrencyUnit currency) {
224         return BigMoney.of(currency, BigDecimal.ZERO);
225     }
226 
227     /**
228      * Obtains an instance of {@code BigMoney} representing zero at a specific scale.
229      * <p>
230      * For example, {@code zero(USD, 2)} creates the instance {@code USD 0.00}.
231      *
232      * @param currency  the currency, not null
233      * @param scale  the scale to use, zero or positive
234      * @return the instance representing zero, never null
235      * @throws IllegalArgumentException if the scale is negative
236      */
237     public static BigMoney zero(CurrencyUnit currency, int scale) {
238         return BigMoney.of(currency, BigDecimal.valueOf(0, scale));
239     }
240 
241     //-----------------------------------------------------------------------
242     /**
243      * Obtains an instance of {@code BigMoney} from a provider.
244      * <p>
245      * This allows you to create an instance from any class that implements the
246      * provider, such as {@code Money}.
247      * This method simply calls {@link BigMoneyProvider#toBigMoney()} checking for nulls.
248      *
249      * @param moneyProvider  the money to convert, not null
250      * @return the new instance, never null
251      */
252     public static BigMoney of(BigMoneyProvider moneyProvider) {
253         MoneyUtils.checkNotNull(moneyProvider, "BigMoneyProvider must not be null");
254         BigMoney money = moneyProvider.toBigMoney();
255         MoneyUtils.checkNotNull(money, "BigMoneyProvider must not return null");
256         return money;
257     }
258 
259     //-----------------------------------------------------------------------
260     /**
261      * Obtains an instance of {@code BigMoney} as the total value of an array.
262      * <p>
263      * The array must contain at least one monetary value.
264      * Subsequent amounts are added as though using {@link #plus(BigMoneyProvider)}.
265      * All amounts must be in the same currency.
266      * 
267      * @param monies  the monetary values to total, not empty, no null elements, not null
268      * @return the total, never null
269      * @throws IllegalArgumentException if the array is empty
270      * @throws CurrencyMismatchException if the currencies differ
271      */
272     public static BigMoney total(BigMoneyProvider... monies) {
273         MoneyUtils.checkNotNull(monies, "Money array must not be null");
274         if (monies.length == 0) {
275             throw new IllegalArgumentException("Money array must not be empty");
276         }
277         BigMoney total = of(monies[0]);
278         MoneyUtils.checkNotNull(total, "Money arary must not contain null entries");
279         for (int i = 1; i < monies.length; i++) {
280             total = total.plus(of(monies[i]));
281         }
282         return total;
283     }
284 
285     /**
286      * Obtains an instance of {@code BigMoney} as the total value of a collection.
287      * <p>
288      * The iterable must provide at least one monetary value.
289      * Subsequent amounts are added as though using {@link #plus(BigMoneyProvider)}.
290      * All amounts must be in the same currency.
291      * 
292      * @param monies  the monetary values to total, not empty, no null elements, not null
293      * @return the total, never null
294      * @throws IllegalArgumentException if the iterable is empty
295      * @throws CurrencyMismatchException if the currencies differ
296      */
297     public static BigMoney total(Iterable<? extends BigMoneyProvider> monies) {
298         MoneyUtils.checkNotNull(monies, "Money iterator must not be null");
299         Iterator<? extends BigMoneyProvider> it = monies.iterator();
300         if (it.hasNext() == false) {
301             throw new IllegalArgumentException("Money iterator must not be empty");
302         }
303         BigMoney total = of(it.next());
304         MoneyUtils.checkNotNull(total, "Money iterator must not contain null entries");
305         while (it.hasNext()) {
306             total = total.plus(it.next());
307         }
308         return total;
309     }
310 
311     /**
312      * Obtains an instance of {@code Money} as the total value of
313      * a possibly empty array.
314      * <p>
315      * The amounts are added as though using {@link #plus(BigMoneyProvider)} starting
316      * from zero in the specified currency.
317      * All amounts must be in the same currency.
318      * 
319      * @param currency  the currency to total in, not null
320      * @param monies  the monetary values to total, no null elements, not null
321      * @return the total, never null
322      * @throws CurrencyMismatchException if the currencies differ
323      */
324     public static BigMoney total(CurrencyUnit currency, BigMoneyProvider... monies) {
325         return BigMoney.zero(currency).plus(Arrays.asList(monies));
326     }
327 
328     /**
329      * Obtains an instance of {@code Money} as the total value of
330      * a possibly empty collection.
331      * <p>
332      * The amounts are added as though using {@link #plus(BigMoneyProvider)} starting
333      * from zero in the specified currency.
334      * All amounts must be in the same currency.
335      * 
336      * @param currency  the currency to total in, not null
337      * @param monies  the monetary values to total, no null elements, not null
338      * @return the total, never null
339      * @throws CurrencyMismatchException if the currencies differ
340      */
341     public static BigMoney total(CurrencyUnit currency, Iterable<? extends BigMoneyProvider> monies) {
342         return BigMoney.zero(currency).plus(monies);
343     }
344 
345     //-----------------------------------------------------------------------
346     /**
347      * Parses an instance of {@code BigMoney} from a string.
348      * <p>
349      * The string format is '<currencyCode> <amount>'.
350      * The currency code must be a valid three letter currency.
351      * The amount must match the regular expression {@code [+-]?[0-9]*[.]?[0-9]*}.
352      * This matches the output from {@link #toString()}.
353      * <p>
354      * For example, {@code parse("USD 25")} creates the instance {@code USD 25}
355      * while {@code parse("USD 25.95")} creates the instance {@code USD 25.95}.
356      *
357      * @param moneyStr  the money string to parse, not null
358      * @return the parsed instance, never null
359      * @throws IllegalArgumentException if the string is malformed
360      * @throws ArithmeticException if the amount is too large
361      */
362     @FromString
363     public static BigMoney parse(String moneyStr) {
364         MoneyUtils.checkNotNull(moneyStr, "Money must not be null");
365         if (moneyStr.length() < 5 || moneyStr.charAt(3) != ' ') {
366             throw new IllegalArgumentException("Money '" + moneyStr + "' cannot be parsed");
367         }
368         String currStr = moneyStr.substring(0, 3);
369         String amountStr = moneyStr.substring(4);
370         if (PARSE_REGEX.matcher(amountStr).matches() == false) {
371             throw new IllegalArgumentException("Money amount '" + moneyStr + "' cannot be parsed");
372         }
373         return BigMoney.of(CurrencyUnit.of(currStr), new BigDecimal(amountStr));
374     }
375 
376     //-----------------------------------------------------------------------
377     /**
378      * Ensures that a {@code BigMoney} is not {@code null}.
379      * <p>
380      * If the input money is not {@code null}, then it is returned, providing
381      * that the currency matches the specified currency.
382      * If the input money is {@code null}, then zero money in the currency
383      * is returned with a scale of zero.
384      * 
385      * @param money  the monetary value to check, may be null
386      * @param currency  the currency to use, not null
387      * @return the input money or zero in the specified currency, never null
388      * @throws CurrencyMismatchException if the input money is non-null and the currencies differ
389      */
390     public static BigMoney nonNull(BigMoney money, CurrencyUnit currency) {
391         if (money == null) {
392             return zero(currency);
393         }
394         if (money.getCurrencyUnit().equals(currency) == false) {
395             MoneyUtils.checkNotNull(currency, "Currency must not be null");
396             throw new CurrencyMismatchException(money.getCurrencyUnit(), currency);
397         }
398         return money;
399     }
400 
401     //-----------------------------------------------------------------------
402     /**
403      * Constructor, creating a new monetary instance.
404      * 
405      * @param currency  the currency to use, not null
406      * @param amount  the amount of money, not null
407      */
408     BigMoney(CurrencyUnit currency, BigDecimal amount) {
409         assert currency != null : "Joda-Money bug: Currency must not be null";
410         assert amount != null : "Joda-Money bug: Amount must not be null";
411         this.currency = currency;
412         this.amount = amount;
413     }
414 
415     /**
416      * Block malicious data streams.
417      * 
418      * @param ois  the input stream, not null
419      * @throws InvalidObjectException
420      */
421     private void readObject(ObjectInputStream ois) throws InvalidObjectException {
422         throw new InvalidObjectException("Serialization delegate required");
423     }
424 
425     /**
426      * Uses a serialization delegate.
427      * 
428      * @return the replacing object, never null
429      */
430     private Object writeReplace() {
431         return new Ser(Ser.BIG_MONEY, this);
432     }
433 
434     //-----------------------------------------------------------------------
435     /**
436      * Returns a new {@code BigMoney}, returning {@code this} if possible.
437      * <p>
438      * This instance is immutable and unaffected by this method.
439      * 
440      * @param newAmount  the new amount to use, not null
441      * @return the new instance, never null
442      */
443     private BigMoney with(BigDecimal newAmount) {
444         if (newAmount == amount) {
445             return this;
446         }
447         return new BigMoney(currency, newAmount);
448     }
449 
450     //-----------------------------------------------------------------------
451     /**
452      * Gets the currency.
453      * 
454      * @return the currency, never null
455      */
456     public CurrencyUnit getCurrencyUnit() {
457         return currency;
458     }
459 
460     //-----------------------------------------------------------------------
461     /**
462      * Returns a copy of this monetary value with the specified currency.
463      * <p>
464      * The returned instance will have the specified currency and the amount
465      * from this instance. No currency conversion or alteration to the scale occurs.
466      * <p>
467      * This instance is immutable and unaffected by this method.
468      * 
469      * @param currency  the currency to use, not null
470      * @return the new instance with the input currency set, never null
471      */
472     public BigMoney withCurrencyUnit(CurrencyUnit currency) {
473         MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
474         if (this.currency == currency) {
475             return this;
476         }
477         return new BigMoney(currency, amount);
478     }
479 
480     //-----------------------------------------------------------------------
481     /**
482      * Gets the scale of the {@code BigDecimal} amount.
483      * <p>
484      * The scale has the same meaning as in {@link BigDecimal}.
485      * Positive values represent the number of decimal places in use.
486      * Negative numbers represent the opposite.
487      * For example, a scale of 2 means that the money will have two decimal places
488      * such as 'USD 43.25'. Whereas, a scale of -3 means that only thousands can be
489      * represented, such as 'GBP 124000'.
490      * 
491      * @return the scale in use
492      * @see #withScale
493      */
494     public int getScale() {
495         return amount.scale();
496     }
497 
498     /**
499      * Checks if this money has the scale of the currency.
500      * <p>
501      * Each currency has a default scale, such as 2 for USD and 0 for JPY.
502      * This method checks if the current scale matches the default scale.
503      * 
504      * @return true if the scale equals the current default scale
505      */
506     public boolean isCurrencyScale() {
507         return amount.scale() == currency.getDecimalPlaces();
508     }
509 
510     //-----------------------------------------------------------------------
511     /**
512      * Returns a copy of this monetary value with the specified scale,
513      * truncating the amount if necessary.
514      * <p>
515      * The returned instance will have this currency and the new scaled amount.
516      * For example, scaling 'USD 43.271' to a scale of 1 will yield 'USD 43.2'.
517      * No rounding is performed on the amount, so it must have a
518      * scale less than or equal to the new scale.
519      * <p>
520      * This instance is immutable and unaffected by this method.
521      * 
522      * @param scale  the scale to use
523      * @return the new instance with the input amount set, never null
524      * @throws ArithmeticException if the rounding fails
525      */
526     public BigMoney withScale(int scale) {
527         return withScale(scale, RoundingMode.UNNECESSARY);
528     }
529 
530     /**
531      * Returns a copy of this monetary value with the specified scale,
532      * using the specified rounding mode if necessary.
533      * <p>
534      * The returned instance will have this currency and the new scaled amount.
535      * For example, scaling 'USD 43.271' to a scale of 1 with HALF_EVEN rounding
536      * will yield 'USD 43.3'.
537      * <p>
538      * This instance is immutable and unaffected by this method.
539      * 
540      * @param scale  the scale to use
541      * @param roundingMode  the rounding mode to use, not null
542      * @return the new instance with the input amount set, never null
543      * @throws ArithmeticException if the rounding fails
544      */
545     public BigMoney withScale(int scale, RoundingMode roundingMode) {
546         MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
547         if (scale == amount.scale()) {
548             return this;
549         }
550         return BigMoney.of(currency, amount.setScale(scale, roundingMode));
551     }
552 
553     //-----------------------------------------------------------------------
554     /**
555      * Returns a copy of this monetary value with the scale of the currency,
556      * truncating the amount if necessary.
557      * <p>
558      * The returned instance will have this currency and the new scaled amount.
559      * For example, scaling 'USD 43.271' will yield 'USD 43.27' as USD has a scale of 2.
560      * No rounding is performed on the amount, so it must have a
561      * scale less than or equal to the new scale.
562      * <p>
563      * This instance is immutable and unaffected by this method.
564      * 
565      * @return the new instance with the input amount set, never null
566      * @throws ArithmeticException if the rounding fails
567      */
568     public BigMoney withCurrencyScale() {
569         return withScale(currency.getDecimalPlaces(), RoundingMode.UNNECESSARY);
570     }
571 
572     /**
573      * Returns a copy of this monetary value with the scale of the currency,
574      * using the specified rounding mode if necessary.
575      * <p>
576      * The returned instance will have this currency and the new scaled amount.
577      * For example, scaling 'USD 43.271' will yield 'USD 43.27' as USD has a scale of 2.
578      * <p>
579      * This instance is immutable and unaffected by this method.
580      * 
581      * @param roundingMode  the rounding mode to use, not null
582      * @return the new instance with the input amount set, never null
583      * @throws ArithmeticException if the rounding fails
584      */
585     public BigMoney withCurrencyScale(RoundingMode roundingMode) {
586         return withScale(currency.getDecimalPlaces(), roundingMode);
587     }
588 
589     //-----------------------------------------------------------------------
590     /**
591      * Gets the amount.
592      * <p>
593      * This returns the value of the money as a {@code BigDecimal}.
594      * The scale will be the scale of this money.
595      * 
596      * @return the amount, never null
597      */
598     public BigDecimal getAmount() {
599         return amount;
600     }
601 
602     /**
603      * Gets the amount in major units as a {@code BigDecimal} with scale 0.
604      * <p>
605      * This returns the monetary amount in terms of the major units of the currency,
606      * truncating the amount if necessary.
607      * For example, 'EUR 2.35' will return 2, and 'BHD -1.345' will return -1.
608      * <p>
609      * This is returned as a {@code BigDecimal} rather than a {@code BigInteger}.
610      * This is to allow further calculations to be performed on the result.
611      * Should you need a {@code BigInteger}, simply call {@link BigDecimal#toBigInteger()}.
612      * 
613      * @return the major units part of the amount, never null
614      */
615     public BigDecimal getAmountMajor() {
616         return amount.setScale(0, RoundingMode.DOWN);
617     }
618 
619     /**
620      * Gets the amount in major units as a {@code long}.
621      * <p>
622      * This returns the monetary amount in terms of the major units of the currency,
623      * truncating the amount if necessary.
624      * For example, 'EUR 2.35' will return 2, and 'BHD -1.345' will return -1.
625      * 
626      * @return the major units part of the amount
627      * @throws ArithmeticException if the amount is too large for a {@code long}
628      */
629     public long getAmountMajorLong() {
630         return getAmountMajor().longValueExact();
631     }
632 
633     /**
634      * Gets the amount in major units as an {@code int}.
635      * <p>
636      * This returns the monetary amount in terms of the major units of the currency,
637      * truncating the amount if necessary.
638      * For example, 'EUR 2.35' will return 2, and 'BHD -1.345' will return -1.
639      * 
640      * @return the major units part of the amount
641      * @throws ArithmeticException if the amount is too large for an {@code int}
642      */
643     public int getAmountMajorInt() {
644         return getAmountMajor().intValueExact();
645     }
646 
647     /**
648      * Gets the amount in minor units as a {@code BigDecimal} with scale 0.
649      * <p>
650      * This returns the monetary amount in terms of the minor units of the currency,
651      * truncating the amount if necessary.
652      * For example, 'EUR 2.35' will return 235, and 'BHD -1.345' will return -1345.
653      * <p>
654      * This is returned as a {@code BigDecimal} rather than a {@code BigInteger}.
655      * This is to allow further calculations to be performed on the result.
656      * Should you need a {@code BigInteger}, simply call {@link BigDecimal#toBigInteger()}.
657      * 
658      * @return the minor units part of the amount, never null
659      */
660     public BigDecimal getAmountMinor() {
661         int cdp = getCurrencyUnit().getDecimalPlaces();
662         return amount.setScale(cdp, RoundingMode.DOWN).movePointRight(cdp);
663     }
664 
665     /**
666      * Gets the amount in minor units as a {@code long}.
667      * <p>
668      * This returns the monetary amount in terms of the minor units of the currency,
669      * truncating the amount if necessary.
670      * For example, 'EUR 2.35' will return 235, and 'BHD -1.345' will return -1345.
671      * 
672      * @return the minor units part of the amount
673      * @throws ArithmeticException if the amount is too large for a {@code long}
674      */
675     public long getAmountMinorLong() {
676         return getAmountMinor().longValueExact();
677     }
678 
679     /**
680      * Gets the amount in minor units as an {@code int}.
681      * <p>
682      * This returns the monetary amount in terms of the minor units of the currency,
683      * truncating the amount if necessary.
684      * For example, 'EUR 2.35' will return 235, and 'BHD -1.345' will return -1345.
685      * 
686      * @return the minor units part of the amount
687      * @throws ArithmeticException if the amount is too large for an {@code int}
688      */
689     public int getAmountMinorInt() {
690         return getAmountMinor().intValueExact();
691     }
692 
693     /**
694      * Gets the minor part of the amount.
695      * <p>
696      * This return the minor unit part of the monetary amount.
697      * This is defined as the amount in minor units excluding major units.
698      * <p>
699      * For example, EUR has a scale of 2, so the minor part is always between 0 and 99
700      * for positive amounts, and 0 and -99 for negative amounts.
701      * Thus 'EUR 2.35' will return 35, and 'EUR -1.34' will return -34.
702      * 
703      * @return the minor part of the amount, negative if the amount is negative
704      */
705     public int getMinorPart() {
706         int cdp = getCurrencyUnit().getDecimalPlaces();
707         return amount.setScale(cdp, RoundingMode.DOWN)
708                     .remainder(BigDecimal.ONE)
709                     .movePointRight(cdp).intValueExact();
710     }
711 
712     //-----------------------------------------------------------------------
713     /**
714      * Checks if the amount is zero.
715      * 
716      * @return true if the amount is zero
717      */
718     public boolean isZero() {
719         return amount.compareTo(BigDecimal.ZERO) == 0;
720     }
721 
722     /**
723      * Checks if the amount is greater than zero.
724      * 
725      * @return true if the amount is greater than zero
726      */
727     public boolean isPositive() {
728         return amount.compareTo(BigDecimal.ZERO) > 0;
729     }
730 
731     /**
732      * Checks if the amount is zero or greater.
733      * 
734      * @return true if the amount is zero or greater
735      */
736     public boolean isPositiveOrZero() {
737         return amount.compareTo(BigDecimal.ZERO) >= 0;
738     }
739 
740     /**
741      * Checks if the amount is less than zero.
742      * 
743      * @return true if the amount is less than zero
744      */
745     public boolean isNegative() {
746         return amount.compareTo(BigDecimal.ZERO) < 0;
747     }
748 
749     /**
750      * Checks if the amount is zero or less.
751      * 
752      * @return true if the amount is zero or less
753      */
754     public boolean isNegativeOrZero() {
755         return amount.compareTo(BigDecimal.ZERO) <= 0;
756     }
757 
758     //-----------------------------------------------------------------------
759     /**
760      * Returns a copy of this monetary value with the specified amount.
761      * <p>
762      * The returned instance will have this currency and the new amount.
763      * The scale of the returned instance will be that of the specified BigDecimal.
764      * <p>
765      * This instance is immutable and unaffected by this method.
766      * 
767      * @param amount  the monetary amount to set in the returned instance, not null
768      * @return the new instance with the input amount set, never null
769      */
770     public BigMoney withAmount(BigDecimal amount) {
771         MoneyUtils.checkNotNull(amount, "Amount must not be null");
772         if (this.amount.equals(amount)) {
773             return this;
774         }
775         return BigMoney.of(currency, amount);
776     }
777 
778     /**
779      * Returns a copy of this monetary value with the specified amount using a well-defined
780      * conversion from a {@code double}.
781      * <p>
782      * The returned instance will have this currency and the new amount.
783      * <p>
784      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
785      * the most expected answer for most programming scenarios.
786      * Any {@code double} literal in code will be converted to
787      * exactly the same BigDecimal with the same scale.
788      * For example, the literal '1.425d' will be converted to '1.425'.
789      * The scale of the money will be that of the BigDecimal produced.
790      * <p>
791      * This instance is immutable and unaffected by this method.
792      * 
793      * @param amount  the monetary amount to set in the returned instance
794      * @return the new instance with the input amount set, never null
795      */
796     public BigMoney withAmount(double amount) {
797         return withAmount(BigDecimal.valueOf(amount));
798     }
799 
800     //-----------------------------------------------------------------------
801     /**
802      * Validates that the currency of this money and the specified money match.
803      * 
804      * @param moneyProvider  the money to check, not null
805      * @throws CurrencyMismatchException if the currencies differ
806      */
807     private BigMoney checkCurrencyEqual(BigMoneyProvider moneyProvider) {
808         BigMoney money = of(moneyProvider);
809         if (isSameCurrency(money) == false) {
810             throw new CurrencyMismatchException(getCurrencyUnit(), money.getCurrencyUnit());
811         }
812         return money;
813     }
814 
815     //-----------------------------------------------------------------------
816     /**
817      * Returns a copy of this monetary value with a collection of monetary amounts added.
818      * <p>
819      * This adds the specified amounts to this monetary amount, returning a new object.
820      * The amounts are added as though using {@link #plus(BigMoneyProvider)}.
821      * The amounts must be in the same currency.
822      * <p>
823      * This instance is immutable and unaffected by this method.
824      * 
825      * @param moniesToAdd  the monetary values to add, no null elements, not null
826      * @return the new instance with the input amounts added, never null
827      * @throws CurrencyMismatchException if the currencies differ
828      */
829     public BigMoney plus(Iterable<? extends BigMoneyProvider> moniesToAdd) {
830         BigDecimal total = amount;
831         for (BigMoneyProvider moneyProvider : moniesToAdd) {
832             BigMoney money = checkCurrencyEqual(moneyProvider);
833             total = total.add(money.amount);
834         }
835         return with(total);
836     }
837 
838     //-----------------------------------------------------------------------
839     /**
840      * Returns a copy of this monetary value with the amount added.
841      * <p>
842      * This adds the specified amount to this monetary amount, returning a new object.
843      * The amount added must be in the same currency.
844      * <p>
845      * No precision is lost in the result.
846      * The scale of the result will be the maximum of the two scales.
847      * For example, 'USD 25.95' plus 'USD 3.021' gives 'USD 28.971'.
848      * <p>
849      * This instance is immutable and unaffected by this method.
850      * 
851      * @param moneyToAdd  the monetary value to add, not null
852      * @return the new instance with the input amount added, never null
853      * @throws CurrencyMismatchException if the currencies differ
854      */
855     public BigMoney plus(BigMoneyProvider moneyToAdd) {
856         BigMoney toAdd = checkCurrencyEqual(moneyToAdd);
857         return plus(toAdd.getAmount());
858     }
859 
860     /**
861      * Returns a copy of this monetary value with the amount added.
862      * <p>
863      * This adds the specified amount to this monetary amount, returning a new object.
864      * <p>
865      * No precision is lost in the result.
866      * The scale of the result will be the maximum of the two scales.
867      * For example, 'USD 25.95' plus '3.021' gives 'USD 28.971'.
868      * <p>
869      * This instance is immutable and unaffected by this method.
870      * 
871      * @param amountToAdd  the monetary value to add, not null
872      * @return the new instance with the input amount added, never null
873      */
874     public BigMoney plus(BigDecimal amountToAdd) {
875         MoneyUtils.checkNotNull(amountToAdd, "Amount must not be null");
876         if (amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
877             return this;
878         }
879         BigDecimal newAmount = amount.add(amountToAdd);
880         return BigMoney.of(currency, newAmount);
881     }
882 
883     /**
884      * Returns a copy of this monetary value with the amount added.
885      * <p>
886      * This adds the specified amount to this monetary amount, returning a new object.
887      * <p>
888      * No precision is lost in the result.
889      * The scale of the result will be the maximum of the two scales.
890      * For example, 'USD 25.95' plus '3.021d' gives 'USD 28.971'.
891      * <p>
892      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
893      * the most expected answer for most programming scenarios.
894      * Any {@code double} literal in code will be converted to
895      * exactly the same BigDecimal with the same scale.
896      * For example, the literal '1.45d' will be converted to '1.45'.
897      * <p>
898      * This instance is immutable and unaffected by this method.
899      * 
900      * @param amountToAdd  the monetary value to add, not null
901      * @return the new instance with the input amount added, never null
902      */
903     public BigMoney plus(double amountToAdd) {
904         if (amountToAdd == 0) {
905             return this;
906         }
907         BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd));
908         return BigMoney.of(currency, newAmount);
909     }
910 
911     /**
912      * Returns a copy of this monetary value with the amount in major units added.
913      * <p>
914      * This adds the specified amount in major units to this monetary amount,
915      * returning a new object. The minor units will be untouched in the result.
916      * <p>
917      * No precision is lost in the result.
918      * The scale of the result will be the maximum of the current scale and 0.
919      * For example, 'USD 23.45' plus '138' gives 'USD 161.45'.
920      * <p>
921      * This instance is immutable and unaffected by this method.
922      * 
923      * @param amountToAdd  the monetary value to add, not null
924      * @return the new instance with the input amount added, never null
925      */
926     public BigMoney plusMajor(long amountToAdd) {
927         if (amountToAdd == 0) {
928             return this;
929         }
930         BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd));
931         return BigMoney.of(currency, newAmount);
932     }
933 
934     /**
935      * Returns a copy of this monetary value with the amount in minor units added.
936      * <p>
937      * This adds the specified amount in minor units to this monetary amount,
938      * returning a new object.
939      * <p>
940      * No precision is lost in the result.
941      * The scale of the result will be the maximum of the current scale and the default currency scale.
942      * For example, 'USD 23.45' plus '138' gives 'USD 24.83'.
943      * <p>
944      * This instance is immutable and unaffected by this method.
945      * 
946      * @param amountToAdd  the monetary value to add, not null
947      * @return the new instance with the input amount added, never null
948      */
949     public BigMoney plusMinor(long amountToAdd) {
950         if (amountToAdd == 0) {
951             return this;
952         }
953         BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd, currency.getDecimalPlaces()));
954         return BigMoney.of(currency, newAmount);
955     }
956 
957     //-----------------------------------------------------------------------
958     /**
959      * Returns a copy of this monetary value with the amount in the same currency added
960      * retaining the scale by rounding the result.
961      * <p>
962      * The scale of the result will be the same as the scale of this instance.
963      * For example,'USD 25.95' plus 'USD 3.021' gives 'USD 28.97' with most rounding modes.
964      * <p>
965      * This instance is immutable and unaffected by this method.
966      * 
967      * @param moneyToAdd  the monetary value to add, not null
968      * @param roundingMode  the rounding mode to use to adjust the scale, not null
969      * @return the new instance with the input amount added, never null
970      */
971     public BigMoney plusRetainScale(BigMoneyProvider moneyToAdd, RoundingMode roundingMode) {
972         BigMoney toAdd = checkCurrencyEqual(moneyToAdd);
973         return plusRetainScale(toAdd.getAmount(), roundingMode);
974     }
975 
976     /**
977      * Returns a copy of this monetary value with the amount added retaining
978      * the scale by rounding the result.
979      * <p>
980      * The scale of the result will be the same as the scale of this instance.
981      * For example,'USD 25.95' plus '3.021' gives 'USD 28.97' with most rounding modes.
982      * <p>
983      * This instance is immutable and unaffected by this method.
984      * 
985      * @param amountToAdd  the monetary value to add, not null
986      * @param roundingMode  the rounding mode to use to adjust the scale, not null
987      * @return the new instance with the input amount added, never null
988      */
989     public BigMoney plusRetainScale(BigDecimal amountToAdd, RoundingMode roundingMode) {
990         MoneyUtils.checkNotNull(amountToAdd, "Amount must not be null");
991         if (amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
992             return this;
993         }
994         BigDecimal newAmount = amount.add(amountToAdd);
995         newAmount = newAmount.setScale(getScale(), roundingMode);
996         return BigMoney.of(currency, newAmount);
997     }
998 
999     /**
1000      * Returns a copy of this monetary value with the amount added retaining
1001      * the scale by rounding the result.
1002      * <p>
1003      * The scale of the result will be the same as the scale of this instance.
1004      * For example,'USD 25.95' plus '3.021d' gives 'USD 28.97' with most rounding modes.
1005      * <p>
1006      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1007      * the most expected answer for most programming scenarios.
1008      * Any {@code double} literal in code will be converted to
1009      * exactly the same BigDecimal with the same scale.
1010      * For example, the literal '1.45d' will be converted to '1.45'.
1011      * <p>
1012      * This instance is immutable and unaffected by this method.
1013      * 
1014      * @param amountToAdd  the monetary value to add, not null
1015      * @param roundingMode  the rounding mode to use to adjust the scale, not null
1016      * @return the new instance with the input amount added, never null
1017      */
1018     public BigMoney plusRetainScale(double amountToAdd, RoundingMode roundingMode) {
1019         if (amountToAdd == 0) {
1020             return this;
1021         }
1022         BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd));
1023         newAmount = newAmount.setScale(getScale(), roundingMode);
1024         return BigMoney.of(currency, newAmount);
1025     }
1026 
1027     //-----------------------------------------------------------------------
1028     /**
1029      * Returns a copy of this monetary value with a collection of monetary amounts subtracted.
1030      * <p>
1031      * This subtracts the specified amounts from this monetary amount, returning a new object.
1032      * The amounts are subtracted one by one as though using {@link #minus(BigMoneyProvider)}.
1033      * The amounts must be in the same currency.
1034      * <p>
1035      * This instance is immutable and unaffected by this method.
1036      * 
1037      * @param moniesToSubtract  the monetary values to subtract, no null elements, not null
1038      * @return the new instance with the input amounts subtracted, never null
1039      * @throws CurrencyMismatchException if the currencies differ
1040      */
1041     public BigMoney minus(Iterable<? extends BigMoneyProvider> moniesToSubtract) {
1042         BigDecimal total = amount;
1043         for (BigMoneyProvider moneyProvider : moniesToSubtract) {
1044             BigMoney money = checkCurrencyEqual(moneyProvider);
1045             total = total.subtract(money.amount);
1046         }
1047         return with(total);
1048     }
1049 
1050     //-----------------------------------------------------------------------
1051     /**
1052      * Returns a copy of this monetary value with the amount subtracted.
1053      * <p>
1054      * This subtracts the specified amount from this monetary amount, returning a new object.
1055      * The amount subtracted must be in the same currency.
1056      * <p>
1057      * No precision is lost in the result.
1058      * The scale of the result will be the maximum of the two scales.
1059      * For example,'USD 25.95' minus 'USD 3.021' gives 'USD 22.929'.
1060      * <p>
1061      * This instance is immutable and unaffected by this method.
1062      * 
1063      * @param moneyToSubtract  the monetary value to subtract, not null
1064      * @return the new instance with the input amount subtracted, never null
1065      * @throws CurrencyMismatchException if the currencies differ
1066      */
1067     public BigMoney minus(BigMoneyProvider moneyToSubtract) {
1068         BigMoney toSubtract = checkCurrencyEqual(moneyToSubtract);
1069         return minus(toSubtract.getAmount());
1070     }
1071 
1072     /**
1073      * Returns a copy of this monetary value with the amount subtracted.
1074      * <p>
1075      * This subtracts the specified amount from this monetary amount, returning a new object.
1076      * <p>
1077      * No precision is lost in the result.
1078      * The scale of the result will be the maximum of the two scales.
1079      * For example,'USD 25.95' minus '3.021' gives 'USD 22.929'.
1080      * <p>
1081      * This instance is immutable and unaffected by this method.
1082      * 
1083      * @param amountToSubtract  the monetary value to subtract, not null
1084      * @return the new instance with the input amount subtracted, never null
1085      */
1086     public BigMoney minus(BigDecimal amountToSubtract) {
1087         MoneyUtils.checkNotNull(amountToSubtract, "Amount must not be null");
1088         if (amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
1089             return this;
1090         }
1091         BigDecimal newAmount = amount.subtract(amountToSubtract);
1092         return BigMoney.of(currency, newAmount);
1093     }
1094 
1095     /**
1096      * Returns a copy of this monetary value with the amount subtracted.
1097      * <p>
1098      * This subtracts the specified amount from this monetary amount, returning a new object.
1099      * <p>
1100      * No precision is lost in the result.
1101      * The scale of the result will be the maximum of the two scales.
1102      * For example,'USD 25.95' minus '3.021d' gives 'USD 22.929'.
1103      * <p>
1104      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1105      * the most expected answer for most programming scenarios.
1106      * Any {@code double} literal in code will be converted to
1107      * exactly the same BigDecimal with the same scale.
1108      * For example, the literal '1.45d' will be converted to '1.45'.
1109      * <p>
1110      * This instance is immutable and unaffected by this method.
1111      * 
1112      * @param amountToSubtract  the monetary value to subtract, not null
1113      * @return the new instance with the input amount subtracted, never null
1114      */
1115     public BigMoney minus(double amountToSubtract) {
1116         if (amountToSubtract == 0) {
1117             return this;
1118         }
1119         BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract));
1120         return BigMoney.of(currency, newAmount);
1121     }
1122 
1123     /**
1124      * Returns a copy of this monetary value with the amount in major units subtracted.
1125      * <p>
1126      * This subtracts the specified amount in major units from this monetary amount,
1127      * returning a new object. The minor units will be untouched in the result.
1128      * <p>
1129      * No precision is lost in the result.
1130      * The scale of the result will be the maximum of the current scale and 0.
1131      * For example, 'USD 23.45' minus '138' gives 'USD -114.55'.
1132      * <p>
1133      * This instance is immutable and unaffected by this method.
1134      * 
1135      * @param amountToSubtract  the monetary value to subtract, not null
1136      * @return the new instance with the input amount subtracted, never null
1137      */
1138     public BigMoney minusMajor(long amountToSubtract) {
1139         if (amountToSubtract == 0) {
1140             return this;
1141         }
1142         BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract));
1143         return BigMoney.of(currency, newAmount);
1144     }
1145 
1146     /**
1147      * Returns a copy of this monetary value with the amount in minor units subtracted.
1148      * <p>
1149      * This subtracts the specified amount in minor units from this monetary amount,
1150      * returning a new object.
1151      * <p>
1152      * No precision is lost in the result.
1153      * The scale of the result will be the maximum of the current scale and the default currency scale.
1154      * For example, USD 23.45 minus '138' gives 'USD 22.07'.
1155      * <p>
1156      * This instance is immutable and unaffected by this method.
1157      * 
1158      * @param amountToSubtract  the monetary value to subtract, not null
1159      * @return the new instance with the input amount subtracted, never null
1160      */
1161     public BigMoney minusMinor(long amountToSubtract) {
1162         if (amountToSubtract == 0) {
1163             return this;
1164         }
1165         BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract, currency.getDecimalPlaces()));
1166         return BigMoney.of(currency, newAmount);
1167     }
1168 
1169     //-----------------------------------------------------------------------
1170     /**
1171      * Returns a copy of this monetary value with the amount in the same currency subtracted
1172      * retaining the scale by rounding the result.
1173      * <p>
1174      * The scale of the result will be the same as the scale of this instance.
1175      * For example,'USD 25.95' minus 'USD 3.029' gives 'USD 22.92 with most rounding modes.
1176      * <p>
1177      * This instance is immutable and unaffected by this method.
1178      * 
1179      * @param moneyToSubtract  the monetary value to add, not null
1180      * @param roundingMode  the rounding mode to use to adjust the scale, not null
1181      * @return the new instance with the input amount subtracted, never null
1182      */
1183     public BigMoney minusRetainScale(BigMoneyProvider moneyToSubtract, RoundingMode roundingMode) {
1184         BigMoney toSubtract = checkCurrencyEqual(moneyToSubtract);
1185         return minusRetainScale(toSubtract.getAmount(), roundingMode);
1186     }
1187 
1188     /**
1189      * Returns a copy of this monetary value with the amount subtracted retaining
1190      * the scale by rounding the result.
1191      * <p>
1192      * The scale of the result will be the same as the scale of this instance.
1193      * For example,'USD 25.95' minus '3.029' gives 'USD 22.92' with most rounding modes.
1194      * <p>
1195      * This instance is immutable and unaffected by this method.
1196      * 
1197      * @param amountToSubtract  the monetary value to add, not null
1198      * @param roundingMode  the rounding mode to use to adjust the scale, not null
1199      * @return the new instance with the input amount subtracted, never null
1200      */
1201     public BigMoney minusRetainScale(BigDecimal amountToSubtract, RoundingMode roundingMode) {
1202         MoneyUtils.checkNotNull(amountToSubtract, "Amount must not be null");
1203         if (amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
1204             return this;
1205         }
1206         BigDecimal newAmount = amount.subtract(amountToSubtract);
1207         newAmount = newAmount.setScale(getScale(), roundingMode);
1208         return BigMoney.of(currency, newAmount);
1209     }
1210 
1211     /**
1212      * Returns a copy of this monetary value with the amount subtracted retaining
1213      * the scale by rounding the result.
1214      * <p>
1215      * The scale of the result will be the same as the scale of this instance.
1216      * For example,'USD 25.95' minus '3.029d' gives 'USD 22.92' with most rounding modes.
1217      * <p>
1218      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1219      * the most expected answer for most programming scenarios.
1220      * Any {@code double} literal in code will be converted to
1221      * exactly the same BigDecimal with the same scale.
1222      * For example, the literal '1.45d' will be converted to '1.45'.
1223      * <p>
1224      * This instance is immutable and unaffected by this method.
1225      * 
1226      * @param amountToSubtract  the monetary value to add, not null
1227      * @param roundingMode  the rounding mode to use to adjust the scale, not null
1228      * @return the new instance with the input amount subtracted, never null
1229      */
1230     public BigMoney minusRetainScale(double amountToSubtract, RoundingMode roundingMode) {
1231         if (amountToSubtract == 0) {
1232             return this;
1233         }
1234         BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract));
1235         newAmount = newAmount.setScale(getScale(), roundingMode);
1236         return BigMoney.of(currency, newAmount);
1237     }
1238 
1239     //-----------------------------------------------------------------------
1240     /**
1241      * Returns a copy of this monetary value multiplied by the specified value.
1242      * <p>
1243      * No precision is lost in the result.
1244      * The result has a scale equal to the sum of the two scales.
1245      * For example, 'USD 1.13' multiplied by '2.5' gives 'USD 2.825'.
1246      * <p>
1247      * This instance is immutable and unaffected by this method.
1248      * 
1249      * @param valueToMultiplyBy  the scalar value to multiply by, not null
1250      * @return the new multiplied instance, never null
1251      */
1252     public BigMoney multipliedBy(BigDecimal valueToMultiplyBy) {
1253         MoneyUtils.checkNotNull(valueToMultiplyBy, "Multiplier must not be null");
1254         if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
1255             return this;
1256         }
1257         BigDecimal newAmount = amount.multiply(valueToMultiplyBy);
1258         return BigMoney.of(currency, newAmount);
1259     }
1260 
1261     /**
1262      * Returns a copy of this monetary value multiplied by the specified value.
1263      * <p>
1264      * No precision is lost in the result.
1265      * The result has a scale equal to the sum of the two scales.
1266      * For example, 'USD 1.13' multiplied by '2.5' gives 'USD 2.825'.
1267      * <p>
1268      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1269      * the most expected answer for most programming scenarios.
1270      * Any {@code double} literal in code will be converted to
1271      * exactly the same BigDecimal with the same scale.
1272      * For example, the literal '1.45d' will be converted to '1.45'.
1273      * <p>
1274      * This instance is immutable and unaffected by this method.
1275      * 
1276      * @param valueToMultiplyBy  the scalar value to multiply by, not null
1277      * @return the new multiplied instance, never null
1278      */
1279     public BigMoney multipliedBy(double valueToMultiplyBy) {
1280         if (valueToMultiplyBy == 1) {
1281             return this;
1282         }
1283         BigDecimal newAmount = amount.multiply(BigDecimal.valueOf(valueToMultiplyBy));
1284         return BigMoney.of(currency, newAmount);
1285     }
1286 
1287     /**
1288      * Returns a copy of this monetary value multiplied by the specified value.
1289      * <p>
1290      * No precision is lost in the result.
1291      * The result has a scale equal to the scale of this money.
1292      * For example, 'USD 1.13' multiplied by '2' gives 'USD 2.26'.
1293      * <p>
1294      * This instance is immutable and unaffected by this method.
1295      * 
1296      * @param valueToMultiplyBy  the scalar value to multiply by, not null
1297      * @return the new multiplied instance, never null
1298      */
1299     public BigMoney multipliedBy(long valueToMultiplyBy) {
1300         if (valueToMultiplyBy == 1) {
1301             return this;
1302         }
1303         BigDecimal newAmount = amount.multiply(BigDecimal.valueOf(valueToMultiplyBy));
1304         return BigMoney.of(currency, newAmount);
1305     }
1306 
1307     //-----------------------------------------------------------------------
1308     /**
1309      * Returns a copy of this monetary value multiplied by the specified value
1310      * using the specified rounding mode to adjust the scale of the result.
1311      * <p>
1312      * This multiplies this money by the specified value, retaining the scale of this money.
1313      * This will frequently lose precision, hence the need for a rounding mode.
1314      * For example, 'USD 1.13' multiplied by '2.5' and rounding down gives 'USD 2.82'.
1315      * <p>
1316      * This instance is immutable and unaffected by this method.
1317      * 
1318      * @param valueToMultiplyBy  the scalar value to multiply by, not null
1319      * @param roundingMode  the rounding mode to use to bring the decimal places back in line, not null
1320      * @return the new multiplied instance, never null
1321      * @throws ArithmeticException if the rounding fails
1322      */
1323     public BigMoney multiplyRetainScale(BigDecimal valueToMultiplyBy, RoundingMode roundingMode) {
1324         MoneyUtils.checkNotNull(valueToMultiplyBy, "Multiplier must not be null");
1325         MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1326         if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
1327             return this;
1328         }
1329         BigDecimal newAmount = amount.multiply(valueToMultiplyBy);
1330         newAmount = newAmount.setScale(getScale(), roundingMode);
1331         return BigMoney.of(currency, newAmount);
1332     }
1333 
1334     /**
1335      * Returns a copy of this monetary value multiplied by the specified value
1336      * using the specified rounding mode to adjust the scale of the result.
1337      * <p>
1338      * This multiplies this money by the specified value, retaining the scale of this money.
1339      * This will frequently lose precision, hence the need for a rounding mode.
1340      * For example, 'USD 1.13' multiplied by '2.5' and rounding down gives 'USD 2.82'.
1341      * <p>
1342      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1343      * the most expected answer for most programming scenarios.
1344      * Any {@code double} literal in code will be converted to
1345      * exactly the same BigDecimal with the same scale.
1346      * For example, the literal '1.45d' will be converted to '1.45'.
1347      * <p>
1348      * This instance is immutable and unaffected by this method.
1349      * 
1350      * @param valueToMultiplyBy  the scalar value to multiply by, not null
1351      * @param roundingMode  the rounding mode to use to bring the decimal places back in line, not null
1352      * @return the new multiplied instance, never null
1353      * @throws ArithmeticException if the rounding fails
1354      */
1355     public BigMoney multiplyRetainScale(double valueToMultiplyBy, RoundingMode roundingMode) {
1356         return multiplyRetainScale(BigDecimal.valueOf(valueToMultiplyBy), roundingMode);
1357     }
1358 
1359     //-----------------------------------------------------------------------
1360     /**
1361      * Returns a copy of this monetary value divided by the specified value
1362      * using the specified rounding mode to adjust the scale.
1363      * <p>
1364      * The result has the same scale as this instance.
1365      * For example, 'USD 1.13' divided by '2.5' and rounding down gives 'USD 0.45'
1366      * (amount rounded down from 0.452).
1367      * <p>
1368      * This instance is immutable and unaffected by this method.
1369      * 
1370      * @param valueToDivideBy  the scalar value to divide by, not null
1371      * @param roundingMode  the rounding mode to use, not null
1372      * @return the new divided instance, never null
1373      * @throws ArithmeticException if dividing by zero
1374      * @throws ArithmeticException if the rounding fails
1375      */
1376     public BigMoney dividedBy(BigDecimal valueToDivideBy, RoundingMode roundingMode) {
1377         MoneyUtils.checkNotNull(valueToDivideBy, "Divisor must not be null");
1378         MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1379         if (valueToDivideBy.compareTo(BigDecimal.ONE) == 0) {
1380             return this;
1381         }
1382         BigDecimal newAmount = amount.divide(valueToDivideBy, roundingMode);
1383         return BigMoney.of(currency, newAmount);
1384     }
1385 
1386     /**
1387      * Returns a copy of this monetary value divided by the specified value
1388      * using the specified rounding mode to adjust the scale.
1389      * <p>
1390      * The result has the same scale as this instance.
1391      * For example, 'USD 1.13' divided by '2.5' and rounding down gives 'USD 0.45'
1392      * (amount rounded down from 0.452).
1393      * <p>
1394      * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1395      * the most expected answer for most programming scenarios.
1396      * Any {@code double} literal in code will be converted to
1397      * exactly the same BigDecimal with the same scale.
1398      * For example, the literal '1.45d' will be converted to '1.45'.
1399      * <p>
1400      * This instance is immutable and unaffected by this method.
1401      * 
1402      * @param valueToDivideBy  the scalar value to divide by, not null
1403      * @param roundingMode  the rounding mode to use, not null
1404      * @return the new divided instance, never null
1405      * @throws ArithmeticException if dividing by zero
1406      * @throws ArithmeticException if the rounding fails
1407      */
1408     public BigMoney dividedBy(double valueToDivideBy, RoundingMode roundingMode) {
1409         MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1410         if (valueToDivideBy == 1) {
1411             return this;
1412         }
1413         BigDecimal newAmount = amount.divide(BigDecimal.valueOf(valueToDivideBy), roundingMode);
1414         return BigMoney.of(currency, newAmount);
1415     }
1416 
1417     /**
1418      * Returns a copy of this monetary value divided by the specified value
1419      * using the specified rounding mode to adjust the decimal places in the result.
1420      * <p>
1421      * The result has the same scale as this instance.
1422      * For example, 'USD 1.13' divided by '2' and rounding down gives 'USD 0.56'
1423      * (amount rounded down from 0.565).
1424      * <p>
1425      * This instance is immutable and unaffected by this method.
1426      * 
1427      * @param valueToDivideBy  the scalar value to divide by, not null
1428      * @param roundingMode  the rounding mode to use, not null
1429      * @return the new divided instance, never null
1430      * @throws ArithmeticException if dividing by zero
1431      */
1432     public BigMoney dividedBy(long valueToDivideBy, RoundingMode roundingMode) {
1433         if (valueToDivideBy == 1) {
1434             return this;
1435         }
1436         BigDecimal newAmount = amount.divide(BigDecimal.valueOf(valueToDivideBy), roundingMode);
1437         return BigMoney.of(currency, newAmount);
1438     }
1439 
1440     //-----------------------------------------------------------------------
1441     /**
1442      * Returns a copy of this monetary value with the amount negated.
1443      * <p>
1444      * This instance is immutable and unaffected by this method.
1445      * 
1446      * @return the new instance with the amount negated, never null
1447      */
1448     public BigMoney negated() {
1449         if (isZero()) {
1450             return this;
1451         }
1452         return BigMoney.of(currency, amount.negate());
1453     }
1454 
1455     /**
1456      * Returns a copy of this monetary value with a positive amount.
1457      * <p>
1458      * This instance is immutable and unaffected by this method.
1459      * 
1460      * @return the new instance with the amount converted to be positive, never null
1461      */
1462     public BigMoney abs() {
1463         return (isNegative() ? negated() : this);
1464     }
1465 
1466     //-----------------------------------------------------------------------
1467     /**
1468      * Returns a copy of this monetary value rounded to the specified scale without
1469      * changing the current scale.
1470      * <p>
1471      * Scale is described in {@link BigDecimal} and represents the point below which
1472      * the monetary value is zero. Negative scales round increasingly large numbers.
1473      * Unlike {@link #withScale(int)}, this scale of the result is unchanged.
1474      * <ul>
1475      * <li>Rounding 'EUR 45.23' to a scale of -1 returns 40.00 or 50.00 depending on the rounding mode.
1476      * <li>Rounding 'EUR 45.23' to a scale of 0 returns 45.00 or 46.00 depending on the rounding mode.
1477      * <li>Rounding 'EUR 45.23' to a scale of 1 returns 45.20 or 45.30 depending on the rounding mode.
1478      * <li>Rounding 'EUR 45.23' to a scale of 2 has no effect (it already has that scale).
1479      * <li>Rounding 'EUR 45.23' to a scale of 3 has no effect (the scale is not increased).
1480      * </ul>
1481      * This instance is immutable and unaffected by this method.
1482      * 
1483      * @param scale  the new scale
1484      * @param roundingMode  the rounding mode to use, not null
1485      * @return the new instance with the amount converted to be positive, never null
1486      * @throws ArithmeticException if the rounding fails
1487      */
1488     public BigMoney rounded(int scale, RoundingMode roundingMode) {
1489         MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1490         if (scale >= getScale()) {
1491             return this;
1492         }
1493         int currentScale = amount.scale();
1494         BigDecimal newAmount = amount.setScale(scale, roundingMode).setScale(currentScale);
1495         return BigMoney.of(currency, newAmount);
1496     }
1497 
1498     //-----------------------------------------------------------------------
1499     /**
1500      * Returns a copy of this monetary value converted into another currency
1501      * using the specified conversion rate.
1502      * <p>
1503      * The scale of the result will be the sum of the scale of this money and
1504      * the scale of the multiplier. If desired, the scale of the result can be
1505      * adjusted to the scale of the new currency using {@link #withCurrencyScale()}.
1506      * <p>
1507      * This instance is immutable and unaffected by this method.
1508      * 
1509      * @param currency  the new currency, not null
1510      * @param conversionMultipler  the conversion factor between the currencies, not null
1511      * @return the new multiplied instance, never null
1512      * @throws IllegalArgumentException if the currency is the same as this currency
1513      * @throws IllegalArgumentException if the conversion multiplier is negative
1514      */
1515     public BigMoney convertedTo(CurrencyUnit currency, BigDecimal conversionMultipler) {
1516         MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
1517         MoneyUtils.checkNotNull(conversionMultipler, "Multiplier must not be null");
1518         if (this.currency == currency) {
1519             throw new IllegalArgumentException("Cannot convert to the same currency");
1520         }
1521         if (conversionMultipler.compareTo(BigDecimal.ZERO) < 0) {
1522             throw new IllegalArgumentException("Cannot convert using a negative conversion multiplier");
1523         }
1524         BigDecimal newAmount = amount.multiply(conversionMultipler);
1525         return BigMoney.of(currency, newAmount);
1526     }
1527 
1528     /**
1529      * Returns a copy of this monetary value converted into another currency
1530      * using the specified conversion rate, with a rounding mode used to adjust
1531      * the decimal places in the result.
1532      * <p>
1533      * The result will have the same scale as this instance even though it will
1534      * be in a different currency.
1535      * <p>
1536      * This instance is immutable and unaffected by this method.
1537      * 
1538      * @param currency  the new currency, not null
1539      * @param conversionMultipler  the conversion factor between the currencies, not null
1540      * @param roundingMode  the rounding mode to use to bring the decimal places back in line, not null
1541      * @return the new multiplied instance, never null
1542      * @throws IllegalArgumentException if the currency is the same as this currency
1543      * @throws IllegalArgumentException if the conversion multiplier is negative
1544      * @throws ArithmeticException if the rounding fails
1545      */
1546     public BigMoney convertRetainScale(CurrencyUnit currency, BigDecimal conversionMultipler, RoundingMode roundingMode) {
1547         return convertedTo(currency, conversionMultipler).withScale(getScale(), roundingMode);
1548     }
1549 
1550     //-----------------------------------------------------------------------
1551     /**
1552      * Implements the {@code BigMoneyProvider} interface, trivially
1553      * returning {@code this}.
1554      * 
1555      * @return the money instance, never null
1556      */
1557     public BigMoney toBigMoney() {
1558         return this;
1559     }
1560 
1561     /**
1562      * Converts this money to an instance of {@code Money} without rounding.
1563      * If the scale of this money exceeds the currency scale an exception will be thrown.
1564      * 
1565      * @return the money instance, never null
1566      * @throws ArithmeticException if the rounding fails
1567      */
1568     public Money toMoney() {
1569         return Money.of(this);
1570     }
1571 
1572     /**
1573      * Converts this money to an instance of {@code Money}.
1574      * 
1575      * @param roundingMode  the rounding mode to use, not null
1576      * @return the money instance, never null
1577      * @throws ArithmeticException if the rounding fails
1578      */
1579     public Money toMoney(RoundingMode roundingMode) {
1580         return Money.of(this, roundingMode);
1581     }
1582 
1583     //-----------------------------------------------------------------------
1584     /**
1585      * Checks if this instance and the specified instance have the same currency.
1586      * 
1587      * @param money  the money to check, not null
1588      * @return true if they have the same currency
1589      */
1590     public boolean isSameCurrency(BigMoneyProvider money) {
1591         return (currency.equals(of(money).getCurrencyUnit()));
1592     }
1593 
1594     //-----------------------------------------------------------------------
1595     /**
1596      * Compares this monetary value to another.
1597      * The compared values must be in the same currency.
1598      * 
1599      * @param other  the other monetary value, not null
1600      * @return -1 if this is less than , 0 if equal, 1 if greater than
1601      * @throws CurrencyMismatchException if the currencies differ
1602      */
1603     public int compareTo(BigMoneyProvider other) {
1604         BigMoney otherMoney = of(other);
1605         if (currency.equals(otherMoney.currency) == false) {
1606             throw new CurrencyMismatchException(getCurrencyUnit(), otherMoney.getCurrencyUnit());
1607         }
1608         return amount.compareTo(otherMoney.amount);
1609     }
1610 
1611     /**
1612      * Checks if this monetary value is equal to another.
1613      * <p>
1614      * This ignores the scale of the amount.
1615      * Thus, 'USD 30.00' and 'USD 30' are equal.
1616      * <p>
1617      * The compared values must be in the same currency.
1618      * 
1619      * @param other  the other monetary value, not null
1620      * @return true is this is greater than the specified monetary value
1621      * @throws CurrencyMismatchException if the currencies differ
1622      * @see #equals(Object)
1623      */
1624     public boolean isEqual(BigMoneyProvider other) {
1625         return compareTo(other) == 0;
1626     }
1627 
1628     /**
1629      * Checks if this monetary value is greater than another.
1630      * The compared values must be in the same currency.
1631      * 
1632      * @param other  the other monetary value, not null
1633      * @return true is this is greater than the specified monetary value
1634      * @throws CurrencyMismatchException if the currencies differ
1635      */
1636     public boolean isGreaterThan(BigMoneyProvider other) {
1637         return compareTo(other) > 0;
1638     }
1639 
1640     /**
1641      * Checks if this monetary value is less than another.
1642      * The compared values must be in the same currency.
1643      * 
1644      * @param other  the other monetary value, not null
1645      * @return true is this is less than the specified monetary value
1646      * @throws CurrencyMismatchException if the currencies differ
1647      */
1648     public boolean isLessThan(BigMoneyProvider other) {
1649         return compareTo(other) < 0;
1650     }
1651 
1652     //-----------------------------------------------------------------------
1653     /**
1654      * Checks if this monetary value equals another.
1655      * <p>
1656      * Like BigDecimal, this method compares the scale of the amount.
1657      * Thus, 'USD 30.00' and 'USD 30' are not equal.
1658      * <p>
1659      * The compared values must be in the same currency.
1660      * 
1661      * @param other  the other object, null returns false
1662      * @return true if this instance equals the other instance
1663      * @see #isEqual
1664      */
1665     @Override
1666     public boolean equals(Object other) {
1667         if (this == other) {
1668             return true;
1669         }
1670         if (other instanceof BigMoney) {
1671             BigMoney otherMoney = (BigMoney) other;
1672             return currency.equals(otherMoney.getCurrencyUnit()) &&
1673                     amount.equals(otherMoney.amount);
1674         }
1675         return false;
1676     }
1677 
1678     /**
1679      * Returns a hash code for this monetary value.
1680      * 
1681      * @return a suitable hash code
1682      */
1683     @Override
1684     public int hashCode() {
1685         return currency.hashCode() ^ amount.hashCode();
1686     }
1687 
1688     //-----------------------------------------------------------------------
1689     /**
1690      * Gets this monetary value as a string.
1691      * <p>
1692      * The format is the 3 letter ISO currency code, followed by a space,
1693      * followed by the amount as per {@link BigDecimal#toPlainString()}.
1694      * 
1695      * @return the string representation of this monetary value, never null
1696      */
1697     @Override
1698     @ToString
1699     public String toString() {
1700         return new StringBuilder()
1701             .append(currency.getCode())
1702             .append(' ')
1703             .append(amount.toPlainString())
1704             .toString();
1705     }
1706 
1707 }