001/* 002 Copyright (c) 2012, 2017, 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.format.MonetaryAmountFormat; 029 030import org.javamoney.moneta.spi.FastMoneyAmountFactory; 031import org.javamoney.moneta.spi.DefaultNumberValue; 032import org.javamoney.moneta.spi.MonetaryConfig; 033import org.javamoney.moneta.spi.MoneyUtils; 034 035import java.io.Serializable; 036import java.math.BigDecimal; 037import java.util.Objects; 038import java.util.logging.Level; 039import java.util.logging.Logger; 040 041/** 042 * <code>long</code> based implementation of {@link MonetaryAmount}.This class internally uses a 043 * single long number as numeric representation, which basically is interpreted as minor units. 044 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money}, 045 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision. 046 * As an example performing the following calculation one million times, results in slightly 047 * different results: 048 * 049 * <pre><code> 050 * Money money1 = money1.add(Money.of("EUR", 1234567.3444)); 051 * money1 = money1.subtract(Money.of("EUR", 232323)); 052 * money1 = money1.multiply(3.4); 053 * money1 = money1.divide(5.456); 054 * </code></pre> 055 * 056 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in 057 * 3680 ms, or roughly 3ns/loop. 058 * 059 * whereas 060 * 061 * <pre><code> 062 * FastMoney money1 = money1.add(FastMoney.of("EUR", 1234567.3444)); 063 * money1 = money1.subtract(FastMoney.of("EUR", 232323)); 064 * money1 = money1.multiply(3.4); 065 * money1 = money1.divide(5.456); 066 * </code></pre> 067 * 068 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms, 069 * which is less than 1ns/loop. 070 * 071 * Also note than mixing up types may drastically change the performance behavior. E.g. replacing the 072 * code above with the following: * 073 * 074 * <pre><code> 075 * FastMoney money1 = money1.add(Money.of("EUR", 1234567.3444)); 076 * money1 = money1.subtract(FastMoney.of("EUR", 232323)); 077 * money1 = money1.multiply(3.4); 078 * money1 = money1.divide(5.456); 079 * </code></pre> 080 * 081 * executed one million (1000000) times may execute significantly longer, since monetary amount type 082 * conversion is involved. 083 * 084 * Basically, when mixing amount implementations, the performance of the amount, on which most of 085 * the operations are operated, has the most significant impact on the overall performance behavior. 086 * 087 * @author Anatole Tresch 088 * @author Werner Keil 089 * @version 1.0.1 090 * @since 1.0 091 */ 092public final class FastMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable { 093 094 private static final long serialVersionUID = 1L; 095 096 /** 097 * The logger used. 098 */ 099 private static final Logger LOG = Logger.getLogger(FastMoney.class.getName()); 100 101 /** 102 * The currency of this amount. 103 */ 104 private final CurrencyUnit currency; 105 106 /** 107 * The numeric part of this amount. 108 */ 109 private final long number; 110 111 /** 112 * The current scale represented by the number. 113 */ 114 private static final int SCALE = 5; 115 116 /** 117 * the {@link MonetaryContext} used by this instance, e.g. on division. 118 */ 119 private static final MonetaryContext MONETARY_CONTEXT = 120 MonetaryContextBuilder.of(FastMoney.class).setMaxScale(SCALE).setFixedScale(true).setPrecision(19).build(); 121 122 /** 123 * Maximum possible value supported, using XX (no currency). 124 */ 125 public static final FastMoney MAX_VALUE = new FastMoney(Long.MAX_VALUE, Monetary.getCurrency("XXX")); 126 /** 127 * Maximum possible numeric value supported. 128 */ 129 private static final BigDecimal MAX_BD = MAX_VALUE.getBigDecimal(); 130 /** 131 * Minimum possible value supported, using XX (no currency). 132 */ 133 public static final FastMoney MIN_VALUE = new FastMoney(Long.MIN_VALUE, Monetary.getCurrency("XXX")); 134 /** 135 * Minimum possible numeric value supported. 136 */ 137 private static final BigDecimal MIN_BD = MIN_VALUE.getBigDecimal(); 138 139 140 /** 141 * Creates a new instance os {@link FastMoney}. 142 * 143 * @param currency the currency, not null. 144 * @param number the amount, not null. 145 */ 146 private FastMoney(Number number, CurrencyUnit currency, boolean allowInternalRounding) { 147 Objects.requireNonNull(currency, "Currency is required."); 148 this.currency = currency; 149 Objects.requireNonNull(number, "Number is required."); 150 this.number = getInternalNumber(number, allowInternalRounding); 151 } 152 153 /** 154 * Creates a new instance os {@link FastMoney}. 155 * 156 * @param currency the currency, not null. 157 * @param numberValue the numeric value, not null. 158 */ 159 private FastMoney(NumberValue numberValue, CurrencyUnit currency, boolean allowInternalRounding) { 160 Objects.requireNonNull(currency, "Currency is required."); 161 this.currency = currency; 162 Objects.requireNonNull(numberValue, "Number is required."); 163 this.number = getInternalNumber(numberValue.numberValue(BigDecimal.class), allowInternalRounding); 164 } 165 166 /** 167 * Creates a new instance os {@link FastMoney}. 168 * 169 * @param number The format number value 170 * @param currency the currency, not null. 171 */ 172 private FastMoney(long number, CurrencyUnit currency) { 173 Objects.requireNonNull(currency, "Currency is required."); 174 this.currency = currency; 175 this.number = number; 176 } 177 178 /** 179 * Returns the amount’s currency, modelled as {@link CurrencyUnit}. 180 * Implementations may co-variantly change the return type to a more 181 * specific implementation of {@link CurrencyUnit} if desired. 182 * 183 * @return the currency, never {@code null} 184 * @see javax.money.MonetaryAmount#getCurrency() 185 */ 186 @Override 187 public CurrencyUnit getCurrency() { 188 return currency; 189 } 190 191 /** 192 * Access the {@link MonetaryContext} used by this instance. 193 * 194 * @return the {@link MonetaryContext} used, never null. 195 * @see javax.money.MonetaryAmount#getContext() 196 */ 197 @Override 198 public MonetaryContext getContext() { 199 return MONETARY_CONTEXT; 200 } 201 202 private long getInternalNumber(Number number, boolean allowInternalRounding) { 203 BigDecimal bd = MoneyUtils.getBigDecimal(number); 204 if (!allowInternalRounding && bd.scale() > SCALE) { 205 throw new ArithmeticException(number + " can not be represented by this class, scale > " + SCALE); 206 } 207 if (bd.compareTo(MIN_BD) < 0) { 208 throw new ArithmeticException("Overflow: " + number + " < " + MIN_BD); 209 } else if (bd.compareTo(MAX_BD) > 0) { 210 throw new ArithmeticException("Overflow: " + number + " > " + MAX_BD); 211 } 212 return bd.movePointRight(SCALE).longValue(); 213 } 214 215 216 /** 217 * Static factory method for creating a new instance of {@link FastMoney}. 218 * 219 * @param currency The target currency, not null. 220 * @param numberBinding The numeric part, not null. 221 * @return A new instance of {@link FastMoney}. 222 */ 223 public static FastMoney of(NumberValue numberBinding, CurrencyUnit currency) { 224 return new FastMoney(numberBinding, currency, false); 225 } 226 227 /** 228 * Static factory method for creating a new instance of {@link FastMoney}. 229 * 230 * @param currency The target currency, not null. 231 * @param number The numeric part, not null. 232 * @return A new instance of {@link FastMoney}. 233 */ 234 public static FastMoney of(Number number, CurrencyUnit currency) { 235 return new FastMoney(number, currency, false); 236 } 237 238 /** 239 * Static factory method for creating a new instance of {@link FastMoney}. 240 * 241 * @param currencyCode The target currency as currency code. 242 * @param number The numeric part, not null. 243 * @return A new instance of {@link FastMoney}. 244 */ 245 public static FastMoney of(Number number, String currencyCode) { 246 CurrencyUnit currency = Monetary.getCurrency(currencyCode); 247 return of(number, currency); 248 } 249 250 /** 251 * Obtains an instance of {@link FastMoney} representing zero. 252 * @param currency the target currency 253 * @return an instance of {@link FastMoney} representing zero. 254 * @since 1.0.1 255 */ 256 public static FastMoney zero(CurrencyUnit currency) { 257 return of(BigDecimal.ZERO, currency); 258 } 259 260 /** 261 * Obtains an instance of {@code FastMoney} from an amount in minor units. 262 * For example, {@code ofMinor(USD, 1234)} creates the instance {@code USD 12.34}. 263 * @param currency the currency, not null 264 * @param amountMinor the amount of money in the minor division of the currency 265 * @return the monetary amount from minor units 266 * @see CurrencyUnit#getDefaultFractionDigits() 267 * @see FastMoney#ofMinor(CurrencyUnit, long, int) 268 * @throws NullPointerException when the currency is null 269 * @throws IllegalArgumentException when {@link CurrencyUnit#getDefaultFractionDigits()} is lesser than zero. 270 * @since 1.0.1 271 */ 272 public static FastMoney ofMinor(CurrencyUnit currency, long amountMinor) { 273 return ofMinor(currency, amountMinor, currency.getDefaultFractionDigits()); 274 } 275 276 /** 277 * Obtains an instance of {@code FastMoney} from an amount in minor units. 278 * For example, {@code ofMinor(USD, 1234, 2)} creates the instance {@code USD 12.34}. 279 * @param currency the currency, not null 280 * @param amountMinor the amount of money in the minor division of the currency 281 * @param factionDigits number of digits 282 * @return the monetary amount from minor units 283 * @see CurrencyUnit#getDefaultFractionDigits() 284 * @throws NullPointerException when the currency is null 285 * @throws IllegalArgumentException when the factionDigits is negative 286 * @since 1.0.1 287 */ 288 public static FastMoney ofMinor(CurrencyUnit currency, long amountMinor, int factionDigits) { 289 if(factionDigits < 0) { 290 throw new IllegalArgumentException("The factionDigits cannot be negative"); 291 } 292 return of(BigDecimal.valueOf(amountMinor, factionDigits), currency); 293 } 294 295 /* 296 * @see java.lang.Comparable#compareTo(java.lang.Object) 297 */ 298 @Override 299 public int compareTo(MonetaryAmount o) { 300 Objects.requireNonNull(o); 301 int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); 302 if (compare == 0) { 303 compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class)); 304 } 305 return compare; 306 } 307 308 /* 309 * (non-Javadoc) 310 * @see java.lang.Object#hashCode() 311 */ 312 @Override 313 public int hashCode() { 314 return Objects.hash(currency, number); 315 } 316 317 /* 318 * (non-Javadoc) 319 * @see java.lang.Object#equals(java.lang.Object) 320 */ 321 @Override 322 public boolean equals(Object obj) { 323 if (obj == this) { 324 return true; 325 } 326 if (obj instanceof FastMoney) { 327 FastMoney other = (FastMoney) obj; 328 return Objects.equals(currency, other.currency) && Objects.equals(number, other.number); 329 } 330 return false; 331 } 332 333 334 /* 335 * (non-Javadoc) 336 * @see MonetaryAmount#abs() 337 */ 338 @Override 339 public FastMoney abs() { 340 if (this.isPositiveOrZero()) { 341 return this; 342 } 343 return this.negate(); 344 } 345 346 // Arithmetic Operations 347 348 /* 349 * (non-Javadoc) 350 * @see MonetaryAmount#add(MonetaryAmount) 351 */ 352 @Override 353 public FastMoney add(MonetaryAmount amount) { 354 checkAmountParameter(amount); 355 if (amount.isZero()) { 356 return this; 357 } 358 return new FastMoney(addExact(this.number, getInternalNumber(amount.getNumber(), false)), getCurrency()); 359 } 360 361 private long addExact(long num1, long num2) { 362 if(num1==0){ 363 return num2; 364 } 365 if(num2==0){ 366 return num1; 367 } 368 boolean pos = num1>0 && num2 >0; 369 boolean neg = num1<0 && num2 <0; 370 long exact = num1 + num2; 371 if(pos && exact <=0){ 372 throw new ArithmeticException("Long evaluation positive overflow."); 373 } 374 if(neg && exact >=0){ 375 throw new ArithmeticException("Long evaluation negative overflow."); 376 } 377 return exact; 378 } 379 380 private void checkAmountParameter(MonetaryAmount amount) { 381 MoneyUtils.checkAmountParameter(amount, this.currency); 382 // numeric check for overflow... 383 if (amount.getNumber().getScale() > SCALE) { 384 throw new ArithmeticException("Parameter exceeds maximal scale: " + SCALE); 385 } 386 if (amount.getNumber().getPrecision() > MAX_BD.precision()) { 387 throw new ArithmeticException("Parameter exceeds maximal precision: " + SCALE); 388 } 389 } 390 391 392 /* 393 * (non-Javadoc) 394 * @see MonetaryAmount#divide(java.lang.Number) 395 */ 396 @Override 397 public FastMoney divide(Number divisor) { 398 if (isInfinityAndNotNaN(divisor)) { 399 return new FastMoney(0L, getCurrency()); 400 } 401 checkNumber(divisor); 402 if (isOne(divisor)) { 403 return this; 404 } 405 return new FastMoney(Math.round(this.number / divisor.doubleValue()), getCurrency()); 406 } 407 408 /** 409 * Just to don't break the compatibility. 410 * Don't use it 411 * @param number 412 */ 413 private static boolean isInfinityAndNotNaN(Number number) { 414 if (Double.class == number.getClass() || Float.class == number.getClass()) { 415 double dValue = number.doubleValue(); 416 if (!Double.isNaN(dValue) && Double.isInfinite(dValue)) { 417 return true; 418 } 419 } 420 return false; 421 } 422 423 /* 424 * (non-Javadoc) 425 * @see MonetaryAmount#divideAndRemainder(java.lang.Number) 426 */ 427 @Override 428 public FastMoney[] divideAndRemainder(Number divisor) { 429 if (isInfinityAndNotNaN(divisor)) { 430 FastMoney zero = new FastMoney(0L, getCurrency()); 431 return new FastMoney[]{zero, zero}; 432 } 433 checkNumber(divisor); 434 BigDecimal div = MoneyUtils.getBigDecimal(divisor); 435 BigDecimal[] res = getBigDecimal().divideAndRemainder(div); 436 return new FastMoney[]{new FastMoney(res[0], getCurrency(), true), new FastMoney(res[1], getCurrency(), true)}; 437 } 438 439 /* 440 * (non-Javadoc) 441 * @see MonetaryAmount#divideToIntegralValue(java.lang.Number) 442 */ 443 @Override 444 public FastMoney divideToIntegralValue(Number divisor) { 445 if (isInfinityAndNotNaN(divisor)) { 446 return new FastMoney(0L, getCurrency()); 447 } 448 checkNumber(divisor); 449 if (isOne(divisor)) { 450 return this; 451 } 452 BigDecimal div = MoneyUtils.getBigDecimal(divisor); 453 return new FastMoney(getBigDecimal().divideToIntegralValue(div), getCurrency(), false); 454 } 455 456 @Override 457 public FastMoney multiply(Number multiplicand) { 458 checkNoInfinityOrNaN(multiplicand); 459 checkNumber(multiplicand); 460 if (isOne(multiplicand)) { 461 return this; 462 } 463 return new FastMoney(multiplyExact(this.number, getInternalNumber(multiplicand, false)) / 100000L, 464 getCurrency()); 465 } 466 467 private static void checkNoInfinityOrNaN(Number number) { 468 if (Double.class == number.getClass() || Float.class == number.getClass()) { 469 double dValue = number.doubleValue(); 470 if (Double.isNaN(dValue)) { 471 throw new ArithmeticException("Not a valid input: NaN."); 472 } else if (Double.isInfinite(dValue)) { 473 throw new ArithmeticException("Not a valid input: INFINITY: " + dValue); 474 } 475 } 476 } 477 478 private long multiplyExact(long num1, long num2) { 479 if(num1==0 || num2==0){ 480 return 0; 481 } 482 483 // Hacker's Delight, 484 int leadingZeros = 485 Long.numberOfLeadingZeros(num1) + 486 Long.numberOfLeadingZeros(~num1) + 487 Long.numberOfLeadingZeros(num2) + 488 Long.numberOfLeadingZeros(~num2); 489 490 if (leadingZeros > Long.SIZE + 1 ) { 491 // in this case, an overflow is impossible 492 return num1 * num2; 493 } 494 495 if (leadingZeros < Long.SIZE){ 496 if(Long.signum(num1)*Long.signum(num2) > 0){ 497 throw new ArithmeticException("Long evaluation positive overflow."); 498 } else { 499 throw new ArithmeticException("Long evaluation negative overflow."); 500 } 501 } 502 503 long exact = num1 * num2; 504 505 // very expensive - this check is only executed in edge cases 506 // zero check is not needed, see above 507 if( exact / num1 != num2 ) { 508 throw new ArithmeticException("overflow"); 509 } 510 511 return exact; 512 } 513 514 /* 515 * (non-Javadoc) 516 * @see MonetaryAmount#negate() 517 */ 518 @Override 519 public FastMoney negate() { 520 return new FastMoney(multiplyExact(this.number, -1), getCurrency()); 521 } 522 523 /* 524 * (non-Javadoc) 525 * @see MonetaryAmount#plus() 526 */ 527 @Override 528 public FastMoney plus() { 529 return this; 530 } 531 532 /* 533 * (non-Javadoc) 534 * @see MonetaryAmount#subtract(MonetaryAmount) 535 */ 536 @Override 537 public FastMoney subtract(MonetaryAmount subtrahend) { 538 checkAmountParameter(subtrahend); 539 if (subtrahend.isZero()) { 540 return this; 541 } 542 return new FastMoney(subtractExact(this.number, getInternalNumber(subtrahend.getNumber(), false)), 543 getCurrency()); 544 } 545 546 private long subtractExact(long num1, long num2) { 547 if(num2==0){ 548 return num1; 549 } 550 if(num1==num2){ 551 return 0; 552 } 553 boolean pos = num1>num2; 554 long exact = num1 - num2; 555 if(pos && exact <=0){ 556 throw new ArithmeticException("Long evaluation negative overflow."); 557 } 558 if(!pos && exact >=0){ 559 throw new ArithmeticException("Long evaluation positive overflow."); 560 } 561 return exact; 562 } 563 564 /* 565 * (non-Javadoc) 566 * @see MonetaryAmount#remainder(java.lang.Number) 567 */ 568 @Override 569 public FastMoney remainder(Number divisor) { 570 checkNumber(divisor); 571 return new FastMoney(this.number % getInternalNumber(divisor, false), getCurrency()); 572 } 573 574 private boolean isOne(Number number) { 575 BigDecimal bd = MoneyUtils.getBigDecimal(number); 576 try { 577 return bd.scale() == 0 && bd.longValueExact() == 1L; 578 } catch (Exception e) { 579 // The only way to end up here is that longValueExact throws an ArithmeticException, 580 // so the amount is definitively not equal to 1. 581 return false; 582 } 583 } 584 585 /* 586 * (non-Javadoc) 587 * @see MonetaryAmount#scaleByPowerOfTen(int) 588 */ 589 @Override 590 public FastMoney scaleByPowerOfTen(int n) { 591 return new FastMoney(getNumber().numberValue(BigDecimal.class).scaleByPowerOfTen(n), getCurrency(), true); 592 } 593 594 /* 595 * (non-Javadoc) 596 * @see MonetaryAmount#isZero() 597 */ 598 @Override 599 public boolean isZero() { 600 return this.number == 0L; 601 } 602 603 /* 604 * (non-Javadoc) 605 * @see MonetaryAmount#isPositive() 606 */ 607 @Override 608 public boolean isPositive() { 609 return this.number > 0L; 610 } 611 612 /* 613 * (non-Javadoc) 614 * @see MonetaryAmount#isPositiveOrZero() 615 */ 616 @Override 617 public boolean isPositiveOrZero() { 618 return this.number >= 0L; 619 } 620 621 /* 622 * (non-Javadoc) 623 * @see MonetaryAmount#isNegative() 624 */ 625 @Override 626 public boolean isNegative() { 627 return this.number < 0L; 628 } 629 630 /* 631 * (non-Javadoc) 632 * @see MonetaryAmount#isNegativeOrZero() 633 */ 634 @Override 635 public boolean isNegativeOrZero() { 636 return this.number <= 0L; 637 } 638 639 /* 640 * (non-Javadoc) 641 * @see MonetaryAmount#getScale() 642 */ 643 public int getScale() { 644 return FastMoney.SCALE; 645 } 646 647 /* 648 * (non-Javadoc) 649 * @see MonetaryAmount#getPrecision() 650 */ 651 public int getPrecision() { 652 return getNumber().numberValue(BigDecimal.class).precision(); 653 } 654 655 /* 656 * (non-Javadoc) 657 * @see MonetaryAmount#signum() 658 */ 659 660 @Override 661 public int signum() { 662 if (this.number < 0) { 663 return -1; 664 } 665 if (this.number == 0) { 666 return 0; 667 } 668 return 1; 669 } 670 671 /* 672 * (non-Javadoc) 673 * @see MonetaryAmount#lessThan(MonetaryAmount) 674 */ 675 @Override 676 public boolean isLessThan(MonetaryAmount amount) { 677 checkAmountParameter(amount); 678 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) < 0; 679 } 680 681 /* 682 * (non-Javadoc) 683 * @see MonetaryAmount#lessThan(java.lang.Number) 684 */ 685 public boolean isLessThan(Number number) { 686 checkNumber(number); 687 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) < 0; 688 } 689 690 /* 691 * (non-Javadoc) 692 * @see MonetaryAmount#lessThanOrEqualTo(MonetaryAmount) 693 */ 694 @Override 695 public boolean isLessThanOrEqualTo(MonetaryAmount amount) { 696 checkAmountParameter(amount); 697 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) <= 0; 698 } 699 700 /* 701 * (non-Javadoc) 702 * @see MonetaryAmount#lessThanOrEqualTo(java.lang.Number) 703 */ 704 public boolean isLessThanOrEqualTo(Number number) { 705 checkNumber(number); 706 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) <= 0; 707 } 708 709 /* 710 * (non-Javadoc) 711 * @see MonetaryAmount#greaterThan(MonetaryAmount) 712 */ 713 @Override 714 public boolean isGreaterThan(MonetaryAmount amount) { 715 checkAmountParameter(amount); 716 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) > 0; 717 } 718 719 /* 720 * (non-Javadoc) 721 * @see MonetaryAmount#greaterThan(java.lang.Number) 722 */ 723 public boolean isGreaterThan(Number number) { 724 checkNumber(number); 725 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) > 0; 726 } 727 728 /* 729 * (non-Javadoc) 730 * @see MonetaryAmount#greaterThanOrEqualTo(MonetaryAmount ) #see 731 */ 732 @Override 733 public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) { 734 checkAmountParameter(amount); 735 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) >= 0; 736 } 737 738 /* 739 * (non-Javadoc) 740 * @see MonetaryAmount#greaterThanOrEqualTo(java.lang.Number) 741 */ 742 public boolean isGreaterThanOrEqualTo(Number number) { 743 checkNumber(number); 744 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) >= 0; 745 } 746 747 /* 748 * (non-Javadoc) 749 * @see MonetaryAmount#isEqualTo(MonetaryAmount) 750 */ 751 @Override 752 public boolean isEqualTo(MonetaryAmount amount) { 753 checkAmountParameter(amount); 754 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) == 0; 755 } 756 757 /* 758 * (non-Javadoc) 759 * @see MonetaryAmount#hasSameNumberAs(java.lang.Number) 760 */ 761 public boolean hasSameNumberAs(Number number) { 762 checkNumber(number); 763 try { 764 return this.number == getInternalNumber(number, false); 765 } catch (ArithmeticException e) { 766 return false; 767 } 768 } 769 770 771 /** 772 * Gets the number representation of the numeric value of this item. 773 * 774 * @return The {@link Number} represention matching best. 775 */ 776 @Override 777 public NumberValue getNumber() { 778 return new DefaultNumberValue(getBigDecimal()); 779 } 780 781 @Override 782 public String toString() { 783 return currency.toString() + ' ' + getBigDecimal(); 784 } 785 786 // Internal helper methods 787 788 /** 789 * Internal method to check for correct number parameter. 790 * 791 * @param number the number to be checked, including null.. 792 * @throws NullPointerException If the number is null 793 * @throws java.lang.ArithmeticException If the number exceeds the capabilities of this class. 794 */ 795 protected void checkNumber(Number number) { 796 Objects.requireNonNull(number, "Number is required."); 797 // numeric check for overflow... 798 if (number.longValue() > MAX_BD.longValue()) { 799 throw new ArithmeticException("Value exceeds maximal value: " + MAX_BD); 800 } 801 BigDecimal bd = MoneyUtils.getBigDecimal(number); 802 if (bd.precision() > MAX_BD.precision()) { 803 throw new ArithmeticException("Precision exceeds maximal precision: " + MAX_BD.precision()); 804 } 805 if (bd.scale() > SCALE) { 806 String val = MonetaryConfig.getConfig() 807 .get("org.javamoney.moneta.FastMoney.enforceScaleCompatibility"); 808 if(val==null){ 809 val = "false"; 810 } 811 if (Boolean.parseBoolean(val)) { 812 throw new ArithmeticException("Scale of " + bd + " exceeds maximal scale: " + SCALE); 813 } else { 814 if (LOG.isLoggable(Level.FINEST)) { 815 LOG.finest("Scale exceeds maximal scale of FastMoney (" + SCALE + 816 "), implicit rounding will be applied to " + number); 817 } 818 } 819 } 820 } 821 822 /* 823 * }(non-Javadoc) 824 * @see MonetaryAmount#adjust(org.javamoney.bp.AmountAdjuster) 825 */ 826 @Override 827 public FastMoney with(MonetaryOperator operator) { 828 Objects.requireNonNull(operator); 829 try { 830 return FastMoney.class.cast(operator.apply(this)); 831 } catch (ArithmeticException e) { 832 throw e; 833 } catch (Exception e) { 834 throw new MonetaryException("Operator failed: " + operator, e); 835 } 836 } 837 838 @Override 839 public <R> R query(MonetaryQuery<R> query) { 840 Objects.requireNonNull(query); 841 try { 842 return query.queryFrom(this); 843 } catch (MonetaryException | ArithmeticException e) { 844 throw e; 845 } catch (Exception e) { 846 throw new MonetaryException("Query failed: " + query, e); 847 } 848 } 849 850 public static FastMoney from(MonetaryAmount amount) { 851 if (FastMoney.class.isInstance(amount)) { 852 return FastMoney.class.cast(amount); 853 } 854 return new FastMoney(amount.getNumber(), amount.getCurrency(), false); 855 } 856 857 /** 858 * Obtains an instance of FastMoney from a text string such as 'EUR 25.25'. 859 * 860 * @param text the text to parse not null 861 * @return FastMoney instance 862 * @throws NumberFormatException if the amount is not a number 863 * @throws javax.money.UnknownCurrencyException if the currency is not resolvable 864 */ 865 public static FastMoney parse(CharSequence text) { 866 return parse(text, DEFAULT_FORMATTER); 867 } 868 869 /** 870 * Obtains an instance of FastMoney from a text using specific formatter. 871 * 872 * @param text the text to parse not null 873 * @param formatter the formatter to use not null 874 * @return FastMoney instance 875 */ 876 public static FastMoney parse(CharSequence text, MonetaryAmountFormat formatter) { 877 return from(formatter.parse(text)); 878 } 879 880 private static final ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat 881 .of(ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle.FAST_MONEY); 882 883 private BigDecimal getBigDecimal() { 884 return BigDecimal.valueOf(this.number).movePointLeft(SCALE); 885 } 886 887 @Override 888 public FastMoney multiply(double multiplicand) { 889 NumberVerifier.checkNoInfinityOrNaN(multiplicand); 890 if (multiplicand == 1.0) { 891 return this; 892 } 893 if (multiplicand == 0.0) { 894 return new FastMoney(0, this.currency); 895 } 896 return new FastMoney(Math.round(this.number * multiplicand), this.currency); 897 } 898 899 @Override 900 public FastMoney divide(long divisor) { 901 if (divisor == 1L) { 902 return this; 903 } 904 return new FastMoney(this.number / divisor, this.currency); 905 } 906 907 @Override 908 public FastMoney divide(double divisor) { 909 if (NumberVerifier.isInfinityAndNotNaN(divisor)) { 910 return new FastMoney(0L, getCurrency()); 911 } 912 if (divisor == 1.0d) { 913 return this; 914 } 915 return new FastMoney(Math.round(this.number / divisor), getCurrency()); 916 } 917 918 @Override 919 public FastMoney remainder(long divisor) { 920 return remainder(BigDecimal.valueOf(divisor)); 921 } 922 923 @Override 924 public FastMoney remainder(double divisor) { 925 if (NumberVerifier.isInfinityAndNotNaN(divisor)) { 926 return new FastMoney(0L, getCurrency()); 927 } 928 return remainder(new BigDecimal(String.valueOf(divisor))); 929 } 930 931 @Override 932 public FastMoney[] divideAndRemainder(long divisor) { 933 return divideAndRemainder(BigDecimal.valueOf(divisor)); 934 } 935 936 @Override 937 public FastMoney[] divideAndRemainder(double divisor) { 938 if (NumberVerifier.isInfinityAndNotNaN(divisor)) { 939 FastMoney zero = new FastMoney(0L, getCurrency()); 940 return new FastMoney[]{zero, zero}; 941 } else if (Double.isNaN(divisor)) { 942 throw new ArithmeticException("Not a number: NaN."); 943 } 944 return divideAndRemainder(new BigDecimal(String.valueOf(divisor))); 945 } 946 947 @Override 948 public FastMoney stripTrailingZeros() { 949 return this; 950 } 951 952 @Override 953 public FastMoney multiply(long multiplicand) { 954 if (multiplicand == 1) { 955 return this; 956 } 957 if (multiplicand == 0) { 958 return new FastMoney(0L, this.currency); 959 } 960 return new FastMoney(multiplyExact(multiplicand, this.number), this.currency); 961 } 962 963 @Override 964 public FastMoney divideToIntegralValue(long divisor) { 965 if (divisor == 1) { 966 return this; 967 } 968 return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); 969 } 970 971 @Override 972 public FastMoney divideToIntegralValue(double divisor) { 973 if (NumberVerifier.isInfinityAndNotNaN(divisor)) { 974 return new FastMoney(0L, getCurrency()); 975 } 976 if (divisor == 1.0) { 977 return this; 978 } 979 return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); 980 } 981 982 @Override 983 public MonetaryAmountFactory<FastMoney> getFactory() { 984 return new FastMoneyAmountFactory().setAmount(this); 985 } 986 987}