Skip to content

Commit 4b997ed

Browse files
committed
#58 Rough draft of Number type for conversions
1 parent 8044bff commit 4b997ed

7 files changed

+542
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package ch.jalu.typeresolver;
2+
3+
import ch.jalu.typeresolver.numbers.ValueRange;
4+
5+
import java.util.Optional;
6+
7+
public interface NumberType<N> {
8+
9+
Class<N> getType();
10+
11+
N convertUnsafe(Number number);
12+
13+
Optional<N> convertIfNoLossOfMagnitude(Number number);
14+
15+
ValueRange getValueRange();
16+
17+
default boolean valueRangeIsEqualOrSupersetOf(NumberType<?> otherType) {
18+
return this.getValueRange().isEqualOrSupersetOf(otherType.getValueRange());
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package ch.jalu.typeresolver.numbers;
2+
3+
import java.util.Optional;
4+
5+
public interface ConvertingValueRange extends ValueRange { // should be a sealed type
6+
7+
Optional<Number> convertToTypeIfNoLossOfMagnitude(Number number);
8+
9+
boolean isEqualOrSupersetOf2(ConvertingValueRange other);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package ch.jalu.typeresolver.numbers;
2+
3+
import java.math.BigDecimal;
4+
import java.math.BigInteger;
5+
import java.util.Optional;
6+
import java.util.concurrent.atomic.AtomicInteger;
7+
import java.util.concurrent.atomic.AtomicLong;
8+
9+
public enum DecimalNumberRange implements ConvertingValueRange {
10+
11+
FLOAT(Float.MIN_VALUE, Float.MAX_VALUE),
12+
13+
DOUBLE(Double.MIN_VALUE, Double.MAX_VALUE);
14+
15+
private final BigDecimal minValue;
16+
private final BigDecimal maxValue;
17+
18+
DecimalNumberRange(double minValue, double maxValue) {
19+
this.minValue = BigDecimal.valueOf(minValue);
20+
this.maxValue = BigDecimal.valueOf(maxValue);
21+
}
22+
23+
@Override
24+
public BigDecimal getMinValue() {
25+
return minValue;
26+
}
27+
28+
@Override
29+
public BigDecimal getMaxValue() {
30+
return maxValue;
31+
}
32+
33+
@Override
34+
public boolean supportsDecimals() {
35+
return true;
36+
}
37+
38+
@Override
39+
public Optional<Number> convertToTypeIfNoLossOfMagnitude(Number number) {
40+
if (number instanceof Integer || number instanceof Long || number instanceof Float
41+
|| number instanceof AtomicInteger || number instanceof AtomicLong || number instanceof Short
42+
|| number instanceof Byte) {
43+
return Optional.of(toNumberTypeUnsafe(number));
44+
}
45+
if (number instanceof Double) {
46+
double value = (double) number;
47+
if (this == DOUBLE || Float.MIN_VALUE <= value && value <= Float.MAX_VALUE) {
48+
return Optional.of(value);
49+
}
50+
return Optional.empty();
51+
}
52+
if (number instanceof BigInteger) {
53+
BigDecimal bd = new BigDecimal((BigInteger) number);
54+
return minValue.compareTo(bd) <= 0 && maxValue.compareTo(bd) >= 0
55+
? Optional.of(number.doubleValue())
56+
: Optional.empty();
57+
}
58+
if (number instanceof BigDecimal) {
59+
BigDecimal bd = (BigDecimal) number;
60+
return minValue.compareTo(bd) <= 0 && maxValue.compareTo(bd) >= 0
61+
? Optional.of(number.doubleValue())
62+
: Optional.empty();
63+
}
64+
return Optional.empty(); // todo throw on unknown?
65+
}
66+
67+
@Override
68+
public boolean isEqualOrSupersetOf2(ConvertingValueRange other) {
69+
if (other instanceof NonDecimalNumberRange) {
70+
return true;
71+
} else if (other instanceof InfiniteNumberRange) {
72+
return false;
73+
} else if (other instanceof DecimalNumberRange) {
74+
return this != FLOAT || other != DOUBLE;
75+
}
76+
throw new IllegalStateException("No other range types expected");
77+
}
78+
79+
private Number toNumberTypeUnsafe(Number number) {
80+
switch (this) {
81+
case FLOAT:
82+
return number.floatValue();
83+
case DOUBLE:
84+
return number.doubleValue();
85+
default:
86+
throw new IllegalStateException("Unsupported range type: " + this);
87+
}
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package ch.jalu.typeresolver.numbers;
2+
3+
import java.math.BigDecimal;
4+
import java.math.BigInteger;
5+
import java.util.Optional;
6+
import java.util.concurrent.atomic.AtomicInteger;
7+
import java.util.concurrent.atomic.AtomicLong;
8+
9+
public enum InfiniteNumberRange implements ConvertingValueRange {
10+
11+
BIG_DECIMAL,
12+
13+
BIG_INTEGER;
14+
15+
@Override
16+
public BigDecimal getMinValue() {
17+
return null;
18+
}
19+
20+
@Override
21+
public BigDecimal getMaxValue() {
22+
return null;
23+
}
24+
25+
@Override
26+
public boolean supportsDecimals() {
27+
return this == BIG_DECIMAL;
28+
}
29+
30+
public static BigDecimal toBigDecimal(Number number) {
31+
if (number instanceof BigInteger) {
32+
return new BigDecimal((BigInteger) number);
33+
}
34+
if (number instanceof BigDecimal) {
35+
return (BigDecimal) number;
36+
}
37+
if (number instanceof Integer || number instanceof Long
38+
|| number instanceof AtomicInteger || number instanceof AtomicLong
39+
|| number instanceof Short || number instanceof Byte) {
40+
long value = number.longValue();
41+
return BigDecimal.valueOf(value);
42+
}
43+
if (number instanceof Double || number instanceof Float) {
44+
double value = number.doubleValue();
45+
return BigDecimal.valueOf(value);
46+
}
47+
return null; // todo
48+
}
49+
50+
public static BigInteger toBigInteger(Number number) {
51+
if (number instanceof BigInteger) {
52+
return (BigInteger) number;
53+
}
54+
if (number instanceof BigDecimal) {
55+
return ((BigDecimal) number).unscaledValue();
56+
}
57+
// todo extract type checks?
58+
if (number instanceof Integer || number instanceof Long
59+
|| number instanceof AtomicInteger || number instanceof AtomicLong
60+
|| number instanceof Short || number instanceof Byte) {
61+
long value = number.longValue();
62+
return BigInteger.valueOf(value);
63+
}
64+
if (number instanceof Double || number instanceof Float) {
65+
double value = number.doubleValue();
66+
return new BigInteger(Double.toString(value));
67+
}
68+
return null; // todo
69+
}
70+
71+
@Override
72+
public Optional<Number> convertToTypeIfNoLossOfMagnitude(Number number) {
73+
switch (this) {
74+
case BIG_DECIMAL:
75+
return Optional.of(toBigDecimal(number));
76+
case BIG_INTEGER:
77+
return Optional.of(toBigInteger(number));
78+
default:
79+
throw new IllegalStateException("Unsupported range type: " + this.name());
80+
}
81+
}
82+
83+
@Override
84+
public boolean isEqualOrSupersetOf2(ConvertingValueRange other) {
85+
return true;
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package ch.jalu.typeresolver.numbers;
2+
3+
import javax.annotation.Nullable;
4+
import java.math.BigDecimal;
5+
import java.math.BigInteger;
6+
import java.util.Optional;
7+
import java.util.concurrent.atomic.AtomicInteger;
8+
import java.util.concurrent.atomic.AtomicLong;
9+
10+
public enum NonDecimalNumberRange implements ConvertingValueRange {
11+
12+
/** [-128, 127]. */
13+
BYTE(Byte.MIN_VALUE, Byte.MAX_VALUE),
14+
15+
/** [0, 65535]. */
16+
CHARACTER(Character.MIN_VALUE, Character.MAX_VALUE), // todo character unused
17+
18+
/** [-32767, 32768]. */
19+
SHORT(Short.MIN_VALUE, Short.MAX_VALUE),
20+
21+
/** [-2147483648, 2147483647]. */
22+
INTEGER(Integer.MIN_VALUE, Integer.MAX_VALUE),
23+
24+
/** [-9223372036854775808, 9223372036854775807]. */
25+
LONG(-1, -1);
26+
27+
private final int minVal;
28+
private final int maxVal;
29+
30+
NonDecimalNumberRange(int minVal, int maxVal) {
31+
this.minVal = minVal;
32+
this.maxVal = maxVal;
33+
}
34+
35+
@Override
36+
public BigDecimal getMinValue() {
37+
return BigDecimal.valueOf(minVal);
38+
}
39+
40+
@Override
41+
public BigDecimal getMaxValue() {
42+
return BigDecimal.valueOf(maxVal);
43+
}
44+
45+
@Override
46+
public boolean supportsDecimals() {
47+
return false;
48+
}
49+
50+
public Number convertToBasicTypeUnsafe(Number number) {
51+
switch (this) {
52+
case LONG:
53+
return number.longValue();
54+
case INTEGER:
55+
return number.intValue();
56+
case SHORT:
57+
return number.shortValue();
58+
case CHARACTER:
59+
throw new UnsupportedOperationException("Should not be called with " + this.name());
60+
case BYTE:
61+
return number.byteValue();
62+
default:
63+
throw new IllegalStateException("Unsupported range type: " + this);
64+
}
65+
}
66+
67+
boolean isSupersetOrEqualTo(NonDecimalNumberRange otherRange) {
68+
switch (this) {
69+
case LONG:
70+
return true;
71+
case INTEGER:
72+
return otherRange != LONG;
73+
case SHORT:
74+
return otherRange == SHORT || otherRange == BYTE;
75+
case CHARACTER:
76+
return otherRange == CHARACTER;
77+
case BYTE:
78+
return otherRange == BYTE;
79+
default:
80+
throw new IllegalStateException("Unsupported range: " + this);
81+
}
82+
}
83+
84+
@Override
85+
public Optional<Number> convertToTypeIfNoLossOfMagnitude(Number number) {
86+
NonDecimalNumberRange otherRange = toNonDecimalNumberRange(number);
87+
if (otherRange != null) {
88+
Number simpleConvertedNumber = convertFromNumberInNonDecimalNumberRange(number, otherRange);
89+
return Optional.ofNullable(simpleConvertedNumber);
90+
}
91+
92+
if (number instanceof BigInteger) {
93+
BigInteger bi = (BigInteger) number;
94+
if (LongValueRange.isInRange(bi)) {
95+
return Optional.ofNullable(convertFromNumberInNonDecimalNumberRange(bi, NonDecimalNumberRange.LONG));
96+
}
97+
} else if (number instanceof BigDecimal) {
98+
BigDecimal bd = (BigDecimal) number;
99+
if (LongValueRange.isInRange(bd)) {
100+
return Optional.ofNullable(convertFromNumberInNonDecimalNumberRange(bd, NonDecimalNumberRange.LONG));
101+
}
102+
}
103+
return Optional.empty();
104+
}
105+
106+
@Nullable
107+
private Number convertFromNumberInNonDecimalNumberRange(Number number, NonDecimalNumberRange otherRange) {
108+
if (this.isSupersetOrEqualTo(otherRange)) {
109+
return convertToBasicTypeUnsafe(number);
110+
}
111+
if (otherRange == LONG) {
112+
long value = number.longValue();
113+
return minVal <= value && value <= maxVal ? value : null;
114+
} else {
115+
int value = number.intValue();
116+
return minVal <= value && value <= maxVal ? convertToBasicTypeUnsafe(value) : null;
117+
}
118+
}
119+
120+
private NonDecimalNumberRange toNonDecimalNumberRange(Number number) {
121+
if (number instanceof Integer || number instanceof AtomicInteger) {
122+
return INTEGER;
123+
} else if (number instanceof Long || number instanceof AtomicLong) {
124+
return LONG;
125+
} else if (number instanceof Byte) {
126+
return BYTE;
127+
} else if (number instanceof Short) {
128+
return SHORT;
129+
}
130+
return null;
131+
}
132+
133+
@Override
134+
public boolean isEqualOrSupersetOf2(ConvertingValueRange other) {
135+
if (other instanceof InfiniteNumberRange || other instanceof DecimalNumberRange) {
136+
return false;
137+
}
138+
if (other instanceof NonDecimalNumberRange) {
139+
return isSupersetOrEqualTo((NonDecimalNumberRange) other);
140+
}
141+
throw new IllegalStateException("No other range type supported");
142+
}
143+
144+
private static final class LongValueRange {
145+
146+
private static final BigInteger LONG_MIN_BI = BigInteger.valueOf(Long.MIN_VALUE);
147+
private static final BigInteger LONG_MAX_BI = BigInteger.valueOf(Long.MAX_VALUE);
148+
private static final BigDecimal LONG_MIN_BD = new BigDecimal(LONG_MIN_BI);
149+
private static final BigDecimal LONG_MAX_BD = new BigDecimal(LONG_MAX_BI);
150+
151+
private LongValueRange() {
152+
}
153+
154+
static boolean isInRange(BigInteger bigInteger) {
155+
return LONG_MIN_BI.compareTo(bigInteger) <= 0 && LONG_MAX_BI.compareTo(bigInteger) >= 0;
156+
}
157+
158+
static boolean isInRange(BigDecimal bigDecimal) {
159+
return LONG_MIN_BD.compareTo(bigDecimal) <= 0 && LONG_MAX_BD.compareTo(bigDecimal) >= 0;
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)