You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/07/09 12:09:30 UTC

[tomcat] 02/02: Check requirements of functional interface rather than annotation

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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org