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