in freemarker-core/src/main/java/freemarker/core/JSONParser.java [163:258]
private TemplateNumberModel tryConsumeNumber() throws JSONParseException {
if (p >= ln) {
return null;
}
char c = src.charAt(p);
boolean negative = c == '-';
if (!(negative || isDigit(c) || c == '.')) {
return null;
}
int startP = p;
if (negative) {
if (p + 1 >= ln) {
throw newParseException("Expected a digit after \"-\", but reached end-of-file.");
}
char lookAheadC = src.charAt(p + 1);
if (!(isDigit(lookAheadC) || lookAheadC == '.')) {
return null;
}
p++; // Consume "-" only, not the digit
}
long longSum = 0;
boolean firstDigit = true;
consumeLongFittingHead: do {
c = src.charAt(p);
if (!isDigit(c)) {
if (c == '.' && firstDigit) {
throw newParseException("JSON doesn't allow numbers starting with \".\".");
}
break consumeLongFittingHead;
}
int digit = c - '0';
if (longSum == 0) {
if (!firstDigit) {
throw newParseException("JSON doesn't allow superfluous leading 0-s.", p - 1);
}
longSum = !negative ? digit : -digit;
p++;
} else {
long prevLongSum = longSum;
longSum = longSum * 10 + (!negative ? digit : -digit);
if (!negative && prevLongSum > longSum || negative && prevLongSum < longSum) {
// We had an overflow => Can't consume this digit as long-fitting
break consumeLongFittingHead;
}
p++;
}
firstDigit = false;
} while (p < ln);
if (p < ln && isBigDecimalFittingTailCharacter(c)) {
char lastC = c;
p++;
consumeBigDecimalFittingTail: while (p < ln) {
c = src.charAt(p);
if (isBigDecimalFittingTailCharacter(c)) {
p++;
} else if ((c == '+' || c == '-') && isE(lastC)) {
p++;
} else {
break consumeBigDecimalFittingTail;
}
lastC = c;
}
String numStr = src.substring(startP, p);
BigDecimal bd;
try {
bd = new BigDecimal(numStr);
} catch (NumberFormatException e) {
throw new JSONParseException("Malformed number: " + numStr, src, startP, e);
}
if (bd.compareTo(MIN_INT_AS_BIGDECIMAL) >= 0 && bd.compareTo(MAX_INT_AS_BIGDECIMAL) <= 0) {
if (NumberUtil.isIntegerBigDecimal(bd)) {
return new SimpleNumber(bd.intValue());
}
} else if (bd.compareTo(MIN_LONG_AS_BIGDECIMAL) >= 0 && bd.compareTo(MAX_LONG_AS_BIGDECIMAL) <= 0) {
if (NumberUtil.isIntegerBigDecimal(bd)) {
return new SimpleNumber(bd.longValue());
}
}
return new SimpleNumber(bd);
} else {
return new SimpleNumber(
longSum <= Integer.MAX_VALUE && longSum >= Integer.MIN_VALUE
? (Number) (int) longSum
: longSum);
}
}