1 package com.fasterxml.jackson.core.io;
2
3 import java.math.BigDecimal;
4
5 public final class NumberInput
6 {
7     /**
8      * Textual representation of a double constant that can cause nasty problems
9      * with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308).
10      */

11     public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308";
12
13     /**
14      * Constants needed for parsing longs from basic int parsing methods
15      */

16     final static long L_BILLION = 1000000000;
17
18     final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1);
19     final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE);
20
21     /**
22      * Fast method for parsing integers that are known to fit into
23      * regular 32-bit signed int type. This means that length is
24      * between 1 and 9 digits (inclusive)
25      *<p>
26      * Note: public to let unit tests call it
27      */

28     public static int parseInt(char[] ch, int off, int len)
29     {
30         int num = ch[off + len - 1] - '0';
31         
32         switch(len) {
33         case 9: 
34           num += (ch[off++] - '0') * 100000000;
35         case 8: 
36           num += (ch[off++] - '0') * 10000000;
37         case 7: 
38           num += (ch[off++] - '0') * 1000000;
39         case 6: 
40           num += (ch[off++] - '0') * 100000;
41         case 5: 
42           num += (ch[off++] - '0') * 10000;
43         case 4: 
44           num += (ch[off++] - '0') * 1000;
45         case 3: 
46           num += (ch[off++] - '0') * 100;
47         case 2: 
48           num += (ch[off] - '0') * 10;
49         }
50         return num;
51     }
52
53     /**
54      * Helper method to (more) efficiently parse integer numbers from
55      * String values.
56      */

57     public static int parseInt(String s)
58     {
59         /* Ok: let's keep strategy simple: ignoring optional minus sign,
60          * we'll accept 1 - 9 digits and parse things efficiently;
61          * otherwise just defer to JDK parse functionality.
62          */

63         char c = s.charAt(0);
64         int len = s.length();
65         boolean neg = (c == '-');
66         int offset = 1;
67         // must have 1 - 9 digits after optional sign:
68         // negative?
69         if (neg) {
70             if (len == 1 || len > 10) {
71                 return Integer.parseInt(s);
72             }
73             c = s.charAt(offset++);
74         } else {
75             if (len > 9) {
76                 return Integer.parseInt(s);
77             }
78         }
79         if (c > '9' || c < '0') {
80             return Integer.parseInt(s);
81         }
82         int num = c - '0';
83         if (offset < len) {
84             c = s.charAt(offset++);
85             if (c > '9' || c < '0') {
86                 return Integer.parseInt(s);
87             }
88             num = (num * 10) + (c - '0');
89             if (offset < len) {
90                 c = s.charAt(offset++);
91                 if (c > '9' || c < '0') {
92                     return Integer.parseInt(s);
93                 }
94                 num = (num * 10) + (c - '0');
95                 // Let's just loop if we have more than 3 digits:
96                 if (offset < len) {
97                     do {
98                         c = s.charAt(offset++);
99                         if (c > '9' || c < '0') {
100                             return Integer.parseInt(s);
101                         }
102                         num = (num * 10) + (c - '0');
103                     } while (offset < len);
104                 }
105             }
106         }
107         return neg ? -num : num;
108     }
109     
110     public static long parseLong(char[] ch, int off, int len)
111     {
112         // Note: caller must ensure length is [10, 18]
113         int len1 = len-9;
114         long val = parseInt(ch, off, len1) * L_BILLION;
115         return val + (long) parseInt(ch, off+len1, 9);
116     }
117
118     public static long parseLong(String s)
119     {
120         /* Ok, now; as the very first thing, let's just optimize case of "fake longs";
121          * that is, if we know they must be ints, call int parsing
122          */

123         int length = s.length();
124         if (length <= 9) {
125             return (long) parseInt(s);
126         }
127         // !!! TODO: implement efficient 2-int parsing...
128         return Long.parseLong(s);
129     }
130     
131     /**
132      * Helper method for determining if given String representation of
133      * an integral number would fit in 64-bit Java long or not.
134      * Note that input String must NOT contain leading minus sign (even
135      * if 'negative' is set to true).
136      *
137      * @param negative Whether original number had a minus sign (which is
138      *    NOT passed to this method) or not
139      */

