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