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 24d8b7549a7c46bb3039920beb90a27c25274226 Author: opencode <[email protected]> AuthorDate: Sun Oct 18 16:29:15 2026 +0200 Coerce EL stream predicate results in the match operations The anyMatch, allMatch and noneMatch stream operations treated the result of each predicate evaluation as an already boxed boolean by casting it. A predicate returning null produced a raw NullPointerException when the loop next inspected the result, and a predicate returning any other non-boolean type, such as a number, produced a ClassCastException. The filter operation, which takes the same predicate type, instead coerced the result: null and the empty string meant false, a convertible string was honoured, and anything else was reported as an ELException. The Jakarta EL specification types the predicate as a function to boolean and defines a coercion for producing a boolean from an arbitrary value, which is what the filter operation applies. Coerce the predicate results in the three match operations with the same coercion used by filter, so the predicate-consuming operations agree on what a predicate result means and invalid results are reported as conversion errors rather than as unexpected runtime exceptions. The short-circuit behaviour of the three operations is unchanged. Adds tests for a null predicate result and for a non-boolean predicate result on the three operations. The null result tests report the raw NullPointerException against the previous implementation. --- java/org/apache/el/stream/Stream.java | 6 +-- .../apache/el/stream/TestCollectionOperations.java | 59 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/java/org/apache/el/stream/Stream.java b/java/org/apache/el/stream/Stream.java index 5e091824d8..a25a8cfdc4 100644 --- a/java/org/apache/el/stream/Stream.java +++ b/java/org/apache/el/stream/Stream.java @@ -514,7 +514,7 @@ public class Stream { Boolean match = Boolean.FALSE; while (!match.booleanValue() && iterator.hasNext()) { - match = (Boolean) le.invoke(iterator.next()); + match = ELSupport.coerceToBoolean(null, le.invoke(iterator.next()), true); } return new Optional(match); @@ -535,7 +535,7 @@ public class Stream { Boolean match = Boolean.TRUE; while (match.booleanValue() && iterator.hasNext()) { - match = (Boolean) le.invoke(iterator.next()); + match = ELSupport.coerceToBoolean(null, le.invoke(iterator.next()), true); } return new Optional(match); @@ -556,7 +556,7 @@ public class Stream { Boolean match = Boolean.FALSE; while (!match.booleanValue() && iterator.hasNext()) { - match = (Boolean) le.invoke(iterator.next()); + match = ELSupport.coerceToBoolean(null, le.invoke(iterator.next()), true); } return new Optional(Boolean.valueOf(!match.booleanValue())); diff --git a/test/org/apache/el/stream/TestCollectionOperations.java b/test/org/apache/el/stream/TestCollectionOperations.java index d204043fd6..1a37d1ab70 100644 --- a/test/org/apache/el/stream/TestCollectionOperations.java +++ b/test/org/apache/el/stream/TestCollectionOperations.java @@ -751,6 +751,65 @@ public class TestCollectionOperations { } + @Test(expected=ELException.class) + public void testAnyMatch04() { + // A predicate that returns a non-boolean must be coerced, producing an + // ELException, as for filter() + ELProcessor processor = new ELProcessor(); + + processor.getValue( + "[1,2,3].stream().anyMatch(x->x)", + Object.class); + } + + + @Test + public void testAnyMatch05() { + // A null predicate result is coerced to false, as for filter() + ELProcessor processor = new ELProcessor(); + + Optional result = (Optional) processor.getValue( + "[1,2,3].stream().anyMatch(x->null)", + Object.class); + + Assert.assertEquals(Boolean.FALSE, result.get()); + } + + + @Test(expected=ELException.class) + public void testAllMatch05() { + ELProcessor processor = new ELProcessor(); + + processor.getValue( + "[1,2,3].stream().allMatch(x->x)", + Object.class); + } + + + @Test + public void testAllMatch06() { + ELProcessor processor = new ELProcessor(); + + Optional result = (Optional) processor.getValue( + "[1,2,3].stream().allMatch(x->null)", + Object.class); + + Assert.assertEquals(Boolean.FALSE, result.get()); + } + + + @Test + public void testNoneMatch05() { + ELProcessor processor = new ELProcessor(); + + Optional result = (Optional) processor.getValue( + "[1,2,3].stream().noneMatch(x->null)", + Object.class); + + Assert.assertEquals(Boolean.TRUE, result.get()); + } + + @Test public void testFindFirst01() { ELProcessor processor = new ELProcessor(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
