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

rmaucher pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 578ecebdd4f49b717c616e888d8adf42a85b1701
Author: opencode <[email protected]>
AuthorDate: Sun Oct 18 16:42:19 2026 +0200

    Accept null results from EL stream comparator lambdas
    
    The max, min and sorted stream operations evaluated their comparator
    lambda and coerced the result to a number with the boxed integer type
    as the target. That coercion returns null for a null value, so a
    comparator lambda that returned null triggered a null pointer
    exception when the coerced result was unboxed.
    
    The Jakarta EL specification types the comparator as a function to the
    primitive int, and the coercion to a primitive numeric type maps null
    and the empty string to zero. Coerce to the primitive type instead. A
    null comparator result now means that the two elements compare as
    equal, which for sorted leaves a stable sort in its input order and
    for max and min leaves the current element, while results that cannot
    be coerced at all continue to be reported as an ELException. The
    handling of non-null numeric results, including the truncation of
    fractional values, is unchanged.
    
    Adds tests for a null comparator result on the three operations and a
    test that a non-coercible comparator result is reported as an
    ELException. The null result tests report the null pointer exception
    against the previous implementation.
---
 java/org/apache/el/stream/Stream.java              | 10 ++--
 .../apache/el/stream/TestCollectionOperations.java | 59 ++++++++++++++++++++++
 2 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/el/stream/Stream.java 
b/java/org/apache/el/stream/Stream.java
index f277757e75..4f3a01dee5 100644
--- a/java/org/apache/el/stream/Stream.java
+++ b/java/org/apache/el/stream/Stream.java
@@ -621,9 +621,11 @@ public class Stream {
 
         while (iterator.hasNext()) {
             Object obj = iterator.next();
-            if (isMax && ELSupport.coerceToNumber(null, le.invoke(obj, 
result), Integer.class).intValue() > 0) {
+            // Coerce to the primitive type so that a null comparator result
+            // coerces to zero rather than producing a NullPointerException
+            if (isMax && ELSupport.coerceToNumber(null, le.invoke(obj, 
result), Integer.TYPE).intValue() > 0) {
                 result = obj;
-            } else if (!isMax && ELSupport.coerceToNumber(null, le.invoke(obj, 
result), Integer.class).intValue() < 0) {
+            } else if (!isMax && ELSupport.coerceToNumber(null, le.invoke(obj, 
result), Integer.TYPE).intValue() < 0) {
                 result = obj;
             }
         }
@@ -639,7 +641,9 @@ public class Stream {
     private record LambdaExpressionComparator(LambdaExpression le) implements 
Comparator<Object> {
         @Override
         public int compare(Object o1, Object o2) {
-            return ELSupport.coerceToNumber(null, le.invoke(o1, o2), 
Integer.class).intValue();
+            // Coerce to the primitive type so that a null comparator result
+            // coerces to zero rather than producing a NullPointerException
+            return ELSupport.coerceToNumber(null, le.invoke(o1, o2), 
Integer.TYPE).intValue();
         }
     }
 
diff --git a/test/org/apache/el/stream/TestCollectionOperations.java 
b/test/org/apache/el/stream/TestCollectionOperations.java
index a0527488ff..c21d78d83d 100644
--- a/test/org/apache/el/stream/TestCollectionOperations.java
+++ b/test/org/apache/el/stream/TestCollectionOperations.java
@@ -214,6 +214,23 @@ public class TestCollectionOperations extends ELBaseTest {
     }
 
 
+    @Test
+    public void testSortedLambdaExpression02() {
+        // A comparator that returns null coerces to zero, treating all
+        // elements as equal. The sort is stable, so the order is unchanged.
+        ELProcessor processor = new ELProcessor();
+        Object result = processor.getValue(
+                "[3, 1, 2].stream().sorted((x,y)->null).toList()",
+                List.class);
+        List<Long> expected = new ArrayList<>(3);
+        expected.add(3L);
+        expected.add(1L);
+        expected.add(2L);
+
+        Assert.assertEquals(expected, result);
+    }
+
+
     @Test
     public void testForEach01() {
         ELProcessor processor = new ELProcessor();
@@ -434,6 +451,34 @@ public class TestCollectionOperations extends ELBaseTest {
 
         Assert.assertEquals(bean03, ((Optional) result).get());
     }
+
+
+    @Test
+    public void testMaxLambda03() {
+        // A comparator that returns null coerces to zero, treating all
+        // elements as equal. The first element is retained.
+        ELProcessor processor = new ELProcessor();
+
+        Object result = processor.getValue(
+                "[1, 2, 3].stream().max((x,y)->null)",
+                Object.class);
+
+        Assert.assertEquals(1L, ((Optional) result).get());
+    }
+
+
+    @Test(expected=ELException.class)
+    public void testMaxLambda04() {
+        // A comparator result that cannot be coerced to a number is reported
+        // as an ELException
+        ELProcessor processor = new ELProcessor();
+
+        processor.getValue(
+                "[1, 2, 3].stream().max((x,y)->'abc')",
+                Object.class);
+    }
+
+
     @Test
     public void testMin01() {
         ELProcessor processor = new ELProcessor();
@@ -494,6 +539,20 @@ public class TestCollectionOperations extends ELBaseTest {
     }
 
 
+    @Test
+    public void testMinLambda02() {
+        // A comparator that returns null coerces to zero, treating all
+        // elements as equal. The first element is retained.
+        ELProcessor processor = new ELProcessor();
+
+        Object result = processor.getValue(
+                "[1, 2, 3].stream().min((x,y)->null)",
+                Object.class);
+
+        Assert.assertEquals(1L, ((Optional) result).get());
+    }
+
+
     @Test
     public void testAverage01() {
         ELProcessor processor = new ELProcessor();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to