1c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinpackage org.hamcrest.number;
2c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
3c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport org.hamcrest.Description;
4c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport org.hamcrest.Matcher;
5c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport org.hamcrest.TypeSafeMatcher;
6c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
7c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport java.math.BigDecimal;
8c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinimport java.math.MathContext;
9c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
10c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffinpublic class BigDecimalCloseTo extends TypeSafeMatcher<BigDecimal> {
11c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
12c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  private final BigDecimal delta;
13c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  private final BigDecimal value;
14c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
15c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  public BigDecimalCloseTo(BigDecimal value, BigDecimal error) {
16c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      this.delta = error;
17c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      this.value = value;
18c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  }
19c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
20c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  @Override
21c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  public boolean matchesSafely(BigDecimal item) {
22c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      return actualDelta(item).compareTo(BigDecimal.ZERO) <= 0;
23c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  }
24c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
25c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  @Override
26c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  public void describeMismatchSafely(BigDecimal item, Description mismatchDescription) {
27c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      mismatchDescription.appendValue(item)
28c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendText(" differed by ")
29c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendValue(actualDelta(item))
30c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendText(" more than delta ")
31c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendValue(delta);
32c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  }
33c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
34c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  @Override
35c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  public void describeTo(Description description) {
36c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      description.appendText("a numeric value within ")
37c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendValue(delta)
38c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendText(" of ")
39c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin              .appendValue(value);
40c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  }
41c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
42c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  private BigDecimal actualDelta(BigDecimal item) {
43c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      return item.subtract(value, MathContext.DECIMAL128).abs().subtract(delta, MathContext.DECIMAL128).stripTrailingZeros();
44c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  }
45c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
46c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  /**
47c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * Creates a matcher of {@link java.math.BigDecimal}s that matches when an examined BigDecimal is equal
48c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * to the specified <code>operand</code>, within a range of +/- <code>error</code>. The comparison for equality
49c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * is done by BigDecimals {@link java.math.BigDecimal#compareTo(java.math.BigDecimal)} method.
50c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * For example:
51c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * <pre>assertThat(new BigDecimal("1.03"), is(closeTo(new BigDecimal("1.0"), new BigDecimal("0.03"))))</pre>
52c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   *
53c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * @param operand
54c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   *     the expected value of matching BigDecimals
55c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   * @param error
56c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   *     the delta (+/-) within which matches will be allowed
57c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin   */
58c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  public static Matcher<BigDecimal> closeTo(BigDecimal operand, BigDecimal error) {
59c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin      return new BigDecimalCloseTo(operand, error);
60c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin  }
61c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
62c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin}
63c1dbb44e71e47410ad5685aba3ef3fccb095a2b4Paul Duffin
64