This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch backport/CAMEL-24407-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 7da3c4217d9b1f6e3bca79b4d3bdb6515a046211
Author: Karol <[email protected]>
AuthorDate: Mon Aug 31 10:05:32 2026 +0200

    CAMEL-24407: simple predicate fails for long digital strings
    
    Numbers with more digits than a long can hold, such as bank account
    numbers, made comparisons throw NumberFormatException. isNumber only
    checks that the text is all digits, and the callers then parsed it
    with Long.parseLong or Integer.valueOf.
    
    Compare those as BigInteger instead. A number that does not fit in a
    long can never equal an int or long, so equality with one is false.
    A numeric literal in a simple predicate that does not fit in a long is
    now kept as literal text, so it takes the same comparison path.
    
    Co-authored-by: Claude <[email protected]>
---
 .../language/simple/SimplePredicateParser.java     |  5 +-
 .../language/simple/ast/NumericExpression.java     | 12 +++
 .../camel/converter/TypeCoerceCompareTest.java     | 21 +++++
 .../camel/language/simple/SimpleOperatorTest.java  | 17 ++++
 .../org/apache/camel/util/ObjectHelperTest.java    | 22 +++++
 .../org/apache/camel/support/ObjectHelper.java     | 98 +++++++++++++++-------
 .../org/apache/camel/support/ObjectHelperTest.java | 29 +++++++
 7 files changed, 168 insertions(+), 36 deletions(-)

diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
index 016e819faa3d..94d3cd2aac86 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
@@ -58,9 +58,6 @@ import org.apache.camel.support.ExpressionToPredicateAdapter;
 import org.apache.camel.support.builder.PredicateBuilder;
 import org.apache.camel.util.StringHelper;
 
-import static org.apache.camel.support.ObjectHelper.isFloatingNumber;
-import static org.apache.camel.support.ObjectHelper.isNumber;
-
 /**
  * A parser to parse simple language as a Camel {@link Predicate}
  */
