1
2
3
4
5
6 package dev.metaschema.core.metapath.item.atomic;
7
8 import java.math.BigDecimal;
9 import java.math.RoundingMode;
10
11 import dev.metaschema.core.datatype.adapter.DecimalAdapter;
12 import dev.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider;
13 import dev.metaschema.core.metapath.function.ArithmeticFunctionException;
14 import dev.metaschema.core.metapath.function.CastFunctionException;
15 import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
16 import dev.metaschema.core.metapath.item.atomic.impl.DecimalItemImpl;
17 import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
18 import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
19 import dev.metaschema.core.util.ObjectUtils;
20 import edu.umd.cs.findbugs.annotations.NonNull;
21
22
23
24
25 public interface IDecimalItem extends INumericItem {
26
27
28
29 @NonNull
30 IDecimalItem ZERO = valueOf(ObjectUtils.notNull(BigDecimal.ZERO));
31
32
33
34
35
36
37 @NonNull
38 static IAtomicOrUnionType<IDecimalItem> type() {
39 return MetaschemaDataTypeProvider.DECIMAL.getItemType();
40 }
41
42 @Override
43 default IAtomicOrUnionType<? extends IDecimalItem> getType() {
44 return type();
45 }
46
47
48
49
50
51
52
53
54
55
56 @NonNull
57 static IDecimalItem valueOf(@NonNull String value) {
58 try {
59 return valueOf(MetaschemaDataTypeProvider.DECIMAL.parse(value));
60 } catch (IllegalArgumentException ex) {
61 throw new InvalidTypeMetapathException(
62 null,
63 String.format("Invalid decimal value '%s'. %s",
64 value,
65 ex.getLocalizedMessage()),
66 ex);
67 }
68 }
69
70
71
72
73
74
75
76
77 @NonNull
78 static IDecimalItem valueOf(long value) {
79 return valueOf(ObjectUtils.notNull(BigDecimal.valueOf(value)));
80 }
81
82
83
84
85
86
87
88
89 @NonNull
90 static IDecimalItem valueOf(double value) {
91 return valueOf(ObjectUtils.notNull(Double.toString(value)));
92 }
93
94
95
96
97
98
99
100
101 @NonNull
102 static IDecimalItem valueOf(boolean value) {
103 return valueOf(DecimalItemImpl.toBigDecimal(value));
104 }
105
106
107
108
109
110
111
112
113 @NonNull
114 static IDecimalItem valueOf(@NonNull BigDecimal value) {
115 return new DecimalItemImpl(value);
116 }
117
118
119
120
121
122
123
124
125
126
127
128 @NonNull
129 static IDecimalItem cast(@NonNull IAnyAtomicItem item) {
130 IDecimalItem retval;
131 if (item instanceof IDecimalItem) {
132 retval = (IDecimalItem) item;
133 } else if (item instanceof INumericItem) {
134 retval = valueOf(((INumericItem) item).asDecimal());
135 } else if (item instanceof IBooleanItem) {
136 retval = valueOf(((IBooleanItem) item).toBoolean());
137 } else {
138 try {
139 retval = valueOf(item.asString());
140 } catch (IllegalStateException | InvalidTypeMetapathException ex) {
141
142 throw new InvalidValueForCastFunctionException(ex);
143 }
144 }
145 return retval;
146 }
147
148 @Override
149 default IDecimalItem castAsType(IAnyAtomicItem item) {
150 return cast(item);
151 }
152
153 @Override
154 default boolean toEffectiveBoolean() {
155 return !BigDecimal.ZERO.equals(asDecimal());
156 }
157
158 @SuppressWarnings("null")
159 @Override
160 default INumericItem abs() {
161 return new DecimalItemImpl(asDecimal().abs());
162 }
163
164 @SuppressWarnings("null")
165 @Override
166 default IIntegerItem ceiling() {
167 return IIntegerItem.valueOf(asDecimal().setScale(0, RoundingMode.CEILING).toBigIntegerExact());
168 }
169
170 @SuppressWarnings("null")
171 @Override
172 default IIntegerItem floor() {
173 return IIntegerItem.valueOf(asDecimal().setScale(0, RoundingMode.FLOOR).toBigIntegerExact());
174 }
175
176
177
178
179
180
181
182
183
184 @Override
185 default int toIntValueExact() {
186 try {
187
188
189 return asDecimal().intValueExact();
190 } catch (ArithmeticException ex) {
191 throw new CastFunctionException(
192 CastFunctionException.INPUT_VALUE_TOO_LARGE,
193 this,
194 String.format("Decimal value '%s' is out of range for a Java int.", asString()),
195 ex);
196 }
197 }
198
199
200
201
202
203
204
205
206
207 @NonNull
208 default IDecimalItem add(@NonNull IDecimalItem addend) {
209 BigDecimal addendLeft = asDecimal();
210 BigDecimal addendRight = addend.asDecimal();
211 return valueOf(ObjectUtils.notNull(addendLeft.add(addendRight)));
212 }
213
214
215
216
217
218
219
220
221
222
223 @NonNull
224 default IDecimalItem subtract(@NonNull IDecimalItem subtrahend) {
225 BigDecimal minuendDecimal = asDecimal();
226 BigDecimal subtrahendDecimal = subtrahend.asDecimal();
227 return valueOf(ObjectUtils.notNull(minuendDecimal.subtract(subtrahendDecimal, DecimalAdapter.mathContext())));
228 }
229
230
231
232
233
234
235
236
237
238 @NonNull
239 default IDecimalItem multiply(@NonNull IDecimalItem multiplier) {
240 return valueOf(ObjectUtils.notNull(asDecimal().multiply(multiplier.asDecimal(), DecimalAdapter.mathContext())));
241 }
242
243
244
245
246
247
248
249
250
251
252
253 @NonNull
254 default IDecimalItem divide(@NonNull IDecimalItem divisor) {
255
256 BigDecimal divisorDecimal = divisor.asDecimal();
257
258 if (BigDecimal.ZERO.compareTo(divisorDecimal) == 0) {
259 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
260 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
261 }
262 return valueOf(ObjectUtils.notNull(asDecimal().divide(divisorDecimal, DecimalAdapter.mathContext())));
263 }
264
265
266
267
268
269
270
271
272
273 @Override
274 @NonNull
275 default IIntegerItem integerDivide(INumericItem divisor) {
276
277 BigDecimal decimalDivisor = divisor.asDecimal();
278
279 if (BigDecimal.ZERO.compareTo(decimalDivisor) == 0) {
280 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
281 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
282 }
283
284 BigDecimal decimalDividend = asDecimal();
285 return IIntegerItem.valueOf(
286 ObjectUtils.notNull(decimalDividend
287 .divideToIntegralValue(decimalDivisor, DecimalAdapter.mathContext()).toBigInteger()));
288 }
289
290
291
292
293
294
295
296
297
298
299 @Override
300 @NonNull
301 default IDecimalItem mod(INumericItem divisor) {
302
303 BigDecimal decimalDivisor = divisor.asDecimal();
304
305 if (BigDecimal.ZERO.compareTo(decimalDivisor) == 0) {
306 throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO,
307 ArithmeticFunctionException.DIVISION_BY_ZERO_MESSAGE);
308 }
309 return valueOf(ObjectUtils.notNull(asDecimal().remainder(decimalDivisor, DecimalAdapter.mathContext())));
310 }
311
312 @Override
313 default IDecimalItem negate() {
314 return valueOf(ObjectUtils.notNull(asDecimal().negate(DecimalAdapter.mathContext())));
315 }
316
317
318
319
320
321
322
323
324
325 default int compareTo(@NonNull IDecimalItem item) {
326 return asDecimal().compareTo(item.asDecimal());
327 }
328 }