140     public static boolean inLongRange(char[] ch, int off, int len,
141             boolean negative)
142     {
143         String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
144         int cmpLen = cmpStr.length();
145         if (len < cmpLen) return true;
146         if (len > cmpLen) return false;
147
148         for (int i = 0; i < cmpLen; ++i) {
149             int diff = ch[off+i] - cmpStr.charAt(i);
150             if (diff != 0) {
151                 return (diff < 0);
152             }
153         }
154         return true;
155     }
156
157     /**
158      * Similar to {@link #inLongRange(char[],int,int,boolean)}, but
159      * with String argument
160      *
161      * @param negative Whether original number had a minus sign (which is
162      *    NOT passed to this method) or not
163      */

164     public static boolean inLongRange(String s, boolean negative)
165     {
166         String cmp = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
167         int cmpLen = cmp.length();
168         int alen = s.length();
169         if (alen < cmpLen) return true;
170         if (alen > cmpLen) return false;
171
172         // could perhaps just use String.compareTo()?
173         for (int i = 0; i < cmpLen; ++i) {
174             int diff = s.charAt(i) - cmp.charAt(i);
175             if (diff != 0) {
176                 return (diff < 0);
177             }
178         }
179         return true;
180     }
181
182     public static int parseAsInt(String s, int def)
183     {
184         if (s == null) {
185             return def;
186         }
187         s = s.trim();
188         int len = s.length();
189         if (len == 0) {
190             return def;
191         }
192         // One more thing: use integer parsing for 'simple'
193         int i = 0;
194         if (i < len) { // skip leading sign:
195             char c = s.charAt(0);
196             if (c == '+') { // for plus, actually physically remove
197                 s = s.substring(1);
198                 len = s.length();
199             } else if (c == '-') { // minus, just skip for checks, must retain
200                 ++i;
201             }
202         }
203         for (; i < len; ++i) {
204             char c = s.charAt(i);
205             // if other symbols, parse as Double, coerce
206             if (c > '9' || c < '0') {
207                 try {
208                     return (int) parseDouble(s);
209                 } catch (NumberFormatException e) {
210                     return def;
211                 }
212             }
213         }
214         try {
215             return Integer.parseInt(s);
216         } catch (NumberFormatException e) { }
217         return def;
218     }
219
220     public static long parseAsLong(String s, long def)
221     {
222         if (s == null) {
223             return def;
224         }
225         s = s.trim();
226         int len = s.length();
227         if (len == 0) {
228             return def;
229         }
230         // One more thing: use long parsing for 'simple'
231         int i = 0;
232         if (i < len) { // skip leading sign:
233             char c = s.charAt(0);
234             if (c == '+') { // for plus, actually physically remove
235                 s = s.substring(1);
236                 len = s.length();
237             } else if (c == '-') { // minus, just skip for checks, must retain
238                 ++i;
239             }
240         }
241         for (; i < len; ++i) {
242             char c = s.charAt(i);
243             // if other symbols, parse as Double, coerce
244             if (c > '9' || c < '0') {
245                 try {
246                     return (long) parseDouble(s);
247                 } catch (NumberFormatException e) {
248                     return def;
249                 }
250             }
251         }
252         try {
253             return Long.parseLong(s);
254         } catch (NumberFormatException e) { }
255         return def;
256     }
257     
258     public static double parseAsDouble(String s, double def)
259     {
260         if (s == null) { return def; }
261         s = s.trim();
262         int len = s.length();
263         if (len == 0) {
264             return def;
265         }
266         try {
267             return parseDouble(s);
268         } catch (NumberFormatException e) { }
269         return def;
270     }
271
272     public static double parseDouble(String s) throws NumberFormatException {
273         // [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
274         /* as per [JACKSON-827], let's use MIN_VALUE as it is available on all JDKs; normalized
275          * only in JDK 1.6. In practice, should not really matter.
276          */

277         if (NASTY_SMALL_DOUBLE.equals(s)) {
278             return Double.MIN_VALUE;
279         }
280         return Double.parseDouble(s);
281     }
282
283     public static BigDecimal parseBigDecimal(String s) throws NumberFormatException {
284         try { return new BigDecimal(s); } catch (NumberFormatException e) {
285             throw _badBD(s);
286         }
287     }
288
289     public static BigDecimal parseBigDecimal(char[] b) throws NumberFormatException {
290         return parseBigDecimal(b, 0, b.length);
291     }
292     
293     public static BigDecimal parseBigDecimal(char[] b, int off, int len) throws NumberFormatException {
294         try { return new BigDecimal(b, off, len); } catch (NumberFormatException e) {
295             throw _badBD(new String(b, off, len));
296         }
297     }
298
299     private static NumberFormatException _badBD(String s) {
300         return new NumberFormatException("Value \""+s+"\" can not be represented as BigDecimal");
301     }
302 }
303