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

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


The following commit(s) were added to refs/heads/camel-4.22.x by this push:
     new 65cd5d3f4c4e CAMEL-24407: simple predicate fails for long digital 
strings
65cd5d3f4c4e is described below

commit 65cd5d3f4c4e1a92b7e4c3a2ce0753d47886e100
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Aug 31 22:56:28 2026 +0200

    CAMEL-24407: simple predicate fails for long digital strings
    
    Backport of #25945 to camel-4.22.x. The simple language threw
    NumberFormatException when comparing numbers with more digits than a
    long can hold, such as bank account numbers. Numbers that don't fit
    in a long are now compared as BigInteger instead of being force-cast,
    so equality/ordering against oversized numeric literals works
    correctly instead of throwing.
    
    Co-authored-by: Karol <[email protected]>
    Co-authored-by: Claude <[email protected]>
    
    Closes #25978
---
 .../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 c52cc491050d..05ea56bfc31d 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
@@ -59,9 +59,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}
  */
@@ -326,7 +323,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