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 */ parseInt(char[] ch, int off, int len)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 */ parseInt(String s)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 parseLong(char[] ch, int off, int len)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 parseLong(String s)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 */ inLongRange(char[] ch, int off, int len, boolean negative)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 */ inLongRange(String s, boolean negative)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 parseAsInt(String s, int def)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 parseAsLong(String s, long def)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 parseAsDouble(String s, double def)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 parseDouble(String s)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 parseBigDecimal(String s)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 parseBigDecimal(char[] b)289 public static BigDecimal parseBigDecimal(char[] b) throws NumberFormatException { 290 return parseBigDecimal(b, 0, b.length); 291 } 292 parseBigDecimal(char[] b, int off, int len)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 _badBD(String s)299 private static NumberFormatException _badBD(String s) { 300 return new NumberFormatException("Value \""+s+"\" can not be represented as BigDecimal"); 301 } 302 } 303