This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 127050d2b NumberUtils.isCreatable(String) should match
NumberUtils.createNumber(String), exactly (#1626)
127050d2b is described below
commit 127050d2b54bd6224bc53573228e8774dc7be28b
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Apr 16 09:29:30 2026 -0400
NumberUtils.isCreatable(String) should match
NumberUtils.createNumber(String), exactly (#1626)
* Add testLang1641()
* Rename some test methods
* NumberUtils.isCreatable(String) should match
NumberUtils.createNumber(String), exactly
Add tests from PR #1623
---
.../org/apache/commons/lang3/math/NumberUtils.java | 105 +--------------------
.../apache/commons/lang3/math/NumberUtilsTest.java | 22 ++++-
2 files changed, 25 insertions(+), 102 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java
b/src/main/java/org/apache/commons/lang3/math/NumberUtils.java
index d4863e746..7327fac06 100644
--- a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java
+++ b/src/main/java/org/apache/commons/lang3/math/NumberUtils.java
@@ -23,7 +23,6 @@
import java.util.Objects;
import java.util.function.Consumer;
-import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@@ -109,7 +108,7 @@ private static <T> boolean accept(final Consumer<T>
consumer, final T obj) {
try {
consumer.accept(obj);
return true;
- } catch (Exception e) {
+ } catch (final Exception e) {
return false;
}
}
@@ -584,106 +583,12 @@ public static boolean isCreatable(final String str) {
if (StringUtils.isEmpty(str)) {
return false;
}
- final char[] chars = str.toCharArray();
- int sz = chars.length;
- boolean hasExp = false;
- boolean hasDecPoint = false;
- boolean allowSigns = false;
- boolean foundDigit = false;
- // deal with any possible sign up front
- final int start = isSign(chars[0]) ? 1 : 0;
- if (sz > start + 1 && chars[start] == '0' &&
!StringUtils.contains(str, '.')) { // leading 0, skip if is a decimal number
- if (chars[start + 1] == 'x' || chars[start + 1] == 'X') { //
leading 0x/0X
- int i = start + 2;
- if (i == sz) {
- return false; // str == "0x"
- }
- // checking hex (it can't be anything else)
- for (; i < chars.length; i++) {
- if (!CharUtils.isHex(chars[i])) {
- return false;
- }
- }
- return true;
- }
- if (Character.isDigit(chars[start + 1])) {
- // leading 0, but not hex, must be octal
- int i = start + 1;
- for (; i < chars.length; i++) {
- if (!CharUtils.isOctal(chars[i])) {
- return false;
- }
- }
- return true;
- }
- }
- sz--; // don't want to loop to the last char, check it afterwards
- // for type qualifiers
- int i = start;
- // loop to the next to last char or to the last char if we need
another digit to
- // make a valid number (e.g. chars[0..5] = "1234E")
- while (i < sz || i < sz + 1 && allowSigns && !foundDigit) {
- if (CharUtils.isAsciiNumeric(chars[i])) {
- foundDigit = true;
- allowSigns = false;
- } else if (chars[i] == '.') {
- if (hasDecPoint || hasExp) {
- // two decimal points or dec in exponent
- return false;
- }
- hasDecPoint = true;
- } else if (chars[i] == 'e' || chars[i] == 'E') {
- // we've already taken care of hex.
- if (hasExp) {
- // two E's
- return false;
- }
- if (!foundDigit) {
- return false;
- }
- hasExp = true;
- allowSigns = true;
- } else if (isSign(chars[i])) {
- if (!allowSigns) {
- return false;
- }
- allowSigns = false;
- foundDigit = false; // we need a digit after the E
- } else {
- return false;
- }
- i++;
- }
- if (i < chars.length) {
- if (CharUtils.isAsciiNumeric(chars[i])) {
- // no type qualifier, OK
- return true;
- }
- if (chars[i] == 'e' || chars[i] == 'E') {
- // can't have an E at the last byte
- return false;
- }
- if (chars[i] == '.') {
- if (hasDecPoint || hasExp) {
- // two decimal points or dec in exponent
- return false;
- }
- // single trailing decimal point after non-exponent is ok
- return foundDigit;
- }
- if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' || chars[i]
== 'f' || chars[i] == 'F')) {
- return foundDigit;
- }
- if (chars[i] == 'l' || chars[i] == 'L') {
- // not allowing L with an exponent or decimal point
- return foundDigit && !hasExp && !hasDecPoint;
- }
- // last character is illegal
+ try {
+ createNumber(str);
+ return true;
+ } catch (final RuntimeException e) {
return false;
}
- // allowSigns is true iff the val ends in 'E'
- // found digit it to make sure weird stuff like '.' and '1E-' doesn't
pass
- return !allowSigns && foundDigit;
}
/**
diff --git a/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java
b/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java
index a41842c0d..0cd9105af 100644
--- a/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java
@@ -36,7 +36,9 @@
import java.util.function.Function;
import org.apache.commons.lang3.AbstractLangTest;
+import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemProperties;
+import org.apache.commons.lang3.SystemUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -54,8 +56,7 @@ private static void assertCreateNumberZero(final String
number, final Object zer
private boolean checkCreateNumber(final String val) {
try {
- final Object obj = NumberUtils.createNumber(val);
- return obj != null;
+ return NumberUtils.createNumber(val) != null;
} catch (final NumberFormatException e) {
return false;
}
@@ -887,6 +888,23 @@ void testIsCreatable() {
compareIsCreatableWithCreateNumber(".D", false); // LANG-1646
compareIsCreatableWithCreateNumber(".e10", false); // LANG-1646
compareIsCreatableWithCreateNumber(".e10D", false); // LANG-1646
+ compareIsCreatableWithCreateNumber("1E2147483647", true);
+ compareIsCreatableWithCreateNumber("1E+2147483647", true);
+ compareIsCreatableWithCreateNumber("1E-2147483647", true);
+ compareIsCreatableWithCreateNumber("1E-2147483648", false);
+ compareIsCreatableWithCreateNumber("1E2147483648",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
+ compareIsCreatableWithCreateNumber("1E+2147483648",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
+ compareIsCreatableWithCreateNumber("1E+2147483649", false);
+ compareIsCreatableWithCreateNumber("1E-2147483649", false);
+ compareIsCreatableWithCreateNumber("1E2147483648D",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
+ compareIsCreatableWithCreateNumber("1E-2147483648D", false);
+ compareIsCreatableWithCreateNumber("1E2147483648F",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
+ compareIsCreatableWithCreateNumber("1E+2147483648F",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
+ compareIsCreatableWithCreateNumber("1E-2147483648F", false);
+ compareIsCreatableWithCreateNumber("1.0E2147483648",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21));
+ compareIsCreatableWithCreateNumber("1.0E-2147483648", false);
+ compareIsCreatableWithCreateNumber("1E+999999999999999999999", false);
+ compareIsCreatableWithCreateNumber("1E-999999999999999999999", false);
}
@Test