Skip to content
This repository was archived by the owner on Jan 18, 2022. It is now read-only.

Commit 32452e2

Browse files
author
Otávio Santana
authored
Merge pull request #27 from sgrimm-sg/exchange
Fix DefaultExchangeRate's equals and hashCode methods
2 parents efb1673 + e81161a commit 32452e2

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

src/main/java/org/javamoney/moneta/convert/DefaultExchangeRate.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.javamoney.moneta.convert;
1717

1818
import java.io.Serializable;
19+
import java.math.BigDecimal;
1920
import java.util.ArrayList;
2021
import java.util.List;
2122
import java.util.Objects;
@@ -251,7 +252,24 @@ public String toString() {
251252
*/
252253
@Override
253254
public int hashCode() {
254-
return Objects.hash(base, conversionContext, factor, term, chain);
255+
// Numerically equal factors should produce the same hash code, so hash a normalized
256+
// version of the factor rather than the NumberValue object.
257+
BigDecimal normalizedFactor = factor.numberValue(BigDecimal.class).stripTrailingZeros();
258+
259+
// The exchange rate chain includes a reference to "this" if the caller doesn't explicitly
260+
// set a chain in the builder, so we can't naively hash the chain or we'll get infinite
261+
// recursion.
262+
int chainHash = 0;
263+
for (ExchangeRate chainedRate : chain) {
264+
if (chainedRate == this) {
265+
// Use a constant to represent the presence of this object in the chain.
266+
chainHash = Objects.hash(chainHash, "this");
267+
} else {
268+
chainHash = Objects.hash(chainHash, chainedRate);
269+
}
270+
}
271+
272+
return Objects.hash(base, conversionContext, normalizedFactor, term, chainHash);
255273
}
256274

257275
/*
@@ -268,7 +286,8 @@ public boolean equals(Object obj) {
268286
DefaultExchangeRate other = (DefaultExchangeRate) obj;
269287
return Objects.equals(base, other.base) &&
270288
Objects.equals(conversionContext, other.conversionContext) &&
271-
Objects.equals(factor, other.factor) && Objects.equals(term, other.term);
289+
factor.compareTo(other.factor) == 0 &&
290+
Objects.equals(term, other.term);
272291
}
273292
return false;
274293
}

src/test/java/org/javamoney/moneta/function/ExchangeRateSimpleTest.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
import org.javamoney.moneta.spi.DefaultNumberValue;
2727
import org.testng.annotations.Test;
2828

29+
import java.math.BigDecimal;
30+
2931
public class ExchangeRateSimpleTest {
3032
private static final CurrencyUnit EUR = Monetary.getCurrency("EUR");
3133
private static final CurrencyUnit GBP = Monetary.getCurrency("GBP");
3234

3335
@Test
3436
public void equalsTest() {
3537
DefaultNumberValue factor = new DefaultNumberValue(1.1);
38+
DefaultNumberValue bigDecimalFactor = new DefaultNumberValue(new BigDecimal("1.1"));
3639

3740
ExchangeRate rate1 = new ExchangeRateBuilder("myprovider", RateType.ANY)
3841
.setBase(EUR)
@@ -46,6 +49,15 @@ public void equalsTest() {
4649
.setFactor(factor)
4750
.build();
4851

49-
assertEquals(rate1, rate2);
52+
ExchangeRate rate3 = new ExchangeRateBuilder("myprovider", RateType.ANY)
53+
.setBase(EUR)
54+
.setTerm(GBP)
55+
.setFactor(bigDecimalFactor)
56+
.build();
57+
58+
assertEquals(rate1, rate2, "Rates with same factor");
59+
assertEquals(rate1, rate3, "Rates with numerically equal factor");
60+
assertEquals(rate1.hashCode(), rate2.hashCode(), "Hashes with same factor");
61+
assertEquals(rate1.hashCode(), rate3.hashCode(), "Hashes with numerically equal factor");
5062
}
5163
}

0 commit comments

Comments
 (0)