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