@@ -321,7 +318,7 @@ public class SimplePredicateParser extends BaseSimpleParser 
{
         if (!quoted) {
             // if the text is not in a quoted block (literal text), then lets 
see if
             // its numeric then we can optimize this
-            numeric = isNumber(text) || isFloatingNumber(text);
+            numeric = NumericExpression.isNumericValue(text);
         }
         if (numeric) {
             nodes.add(new NumericExpression(imageToken.getToken(), text));
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
index 9f39a63e4f4f..8f38ab8155ed 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/NumericExpression.java
@@ -21,6 +21,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
 import org.apache.camel.language.simple.types.SimpleParserException;
 import org.apache.camel.language.simple.types.SimpleToken;
+import org.apache.camel.support.ObjectHelper;
 
 /**
  * Represents a numeric value.
@@ -47,6 +48,17 @@ public class NumericExpression extends BaseSimpleNode {
         }
     }
 
+    /**
+     * Whether the text can be represented as a numeric value. Numbers with 
more digits than a long can hold, such as
+     * bank account numbers, are kept as literal text instead, so they can be 
compared as big integers.
+     */
+    public static boolean isNumericValue(String text) {
+        if (text.indexOf('.') != -1) {
+            return ObjectHelper.isFloatingNumber(text);
+        }
+        return ObjectHelper.isLongNumber(text);
+    }
+
     public Object getNumber() {
         return number;
     }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java
index 67d35dd47481..68ef8d464f97 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/converter/TypeCoerceCompareTest.java
@@ -41,6 +41,27 @@ public class TypeCoerceCompareTest extends 
ContextTestSupport {
         assertEquals(0, ObjectHelper.typeCoerceCompare(tc, "7.0", "7"));
     }
 
+    @Test
+    public void testCompareStringStringTooBigForLong() {
+        TypeConverter tc = context.getTypeConverter();
+        // numbers such as bank account numbers have more digits than a long 
can hold
+        assertEquals(0, ObjectHelper.typeCoerceCompare(tc, 
"12345678901234567890", "12345678901234567890"));
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, "12345678901234567891", 
"12345678901234567890") > 0);
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, "12345678901234567890", 
"12345678901234567891") < 0);
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, "12345678901234567890", 
"7") > 0);
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, "7", 
"12345678901234567890") < 0);
+    }
+
+    @Test
+    public void testCompareStringNumberTooBigForLong() {
+        TypeConverter tc = context.getTypeConverter();
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, "12345678901234567890", 
7L) > 0);
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, 7L, 
"12345678901234567890") < 0);
+        // does not fit in an int, but still fits in a long
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, "99999999999", 7) > 0);
+        assertTrue(ObjectHelper.typeCoerceCompare(tc, 7, "99999999999") < 0);
+    }
+
     @Test
     public void testCompareStringInteger() {
         TypeConverter tc = context.getTypeConverter();
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
index 6d123c342f72..dd1539b0beb3 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleOperatorTest.java
@@ -973,6 +973,23 @@ public class SimpleOperatorTest extends 
LanguageTestSupport {
         assertExpression("${trim()} ~> ${replace('Hello','Hi',$param)} ~> 
${split($param,' ')} ~> ${size($param)}", 5);
     }
 
+    @Test
+    public void testDigitalStringTooBigForLong() {
+        // CAMEL-24407: numbers such as bank account numbers have more digits 
than a long can hold
+        exchange.getIn().setHeader("Account1", "12345678901234567890");
+        exchange.getIn().setHeader("Account2", "12345678901234567890");
+        exchange.getIn().setHeader("Account3", "12345678901234567891");
+
+        assertPredicate("${header.Account1} == ${header.Account2}", true);
+        assertPredicate("${header.Account1} == ${header.Account3}", false);
+        assertPredicate("${header.Account1} != ${header.Account3}", true);
+        assertPredicate("${header.Account1} < ${header.Account3}", true);
+        assertPredicate("${header.Account3} > ${header.Account1}", true);
+        assertPredicate("${header.Account1} == 12345678901234567890", true);
+        assertPredicate("${header.Account1} == '12345678901234567890'", true);
+        assertPredicate("${header.Account1} > 7", true);
+    }
+
     @Override
     protected String getLanguageName() {
         return "simple";
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java 
b/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java
index 981627e9d617..3b4e1e09b865 100644
--- a/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java
@@ -175,6 +175,28 @@ public class ObjectHelperTest {
         }
     }
 
+    @Test
+    void testEqualsNumberTooBigForLong() throws Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            context.start();
+            TypeConverter tc = context.getTypeConverter();
+
+            // numbers such as bank account numbers have more digits than a 
long can hold
+            assertTrue(ObjectHelper.typeCoerceEquals(tc, 
"12345678901234567890", "12345678901234567890"));
+            assertFalse(ObjectHelper.typeCoerceEquals(tc, 
"12345678901234567890", "12345678901234567891"));
+            assertFalse(ObjectHelper.typeCoerceEquals(tc, 
"12345678901234567890", "7"));
+
+            // such a number cannot be equal to an int or long
+            assertFalse(ObjectHelper.typeCoerceEquals(tc, 
"12345678901234567890", 7L));
+            assertFalse(ObjectHelper.typeCoerceEquals(tc, 7L, 
"12345678901234567890"));
+
+            // does not fit in an int, but still fits in a long
+            assertFalse(ObjectHelper.typeCoerceEquals(tc, "99999999999", 7));
+            assertFalse(ObjectHelper.typeCoerceEquals(tc, 7, "99999999999"));
+            assertTrue(ObjectHelper.typeCoerceEquals(tc, "99999999999", 
99999999999L));
+        }
+    }
+
     @Test
     void testContainsStringBuilder() throws Exception {
         try (CamelContext context = new DefaultCamelContext()) {
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java
index df24b780a78b..f91a7e6e4e88 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java
@@ -22,6 +22,7 @@ import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.math.BigInteger;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -217,19 +218,29 @@ public final class ObjectHelper {
     }
 
     private static boolean typeCoerceIntLong(Object leftValue, String 
rightValue) {
+        Long rightNum = toLong(rightValue);
+        if (rightNum == null) {
+            // too big for a long so it cannot be equal to an int or long
+            return false;
+        }
         if (leftValue instanceof Integer intValue) {
-            return integerPairComparison(intValue, 
Integer.valueOf(rightValue));
+            return longPairComparison(intValue.longValue(), rightNum);
         } else if (leftValue instanceof Long longValue) {
-            return longPairComparison(longValue, Long.valueOf(rightValue));
+            return longPairComparison(longValue, rightNum);
         }
         return false;
     }
 
     private static boolean typeCoerceILString(String leftValue, Object 
rightValue) {
+        Long leftNum = toLong(leftValue);
+        if (leftNum == null) {
+            // too big for a long so it cannot be equal to an int or long
+            return false;
+        }
         if (rightValue instanceof Integer intValue) {
-            return integerPairComparison(Integer.valueOf(leftValue), intValue);
+            return longPairComparison(leftNum, intValue.longValue());
         } else if (rightValue instanceof Long longValue) {
-            return longPairComparison(Long.valueOf(leftValue), longValue);
+            return longPairComparison(leftNum, longValue);
         }
         return false;
     }
@@ -237,7 +248,13 @@ public final class ObjectHelper {
     private static boolean typeCoerceStringPair(String leftNum, String 
rightNum, boolean ignoreCase) {
         if (isNumber(leftNum) && isNumber(rightNum)) {
             // favour to use numeric comparison
-            return longPairComparison(Long.parseLong(leftNum), 
Long.parseLong(rightNum));
+            Long left = toLong(leftNum);
+            Long right = toLong(rightNum);
+            if (left != null && right != null) {
+                return longPairComparison(left, right);
+            }
+            // too big for a long so compare as big integers
+            return new BigInteger(leftNum).equals(new BigInteger(rightNum));
         }
         if (ignoreCase) {
             return leftNum.compareToIgnoreCase(rightNum) == 0;
@@ -276,23 +293,22 @@ public final class ObjectHelper {
             return leftNum.compareTo(rightNum);
         } else if ((rightValue instanceof Integer || rightValue instanceof 
Long) &&
                 leftValue instanceof String leftStr && isNumber(leftStr)) {
-            if (rightValue instanceof Integer rightNum) {
-                Integer leftNum = Integer.valueOf(leftStr);
-                return leftNum.compareTo(rightNum);
-            } else {
-                Long leftNum = Long.valueOf(leftStr);
-                Long rightNum = (Long) rightValue;
-                return leftNum.compareTo(rightNum);
+            long rightNum = ((Number) rightValue).longValue();
+            Long leftNum = toLong(leftStr);
+            if (leftNum == null) {
+                // too big for a long so compare as big integers
+                return new 
BigInteger(leftStr).compareTo(BigInteger.valueOf(rightNum));
             }
+            return Long.compare(leftNum, rightNum);
         } else if (rightValue instanceof String rightStr &&
                 (leftValue instanceof Integer || leftValue instanceof Long) && 
isNumber(rightStr)) {
-            if (leftValue instanceof Integer leftNum) {
-                Integer rightNum = Integer.valueOf(rightStr);
-                return leftNum.compareTo(rightNum);
-            } else if (leftValue instanceof Long leftNum) {
-                Long rightNum = Long.valueOf(rightStr);
-                return leftNum.compareTo(rightNum);
+            long leftNum = ((Number) leftValue).longValue();
+            Long rightNum = toLong(rightStr);
+            if (rightNum == null) {
+                // too big for a long so compare as big integers
+                return BigInteger.valueOf(leftNum).compareTo(new 
BigInteger(rightStr));
             }
+            return Long.compare(leftNum, rightNum);
         } else if (rightValue instanceof Double rightNum && leftValue 
instanceof String leftStr
                 && isFloatingNumber(leftStr)) {
             Double leftNum = Double.valueOf(leftStr);
@@ -355,27 +371,45 @@ public final class ObjectHelper {
 
     private static int typeCoerceCompareStringString(String leftNum, String 
rightNum) {
         // prioritize non-floating numbers first
-        Long num1 = isNumber(leftNum) ? Long.parseLong(leftNum) : null;
-        Long num2 = isNumber(rightNum) ? Long.parseLong(rightNum) : null;
-        Double dec1 = num1 == null && isFloatingNumber(leftNum) ? 
Double.parseDouble(leftNum) : null;
-        Double dec2 = num2 == null && isFloatingNumber(rightNum) ? 
Double.parseDouble(rightNum) : null;
-        if (num1 != null && num2 != null) {
-            return num1.compareTo(num2);
-        } else if (dec1 != null && dec2 != null) {
-            return dec1.compareTo(dec2);
+        if (isNumber(leftNum) && isNumber(rightNum)) {
+            Long num1 = toLong(leftNum);
+            Long num2 = toLong(rightNum);
+            if (num1 != null && num2 != null) {
+                return num1.compareTo(num2);
+            }
+            // too big for a long so compare as big integers
+            return new BigInteger(leftNum).compareTo(new BigInteger(rightNum));
         }
-        // okay mixed but we need to convert to floating
-        if (num1 != null && dec2 != null) {
-            dec1 = Double.parseDouble(leftNum);
-            return dec1.compareTo(dec2);
-        } else if (num2 != null && dec1 != null) {
-            dec2 = Double.parseDouble(rightNum);
+        // mixed or floating numbers are compared as floating
+        Double dec1 = isFloatingNumber(leftNum) ? Double.parseDouble(leftNum) 
: null;
+        Double dec2 = isFloatingNumber(rightNum) ? 
Double.parseDouble(rightNum) : null;
+        if (dec1 != null && dec2 != null) {
             return dec1.compareTo(dec2);
         }
         // fallback to string comparison
         return leftNum.compareTo(rightNum);
     }
 
+    /**
+     * Checks whether the text is an integer number that fits in a {@link 
Long}. Numbers such as bank account numbers
+     * can have more digits than a long can hold, and must be compared as 
{@link BigInteger} instead.
+     */
+    public static boolean isLongNumber(String text) {
+        return isNumber(text) && toLong(text) != null;
+    }
+
+    /**
+     * Parses the text as a long, or <tt>null</tt> if the number has too many 
digits to fit in a {@link Long}. The text
+     * is expected to be checked with {@link #isNumber(String)} first, so 
overflow is the only way this fails.
+     */
+    private static Long toLong(String text) {
+        try {
+            return Long.parseLong(text);
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
     /**
      * Checks whether the text is an integer number
      */
diff --git 
a/core/camel-support/src/test/java/org/apache/camel/support/ObjectHelperTest.java
 
b/core/camel-support/src/test/java/org/apache/camel/support/ObjectHelperTest.java
index 23f42f12191d..1e7a24ace881 100644
--- 
a/core/camel-support/src/test/java/org/apache/camel/support/ObjectHelperTest.java
+++ 
b/core/camel-support/src/test/java/org/apache/camel/support/ObjectHelperTest.java
@@ -62,6 +62,35 @@ class ObjectHelperTest {
         assertFalse(ObjectHelper.isNumber("0.0"));
     }
 
+    @Test
+    @DisplayName("Tests that isLongNumber returns true for integers that fit 
in a long")
+    void isLongNumberIntegers() {
+        assertTrue(ObjectHelper.isLongNumber("1234"));
+        assertTrue(ObjectHelper.isLongNumber("-1234"));
+        assertTrue(ObjectHelper.isLongNumber("0"));
+        assertTrue(ObjectHelper.isLongNumber("9223372036854775807"));
+        assertTrue(ObjectHelper.isLongNumber("-9223372036854775808"));
+        assertTrue(ObjectHelper.isLongNumber("00000000000000000000001"));
+    }
+
+    @Test
+    @DisplayName("Tests that isLongNumber returns false for integers with too 
many digits")
+    void isLongNumberTooBig() {
+        assertFalse(ObjectHelper.isLongNumber("9223372036854775808"));
+        assertFalse(ObjectHelper.isLongNumber("-9223372036854775809"));
+        assertFalse(ObjectHelper.isLongNumber("12345678901234567890"));
+    }
+
+    @Test
+    @DisplayName("Tests that isLongNumber returns false for non-integers")
+    void isLongNumberNonIntegers() {
+        assertFalse(ObjectHelper.isLongNumber(""));
+        assertFalse(ObjectHelper.isLongNumber(" "));
+        assertFalse(ObjectHelper.isLongNumber(null));
+        assertFalse(ObjectHelper.isLongNumber("ABC"));
+        assertFalse(ObjectHelper.isLongNumber("12.34"));
+    }
+
     @Test
     @DisplayName("Tests that isFloatingNumber returns true for empty, space or 
null")
     void isFloatingNumberEmpty() {

Reply via email to