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

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


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 842d526d66 Review "Coerce EL stream predicate results in the match 
operations"
842d526d66 is described below

commit 842d526d6670aee5009bda20386cda4e19e45941
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Nov 5 16:30:55 2026 +0000

    Review "Coerce EL stream predicate results in the match operations"
    
    Additional error handling so ELException is thrown
---
 java/org/apache/el/Messages.properties             |   1 +
 java/org/apache/el/stream/Stream.java              |  18 ++-
 .../apache/el/stream/TestCollectionOperations.java | 125 +++++++++++++++++++++
 3 files changed, 141 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/el/Messages.properties 
b/java/org/apache/el/Messages.properties
index 95a6c572fe..19cc234a58 100644
--- a/java/org/apache/el/Messages.properties
+++ b/java/org/apache/el/Messages.properties
@@ -76,5 +76,6 @@ error.lambda.wrongNestedState=Nested state may only be set 
once
 
 # Stream
 stream.compare.notComparable=Stream elements must implement Comparable
+stream.compare.sortFailed=Stream elements must implement Comparable and be 
comparable with each other
 stream.optional.empty=It is illegal to call get() on an empty optional
 stream.optional.paramNotLambda=The parameter for the method [{0}] should be a 
lambda expression
diff --git a/java/org/apache/el/stream/Stream.java 
b/java/org/apache/el/stream/Stream.java
index 37a9e71988..a9632e7278 100644
--- a/java/org/apache/el/stream/Stream.java
+++ b/java/org/apache/el/stream/Stream.java
@@ -186,7 +186,12 @@ public class Stream {
                 while (iterator.hasNext()) {
                     list.add(iterator.next());
                 }
-                Collections.sort(list);
+                try {
+                    Collections.sort(list);
+                } catch (ClassCastException cce) {
+                    // The elements are not Comparable or are not Comparable 
with each other
+                    throw new 
ELException(MessageFactory.get("stream.compare.sortFailed"), cce);
+                }
                 sorted = list.iterator();
             }
         };
@@ -604,9 +609,16 @@ public class Stream {
         while (iterator.hasNext()) {
             Object obj = iterator.next();
             if ((obj instanceof Comparable)) {
-                if (isMax && ((Comparable) obj).compareTo(result) > 0) {
+                int comparison;
+                try {
+                    comparison = ((Comparable) obj).compareTo(result);
+                } catch (ClassCastException cce) {
+                    // The elements are Comparable but not with each other
+                    throw new ELException(MessageFactory.get("error.compare", 
obj, result), cce);
+                }
+                if (isMax && comparison > 0) {
                     result = (Comparable) obj;
-                } else if (!isMax && ((Comparable) obj).compareTo(result) < 0) 
{
+                } else if (!isMax && comparison < 0) {
                     result = (Comparable) obj;
                 }
             } else {
diff --git a/test/org/apache/el/stream/TestCollectionOperations.java 
b/test/org/apache/el/stream/TestCollectionOperations.java
index 914aaf283c..1d70f49af6 100644
--- a/test/org/apache/el/stream/TestCollectionOperations.java
+++ b/test/org/apache/el/stream/TestCollectionOperations.java
@@ -17,6 +17,7 @@
 package org.apache.el.stream;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -498,6 +499,130 @@ public class TestCollectionOperations {
     }
 
 
+    private static boolean hasCause(Throwable t, Class<? extends Throwable> 
cause) {
+        while (t != null) {
+            if (cause.isInstance(t)) {
+                return true;
+            }
+            t = t.getCause();
+        }
+        return false;
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private static Stream streamOf(Object... elements) {
+        return new Stream((java.util.Iterator<Object>) 
Arrays.asList(elements).iterator());
+    }
+
+
+    /*
+     * The following tests use the Stream directly so the exception is seen 
before any wrapping by the expression
+     * evaluation. The failure must be an ELException with the 
ClassCastException as the cause.
+     */
+    @Test
+    public void testMaxDirectNotMutuallyComparable() {
+        try {
+            streamOf(Integer.valueOf(1), "a").max();
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e.getCause(), 
e.getCause() instanceof ClassCastException);
+        }
+    }
+
+
+    @Test
+    public void testMinDirectNotMutuallyComparable() {
+        try {
+            streamOf("a", Integer.valueOf(1)).min();
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e.getCause(), 
e.getCause() instanceof ClassCastException);
+        }
+    }
+
+
+    @Test
+    public void testSortedDirectNotMutuallyComparable() {
+        Stream sorted = streamOf(Integer.valueOf(1), "a").sorted();
+        try {
+            sorted.toList();
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e.getCause(), 
e.getCause() instanceof ClassCastException);
+        }
+    }
+
+
+    @Test
+    public void testSortedDirectNotComparable() {
+        Stream sorted = streamOf(new Object(), new Object()).sorted();
+        try {
+            sorted.toList();
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e.getCause(), 
e.getCause() instanceof ClassCastException);
+        }
+    }
+
+
+    @Test
+    public void testMax05() {
+        // The elements are Comparable but not with each other. This must be 
reported as an ELException rather than
+        // as the ClassCastException from compareTo()
+        ELProcessor processor = new ELProcessor();
+
+        try {
+            processor.getValue("[1,'a'].stream().max()", Object.class);
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e, hasCause(e, 
ClassCastException.class));
+        }
+    }
+
+
+    @Test
+    public void testMin05() {
+        ELProcessor processor = new ELProcessor();
+
+        try {
+            processor.getValue("['a',1].stream().min()", Object.class);
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e, hasCause(e, 
ClassCastException.class));
+        }
+    }
+
+
+    @Test
+    public void testSorted03() {
+        // Elements that are Comparable but not with each other
+        ELProcessor processor = new ELProcessor();
+
+        try {
+            processor.getValue("[1,'a'].stream().sorted().toList()", 
Object.class);
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e, hasCause(e, 
ClassCastException.class));
+        }
+    }
+
+
+    @Test
+    public void testSorted04() {
+        // Elements that are not Comparable at all
+        ELProcessor processor = new ELProcessor();
+        processor.defineBean("beans", beans);
+
+        try {
+            processor.getValue("beans.stream().sorted().toList()", 
Object.class);
+            Assert.fail("An ELException was expected");
+        } catch (ELException e) {
+            Assert.assertTrue("Unexpected cause: " + e, hasCause(e, 
ClassCastException.class));
+        }
+    }
+
+
     @Test
     public void testMaxLambda01() {
         ELProcessor processor = new ELProcessor();


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

Reply via email to