in freemarker-core/src/main/java/freemarker/core/_ObjectBuilderSettingEvaluator.java [371:479]
private Object fetchNumberLike(boolean optional, boolean resultCoerced)
throws _ObjectBuilderSettingEvaluationException {
int startPos = pos;
boolean isVersion = false;
boolean hasDot = false;
seekTokenEnd: while (true) {
if (pos == src.length()) {
break seekTokenEnd;
}
char c = src.charAt(pos);
if (c == '.') {
if (hasDot) {
// More than one dot
isVersion = true;
} else {
hasDot = true;
}
} else if (!(isASCIIDigit(c) || c == '-')) {
break seekTokenEnd;
}
pos++;
}
if (startPos == pos) {
if (optional) {
return VOID;
} else {
throw new _ObjectBuilderSettingEvaluationException("number-like", src, pos);
}
}
String numStr = src.substring(startPos, pos);
if (isVersion) {
try {
return new Version(numStr);
} catch (IllegalArgumentException e) {
throw new _ObjectBuilderSettingEvaluationException("Malformed version number: " + numStr, e);
}
} else {
// For example, in 1.0f, numStr is "1.0", and typePostfix is "f".
String typePostfix = null;
seekTypePostfixEnd: while (true) {
if (pos == src.length()) {
break seekTypePostfixEnd;
}
char c = src.charAt(pos);
if (Character.isLetter(c)) {
if (typePostfix == null) {
typePostfix = String.valueOf(c);
} else {
typePostfix += c;
}
} else {
break seekTypePostfixEnd;
}
pos++;
}
try {
if (numStr.endsWith(".")) {
throw new NumberFormatException("A number can't end with a dot");
}
if (numStr.startsWith(".") || numStr.startsWith("-.") || numStr.startsWith("+.")) {
throw new NumberFormatException("A number can't start with a dot");
}
if (typePostfix == null) {
// Auto-detect type
if (numStr.indexOf('.') == -1) {
BigInteger biNum = new BigInteger(numStr);
final int bitLength = biNum.bitLength(); // Doesn't include sign bit
if (bitLength <= 31) {
return Integer.valueOf(biNum.intValue());
} else if (bitLength <= 63) {
return Long.valueOf(biNum.longValue());
} else {
return biNum;
}
} else {
if (resultCoerced) {
// The FTL way (BigDecimal is lossless, and it will be coerced to the target type later):
return new BigDecimal(numStr);
} else {
// The Java way (lossy but familiar):
return Double.valueOf(numStr);
}
}
} else { // Has explicitly specified type
if (typePostfix.equalsIgnoreCase("l")) {
return Long.valueOf(numStr);
} else if (typePostfix.equalsIgnoreCase("bi")) {
return new BigInteger(numStr);
} else if (typePostfix.equalsIgnoreCase("bd")) {
return new BigDecimal(numStr);
} else if (typePostfix.equalsIgnoreCase("d")) {
return Double.valueOf(numStr);
} else if (typePostfix.equalsIgnoreCase("f")) {
return Float.valueOf(numStr);
} else {
throw new _ObjectBuilderSettingEvaluationException(
"Unrecognized number type postfix: " + typePostfix);
}
}
} catch (NumberFormatException e) {
throw new _ObjectBuilderSettingEvaluationException("Malformed number: " + numStr, e);
}
}
}