This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 0827d1ce4200ad030a9c3496349b240fefeb53a7 Author: Mark Thomas <ma...@apache.org> AuthorDate: Fri Jul 9 13:07:14 2021 +0100 Check requirements of functional interface rather than annotation The annotation is only a recommendation so check to see if type meets the requirements of a functional interface rather than relying on the annotation. --- java/org/apache/el/lang/ELSupport.java | 57 ++++++++++++++++++- test/org/apache/el/lang/TestELSupport.java | 88 ++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/java/org/apache/el/lang/ELSupport.java b/java/org/apache/el/lang/ELSupport.java index 3f8fa78..bc1b67a 100644 --- a/java/org/apache/el/lang/ELSupport.java +++ b/java/org/apache/el/lang/ELSupport.java @@ -592,7 +592,7 @@ public class ELSupport { return result; } - if (obj instanceof LambdaExpression && type.getAnnotation(FunctionalInterface.class) != null) { + if (obj instanceof LambdaExpression && isFunctionalInterface(type)) { T result = coerceToFunctionalInterface(ctx, (LambdaExpression) obj, type); return result; } @@ -687,6 +687,61 @@ public class ELSupport { } + static boolean isFunctionalInterface(Class<?> type) { + + if (!type.isInterface()) { + return false; + } + + boolean foundAbstractMethod = false; + Method[] methods = type.getMethods(); + for (Method method : methods) { + if (Modifier.isAbstract(method.getModifiers())) { + // Abstract methods that override one of the public methods + // of Object don't count + if (overridesObjectMethod(method)) { + continue; + } + if (foundAbstractMethod) { + // Found more than one + return false; + } else { + foundAbstractMethod = true; + } + } + } + return foundAbstractMethod; + } + + + private static boolean overridesObjectMethod(Method method) { + // There are three methods that can be overridden + if ("equals".equals(method.getName())) { + if (method.getReturnType().equals(boolean.class)) { + if (method.getParameterCount() == 1) { + if (method.getParameterTypes()[0].equals(Object.class)) { + return true; + } + } + } + } else if ("hashCode".equals(method.getName())) { + if (method.getReturnType().equals(int.class)) { + if (method.getParameterCount() == 0) { + return true; + } + } + } else if ("toString".equals(method.getName())) { + if (method.getReturnType().equals(String.class)) { + if (method.getParameterCount() == 0) { + return true; + } + } + } + + return false; + } + + private ELSupport() { // Uility class - hide default constructor; } diff --git a/test/org/apache/el/lang/TestELSupport.java b/test/org/apache/el/lang/TestELSupport.java index 2cf1217..76d174f 100644 --- a/test/org/apache/el/lang/TestELSupport.java +++ b/test/org/apache/el/lang/TestELSupport.java @@ -19,6 +19,7 @@ package org.apache.el.lang; import java.beans.PropertyEditorManager; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Map; import java.util.function.BiPredicate; import java.util.function.Predicate; @@ -372,4 +373,91 @@ public class TestELSupport { return "BLOCK"; } } + + + @Test + public void testIsFunctionalInterface01() { + Assert.assertTrue(ELSupport.isFunctionalInterface(Predicate.class)); + } + + + @Test + public void testIsFunctionalInterface02() { + // Interface but more than one abstract method + Assert.assertFalse(ELSupport.isFunctionalInterface(Map.class)); + } + + + @Test + public void testIsFunctionalInterface03() { + // Not an interface + Assert.assertFalse(ELSupport.isFunctionalInterface(String.class)); + } + + + @Test + public void testIsFunctionalInterface04() { + // Extends a functional interface with no changes + Assert.assertTrue(ELSupport.isFunctionalInterface(FunctionalA.class)); + } + + + @Test + public void testIsFunctionalInterface05() { + // Extends a functional interface with additional abstract method + Assert.assertFalse(ELSupport.isFunctionalInterface(FunctionalB.class)); + } + + + @Test + public void testIsFunctionalInterface06() { + // Extends a functional interface with additional default method + Assert.assertTrue(ELSupport.isFunctionalInterface(FunctionalC.class)); + } + + + @Test + public void testIsFunctionalInterface07() { + // Extends a functional interface and overrides method in Object + Assert.assertTrue(ELSupport.isFunctionalInterface(FunctionalD.class)); + } + + + @Test + public void testIsFunctionalInterface08() { + // Extends a functional interface adds a method that looks like a + // method from Object + Assert.assertFalse(ELSupport.isFunctionalInterface(FunctionalE.class)); + } + + + private static interface FunctionalA<T> extends Predicate<T> { + } + + + private static interface FunctionalB<T> extends Predicate<T> { + public void extra(); + } + + + private static interface FunctionalC<T> extends Predicate<T> { + @SuppressWarnings("unused") + public default void extra() { + } + } + + + private static interface FunctionalD<T> extends Predicate<T> { + @Override + public String toString(); + @Override + public int hashCode(); + @Override + public boolean equals(Object o); + } + + + private static interface FunctionalE<T> extends Predicate<T> { + public boolean equals(String s); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org