Skip to content

Commit 0739ddb

Browse files
committed
NumberUtils.isCreatable(String) should match
NumberUtils.createNumber(String), exactly Add tests from PR #1623
1 parent 7522574 commit 0739ddb

2 files changed

Lines changed: 25 additions & 102 deletions

File tree

src/main/java/org/apache/commons/lang3/math/NumberUtils.java

Lines changed: 5 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.Objects;
2424
import java.util.function.Consumer;
2525

26-
import org.apache.commons.lang3.CharUtils;
2726
import org.apache.commons.lang3.StringUtils;
2827
import org.apache.commons.lang3.Validate;
2928

@@ -109,7 +108,7 @@ private static <T> boolean accept(final Consumer<T> consumer, final T obj) {
109108
try {
110109
consumer.accept(obj);
111110
return true;
112-
} catch (Exception e) {
111+
} catch (final Exception e) {
113112
return false;
114113
}
115114
}
@@ -584,106 +583,12 @@ public static boolean isCreatable(final String str) {
584583
if (StringUtils.isEmpty(str)) {
585584
return false;
586585
}
587-
final char[] chars = str.toCharArray();
588-
int sz = chars.length;
589-
boolean hasExp = false;
590-
boolean hasDecPoint = false;
591-
boolean allowSigns = false;
592-
boolean foundDigit = false;
593-
// deal with any possible sign up front
594-
final int start = isSign(chars[0]) ? 1 : 0;
595-
if (sz > start + 1 && chars[start] == '0' && !StringUtils.contains(str, '.')) { // leading 0, skip if is a decimal number
596-
if (chars[start + 1] == 'x' || chars[start + 1] == 'X') { // leading 0x/0X
597-
int i = start + 2;
598-
if (i == sz) {
599-
return false; // str == "0x"
600-
}
601-
// checking hex (it can't be anything else)
602-
for (; i < chars.length; i++) {
603-
if (!CharUtils.isHex(chars[i])) {
604-
return false;
605-
}
606-
}
607-
return true;
608-
}
609-
if (Character.isDigit(chars[start + 1])) {
610-
// leading 0, but not hex, must be octal
611-
int i = start + 1;
612-
for (; i < chars.length; i++) {
613-
if (!CharUtils.isOctal(chars[i])) {
614-
return false;
615-
}
616-
}
617-
return true;
618-
}
619-
}
620-
sz--; // don't want to loop to the last char, check it afterwards
621-
// for type qualifiers
622-
int i = start;
623-
// loop to the next to last char or to the last char if we need another digit to
624-
// make a valid number (e.g. chars[0..5] = "1234E")
625-
while (i < sz || i < sz + 1 && allowSigns && !foundDigit) {
626-
if (CharUtils.isAsciiNumeric(chars[i])) {
627-
foundDigit = true;
628-
allowSigns = false;
629-
} else if (chars[i] == '.') {
630-
if (hasDecPoint || hasExp) {
631-
// two decimal points or dec in exponent
632-
return false;
633-
}
634-
hasDecPoint = true;
635-
} else if (chars[i] == 'e' || chars[i] == 'E') {
636-
// we've already taken care of hex.
637-
if (hasExp) {
638-
// two E's
639-
return false;
640-
}
641-
if (!foundDigit) {
642-
return false;
643-
}
644-
hasExp = true;
645-
allowSigns = true;
646-
} else if (isSign(chars[i])) {
647-
if (!allowSigns) {
648-
return false;
649-
}
650-
allowSigns = false;
651-
foundDigit = false; // we need a digit after the E
652-
} else {
653-
return false;
654-
}
655-
i++;
656-
}
657-
if (i < chars.length) {
658-
if (CharUtils.isAsciiNumeric(chars[i])) {
659-
// no type qualifier, OK
660-
return true;
661-
}
662-
if (chars[i] == 'e' || chars[i] == 'E') {
663-
// can't have an E at the last byte
664-
return false;
665-
}
666-
if (chars[i] == '.') {
667-
if (hasDecPoint || hasExp) {
668-
// two decimal points or dec in exponent
669-
return false;
670-
}
671-
// single trailing decimal point after non-exponent is ok
672-
return foundDigit;
673-
}
674-
if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F')) {
675-
return foundDigit;
676-
}
677-
if (chars[i] == 'l' || chars[i] == 'L') {
678-
// not allowing L with an exponent or decimal point
679-
return foundDigit && !hasExp && !hasDecPoint;
680-
}
681-
// last character is illegal
586+
try {
587+
createNumber(str);
588+
return true;
589+
} catch (final RuntimeException e) {
682590
return false;
683591
}
684-
// allowSigns is true iff the val ends in 'E'
685-
// found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
686-
return !allowSigns && foundDigit;
687592
}
688593

689594
/**

src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import java.util.function.Function;
3737

3838
import org.apache.commons.lang3.AbstractLangTest;
39+
import org.apache.commons.lang3.JavaVersion;
3940
import org.apache.commons.lang3.SystemProperties;
41+
import org.apache.commons.lang3.SystemUtils;
4042
import org.junit.jupiter.api.Test;
4143
import org.junit.jupiter.params.ParameterizedTest;
4244
import org.junit.jupiter.params.provider.ValueSource;
@@ -54,8 +56,7 @@ private static void assertCreateNumberZero(final String number, final Object zer
5456

5557
private boolean checkCreateNumber(final String val) {
5658
try {
57-
final Object obj = NumberUtils.createNumber(val);
58-
return obj != null;
59+
return NumberUtils.createNumber(val) != null;
5960
} catch (final NumberFormatException e) {
6061
return false;
6162
}
@@ -887,6 +888,23 @@ void testIsCreatable() {
887888
compareIsCreatableWithCreateNumber(".D", false); // LANG-1646
888889
compareIsCreatableWithCreateNumber(".e10", false); // LANG-1646
889890
compareIsCreatableWithCreateNumber(".e10D", false); // LANG-1646
891+
compareIsCreatableWithCreateNumber("1E2147483647", true);
892+
compareIsCreatableWithCreateNumber("1E+2147483647", true);
893+
compareIsCreatableWithCreateNumber("1E-2147483647", true);
894+
compareIsCreatableWithCreateNumber("1E-2147483648", false);
895+
compareIsCreatableWithCreateNumber("1E2147483648", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
896+
compareIsCreatableWithCreateNumber("1E+2147483648", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
897+
compareIsCreatableWithCreateNumber("1E+2147483649", false);
898+
compareIsCreatableWithCreateNumber("1E-2147483649", false);
899+
compareIsCreatableWithCreateNumber("1E2147483648D", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
900+
compareIsCreatableWithCreateNumber("1E-2147483648D", false);
901+
compareIsCreatableWithCreateNumber("1E2147483648F", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
902+
compareIsCreatableWithCreateNumber("1E+2147483648F", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
903+
compareIsCreatableWithCreateNumber("1E-2147483648F", false);
904+
compareIsCreatableWithCreateNumber("1.0E2147483648", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
905+
compareIsCreatableWithCreateNumber("1.0E-2147483648", false);
906+
compareIsCreatableWithCreateNumber("1E+999999999999999999999", false);
907+
compareIsCreatableWithCreateNumber("1E-999999999999999999999", false);
890908
}
891909

892910
@Test

0 commit comments

Comments
 (0)