1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.parser;
18
19 import java.io.Serializable;
20 import java.math.BigDecimal;
21 import java.math.BigInteger;
22 import java.text.DecimalFormat;
23 import java.text.DecimalFormatSymbols;
24 import java.util.Locale;
25
26
27
28
29 public final class NumberParser implements Serializable {
30
31
32 private static final long serialVersionUID = 1L;
33
34 static final DecimalFormat BIGDF = new DecimalFormat("0.0b", new DecimalFormatSymbols(Locale.ROOT));
35 private static boolean isNegative(final Token token) {
36 return token != null && "-".equals(token.image);
37 }
38 static Number parseDouble(final Token negative, final Token s) {
39 return new NumberParser().assignReal(isNegative(negative), s.image).getLiteralValue();
40 }
41
42 static Number parseInteger(final Token negative, final Token s) {
43 return new NumberParser().assignNatural(isNegative(negative), s.image).getLiteralValue();
44 }
45
46 public NumberParser() {
47 this(null);
48 }
49
50 public NumberParser(final Number number) {
51 if (number != null) {
52 this.literal = number;
53 this.clazz = number.getClass();
54 } else {
55 this.literal = null;
56 this.clazz = null;
57 }
58 }
59
60
61 private Number literal;
62
63
64 private Class<? extends Number> clazz;
65
66
67
68
69
70
71
72
73 NumberParser assignNatural(final boolean negative, final String natural) {
74 String s = natural;
75 Number result;
76 Class<? extends Number> rclass;
77
78 final int base;
79 if (s.charAt(0) == '0') {
80 if (s.length() > 1 && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
81 base = 16;
82 s = s.substring(2);
83 } else {
84 base = 8;
85 }
86 } else {
87 base = 10;
88 }
89
90 final int last = s.length() - 1;
91 switch (s.charAt(last)) {
92 case 'l':
93 case 'L': {
94 rclass = Long.class;
95 final long l = Long.parseLong(s.substring(0, last), base);
96 result = negative? -l : l;
97 break;
98 }
99 case 'h':
100 case 'H': {
101 rclass = BigInteger.class;
102 final BigInteger bi = new BigInteger(s.substring(0, last), base);
103 result = negative? bi.negate() : bi;
104 break;
105 }
106 default: {
107
108 rclass = Integer.class;
109 try {
110 final int i = Integer.parseInt(s, base);
111 result = negative? -i : i;
112 } catch (final NumberFormatException take2) {
113 try {
114 final long l = Long.parseLong(s, base);
115 result = negative? -l : l;
116 } catch (final NumberFormatException take3) {
117 final BigInteger bi = new BigInteger(s, base);
118 result = negative? bi.negate() : bi;
119 }
120 }
121 }
122 }
123 literal = result;
124 clazz = rclass;
125 return this;
126 }
127
128
129
130
131
132
133
134 NumberParser assignNatural(final String str) {
135 String s;
136
137 final boolean negative;
138 switch (str.charAt(0)) {
139 case '-':
140 negative = true;
141 s = str.substring(1);
142 break;
143 case '+':
144 negative = false;
145 s = str.substring(1);
146 break;
147 default:
148 negative = false;
149 s = str;
150 }
151 return assignNatural(negative, s);
152 }
153
154
155
156
157
158
159
160
161 NumberParser assignReal(final boolean negative, final String s) {
162 Number result;
163 Class<? extends Number> rclass;
164 if ("#NaN".equals(s) || "NaN".equals(s)) {
165 result = Double.NaN;
166 rclass = Double.class;
167 } else {
168 final int last = s.length() - 1;
169 switch (s.charAt(last)) {
170 case 'b':
171 case 'B': {
172 rclass = BigDecimal.class;
173 final BigDecimal bd = new BigDecimal(s.substring(0, last));
174 result = negative? bd.negate() : bd;
175 break;
176 }
177 case 'f':
178 case 'F': {
179 rclass = Float.class;
180 final float f4 = Float.parseFloat(s.substring(0, last));
181 result = negative? -f4 : f4;
182 break;
183 }
184 case 'd':
185 case 'D':
186 rclass = Double.class;
187 final double f8 = Double.parseDouble(s.substring(0, last));
188 result = negative? -f8 : f8;
189 break;
190 default: {
191
192 rclass = Double.class;
193 try {
194 final double d = Double.parseDouble(s);
195 result = negative? -d : d;
196 } catch (final NumberFormatException take3) {
197 final BigDecimal bd = new BigDecimal(s);
198 result = negative? bd.negate() : bd;
199 }
200 break;
201 }
202 }
203 }
204 literal = result;
205 clazz = rclass;
206 return this;
207 }
208
209
210
211
212
213
214
215 NumberParser assignReal(final String str) {
216 String s;
217
218 final boolean negative;
219 switch (str.charAt(0)) {
220 case '-':
221 negative = true;
222 s = str.substring(1);
223 break;
224 case '+':
225 negative = false;
226 s = str.substring(1);
227 break;
228 default:
229 negative = false;
230 s = str;
231 }
232 return assignReal(negative, s);
233 }
234
235 Class<? extends Number> getLiteralClass() {
236 return clazz;
237 }
238
239 Number getLiteralValue() {
240 return literal;
241 }
242
243 boolean isInteger() {
244 return Integer.class.equals(clazz);
245 }
246
247 @Override
248 public String toString() {
249 if (literal == null || clazz == null || Double.isNaN(literal.doubleValue())) {
250 return "NaN";
251 }
252 if (BigDecimal.class.equals(clazz)) {
253 synchronized (BIGDF) {
254 return BIGDF.format(literal);
255 }
256 }
257 final StringBuilder strb = new StringBuilder(literal.toString());
258 if (Float.class.equals(clazz)) {
259 strb.append('f');
260 } else if (Double.class.equals(clazz)) {
261 strb.append('d');
262 } else if (BigInteger.class.equals(clazz)) {
263 strb.append('h');
264 } else if (Long.class.equals(clazz)) {
265 strb.append('l');
266 }
267 return strb.toString();
268 }
269
270 }