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

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

commit b07764950ed8790f02495e0e8a991f95fac858c3
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 a25a8cfdc4..28cd3a93da 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;
             }
         }
@@ -646,7 +648,9 @@ public class Stream {
 
         @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 1a37d1ab70..b2a04926be 100644
--- a/test/org/apache/el/stream/TestCollectionOperations.java
+++ b/test/org/apache/el/stream/TestCollectionOperations.java
@@ -213,6 +213,23 @@ public class TestCollectionOperations {
     }
 
 
+    @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();
@@ -433,6 +450,34 @@ public class TestCollectionOperations {
 
         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();
@@ -493,6 +538,20 @@ public class TestCollectionOperations {
     }
 
 
+    @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