1 package expo.modules.structuredheaders; 2 3 import java.math.BigDecimal; 4 import java.util.Objects; 5 6 /** 7 * Represents a Decimal. 8 * <p> 9 * A Decimal - despite it's name - is essentially the same thing as an Integer, 10 * but has an implied divisor of 1000 (in other words, a scale of 3). Thus, a 11 * value represented as {@code 0.5} in a field value will be internally stored 12 * as {@code long} with value {@code 500}. The only difference to 13 * {@link IntegerItem} is that {@link #get()} will return a {@link BigDecimal}, 14 * and that the implied divisor is taken into account when serializing the 15 * value. {@link #getAsLong()} provides access to the raw value when the 16 * overhead of {@link BigDecimal} is not needed. 17 * 18 * @see <a href= 19 * "https://greenbytes.de/tech/webdav/draft-ietf-httpbis-header-structure-19.html#decimal">Section 20 * 3.3.2 of draft-ietf-httpbis-header-structure-19</a> 21 */ 22 public class DecimalItem implements NumberItem<BigDecimal> { 23 24 private final long value; 25 private final Parameters params; 26 27 private static final long MIN = -999999999999999L; 28 private static final long MAX = 999999999999999L; 29 private static final BigDecimal THOUSAND = new BigDecimal(1000); 30 DecimalItem(long value, Parameters params)31 private DecimalItem(long value, Parameters params) { 32 if (value < MIN || value > MAX) { 33 throw new IllegalArgumentException("value must be in the range from " + MIN + " to " + MAX); 34 } 35 this.value = value; 36 this.params = Objects.requireNonNull(params, "params must not be null"); 37 } 38 39 /** 40 * Creates a {@link DecimalItem} instance representing the specified 41 * {@code long} value, where the implied divisor is {@code 1000}. 42 * 43 * @param value 44 * a {@code long} value. 45 * @return a {@link DecimalItem} representing {@code value}. 46 */ valueOf(long value)47 public static DecimalItem valueOf(long value) { 48 return new DecimalItem(value, Parameters.EMPTY); 49 } 50 51 /** 52 * Creates a {@link DecimalItem} instance representing the specified 53 * {@code BigDecimal} value, with potential rounding. 54 * 55 * @param value 56 * a {@code BigDecimal} value. 57 * @return a {@link DecimalItem} representing {@code value}. 58 */ valueOf(BigDecimal value)59 public static DecimalItem valueOf(BigDecimal value) { 60 BigDecimal permille = (Objects.requireNonNull(value, "value must not be null")).multiply(THOUSAND); 61 return valueOf(permille.longValue()); 62 } 63 64 @Override withParams(Parameters params)65 public DecimalItem withParams(Parameters params) { 66 if (Objects.requireNonNull(params, "params must not be null").isEmpty()) { 67 return this; 68 } else { 69 return new DecimalItem(this.value, params); 70 } 71 } 72 73 @Override getParams()74 public Parameters getParams() { 75 return params; 76 } 77 78 @Override serializeTo(StringBuilder sb)79 public StringBuilder serializeTo(StringBuilder sb) { 80 81 String sign = value < 0 ? "-" : ""; 82 83 long abs = Math.abs(value); 84 long left = abs / 1000; 85 long right = abs % 1000; 86 87 if (right % 10 == 0) { 88 right /= 10; 89 } 90 if (right % 10 == 0) { 91 right /= 10; 92 } 93 sb.append(sign).append(Long.toString(left)).append('.').append(Long.toString(right)); 94 95 params.serializeTo(sb); 96 97 return sb; 98 } 99 100 @Override 101 public String serialize() { 102 return serializeTo(new StringBuilder(20)).toString(); 103 } 104 105 @Override 106 public BigDecimal get() { 107 return BigDecimal.valueOf(value, 3); 108 } 109 110 @Override 111 public long getAsLong() { 112 return value; 113 } 114 115 @Override 116 public int getDivisor() { 117 return 1000; 118 } 119 } 120