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