1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.metapath.item.atomic;
28
29 import gov.nist.secauto.metaschema.core.metapath.function.ArithmeticFunctionException;
30 import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
31 import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
32 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
33
34 import java.math.BigDecimal;
35 import java.math.BigInteger;
36 import java.math.MathContext;
37 import java.math.RoundingMode;
38
39 import edu.umd.cs.findbugs.annotations.NonNull;
40
41 public interface INumericItem extends IAnyAtomicItem {
42
43 @NonNull
44 static INumericItem cast(@NonNull IAnyAtomicItem item) throws InvalidValueForCastFunctionException {
45 INumericItem retval;
46 if (item instanceof INumericItem) {
47 retval = (INumericItem) item;
48 } else {
49 try {
50 retval = IDecimalItem.valueOf(item.asString());
51 } catch (NumberFormatException ex) {
52 throw new InvalidValueForCastFunctionException(ex);
53 }
54 }
55 return retval;
56 }
57
58 @NonNull
59 BigDecimal asDecimal();
60
61 @NonNull
62 BigInteger asInteger();
63
64 boolean toEffectiveBoolean();
65
66
67
68
69
70
71 @NonNull
72 INumericItem abs();
73
74
75
76
77
78
79 @NonNull
80 IIntegerItem ceiling();
81
82
83
84
85
86
87 @NonNull
88 IIntegerItem floor();
89
90 @NonNull
91 default INumericItem round() {
92 return round(IIntegerItem.ZERO);
93 }
94
95 @NonNull
96 default INumericItem round(@NonNull IIntegerItem precisionItem) {
97 int precision;
98 try {
99 precision = FunctionUtils.asInteger(precisionItem);
100 } catch (ArithmeticException ex) {
101 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR,
102 "Numeric operation overflow/underflow.", ex);
103 }
104 INumericItem retval;
105 if (precision >= 0) {
106
107 if (this instanceof IIntegerItem) {
108 retval = this;
109 } else {
110
111 BigDecimal value = this.asDecimal();
112 if (value.signum() == -1) {
113 retval = IDecimalItem.valueOf(
114 ObjectUtils.notNull(
115 value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_DOWN))));
116 } else {
117 retval = IDecimalItem.valueOf(
118 ObjectUtils.notNull(
119 value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_UP))));
120 }
121 }
122 } else {
123
124 BigInteger value = this.asInteger();
125 BigInteger divisor = BigInteger.TEN.pow(0 - precision);
126
127 @NonNull BigInteger result;
128 if (divisor.compareTo(value.abs()) > 0) {
129 result = ObjectUtils.notNull(BigInteger.ZERO);
130 } else {
131 BigInteger remainder = value.mod(divisor);
132 BigInteger lessRemainder = value.subtract(remainder);
133 BigInteger halfDivisor = divisor.divide(BigInteger.TWO);
134 result = ObjectUtils.notNull(
135 remainder.compareTo(halfDivisor) >= 0 ? lessRemainder.add(divisor) : lessRemainder);
136 }
137 retval = IIntegerItem.valueOf(result);
138 }
139 return retval;
140 }
141 }