001/* 002 Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 004 Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 use this file except in compliance with the License. You may obtain a copy of 006 the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010 Unless required by applicable law or agreed to in writing, software 011 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 License for the specific language governing permissions and limitations under 014 the License. 015 */ 016package org.javamoney.moneta; 017 018import javax.money.CurrencyUnit; 019import javax.money.Monetary; 020import javax.money.MonetaryAmount; 021import javax.money.MonetaryAmountFactory; 022import javax.money.MonetaryContext; 023import javax.money.MonetaryContextBuilder; 024import javax.money.MonetaryException; 025import javax.money.MonetaryOperator; 026import javax.money.MonetaryQuery; 027import javax.money.NumberValue; 028import javax.money.RoundingQueryBuilder; 029 030import org.javamoney.moneta.spi.RoundedMoneyAmountFactory; 031import org.javamoney.moneta.spi.DefaultNumberValue; 032import org.javamoney.moneta.spi.MoneyUtils; 033 034import javax.money.format.MonetaryAmountFormat; 035 036import java.io.Serializable; 037import java.math.BigDecimal; 038import java.math.BigInteger; 039import java.math.MathContext; 040import java.math.RoundingMode; 041import java.util.Objects; 042 043/** 044 * Platform RI: Default immutable implementation of {@link MonetaryAmount} based on 045 * {@link BigDecimal} for the numeric representation. 046 * 047 * As required by {@link MonetaryAmount} this class is final, thread-safe, immutable and 048 * serializable. 049 * 050 * @author Anatole Tresch 051 * @author Werner Keil 052 * @version 0.6.1 053 */ 054public final class RoundedMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable { 055 056 /** 057 * serialVersionUID. 058 */ 059 private static final long serialVersionUID = -6716367273185192901L; 060 public static final String MONETARY_ROUNDING_KEY = "MonetaryRounding"; 061 /** 062 * The default {@link MonetaryContext} applied. 063 */ 064 public static final MonetaryContext DEFAULT_MONETARY_CONTEXT = MonetaryContextBuilder.of(RoundedMoney.class) 065 .set(MONETARY_ROUNDING_KEY, Monetary.getDefaultRounding()). 066 build(); 067 068 /** 069 * The currency of this amount. 070 */ 071 private final CurrencyUnit currency; 072 073 /** 074 * the {@link MonetaryContext} used by this instance, e.g. on division. 075 */ 076 private final MonetaryContext monetaryContext; 077 078 /** 079 * The numeric part of this amount. 080 */ 081 private final BigDecimal number; 082 083 /** 084 * The rounding to be done. 085 */ 086 private final MonetaryOperator rounding; 087 088 089 /** 090 * Creates a new instance os {@link RoundedMoney}. 091 * 092 * @param currency the currency, not null. 093 * @param number the amount, not null. 094 */ 095 public RoundedMoney(Number number, CurrencyUnit currency, MonetaryOperator rounding) { 096 this(number, currency, null, rounding); 097 } 098 099 @Deprecated 100 public RoundedMoney(Number number, CurrencyUnit currency, MathContext mathContext) { 101 Objects.requireNonNull(currency, "Currency is required."); 102 this.currency = currency; 103 this.rounding = Monetary.getRounding(RoundingQueryBuilder.of().set(mathContext).build()); 104 this.monetaryContext = 105 DEFAULT_MONETARY_CONTEXT.toBuilder() 106 .set(MONETARY_ROUNDING_KEY, rounding) 107 .set(mathContext) 108 .build(); 109 Objects.requireNonNull(number, "Number is required."); 110 checkNumber(number); 111 this.number = MoneyUtils.getBigDecimal(number, monetaryContext); 112 } 113 114 @Deprecated 115 public RoundedMoney(Number number, CurrencyUnit currency, MonetaryContext context, MonetaryOperator rounding) { 116 Objects.requireNonNull(currency, "Currency is required."); 117 this.currency = currency; 118 Objects.requireNonNull(number, "Number is required."); 119 checkNumber(number); 120 MonetaryContextBuilder monetaryContextBuilder = DEFAULT_MONETARY_CONTEXT.toBuilder(); 121 if (context != null) { 122 monetaryContextBuilder.importContext(context); 123 } 124 this.rounding = RoundedMoneyMonetaryOperatorFactory.INSTANCE.getDefaultMonetaryOperator(rounding, monetaryContextBuilder.build()); 125 monetaryContextBuilder.set(MONETARY_ROUNDING_KEY, this.rounding); 126 127 this.monetaryContext = monetaryContextBuilder.build(); 128 this.number = MoneyUtils.getBigDecimal(number, monetaryContext); 129 } 130 131 // Static Factory Methods 132 133 /** 134 * Translates a {@code BigDecimal} value and a {@code CurrencyUnit} currency into a 135 * {@code Money}. 136 * 137 * @param number numeric value of the {@code Money}. 138 * @param currency currency unit of the {@code Money}. 139 * @return a {@code Money} combining the numeric value and currency unit. 140 */ 141 public static RoundedMoney of(BigDecimal number, CurrencyUnit currency) { 142 return new RoundedMoney(number, currency, Monetary.getDefaultRounding()); 143 } 144 145 /** 146 * Translates a {@code BigDecimal} value and a {@code CurrencyUnit} currency into a 147 * {@code Money}. 148 * 149 * @param number numeric value of the {@code Money}. 150 * @param currency currency unit of the {@code Money}. 151 * @param rounding The rounding to be applied. 152 * @return a {@code Money} combining the numeric value and currency unit. 153 */ 154 public static RoundedMoney of(BigDecimal number, CurrencyUnit currency, MonetaryOperator rounding) { 155 return new RoundedMoney(number, currency, rounding); 156 } 157 158 /** 159 * Translates a {@code BigDecimal} value and a {@code CurrencyUnit} currency into a 160 * {@code Money}. 161 * 162 * @param number numeric value of the {@code Money}. 163 * @param currency currency unit of the {@code Money}. 164 * @param mathContext the {@link MathContext} to be used. 165 * @return a {@code Money} combining the numeric value and currency unit. 166 */ 167 public static RoundedMoney of(BigDecimal number, CurrencyUnit currency, MathContext mathContext) { 168 return new RoundedMoney(number, currency, mathContext); 169 } 170 171 /** 172 * Static factory method for creating a new instance of {@link RoundedMoney} . 173 * 174 * @param currency The target currency, not null. 175 * @param number The numeric part, not null. 176 * @return A new instance of {@link RoundedMoney}. 177 */ 178 @Deprecated 179 public static RoundedMoney of(Number number, CurrencyUnit currency) { 180 return new RoundedMoney(number, currency, (MonetaryOperator) null); 181 } 182 183 /** 184 * Static factory method for creating a new instance of {@link RoundedMoney} . 185 * 186 * @param currency The target currency, not null. 187 * @param number The numeric part, not null. 188 * @param rounding The rounding to be applied. 189 * @return A new instance of {@link RoundedMoney}. 190 */ 191 public static RoundedMoney of(Number number, CurrencyUnit currency, MonetaryOperator rounding) { 192 return new RoundedMoney(number, currency, rounding); 193 } 194 195 /** 196 * Static factory method for creating a new instance of {@link RoundedMoney} . 197 * 198 * @param currency The target currency, not null. 199 * @param number The numeric part, not null. 200 * @return A new instance of {@link RoundedMoney}. 201 */ 202 public static RoundedMoney of(Number number, CurrencyUnit currency, MonetaryContext monetaryContext) { 203 return new RoundedMoney(number, currency, 204 DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), null); 205 } 206 207 /** 208 * Static factory method for creating a new instance of {@link RoundedMoney} . 209 * 210 * @param currency The target currency, not null. 211 * @param number The numeric part, not null. 212 * @param monetaryContext the {@link MonetaryContext} to be used. 213 * @param rounding The rounding to be applied. 214 * @return A new instance of {@link RoundedMoney}. 215 */ 216 @Deprecated 217 public static RoundedMoney of(CurrencyUnit currency, Number number, MonetaryContext monetaryContext, 218 MonetaryOperator rounding) { 219 return new RoundedMoney(number, currency, 220 DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), rounding); 221 } 222 223 /** 224 * Static factory method for creating a new instance of {@link RoundedMoney} . 225 * 226 * @param currencyCode The target currency as ISO currency code. 227 * @param number The numeric part, not null. 228 * @return A new instance of {@link RoundedMoney}. 229 */ 230 @Deprecated 231 public static RoundedMoney of(Number number, String currencyCode) { 232 return new RoundedMoney(number, Monetary.getCurrency(currencyCode), 233 Monetary.getDefaultRounding()); 234 } 235 236 /** 237 * Static factory method for creating a new instance of {@link RoundedMoney} . 238 * 239 * @param currencyCode The target currency as ISO currency code. 240 * @param number The numeric part, not null. 241 * @param rounding The rounding to be applied. 242 * @return A new instance of {@link RoundedMoney}. 243 */ 244 public static RoundedMoney of(Number number, String currencyCode, MonetaryOperator rounding) { 245 return new RoundedMoney(number, Monetary.getCurrency(currencyCode), rounding); 246 } 247 248 /** 249 * Static factory method for creating a new instance of {@link RoundedMoney} . 250 * 251 * @param currencyCode The target currency as ISO currency code. 252 * @param number The numeric part, not null. 253 * @return A new instance of {@link RoundedMoney}. 254 */ 255 @Deprecated 256 public static RoundedMoney of(Number number, String currencyCode, MonetaryContext monetaryContext) { 257 return new RoundedMoney(number, Monetary.getCurrency(currencyCode), 258 DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), null); 259 } 260 261 /** 262 * Static factory method for creating a new instance of {@link RoundedMoney} . 263 * 264 * @param currencyCode The target currency as ISO currency code. 265 * @param number The numeric part, not null. 266 * @param rounding The rounding to be applied. 267 * @return A new instance of {@link RoundedMoney}. 268 */ 269 public static RoundedMoney of(String currencyCode, Number number, MonetaryContext monetaryContext, 270 MonetaryOperator rounding) { 271 return new RoundedMoney(number, Monetary.getCurrency(currencyCode), 272 DEFAULT_MONETARY_CONTEXT.toBuilder().importContext(monetaryContext).build(), rounding); 273 } 274 275 /** 276 * Obtains an instance of {@link RoundedMoney} representing zero. 277 * @param currency the currency, not null. 278 * @return an instance of {@link RoundedMoney} representing zero. 279 * @since 1.0.1 280 */ 281 public static RoundedMoney zero(CurrencyUnit currency) { 282 return of(BigDecimal.ZERO, currency); 283 } 284 285 286 /** 287 * Obtains an instance of {@code FastMoney} from an amount in minor units. 288 * For example, {@code ofMinor(USD, 1234)} creates the instance {@code USD 12.34}. 289 * @param currency the currency, not null 290 * @param amountMinor the amount of money in the minor division of the currency 291 * @return the Money from minor units 292 * @see CurrencyUnit#getDefaultFractionDigits() 293 * @throws NullPointerException when the currency is null 294 * @throws IllegalArgumentException when {@link CurrencyUnit#getDefaultFractionDigits()} is lesser than zero. 295 * @since 1.0.1 296 */ 297 public static RoundedMoney ofMinor(CurrencyUnit currency, long amountMinor) { 298 return ofMinor(currency, amountMinor, currency.getDefaultFractionDigits()); 299 } 300 301 /** 302 * Obtains an instance of {@code Money} from an amount in minor units. 303 * For example, {@code ofMinor(USD, 1234, 2)} creates the instance {@code USD 12.34}. 304 * @param currency the currency, not null 305 * @param amountMinor the amount of money in the minor division of the currency 306 * @param factionDigits number of digits 307 * @return the monetary amount from minor units 308 * @see CurrencyUnit#getDefaultFractionDigits() 309 * @see Money#ofMinor(CurrencyUnit, long, int) 310 * @throws NullPointerException when the currency is null 311 * @throws IllegalArgumentException when the factionDigits is negative 312 * @since 1.0.1 313 */ 314 public static RoundedMoney ofMinor(CurrencyUnit currency, long amountMinor, int factionDigits) { 315 if(factionDigits < 0) { 316 throw new IllegalArgumentException("The factionDigits cannot be negative"); 317 } 318 return of(BigDecimal.valueOf(amountMinor, factionDigits), currency); 319 } 320 321 /* 322 * (non-Javadoc) 323 * @see MonetaryAmount#getCurrency() 324 */ 325 @Override 326 public CurrencyUnit getCurrency() { 327 return currency; 328 } 329 330 /** 331 * Access the {@link MathContext} used by this instance. 332 * 333 * @return the {@link MathContext} used, never null. 334 */ 335 @Override 336 public MonetaryContext getContext() { 337 return monetaryContext; 338 } 339 340 @Override 341 public RoundedMoney abs() { 342 if (isPositiveOrZero()) { 343 return this; 344 } 345 return negate(); 346 } 347 348 // Arithmetic Operations 349 350 @Override 351 public RoundedMoney add(MonetaryAmount amount) { 352 MoneyUtils.checkAmountParameter(amount, currency); 353 if (amount.isZero()) { 354 return this; 355 } 356 return new RoundedMoney(number.add(amount.getNumber().numberValue(BigDecimal.class)), currency, 357 rounding).with(rounding); 358 } 359 360 /* 361 * (non-Javadoc) 362 * @see javax.money.MonetaryAmount#divide(javax.money.MonetaryAmount) 363 */ 364 @Override 365 public RoundedMoney divide(Number divisor) { 366 BigDecimal bd = MoneyUtils.getBigDecimal(divisor); 367 if (isOne(bd)) { 368 return this; 369 } 370 RoundingMode rm = monetaryContext.get(RoundingMode.class); 371 if(rm==null){ 372 rm = RoundingMode.HALF_EVEN; 373 } 374 BigDecimal dec = number.divide(bd, rm); 375 return new RoundedMoney(dec, currency, rounding).with(rounding); 376 } 377 378 /* 379 * (non-Javadoc) 380 * @see javax.money.MonetaryAmount#divideAndRemainder(javax.money.MonetaryAmount) 381 */ 382 @Override 383 public RoundedMoney[] divideAndRemainder(Number divisor) { 384 BigDecimal bd = MoneyUtils.getBigDecimal(divisor); 385 if (isOne(bd)) { 386 return new RoundedMoney[]{this, new RoundedMoney(0L, getCurrency(), rounding)}; 387 } 388 MathContext mc = monetaryContext.get(MathContext.class); 389 if(mc==null){ 390 mc = MathContext.DECIMAL64; 391 } 392 BigDecimal[] dec = number.divideAndRemainder(MoneyUtils.getBigDecimal(divisor), mc); 393 return new RoundedMoney[]{new RoundedMoney(dec[0], currency, rounding), 394 new RoundedMoney(dec[1], currency, rounding).with(rounding)}; 395 } 396 397 /* 398 * (non-Javadoc) 399 * @see MonetaryAmount#divideToIntegralValue(Number) )D 400 */ 401 @Override 402 public RoundedMoney divideToIntegralValue(Number divisor) { 403 MathContext mc = monetaryContext.get(MathContext.class); 404 if(mc==null){ 405 mc = MathContext.DECIMAL64; 406 } 407 BigDecimal dec = number.divideToIntegralValue(MoneyUtils.getBigDecimal(divisor), mc); 408 return new RoundedMoney(dec, currency, rounding); 409 } 410 411 /* 412 * (non-Javadoc) 413 * @see MonetaryAmount#multiply(Number) 414 */ 415 @Override 416 public RoundedMoney multiply(Number multiplicand) { 417 BigDecimal bd = MoneyUtils.getBigDecimal(multiplicand); 418 if (isOne(bd)) { 419 return this; 420 } 421 MathContext mc = monetaryContext.get(MathContext.class); 422 if(mc==null){ 423 mc = MathContext.DECIMAL64; 424 } 425 BigDecimal dec = number.multiply(bd, mc); 426 return new RoundedMoney(dec, currency, rounding).with(rounding); 427 } 428 429 /* 430 * (non-Javadoc) 431 * @see MonetaryAmount#negate() 432 */ 433 @Override 434 public RoundedMoney negate() { 435 MathContext mc = monetaryContext.get(MathContext.class); 436 if(mc==null){ 437 mc = MathContext.DECIMAL64; 438 } 439 return new RoundedMoney(number.negate(mc), 440 currency, rounding); 441 } 442 443 /* 444 * (non-Javadoc) 445 * @see javax.money.MonetaryAmount#plus() 446 */ 447 @Override 448 public RoundedMoney plus() { 449 return this; 450 } 451 452 /* 453 * (non-Javadoc) 454 * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount) 455 */ 456 @Override 457 public RoundedMoney subtract(MonetaryAmount subtrahend) { 458 MoneyUtils.checkAmountParameter(subtrahend, currency); 459 if (subtrahend.isZero()) { 460 return this; 461 } 462 MathContext mc = monetaryContext.get(MathContext.class); 463 if(mc==null){ 464 mc = MathContext.DECIMAL64; 465 } 466 return new RoundedMoney(number.subtract(subtrahend.getNumber().numberValue(BigDecimal.class), mc), 467 currency, rounding); 468 } 469 470 /* 471 * (non-Javadoc) 472 * @see javax.money.MonetaryAmount#pow(int) 473 */ 474 public RoundedMoney pow(int n) { 475 MathContext mc = monetaryContext.get(MathContext.class); 476 if(mc==null){ 477 mc = MathContext.DECIMAL64; 478 } 479 return new RoundedMoney(number.pow(n, mc), 480 currency, rounding).with(rounding); 481 } 482 483 /* 484 * (non-Javadoc) 485 * @see javax.money.MonetaryAmount#ulp() 486 */ 487 public RoundedMoney ulp() { 488 return new RoundedMoney(number.ulp(), currency, rounding); 489 } 490 491 /* 492 * (non-Javadoc) 493 * @see javax.money.MonetaryAmount#remainder(Number) 494 */ 495 @Override 496 public RoundedMoney remainder(Number divisor) { 497 MathContext mc = monetaryContext.get(MathContext.class); 498 if(mc==null){ 499 mc = MathContext.DECIMAL64; 500 } 501 return new RoundedMoney(number.remainder(MoneyUtils.getBigDecimal(divisor), mc), 502 currency, rounding); 503 } 504 505 /* 506 * (non-Javadoc) 507 * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int) 508 */ 509 @Override 510 public RoundedMoney scaleByPowerOfTen(int n) { 511 return new RoundedMoney(number.scaleByPowerOfTen(n), currency, rounding); 512 } 513 514 /* 515 * (non-Javadoc) 516 * @see javax.money.MonetaryAmount#isZero() 517 */ 518 @Override 519 public boolean isZero() { 520 return number.signum() == 0; 521 } 522 523 /* 524 * (non-Javadoc) 525 * @see javax.money.MonetaryAmount#isPositive() 526 */ 527 @Override 528 public boolean isPositive() { 529 return signum() == 1; 530 } 531 532 /* 533 * (non-Javadoc) 534 * @see javax.money.MonetaryAmount#isPositiveOrZero() 535 */ 536 @Override 537 public boolean isPositiveOrZero() { 538 return signum() >= 0; 539 } 540 541 /* 542 * (non-Javadoc) 543 * @see javax.money.MonetaryAmount#isNegative() 544 */ 545 @Override 546 public boolean isNegative() { 547 return signum() == -1; 548 } 549 550 /* 551 * (non-Javadoc) 552 * @see javax.money.MonetaryAmount#isNegativeOrZero() 553 */ 554 @Override 555 public boolean isNegativeOrZero() { 556 return signum() <= 0; 557 } 558 559 /* 560 * (non-Javadoc) 561 * @see javax.money.MonetaryAmount#with(java.lang.Number) 562 */ 563 public RoundedMoney with(Number amount) { 564 checkNumber(amount); 565 return new RoundedMoney(MoneyUtils.getBigDecimal(amount), currency, rounding); 566 } 567 568 /** 569 * Creates a new Money instance, by just replacing the {@link CurrencyUnit}. 570 * 571 * @param currency the currency unit to be replaced, not {@code null} 572 * @return the new amount with the same numeric value and {@link MathContext}, but the new 573 * {@link CurrencyUnit}. 574 */ 575 public RoundedMoney with(CurrencyUnit currency) { 576 Objects.requireNonNull(currency, "currency required"); 577 return new RoundedMoney(asType(BigDecimal.class), currency, rounding); 578 } 579 580 /* 581 * (non-Javadoc) 582 * @see javax.money.MonetaryAmount#with(CurrencyUnit, java.lang.Number) 583 */ 584 public RoundedMoney with(CurrencyUnit currency, Number amount) { 585 checkNumber(amount); 586 return new RoundedMoney(MoneyUtils.getBigDecimal(amount), currency, rounding); 587 } 588 589 /* 590 * (non-Javadoc) 591 * @see javax.money.MonetaryAmount#getScale() 592 */ 593 public int getScale() { 594 return number.scale(); 595 } 596 597 /* 598 * (non-Javadoc) 599 * @see javax.money.MonetaryAmount#getPrecision() 600 */ 601 public int getPrecision() { 602 return number.precision(); 603 } 604 605 /* 606 * (non-Javadoc) 607 * @see javax.money.MonetaryAmount#signum() 608 */ 609 610 @Override 611 public int signum() { 612 return number.signum(); 613 } 614 615 /* 616 * (non-Javadoc) 617 * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount) 618 */ 619 @Override 620 public boolean isLessThan(MonetaryAmount amount) { 621 MoneyUtils.checkAmountParameter(amount, currency); 622 return number.stripTrailingZeros() 623 .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) < 0; 624 } 625 626 /* 627 * (non-Javadoc) 628 * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount) 629 */ 630 @Override 631 public boolean isLessThanOrEqualTo(MonetaryAmount amount) { 632 MoneyUtils.checkAmountParameter(amount, currency); 633 return number.stripTrailingZeros() 634 .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) <= 0; 635 } 636 637 /* 638 * (non-Javadoc) 639 * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount) 640 */ 641 @Override 642 public boolean isGreaterThan(MonetaryAmount amount) { 643 MoneyUtils.checkAmountParameter(amount, currency); 644 return number.stripTrailingZeros() 645 .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) > 0; 646 } 647 648 /* 649 * (non-Javadoc) 650 * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see 651 */ 652 @Override 653 public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) { 654 MoneyUtils.checkAmountParameter(amount, currency); 655 return number.stripTrailingZeros() 656 .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) >= 0; 657 } 658 659 /* 660 * (non-Javadoc) 661 * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount) 662 */ 663 @Override 664 public boolean isEqualTo(MonetaryAmount amount) { 665 MoneyUtils.checkAmountParameter(amount, currency); 666 return number.stripTrailingZeros() 667 .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) == 0; 668 } 669 670 /* 671 * (non-Javadoc) 672 * @see javax.money.MonetaryAmount#isNotEqualTo(javax.money.MonetaryAmount) 673 */ 674 public boolean isNotEqualTo(MonetaryAmount amount) { 675 MoneyUtils.checkAmountParameter(amount, currency); 676 return number.stripTrailingZeros() 677 .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) != 0; 678 } 679 680 /* 681 * }(non-Javadoc) 682 * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster) 683 */ 684 @Override 685 public RoundedMoney with(MonetaryOperator operator) { 686 Objects.requireNonNull(operator); 687 try { 688 return RoundedMoney.from(operator.apply(this)); 689 } catch (MonetaryException | ArithmeticException e) { 690 throw e; 691 } catch (Exception e) { 692 throw new MonetaryException("Query failed: " + operator, e); 693 } 694 } 695 696 public static RoundedMoney from(MonetaryAmount amt) { 697 if (amt.getClass() == RoundedMoney.class) { 698 return (RoundedMoney) amt; 699 } 700 if (amt.getClass() == FastMoney.class) { 701 return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency()); 702 } else if (amt.getClass() == Money.class) { 703 return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency()); 704 } 705 return RoundedMoney.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency()); 706 } 707 708 /** 709 * Obtains an instance of RoundedMoney from a text string such as 'EUR 710 * 25.25'. 711 * 712 * @param text the input text, not null. 713 * @return RoundedMoney instance 714 * @throws NumberFormatException if the amount is not a number 715 * @throws javax.money.UnknownCurrencyException if the currency is not resolvable 716 */ 717 public static RoundedMoney parse(CharSequence text) { 718 return parse(text, DEFAULT_FORMATTER); 719 } 720 721 /** 722 * Obtains an instance of FastMoney from a text using specific formatter. 723 * 724 * @param text the text to parse not null 725 * @param formatter the formatter to use not null 726 * @return RoundedMoney instance 727 */ 728 public static RoundedMoney parse(CharSequence text, MonetaryAmountFormat formatter) { 729 return from(formatter.parse(text)); 730 } 731 732 private static final ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat 733 .of(ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle.ROUNDED_MONEY); 734 735 /* 736 * }(non-Javadoc) 737 * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster) 738 */ 739 @Override 740 public <T> T query(MonetaryQuery<T> query) { 741 Objects.requireNonNull(query); 742 try { 743 return query.queryFrom(this); 744 } catch (MonetaryException | ArithmeticException e) { 745 throw e; 746 } catch (Exception e) { 747 throw new MonetaryException("Query failed: " + query, e); 748 } 749 } 750 751 752 @Deprecated 753 @SuppressWarnings("unchecked") 754 public <T> T asType(Class<T> type) { 755 if (BigDecimal.class.equals(type)) { 756 return (T) this.number; 757 } 758 if (Number.class.equals(type)) { 759 return (T) this.number; 760 } 761 if (Double.class.equals(type)) { 762 return (T) Double.valueOf(this.number.doubleValue()); 763 } 764 if (Float.class.equals(type)) { 765 return (T) Float.valueOf(this.number.floatValue()); 766 } 767 if (Long.class.equals(type)) { 768 return (T) Long.valueOf(this.number.longValue()); 769 } 770 if (Integer.class.equals(type)) { 771 return (T) Integer.valueOf(this.number.intValue()); 772 } 773 if (Short.class.equals(type)) { 774 return (T) Short.valueOf(this.number.shortValue()); 775 } 776 if (Byte.class.equals(type)) { 777 return (T) Byte.valueOf(this.number.byteValue()); 778 } 779 if (BigInteger.class.equals(type)) { 780 return (T) this.number.toBigInteger(); 781 } 782 throw new IllegalArgumentException("Unsupported representation type: " + type); 783 } 784 785 @Deprecated 786 public <T> T asType(Class<T> type, MonetaryOperator adjuster) { 787 RoundedMoney amount = (RoundedMoney) adjuster.apply(this); 788 return amount.asType(type); 789 } 790 791 /* 792 * (non-Javadoc) 793 * @see java.lang.Object#toString() 794 */ 795 @Override 796 public String toString() { 797 return currency.getCurrencyCode() + ' ' + number; 798 } 799 800 /* 801 * (non-Javadoc) 802 * @see java.lang.Object#hashCode() 803 */ 804 @Override 805 public int hashCode() { 806 return Objects.hash(currency, asNumberStripped()); 807 } 808 809 /* 810 * (non-Javadoc) 811 * @see java.lang.Object#equals(java.lang.Object) 812 */ 813 @Override 814 public boolean equals(Object obj) { 815 if (obj == this) { 816 return true; 817 } 818 if (obj instanceof RoundedMoney) { 819 RoundedMoney other = (RoundedMoney) obj; 820 return Objects.equals(currency, other.currency) && 821 Objects.equals(asNumberStripped(), other.asNumberStripped()); 822 } 823 return false; 824 } 825 826 /* 827 * @see java.lang.Comparable#compareTo(java.lang.Object) 828 */ 829 @Override 830 public int compareTo(MonetaryAmount o) { 831 Objects.requireNonNull(o); 832 int compare; 833 if (currency.equals(o.getCurrency())) { 834 compare = asNumberStripped().compareTo(RoundedMoney.from(o).asNumberStripped()); 835 } else { 836 compare = currency.getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); 837 } 838 return compare; 839 } 840 841 /* 842 * (non-Javadoc) 843 * @see javax.money.MonetaryAmount#getNumber() 844 */ 845 @Override 846 public NumberValue getNumber() { 847 return new DefaultNumberValue(number); 848 } 849 850 /** 851 * Method that returns BigDecimal.ZERO, if {@link #isZero()}, and #number 852 * {@link #stripTrailingZeros()} in all other cases. 853 * 854 * @return the stripped number value. 855 */ 856 public BigDecimal asNumberStripped() { 857 if (isZero()) { 858 return BigDecimal.ZERO; 859 } 860 return number.stripTrailingZeros(); 861 } 862 863 /** 864 * Internal method to check for correct number parameter. 865 * 866 * @param number the number to check. 867 * @throws IllegalArgumentException If the number is null 868 */ 869 private void checkNumber(Number number) { 870 Objects.requireNonNull(number, "Number is required."); 871 } 872 873 @Override 874 public RoundedMoney multiply(long multiplicand) { 875 if (multiplicand == 1L) { 876 return this; 877 } 878 return multiply(MoneyUtils.getBigDecimal(multiplicand)); 879 } 880 881 @Override 882 public RoundedMoney multiply(double multiplicand) { 883 NumberVerifier.checkNoInfinityOrNaN(multiplicand); 884 if (multiplicand == 1.0d) { 885 return this; 886 } 887 return multiply(MoneyUtils.getBigDecimal(multiplicand)); 888 } 889 890 @Override 891 public RoundedMoney divide(long divisor) { 892 if (divisor == 1L) { 893 return this; 894 } 895 return divide(MoneyUtils.getBigDecimal(divisor)); 896 } 897 898 @Override 899 public RoundedMoney divide(double divisor) { 900 if (NumberVerifier.isInfinityAndNotNaN(divisor)) { 901 return new RoundedMoney(0L, getCurrency(), monetaryContext, rounding); 902 } 903 if (divisor == 1.0d) { 904 return this; 905 } 906 return divide(MoneyUtils.getBigDecimal(divisor)); 907 } 908 909 @Override 910 public RoundedMoney remainder(long divisor) { 911 return remainder(MoneyUtils.getBigDecimal(divisor)); 912 } 913 914 @Override 915 public RoundedMoney remainder(double divisor) { 916 if (Money.isInfinityAndNotNaN(divisor)) { 917 return new RoundedMoney(0L, getCurrency(), monetaryContext, rounding); 918 } 919 return remainder(MoneyUtils.getBigDecimal(divisor)); 920 } 921 922 @Override 923 public RoundedMoney[] divideAndRemainder(long divisor) { 924 return divideAndRemainder(MoneyUtils.getBigDecimal(divisor)); 925 } 926 927 @Override 928 public RoundedMoney[] divideAndRemainder(double divisor) { 929 if (Money.isInfinityAndNotNaN(divisor)) { 930 RoundedMoney zero = new RoundedMoney(0L, getCurrency(), monetaryContext, rounding); 931 return new RoundedMoney[]{zero, zero}; 932 } 933 return divideAndRemainder(MoneyUtils.getBigDecimal(divisor)); 934 } 935 936 @Override 937 public RoundedMoney stripTrailingZeros() { 938 if (isZero()) { 939 return of(BigDecimal.ZERO, getCurrency()); 940 } 941 return of(number.stripTrailingZeros(), getCurrency()); 942 } 943 944 @Override 945 public RoundedMoney divideToIntegralValue(long divisor) { 946 return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); 947 } 948 949 @Override 950 public RoundedMoney divideToIntegralValue(double divisor) { 951 return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); 952 } 953 954 @Override 955 public MonetaryAmountFactory<RoundedMoney> getFactory() { 956 return new RoundedMoneyAmountFactory().setAmount(this); 957 } 958 959 private boolean isOne(Number number) { 960 BigDecimal bd = MoneyUtils.getBigDecimal(number); 961 try { 962 return bd.scale() == 0 && bd.longValueExact() == 1L; 963 } catch (Exception e) { 964 // The only way to end up here is that longValueExact throws an ArithmeticException, 965 // so the amount is definitively not equal to 1. 966 return false; 967 } 968 } 969}