← joda-money  /  src/main/java/org/joda/money/BigMoney.java

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