Skip to content

Commit

Permalink
Reworked number implementations to comply to Java type semantic.
Browse files Browse the repository at this point in the history
Signed-off-by: Juergen Fickel <juergen.fickel@bosch-si.com>
  • Loading branch information
Juergen Fickel committed Dec 5, 2018
1 parent 0967980 commit d057ac9
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 88 deletions.
42 changes: 22 additions & 20 deletions json/src/main/java/org/eclipse/ditto/json/AbstractJsonNumber.java
Expand Up @@ -12,8 +12,6 @@

import static java.util.Objects.requireNonNull;

import java.util.Objects;

/**
* Abstract base implementation of JSON number types.
* This class provides common functionality in order to keep its sub-classes as small as possible.
Expand Down Expand Up @@ -44,14 +42,35 @@ public boolean isInt() {
return false;
}

@Override
public int asInt() {
if (isInt()) {
return value.intValue();
}
return super.asInt();
}

@Override
public boolean isLong() {
return false;
}

@Override
public long asLong() {
if (isLong()) {
return value.longValue();
}
return super.asLong();
}

@Override
public boolean isDouble() {
return false;
return true;
}

@Override
public double asDouble() {
return value.doubleValue();
}

/**
Expand All @@ -63,23 +82,6 @@ T getValue() {
return value;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final AbstractJsonNumber<?> that = (AbstractJsonNumber<?>) o;
return Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return Objects.hash(value);
}

@Override
public String toString() {
return value.toString();
Expand Down
43 changes: 43 additions & 0 deletions json/src/main/java/org/eclipse/ditto/json/ImmutableJsonDouble.java
Expand Up @@ -10,6 +10,8 @@
*/
package org.eclipse.ditto.json;

import java.util.Objects;

import javax.annotation.concurrent.Immutable;

/**
Expand Down Expand Up @@ -41,4 +43,45 @@ public double asDouble() {
return getValue();
}

@Override
public boolean isInt() {
final Double value = getValue();
return value.intValue() == value;
}

@Override
public boolean isLong() {
final Double value = getValue();
return value.longValue() == value;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o instanceof AbstractJsonNumber) {
final AbstractJsonNumber that = (AbstractJsonNumber) o;
if (isInt() && that.isInt()) {
return Objects.equals(asInt(), that.asInt());
}
if (isLong() && that.isLong()) {
return Objects.equals(asLong(), that.asLong());
}
return Objects.equals(getValue(), that.getValue());
}
return false;
}

@Override
public int hashCode() {
if (isInt()) {
return asInt();
}
if (isLong()) {
return Long.hashCode(asLong());
}
return getValue().hashCode();
}

}
22 changes: 10 additions & 12 deletions json/src/main/java/org/eclipse/ditto/json/ImmutableJsonInt.java
Expand Up @@ -10,6 +10,9 @@
*/
package org.eclipse.ditto.json;

import java.util.Objects;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/**
Expand Down Expand Up @@ -37,32 +40,27 @@ public boolean isInt() {
}

@Override
public int asInt() {
return getValue();
}

@Override
public long asLong() {
return getValue();
public boolean isLong() {
return true;
}

@Override
public boolean equals(final Object o) {
if (super.equals(o)) {
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (o instanceof AbstractJsonNumber) {
final AbstractJsonNumber that = (AbstractJsonNumber) o;
if (that.isLong()) {
return getValue() == that.asLong();
if (that.isInt()) {
return Objects.equals(getValue(), that.asInt());
}
}
return false;
}

@Override
public int hashCode() {
return super.hashCode();
return getValue();
}

}
23 changes: 15 additions & 8 deletions json/src/main/java/org/eclipse/ditto/json/ImmutableJsonLong.java
Expand Up @@ -10,6 +10,8 @@
*/
package org.eclipse.ditto.json;

import java.util.Objects;

import javax.annotation.concurrent.Immutable;

