001/* 002 * Copyright (c) 2012, 2013, Credit Suisse (Anatole Tresch), Werner Keil. Licensed under the Apache 003 * License, Version 2.0 (the "License"); you may not use this file except in compliance with the 004 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 005 * Unless required by applicable law or agreed to in writing, software distributed under the License 006 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 007 * or implied. See the License for the specific language governing permissions and limitations under 008 * the License. Contributors: Anatole Tresch - initial implementation Werner Keil - extensions and 009 * adaptions. 010 */ 011package org.javamoney.moneta; 012 013import org.javamoney.moneta.internal.FastMoneyAmountFactory; 014import org.javamoney.moneta.spi.AbstractMoney; 015import org.javamoney.moneta.spi.DefaultNumberValue; 016 017import javax.money.*; 018import java.io.Serializable; 019import java.math.BigDecimal; 020import java.util.Objects; 021 022// github.com/JavaMoney/jsr354-ri.git 023 024/** 025 * <type>long</type> based implementation of {@link MonetaryAmount}. This class internally uses a 026 * single long number as numeric representation, which basically is interpreted as minor units.<br/> 027 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money}, 028 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision. 029 * As an example performing the following calculation one million times, results in slightly 030 * different results: 031 * <p/> 032 * <pre> 033 * Money money1 = money1.add(Money.of(EURO, 1234567.3444)); 034 * money1 = money1.subtract(Money.of(EURO, 232323)); 035 * money1 = money1.multiply(3.4); 036 * money1 = money1.divide(5.456); 037 * </pre> 038 * <p/> 039 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in 040 * 3680 ms, or roughly 3ns/loop. 041 * <p/> 042 * whrereas 043 * <p/> 044 * <pre> 045 * FastMoney money1 = money1.add(FastMoney.of(EURO, 1234567.3444)); 046 * money1 = money1.subtract(FastMoney.of(EURO, 232323)); 047 * money1 = money1.multiply(3.4); 048 * money1 = money1.divide(5.456); 049 * </pre> 050 * <p/> 051 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms, 052 * which is less than 1ns/loop. 053 * <p/> 054 * Also note than mixing up types my drastically change the performance behavior. E.g. replacing the 055 * code above with the following: * 056 * <p/> 057 * <pre> 058 * FastMoney money1 = money1.add(Money.of(EURO, 1234567.3444)); 059 * money1 = money1.subtract(FastMoney.of(EURO, 232323)); 060 * money1 = money1.multiply(3.4); 061 * money1 = money1.divide(5.456); 062 * </pre> 063 * <p/> 064 * executed one million (1000000) times may execute significantly longer, since monetary amount type 065 * conversion is involved. 066 * <p/> 067 * Basically, when mixing amount implementations, the performance of the amount, on which most of 068 * the operations are operated, has the most significant impact on the overall performance behavior. 069 * 070 * @author Anatole Tresch 071 * @author Werner Keil 072 * @version 0.5.2 073 */ 074public final class FastMoney extends AbstractMoney implements Comparable<MonetaryAmount>, Serializable{ 075 076 private static final long serialVersionUID = 1L; 077 078 /** 079 * The numeric part of this amount. 080 */ 081 private long number; 082 083 /** 084 * The number value. 085 */ 086 private transient NumberValue numberValue; 087 088 /** 089 * The current scale represented by the number. 090 */ 091 private static final int SCALE = 5; 092 093 /** 094 * the {@link MonetaryContext} used by this instance, e.g. on division. 095 */ 096 private static final MonetaryContext MONETARY_CONTEXT = 097 new MonetaryContext.Builder(FastMoney.class).setMaxScale(SCALE).setFixedScale(true) 098 .setPrecision(String.valueOf(Integer.MAX_VALUE).length()).create(); 099 100 /** 101 * Required for deserialization only. 102 */ 103 private FastMoney(){ 104 } 105 106 /** 107 * Creates a new instance os {@link FastMoney}. 108 * 109 * @param currency the currency, not null. 110 * @param number the amount, not null. 111 */ 112 private FastMoney(CurrencyUnit currency, Number number){ 113 super(currency, MONETARY_CONTEXT); 114 Objects.requireNonNull(number, "Number is required."); 115 this.number = getInternalNumber(number); 116 this.numberValue = new DefaultNumberValue(number); 117 } 118 119 /** 120 * Creates a new instance os {@link FastMoney}. 121 * 122 * @param currency the currency, not null. 123 * @param numberBinding the amount, not null. 124 */ 125 private FastMoney(CurrencyUnit currency, NumberValue numberBinding){ 126 super(currency, MONETARY_CONTEXT); 127 Objects.requireNonNull(numberBinding, "Number is required."); 128 this.number = getInternalNumber(numberBinding.numberValue(BigDecimal.class)); 129 } 130 131 private long getInternalNumber(Number number){ 132 BigDecimal bd = getBigDecimal(number); 133 return bd.movePointRight(SCALE).longValue(); 134 } 135 136 private FastMoney(CurrencyUnit currency, long number){ 137 super(currency, MONETARY_CONTEXT); 138 Objects.requireNonNull(currency, "Currency is required."); 139 this.currency = currency; 140 this.number = number; 141 } 142 143 /** 144 * Static factory method for creating a new instance of {@link FastMoney}. 145 * 146 * @param currency The target currency, not null. 147 * @param numberBinding The numeric part, not null. 148 * @return A new instance of {@link FastMoney}. 149 */ 150 public static FastMoney of(CurrencyUnit currency, NumberValue numberBinding){ 151 return new FastMoney(currency, numberBinding); 152 } 153 154 /** 155 * Static factory method for creating a new instance of {@link FastMoney}. 156 * 157 * @param currency The target currency, not null. 158 * @param number The numeric part, not null. 159 * @return A new instance of {@link FastMoney}. 160 */ 161 public static FastMoney of(CurrencyUnit currency, Number number){ 162 return new FastMoney(currency, number); 163 } 164 165 /** 166 * Static factory method for creating a new instance of {@link FastMoney}. 167 * 168 * @param currencyCode The target currency as currency code. 169 * @param number The numeric part, not null. 170 * @return A new instance of {@link FastMoney}. 171 */ 172 public static FastMoney of(String currencyCode, Number number){ 173 CurrencyUnit currency = MonetaryCurrencies.getCurrency(currencyCode); 174 return of(currency, number); 175 } 176 177 /* 178 * @see java.lang.Comparable#compareTo(java.lang.Object) 179 */ 180 public int compareTo(MonetaryAmount o){ 181 int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); 182 if(compare == 0){ 183 compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class)); 184 } 185 return compare; 186 } 187 188 /* 189 * (non-Javadoc) 190 * @see java.lang.Object#hashCode() 191 */ 192 @Override 193 public int hashCode(){ 194 final int prime = 31; 195 int result = 1; 196 result = prime * result + ((currency == null) ? 0 : currency.hashCode()); 197 result = prime * result + (int) number; 198 return result; 199 } 200 201 /* 202 * (non-Javadoc) 203 * @see java.lang.Object#equals(java.lang.Object) 204 */ 205 @Override 206 public boolean equals(Object obj){ 207 if(this == obj) 208 return true; 209 if(obj == null) 210 return false; 211 if(getClass() != obj.getClass()) 212 return false; 213 FastMoney other = (FastMoney) obj; 214 if(currency == null){ 215 if(other.getCurrency() != null) 216 return false; 217 }else if(!currency.equals(other.getCurrency())) 218 return false; 219 if(number != other.number) 220 return false; 221 return true; 222 } 223 224 /* 225 * (non-Javadoc) 226 * @see javax.money.MonetaryAmount#getCurrency() 227 */ 228 public CurrencyUnit getCurrency(){ 229 return currency; 230 } 231 232 /* 233 * (non-Javadoc) 234 * @see javax.money.MonetaryAmount#abs() 235 */ 236 public FastMoney abs(){ 237 if(this.isPositiveOrZero()){ 238 return this; 239 } 240 return this.negate(); 241 } 242 243 // Arithmetic Operations 244 245 /* 246 * (non-Javadoc) 247 * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount) 248 */ 249 public FastMoney add(MonetaryAmount amount){ 250 checkAmountParameter(amount); 251 return new FastMoney(getCurrency(), this.number + getInternalNumber(amount.getNumber())); 252 } 253 254 /* 255 * (non-Javadoc) 256 * @see javax.money.MonetaryAmount#divide(java.lang.Number) 257 */ 258 public FastMoney divide(Number divisor){ 259 checkNumber(divisor); 260 return new FastMoney(getCurrency(), Math.round(this.number / divisor.doubleValue())); 261 } 262 263 /* 264 * (non-Javadoc) 265 * @see javax.money.MonetaryAmount#divideAndRemainder(java.lang.Number) 266 */ 267 public FastMoney[] divideAndRemainder(Number divisor){ 268 checkNumber(divisor); 269 BigDecimal div = getBigDecimal(divisor); 270 BigDecimal[] res = getBigDecimal().divideAndRemainder(div); 271 return new FastMoney[]{new FastMoney(getCurrency(), res[0]), new FastMoney(getCurrency(), res[1])}; 272 } 273 274 /* 275 * (non-Javadoc) 276 * @see javax.money.MonetaryAmount#divideToIntegralValue(java.lang.Number) 277 */ 278 public FastMoney divideToIntegralValue(Number divisor){ 279 checkNumber(divisor); 280 BigDecimal div = getBigDecimal(divisor); 281 return new FastMoney(getCurrency(), getBigDecimal().divideToIntegralValue(div)); 282 } 283 284 public FastMoney multiply(Number multiplicand){ 285 checkNumber(multiplicand); 286 double multiplicandNum = multiplicand.doubleValue(); 287 return new FastMoney(getCurrency(), Math.round(this.number * multiplicandNum)); 288 } 289 290 /* 291 * (non-Javadoc) 292 * @see javax.money.MonetaryAmount#negate() 293 */ 294 public FastMoney negate(){ 295 return new FastMoney(getCurrency(), this.number * -1); 296 } 297 298 /* 299 * (non-Javadoc) 300 * @see javax.money.MonetaryAmount#plus() 301 */ 302 public FastMoney plus(){ 303 if(this.number >= 0){ 304 return this; 305 } 306 return new FastMoney(getCurrency(), this.number * -1); 307 } 308 309 /* 310 * (non-Javadoc) 311 * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount) 312 */ 313 public FastMoney subtract(MonetaryAmount subtrahend){ 314 checkAmountParameter(subtrahend); 315 if(subtrahend.isZero()){ 316 return this; 317 } 318 return new FastMoney(getCurrency(), this.number - getInternalNumber(subtrahend.getNumber())); 319 } 320 321 /* 322 * (non-Javadoc) 323 * @see javax.money.MonetaryAmount#remainder(java.lang.Number) 324 */ 325 public FastMoney remainder(Number divisor){ 326 checkNumber(divisor); 327 return new FastMoney(getCurrency(), this.number % getInternalNumber(divisor)); 328 } 329 330 /* 331 * (non-Javadoc) 332 * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int) 333 */ 334 public FastMoney scaleByPowerOfTen(int n){ 335 return new FastMoney(getCurrency(), getBigDecimal().scaleByPowerOfTen(n)); 336 } 337 338 /* 339 * (non-Javadoc) 340 * @see javax.money.MonetaryAmount#isZero() 341 */ 342 public boolean isZero(){ 343 return this.number == 0L; 344 } 345 346 /* 347 * (non-Javadoc) 348 * @see javax.money.MonetaryAmount#isPositive() 349 */ 350 public boolean isPositive(){ 351 return this.number > 0L; 352 } 353 354 /* 355 * (non-Javadoc) 356 * @see javax.money.MonetaryAmount#isPositiveOrZero() 357 */ 358 public boolean isPositiveOrZero(){ 359 return this.number >= 0L; 360 } 361 362 /* 363 * (non-Javadoc) 364 * @see javax.money.MonetaryAmount#isNegative() 365 */ 366 public boolean isNegative(){ 367 return this.number < 0L; 368 } 369 370 /* 371 * (non-Javadoc) 372 * @see javax.money.MonetaryAmount#isNegativeOrZero() 373 */ 374 public boolean isNegativeOrZero(){ 375 return this.number <= 0L; 376 } 377 378 /* 379 * (non-Javadoc) 380 * @see javax.money.MonetaryAmount#getScale() 381 */ 382 public int getScale(){ 383 return FastMoney.SCALE; 384 } 385 386 /* 387 * (non-Javadoc) 388 * @see javax.money.MonetaryAmount#getPrecision() 389 */ 390 public int getPrecision(){ 391 return getNumber().numberValue(BigDecimal.class).precision(); 392 } 393 394 /* 395 * (non-Javadoc) 396 * @see javax.money.MonetaryAmount#signum() 397 */ 398 399 public int signum(){ 400 if(this.number < 0){ 401 return -1; 402 } 403 if(this.number == 0){ 404 return 0; 405 } 406 return 1; 407 } 408 409 /* 410 * (non-Javadoc) 411 * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount) 412 */ 413 public boolean isLessThan(MonetaryAmount amount){ 414 checkAmountParameter(amount); 415 return this.number < getInternalNumber(amount.getNumber()); 416 } 417 418 /* 419 * (non-Javadoc) 420 * @see javax.money.MonetaryAmount#lessThan(java.lang.Number) 421 */ 422 public boolean isLessThan(Number number){ 423 checkNumber(number); 424 return this.number < getInternalNumber(number); 425 } 426 427 /* 428 * (non-Javadoc) 429 * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount) 430 */ 431 public boolean isLessThanOrEqualTo(MonetaryAmount amount){ 432 checkAmountParameter(amount); 433 return this.number <= getInternalNumber(amount.getNumber()); 434 } 435 436 /* 437 * (non-Javadoc) 438 * @see javax.money.MonetaryAmount#lessThanOrEqualTo(java.lang.Number) 439 */ 440 public boolean isLessThanOrEqualTo(Number number){ 441 checkNumber(number); 442 return this.number <= getInternalNumber(number); 443 } 444 445 /* 446 * (non-Javadoc) 447 * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount) 448 */ 449 public boolean isGreaterThan(MonetaryAmount amount){ 450 checkAmountParameter(amount); 451 return this.number > getInternalNumber(amount.getNumber()); 452 } 453 454 /* 455 * (non-Javadoc) 456 * @see javax.money.MonetaryAmount#greaterThan(java.lang.Number) 457 */ 458 public boolean isGreaterThan(Number number){ 459 checkNumber(number); 460 return this.number > getInternalNumber(number); 461 } 462 463 /* 464 * (non-Javadoc) 465 * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see 466 */ 467 public boolean isGreaterThanOrEqualTo(MonetaryAmount amount){ 468 checkAmountParameter(amount); 469 return this.number >= getInternalNumber(amount.getNumber()); 470 } 471 472 /* 473 * (non-Javadoc) 474 * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(java.lang.Number) 475 */ 476 public boolean isGreaterThanOrEqualTo(Number number){ 477 checkNumber(number); 478 return this.number >= getInternalNumber(number); 479 } 480 481 /* 482 * (non-Javadoc) 483 * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount) 484 */ 485 public boolean isEqualTo(MonetaryAmount amount){ 486 checkAmountParameter(amount); 487 return this.number == getInternalNumber(amount.getNumber()); 488 } 489 490 /* 491 * (non-Javadoc) 492 * @see javax.money.MonetaryAmount#hasSameNumberAs(java.lang.Number) 493 */ 494 public boolean hasSameNumberAs(Number number){ 495 checkNumber(number); 496 return this.number == getInternalNumber(number); 497 } 498 499 /* 500 * (non-Javadoc) 501 * @see javax.money.MonetaryAmount#isNotEqualTo(javax.money.MonetaryAmount) 502 */ 503 public boolean isNotEqualTo(MonetaryAmount amount){ 504 checkAmountParameter(amount); 505 return this.number != getInternalNumber(amount.getNumber()); 506 } 507 508 /* 509 * (non-Javadoc) 510 * @see javax.money.MonetaryAmount#isNotEqualTo(java.lang.Number) 511 */ 512 public boolean isNotEqualTo(Number number){ 513 checkNumber(number); 514 return this.number != getInternalNumber(number); 515 } 516 517 /** 518 * Gets the number representation of the numeric value of this item. 519 * 520 * @return The {@link Number} represention matching best. 521 */ 522 @Override 523 public NumberValue getNumber(){ 524 if(numberValue == null){ 525 numberValue = new DefaultNumberValue(getBigDecimal()); 526 } 527 return numberValue; 528 } 529 530 // Static Factory Methods 531 532 /* 533 * (non-Javadoc) 534 * @see java.lang.Object#toString() 535 */ 536 @Override 537 public String toString(){ 538 return currency.toString() + ' ' + getBigDecimal(); 539 } 540 541 // Internal helper methods 542 543 /** 544 * Internal method to check for correct number parameter. 545 * 546 * @param number 547 * @throws IllegalArgumentException If the number is null 548 */ 549 private void checkNumber(Number number){ 550 Objects.requireNonNull(number, "Number is required."); 551 } 552 553 /* 554 * }(non-Javadoc) 555 * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster) 556 */ 557 @Override 558 public FastMoney with(MonetaryOperator operator){ 559 Objects.requireNonNull(operator); 560 try{ 561 return FastMoney.class.cast(operator.apply(this)); 562 } 563 catch(Exception e){ 564 throw new MonetaryException("Operator failed: " + operator, e); 565 } 566 } 567 568 @Override 569 public <R> R query(MonetaryQuery<R> query){ 570 Objects.requireNonNull(query); 571 try{ 572 return query.queryFrom(this); 573 } 574 catch(Exception e){ 575 throw new MonetaryException("Query failed: " + query, e); 576 } 577 } 578 579 public static FastMoney from(MonetaryAmount amount){ 580 if(FastMoney.class == amount.getClass()){ 581 return (FastMoney) amount; 582 }else if(Money.class == amount.getClass()){ 583 return new FastMoney(amount.getCurrency(), amount.getNumber()); 584 } 585 return new FastMoney(amount.getCurrency(), amount.getNumber()); 586 } 587 588 private BigDecimal getBigDecimal(){ 589 return BigDecimal.valueOf(this.number).movePointLeft(SCALE); 590 } 591 592 @Override 593 public MonetaryContext getMonetaryContext(){ 594 return MONETARY_CONTEXT; 595 } 596 597 @Override 598 public FastMoney multiply(double amount){ 599 if(amount == 1.0){ 600 return this; 601 } 602 if(amount == 0.0){ 603 return new FastMoney(this.currency, 0); 604 } 605 return new FastMoney(this.currency, Math.round(this.number * amount)); 606 } 607 608 @Override 609 public FastMoney divide(long amount){ 610 if(amount == 1){ 611 return this; 612 } 613 return new FastMoney(this.currency, this.number / amount); 614 } 615 616 @Override 617 public FastMoney divide(double number){ 618 return new FastMoney(getCurrency(), Math.round(this.number / number)); 619 } 620 621 @Override 622 public FastMoney remainder(long number){ 623 return remainder(BigDecimal.valueOf(number)); 624 } 625 626 @Override 627 public FastMoney remainder(double amount){ 628 return remainder(new BigDecimal(String.valueOf(amount))); 629 } 630 631 @Override 632 public FastMoney[] divideAndRemainder(long amount){ 633 return divideAndRemainder(BigDecimal.valueOf(amount)); 634 } 635 636 @Override 637 public FastMoney[] divideAndRemainder(double amount){ 638 return divideAndRemainder(new BigDecimal(String.valueOf(amount))); 639 } 640 641 @Override 642 public FastMoney stripTrailingZeros(){ 643 return this; 644 } 645 646 @Override 647 public FastMoney multiply(long multiplicand){ 648 if(multiplicand == 1){ 649 return this; 650 } 651 if(multiplicand == 0){ 652 return new FastMoney(this.currency, 0L); 653 } 654 return new FastMoney(this.currency, multiplicand * this.number); 655 } 656 657 @Override 658 public FastMoney divideToIntegralValue(long divisor){ 659 if(divisor == 1){ 660 return this; 661 } 662 return divideToIntegralValue(getBigDecimal(divisor)); 663 } 664 665 @Override 666 public FastMoney divideToIntegralValue(double divisor){ 667 if(divisor == 1.0){ 668 return this; 669 } 670 return divideToIntegralValue(getBigDecimal(divisor)); 671 } 672 673 @Override 674 protected MonetaryContext getDefaultMonetaryContext(){ 675 return MONETARY_CONTEXT; 676 } 677 678 @Override 679 public MonetaryAmountFactory<FastMoney> getFactory(){ 680 return new FastMoneyAmountFactory().setAmount(this); 681 } 682 683}