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