/**
Expand All @@ -32,32 +34,37 @@ public static ImmutableJsonLong of(final long value) {
}

@Override
public boolean isLong() {
return true;
public boolean isInt() {
final Long value = getValue();
return value.intValue() == value;
}

@Override
public long asLong() {
return getValue();
public boolean isLong() {
return true;
}

@Override
public boolean equals(final Object o) {
if (super.equals(o)) {
if (this == o) {
return true;
}
if (o instanceof AbstractJsonNumber) {
final AbstractJsonNumber that = (AbstractJsonNumber) o;
if (that.isInt()) {
return getValue() == that.asInt();
if (that.isLong()) {
return Objects.equals(getValue(), that.asLong());
}
}
return false;
}

@Override
public int hashCode() {
return super.hashCode();
final Long value = getValue();
if (isInt()) {
return value.intValue();
}
return value.hashCode();
}

}
122 changes: 108 additions & 14 deletions json/src/test/java/org/eclipse/ditto/json/ImmutableJsonDoubleTest.java
Expand Up @@ -32,9 +32,9 @@
public final class ImmutableJsonDoubleTest {

@Parameterized.Parameters(name = "{0}")
public static Collection<Double> intValues() {
public static Collection<Double> doubleValues() {
return Arrays.asList(Double.NEGATIVE_INFINITY, Double.MIN_VALUE, Double.MAX_VALUE, Double.POSITIVE_INFINITY,
13.3742D, 1.081542E124D);
0D, 0.0D, -0D, -0.0D, 13.3742D, 1.081542E124D);
}

@Parameterized.Parameter
Expand All @@ -54,12 +54,53 @@ public void assertImmutability() {

@Test
public void testHashCodeAndEquals() {
final Double red = 23.0D;
final Double black = 42.23D;

EqualsVerifier.forClass(ImmutableJsonDouble.class)
.withRedefinedSuperclass()
.usingGetClass()
.withNonnullFields("value")
.withPrefabValues(Number.class, red, black)
.verify();
}

@Test
public void jsonDoubleEqualsJsonIntIfSameValue() {
final int value = Integer.MAX_VALUE;
final ImmutableJsonInt intValue = ImmutableJsonInt.of(value);
final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(value);

assertThat(underTest).isEqualTo(intValue);
}

@Test
public void jsonDoubleHasSameHashCodeAsJsonIntIfSameValue() {
final int value = Integer.MAX_VALUE;
final ImmutableJsonInt intValue = ImmutableJsonInt.of(value);
final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(value);

assertThat(underTest.hashCode()).isEqualTo(intValue.hashCode());
}

@Test
public void jsonDoubleEqualsJsonLongIfSameValue() {
final long value = Long.MAX_VALUE;
final ImmutableJsonLong longValue = ImmutableJsonLong.of(value);
final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(value);

assertThat(underTest).isEqualTo(longValue);
}

@Test
public void jsonDoubleHasSameHashCodeAsJsonLongIfSameValue() {
final long value = Long.MAX_VALUE;
final ImmutableJsonLong longValue = ImmutableJsonLong.of(value);
final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(value);

assertThat(underTest.hashCode()).isEqualTo(longValue.hashCode());
}

@Test
public void getValueReturnsExpected() {
assertThat(underTest.getValue()).isEqualTo(doubleValue);
Expand Down Expand Up @@ -91,12 +132,45 @@ public void isNumber() {
}

@Test
public void isNotInt() {
public void isIntReturnsExpected() {
assertThat(underTest.isInt()).isEqualTo(isDoubleValueWithinIntegerRange());
}

@Test
public void jsonDoubleWithFractionsIsNotInt() {
final double doubleValue = 23.42D;
final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(doubleValue);

assertThat(underTest.isInt()).isFalse();
}

@Test
public void isNotLong() {
public void jsonDoubleWithZeroFractionsWithinIntRangeIsEqualToSameInt() {
final double doubleValueWithFractions = 23.0D;
final int equivalentIntValue = 23;

final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(doubleValueWithFractions);

assertThat(underTest.isDouble()).isTrue();
assertThat(underTest.isInt()).isTrue();
assertThat(underTest.isLong()).isTrue();
assertThat(underTest.asInt()).isEqualTo(equivalentIntValue);
}

@Test
public void isLongReturnsExpected() {
if (isDoubleValueWithinIntegerRange() || isDoubleValueWithinLongRange()) {
assertThat(underTest.isLong()).isTrue();
} else {
assertThat(underTest.isLong()).isFalse();
}
}

@Test
public void jsonDoubleWithFractionsIsNotLong() {
final double doubleValue = 23.42D;
final ImmutableJsonDouble underTest = ImmutableJsonDouble.of(doubleValue);

assertThat(underTest.isLong()).isFalse();
}

Expand Down Expand Up @@ -129,19 +203,27 @@ public void tryToGetAsBoolean() {
}

@Test
public void tryToGetAsInt() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> underTest.asInt())
.withMessage("This JSON value is not an int: %s", underTest)
.withNoCause();
public void getAsIntBehavesCorrectly() {
if (underTest.isInt()) {
assertThat(underTest.asInt()).isEqualTo(Double.valueOf(doubleValue).intValue());
} else {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> underTest.asInt())
.withMessage("This JSON value is not an int: %s", underTest)
.withNoCause();
}
}

@Test
public void tryToGetAsLong() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> underTest.asLong())
.withMessage("This JSON value is not a long: %s", underTest)
.withNoCause();
public void getAsLongBehavesCorrectly() {
if (underTest.isLong()) {
assertThat(underTest.asLong()).isEqualTo(Double.valueOf(doubleValue).longValue());
} else {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> underTest.asLong())
.withMessage("This JSON value is not a long: %s", underTest)
.withNoCause();
}
}

@Test
Expand All @@ -168,4 +250,16 @@ public void tryToGetAsArray() {
.withNoCause();
}

private boolean isDoubleValueWithinIntegerRange() {
return Integer.MIN_VALUE <= doubleValue && Integer.MAX_VALUE >= doubleValue && hasNoFraction();
}

private boolean hasNoFraction() {
return 0 == doubleValue % 1;
}

private boolean isDoubleValueWithinLongRange() {
return Long.MIN_VALUE <= doubleValue && Long.MAX_VALUE >= doubleValue && hasNoFraction();
}

}

0 comments on commit d057ac9

Please sign in to comment.