• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.core;
17 
18 import java.io.Serializable;
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.util.Objects;
22 import software.amazon.awssdk.annotations.Immutable;
23 import software.amazon.awssdk.annotations.SdkPublicApi;
24 import software.amazon.awssdk.utils.Validate;
25 
26 /**
27  * An in-memory representation of Number being given to a service or being returned by a service.
28  * This is a SDK representation of a Number. This allows conversion to any desired numeric type by providing constructors
29  * as below
30  * <ul>
31  *   <li>{@link #fromBigDecimal(BigDecimal)} to create from a BigDecimal.</li>
32  *   <li>{@link #fromBigInteger(BigInteger)} to create from a BigInteger.</li>
33  *   <li>{@link #fromDouble(double)} to create from a double</li>
34  *   <li>{@link #fromFloat(float)} to create from a float.</li>
35  *   <li>{@link #fromLong(long)} to create from a long.</li>
36  *   <li>{@link #fromShort(short)} to create from a short.</li>
37  *   <li>{@link #fromInteger(int)} to create from an integer.</li>
38  *   <li>{@link #fromString(String)}  to create from a String</li>
39  * </ul>
40  *
41  * Thus, by doing this, this class is able to preserve arbitrary precision of any given number.
42  * <p>
43  * If {@link SdkNumber} is expected in a particular number format then its corresponding getter methods can be used.
44  * <p>
45  * Example for a {@link SdkNumber} created with {@link BigDecimal} the
46  * {@link #fromBigDecimal(BigDecimal)} can be used.
47  */
48 @SdkPublicApi
49 @Immutable
50 public final class SdkNumber extends Number implements Serializable {
51 
52     private static final long serialVersionUID = 1L;
53     private final Number numberValue;
54     private final String stringValue;
55 
56     /**
57      * @param value Number value as passed in the from COnstructor.
58      * @see #fromBigDecimal(BigDecimal)
59      * @see #fromBigInteger(BigInteger)
60      * @see #fromDouble(double)
61      * @see #fromFloat(float)
62      * @see #fromLong(long)
63      * @see #fromShort(short)
64      * @see #fromInteger(int)
65      */
SdkNumber(Number value)66     private SdkNumber(Number value) {
67         this.numberValue = value;
68         this.stringValue = null;
69     }
70 
71     /**
72      * .
73      *
74      * @param stringValue String value.
75      * @see #fromString(String)
76      */
SdkNumber(String stringValue)77     private SdkNumber(String stringValue) {
78         this.stringValue = stringValue;
79         this.numberValue = null;
80     }
81 
isNumberValueNaN(Number numberValue)82     private static boolean isNumberValueNaN(Number numberValue) {
83         return (numberValue instanceof Double && Double.isNaN((double) numberValue)) ||
84                 (numberValue instanceof Float && Float.isNaN((float) numberValue));
85     }
86 
isNumberValueInfinite(Number numberValue)87     private static boolean isNumberValueInfinite(Number numberValue) {
88         return (numberValue instanceof Double && Double.isInfinite((double) numberValue)) ||
89                 (numberValue instanceof Float && Float.isInfinite((float) numberValue));
90     }
91 
valueOf(Number numberValue)92     private static Number valueOf(Number numberValue) {
93         Number valueOfInfiniteOrNaN = valueOfInfiniteOrNaN(numberValue);
94         return valueOfInfiniteOrNaN != null ? valueOfInfiniteOrNaN : valueInBigDecimal(numberValue);
95     }
96 
valueOfInfiniteOrNaN(Number numberValue)97     private static Number valueOfInfiniteOrNaN(Number numberValue) {
98         if (numberValue instanceof Double
99                 && (Double.isInfinite((double) numberValue) || Double.isNaN((double) numberValue))) {
100             return Double.valueOf(numberValue.doubleValue());
101         } else if ((numberValue instanceof Float
102                 && (Float.isInfinite((float) numberValue) || Float.isNaN((float) numberValue)))) {
103             return Float.valueOf(numberValue.floatValue());
104         } else {
105             return null;
106         }
107     }
108 
109     /**
110      * This function converts a given number to BigDecimal Number where the caller can convert to an primitive number.
111      * This is done to keep the precision.
112      *
113      * @param numberValue The number value.
114      * @return Big Decimal value for the given number.
115      */
valueInBigDecimal(Number numberValue)116     private static BigDecimal valueInBigDecimal(Number numberValue) {
117         if (numberValue instanceof Double) {
118             return BigDecimal.valueOf((double) numberValue);
119         } else if (numberValue instanceof Float) {
120             return BigDecimal.valueOf((float) numberValue);
121         } else if (numberValue instanceof Integer) {
122             return new BigDecimal((int) numberValue);
123         } else if (numberValue instanceof Short) {
124             return new BigDecimal((short) numberValue);
125         } else if (numberValue instanceof Long) {
126             return BigDecimal.valueOf((Long) numberValue);
127         } else if (numberValue instanceof BigDecimal) {
128             return (BigDecimal) numberValue;
129         } else if (numberValue instanceof BigInteger) {
130             return new BigDecimal((BigInteger) numberValue);
131         } else {
132             return new BigDecimal(numberValue.toString());
133         }
134     }
135 
136     /**
137      * Create {@link SdkNumber} from a integer value.
138      *
139      * @param integerValue Integer value.
140      * @return new {@link SdkNumber} for the given int value.
141      */
fromInteger(int integerValue)142     public static SdkNumber fromInteger(int integerValue) {
143         return new SdkNumber(integerValue);
144     }
145 
146     /**
147      * Create {@link SdkNumber} from a BigInteger value.
148      *
149      * @param bigIntegerValue BigInteger value.
150      * @return new {@link SdkNumber} for the given BigInteger value.
151      */
fromBigInteger(BigInteger bigIntegerValue)152     public static SdkNumber fromBigInteger(BigInteger bigIntegerValue) {
153         return new SdkNumber(bigIntegerValue);
154     }
155 
156     /**
157      * Create {@link SdkNumber} from a BigDecimal value.
158      *
159      * @param bigDecimalValue BigInteger value.
160      * @return new {@link SdkNumber} for the given BigDecimal value.
161      */
fromBigDecimal(BigDecimal bigDecimalValue)162     public static SdkNumber fromBigDecimal(BigDecimal bigDecimalValue) {
163         Validate.notNull(bigDecimalValue, "BigDecimal cannot be null");
164         return new SdkNumber(bigDecimalValue);
165     }
166 
167     /**
168      * Create {@link SdkNumber} from a long Value.
169      *
170      * @param longValue long value.
171      * @return new {@link SdkNumber} for the given long value.
172      */
fromLong(long longValue)173     public static SdkNumber fromLong(long longValue) {
174         return new SdkNumber(longValue);
175     }
176 
177     /**
178      * Create {@link SdkNumber} from a double Value.
179      *
180      * @param doubleValue long value.
181      * @return new {@link SdkNumber} for the given double value.
182      */
fromDouble(double doubleValue)183     public static SdkNumber fromDouble(double doubleValue) {
184         return new SdkNumber(doubleValue);
185     }
186 
187     /**
188      * Create {@link SdkNumber} from a long Value.
189      *
190      * @param shortValue long value.
191      * @return new {@link SdkNumber} for the given long value.
192      */
fromShort(short shortValue)193     public static SdkNumber fromShort(short shortValue) {
194         return new SdkNumber(shortValue);
195     }
196 
197     /**
198      * Create {@link SdkNumber} from a float Value.
199      *
200      * @param floatValue float value.
201      * @return new {@link SdkNumber} for the given float value.
202      */
fromFloat(float floatValue)203     public static SdkNumber fromFloat(float floatValue) {
204         return new SdkNumber(floatValue);
205     }
206 
207     /**
208      * Create {@link SdkNumber} from a long Value.
209      *
210      * @param stringValue String value.
211      * @return new {@link SdkNumber} for the given stringValue value.
212      */
fromString(String stringValue)213     public static SdkNumber fromString(String stringValue) {
214         return new SdkNumber(stringValue);
215     }
216 
217     /**
218      * Gets the integer value of the  {@link SdkNumber}.
219      * If we do a intValue() for {@link SdkNumber} constructed
220      * from float, double, long, BigDecimal, BigInteger number type then it
221      * may result in loss of magnitude and a loss of precision.
222      * The result may lose some of the least significant bits of the value.
223      * Precision is not lost while getting a {@link SdkNumber} which was constructed as
224      * lower precision number type like short, byte, integer.
225      *
226      * @return integer value of  {@link SdkNumber} .
227      */
228     @Override
intValue()229     public int intValue() {
230         return numberValue instanceof Integer ? numberValue.intValue() :
231                 stringValue != null ? new BigDecimal(stringValue).intValue()
232                         : valueOf(numberValue).intValue();
233     }
234 
235     /**
236      * Gets the long value of the  {@link SdkNumber}.
237      * If we do a longValue() for {@link SdkNumber} constructed from
238      * float, double, BigDecimal, BigInteger number type then it
239      * may result in loss of magnitude and a loss of precision.
240      * Precision is not lost while getting a {@link SdkNumber} which was constructed from
241      * lower precision type like short, byte, integer.
242      *
243      * @return long value of  {@link SdkNumber}.
244      */
245     @Override
longValue()246     public long longValue() {
247         return numberValue instanceof Long ? numberValue.longValue() :
248                 stringValue != null ? new BigDecimal(stringValue).longValue() : valueOf(numberValue).longValue();
249     }
250 
251     /**
252      * Gets the float value of the  {@link SdkNumber}.
253      * If we do a floatValue() for {@link SdkNumber} constructed from
254      * double, BigDecimal, BigInteger number type then it
255      * may result in loss of magnitude and a loss of precision.
256      * Precision is not lost while getting a {@link SdkNumber} which was constructed from
257      * precision type like short, byte, integer, long.
258      *
259      * @return long value of  {@link SdkNumber}.
260      */
261     @Override
floatValue()262     public float floatValue() {
263         return numberValue instanceof Float ? numberValue.floatValue() :
264                 numberValue != null ? valueOf(numberValue).floatValue() : new BigDecimal(stringValue).floatValue();
265     }
266 
267     /**
268      * Gets the double value of the  {@link SdkNumber}.
269      * If we do a doubleValue() for {@link SdkNumber} constructed from BigDecimal, BigInteger number type then it
270      * may result in loss of magnitude and a loss of precision.
271      * Precision is not lost while getting a {@link SdkNumber} which was constructed from
272      * precision type like short, byte, integer, long, float.
273      *
274      * @return long value of  {@link SdkNumber}.
275      */
276     @Override
doubleValue()277     public double doubleValue() {
278         return numberValue instanceof Double ? numberValue.doubleValue() :
279                 numberValue != null ? valueOf(numberValue).doubleValue() :
280                         new BigDecimal(stringValue).doubleValue();
281     }
282 
283     /**
284      * Gets the bigDecimalValue of the {@link SdkNumber}.
285      * Precision is not lost in this case.
286      * However bigDecimalValue cannot be performed on
287      * a {{@link SdkNumber}} constructed from Float/Double Nan/Infinity.
288      *
289      * @return BigDecimal value of  {@link SdkNumber}
290      * @throws NumberFormatException Exception in thrown if a {@link SdkNumber} was constructed asNan/Infinte number
291      *                               of Double/FLoat type.Since we cannot convert NaN/Infinite numbers to BigDecimal.
292      */
bigDecimalValue()293     public BigDecimal bigDecimalValue() {
294 
295         if (stringValue != null) {
296             return new BigDecimal(stringValue);
297         }
298         if (numberValue instanceof BigDecimal) {
299             return (BigDecimal) numberValue;
300         }
301         if (isNumberValueNaN(numberValue) || isNumberValueInfinite(numberValue)) {
302             throw new NumberFormatException("Nan or Infinite Number can not be converted to BigDecimal.");
303         } else {
304             return valueInBigDecimal(numberValue);
305         }
306     }
307 
308     /**
309      * Gets the String value of the  {@link SdkNumber}.
310      *
311      * @return the stringValue
312      */
stringValue()313     public String stringValue() {
314         return stringValue != null ? stringValue : numberValue.toString();
315     }
316 
317     @Override
toString()318     public String toString() {
319         return stringValue != null ? stringValue : numberValue.toString();
320     }
321 
322     @Override
equals(Object o)323     public boolean equals(Object o) {
324         if (this == o) {
325             return true;
326         }
327         if (!(o instanceof SdkNumber)) {
328             return false;
329         }
330         SdkNumber sdkNumber = (SdkNumber) o;
331         return Objects.equals(stringValue(), sdkNumber.stringValue());
332     }
333 
334     @Override
hashCode()335     public int hashCode() {
336         return Objects.hashCode(stringValue());
337     }
338 }
339