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