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


The following commit(s) were added to refs/heads/main by this push:
     new fda4faa  Add MethodReference support
fda4faa is described below

commit fda4faaaa16233c2d32961f4765b2dd9eb1f4a4c
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Sep 8 16:50:41 2021 +0100

    Add MethodReference support
---
 java/jakarta/el/MethodExpression.java              | 27 +++++++
 java/jakarta/el/MethodReference.java               | 85 ++++++++++++++++++++++
 java/org/apache/el/MethodExpressionImpl.java       | 11 +++
 java/org/apache/el/MethodExpressionLiteral.java    | 27 ++++++-
 java/org/apache/el/parser/AstIdentifier.java       |  6 ++
 java/org/apache/el/parser/AstValue.java            | 20 +++++
 java/org/apache/el/parser/Node.java                |  6 ++
 java/org/apache/el/parser/SimpleNode.java          |  5 ++
 java/org/apache/jasper/el/JspMethodExpression.java | 26 +++++++
 test/jakarta/el/TestMethodReference.java           | 68 +++++++++++++++++
 test/jakarta/el/TesterBean.java                    |  7 ++
 webapps/docs/changelog.xml                         |  5 ++
 12 files changed, 291 insertions(+), 2 deletions(-)

diff --git a/java/jakarta/el/MethodExpression.java 
b/java/jakarta/el/MethodExpression.java
index d0ba8b4..2635960 100644
--- a/java/jakarta/el/MethodExpression.java
+++ b/java/jakarta/el/MethodExpression.java
@@ -64,4 +64,31 @@ public abstract class MethodExpression extends Expression {
         // Expected to be over-ridden by implementation
         return false;
     }
+
+    /**
+     * Obtain the {@link MethodReference} for the method to which this method
+     * expression resolves.
+     *
+     * @param context The EL context for this evaluation
+     *
+     * @return This default implementation always returns <code>null</code>
+     *
+     * @throws NullPointerException
+     *              If the supplied context is <code>null</code>
+     * @throws PropertyNotFoundException
+     *              If a property/variable resolution failed because no match
+     *              was found or a match was found but was not readable
+     * @throws MethodNotFoundException
+     *              If no matching method can be found
+     * @throws ELException
+     *              Wraps any exception throw whilst resolving the property
+     *
+     * @since EL 5.0
+     */
+    public MethodReference getMethodReference(ELContext context) {
+        // Expected to be over-ridden by implementation
+        context.notifyBeforeEvaluation(getExpressionString());
+        context.notifyAfterEvaluation(getExpressionString());
+        return null;
+    }
 }
diff --git a/java/jakarta/el/MethodReference.java 
b/java/jakarta/el/MethodReference.java
new file mode 100644
index 0000000..b7855c6
--- /dev/null
+++ b/java/jakarta/el/MethodReference.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package jakarta.el;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Provides information about the method to which a method expression resolves.
+ */
+public class MethodReference {
+
+    private final Object base;
+    private final MethodInfo methodInfo;
+    private final Annotation[] annotations;
+    private final Object[] evaluatedParameters;
+
+
+    public MethodReference(Object base, MethodInfo methodInfo, Annotation[] 
annotations, Object[] evaluatedParameters) {
+        this.base = base;
+        this.methodInfo = methodInfo;
+        this.annotations = annotations;
+        this.evaluatedParameters = evaluatedParameters;
+    }
+
+
+    /**
+     * Obtain the base object on which the method will be invoked.
+     *
+     * @return The base object on which the method will be invoked or
+     *         {@code null} for literal method expressions.
+     */
+    public Object getBase() {
+        return base;
+    }
+
+
+    /**
+     * Obtain the {@link MethodInfo} for the {@link MethodExpression} for which
+     * this {@link MethodReference} has been generated.
+     *
+     * @return The {@link MethodInfo} for the {@link MethodExpression} for 
which
+     *         this {@link MethodReference} has been generated.
+     */
+    public MethodInfo getMethodInfo() {
+        return this.methodInfo;
+    }
+
+
+    /**
+     * Obtain the annotations on the method to which the associated expression
+     * resolves.
+     *
+     * @return The annotations on the method to which the associated expression
+     *         resolves. If the are no annotations, then an empty array is
+     *         returned.
+     */
+    public Annotation[] getAnnotations() {
+        return annotations;
+    }
+
+
+    /**
+     * Obtain the evaluated parameter values that will be passed to the method
+     * to which the associated expression resolves.
+     *
+     *  @return The evaluated parameters.
+     */
+    public Object[] getEvaluatedParameters() {
+        return evaluatedParameters;
+    }
+}
diff --git a/java/org/apache/el/MethodExpressionImpl.java 
b/java/org/apache/el/MethodExpressionImpl.java
index 2acc78e..539bdca 100644
--- a/java/org/apache/el/MethodExpressionImpl.java
+++ b/java/org/apache/el/MethodExpressionImpl.java
@@ -27,6 +27,7 @@ import jakarta.el.FunctionMapper;
 import jakarta.el.MethodExpression;
 import jakarta.el.MethodInfo;
 import jakarta.el.MethodNotFoundException;
+import jakarta.el.MethodReference;
 import jakarta.el.PropertyNotFoundException;
 import jakarta.el.VariableMapper;
 
@@ -315,4 +316,14 @@ public final class MethodExpressionImpl extends 
MethodExpression implements
     public boolean isParametersProvided() {
         return this.getNode().isParametersProvided();
     }
+
+
+    @Override
+    public MethodReference getMethodReference(ELContext context) {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, 
this.varMapper);
+        ctx.notifyBeforeEvaluation(getExpressionString());
+        MethodReference methodReference = 
this.getNode().getMethodReference(ctx);
+        ctx.notifyAfterEvaluation(getExpressionString());
+        return methodReference;
+    }
 }
diff --git a/java/org/apache/el/MethodExpressionLiteral.java 
b/java/org/apache/el/MethodExpressionLiteral.java
index f71d38f..9fd0046 100644
--- a/java/org/apache/el/MethodExpressionLiteral.java
+++ b/java/org/apache/el/MethodExpressionLiteral.java
@@ -20,17 +20,23 @@ import java.io.Externalizable;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.lang.annotation.Annotation;
 
 import jakarta.el.ELContext;
 import jakarta.el.ELException;
 import jakarta.el.MethodExpression;
 import jakarta.el.MethodInfo;
+import jakarta.el.MethodReference;
 
+import org.apache.el.util.MessageFactory;
 import org.apache.el.util.ReflectionUtil;
 
 
 public class MethodExpressionLiteral extends MethodExpression implements 
Externalizable {
 
+    private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new 
Annotation[0];
+    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
     private Class<?> expectedType;
 
     private String expr;
@@ -51,12 +57,16 @@ public class MethodExpressionLiteral extends 
MethodExpression implements Externa
     @Override
     public MethodInfo getMethodInfo(ELContext context) throws ELException {
         context.notifyBeforeEvaluation(getExpressionString());
-        MethodInfo result =
-                new MethodInfo(this.expr, this.expectedType, this.paramTypes);
+        MethodInfo result = getMethodInfoInternal();
         context.notifyAfterEvaluation(getExpressionString());
         return result;
     }
 
+
+    private MethodInfo getMethodInfoInternal() throws ELException {
+        return new MethodInfo(this.expr, this.expectedType, this.paramTypes);
+    }
+
     @Override
     public Object invoke(ELContext context, Object[] params) throws 
ELException {
         context.notifyBeforeEvaluation(getExpressionString());
@@ -70,6 +80,19 @@ public class MethodExpressionLiteral extends 
MethodExpression implements Externa
         return result;
     }
 
+
+    @Override
+    public MethodReference getMethodReference(ELContext context) {
+        if (context == null) {
+            throw new 
NullPointerException(MessageFactory.get("error.context.null"));
+        }
+        context.notifyBeforeEvaluation(getExpressionString());
+        MethodReference result =
+                new MethodReference(null, getMethodInfoInternal(), 
EMPTY_ANNOTATION_ARRAY, EMPTY_OBJECT_ARRAY);
+        context.notifyAfterEvaluation(getExpressionString());
+        return result;
+    }
+
     @Override
     public String getExpressionString() {
         return this.expr;
diff --git a/java/org/apache/el/parser/AstIdentifier.java 
b/java/org/apache/el/parser/AstIdentifier.java
index 671b711..237604c 100644
--- a/java/org/apache/el/parser/AstIdentifier.java
+++ b/java/org/apache/el/parser/AstIdentifier.java
@@ -22,6 +22,7 @@ import jakarta.el.ELException;
 import jakarta.el.MethodExpression;
 import jakarta.el.MethodInfo;
 import jakarta.el.MethodNotFoundException;
+import jakarta.el.MethodReference;
 import jakarta.el.PropertyNotFoundException;
 import jakarta.el.ValueExpression;
 import jakarta.el.ValueReference;
@@ -171,6 +172,11 @@ public final class AstIdentifier extends SimpleNode {
     }
 
     @Override
+    public MethodReference getMethodReference(EvaluationContext ctx) {
+        return 
this.getMethodExpression(ctx).getMethodReference(ctx.getELContext());
+    }
+
+    @Override
     public void setImage(String image) {
         if (!Validation.isIdentifier(image)) {
             throw new 
ELException(MessageFactory.get("error.identifier.notjava",
diff --git a/java/org/apache/el/parser/AstValue.java 
b/java/org/apache/el/parser/AstValue.java
index 7797e81..d926867 100644
--- a/java/org/apache/el/parser/AstValue.java
+++ b/java/org/apache/el/parser/AstValue.java
@@ -25,6 +25,7 @@ import jakarta.el.ELException;
 import jakarta.el.ELResolver;
 import jakarta.el.LambdaExpression;
 import jakarta.el.MethodInfo;
+import jakarta.el.MethodReference;
 import jakarta.el.PropertyNotFoundException;
 import jakarta.el.ValueReference;
 
@@ -259,6 +260,25 @@ public final class AstValue extends SimpleNode {
         return result;
     }
 
+    @Override
+    public MethodReference getMethodReference(EvaluationContext ctx) {
+        Target t = getTarget(ctx);
+        Method m = null;
+        Object[] values = null;
+        Class<?>[] types = null;
+        if (isParametersProvided()) {
+            values = ((AstMethodParameters) this.jjtGetChild(
+                    this.jjtGetNumChildren() - 1)).getParameters(ctx);
+            types = getTypesFromValues(values);
+        }
+        m = ReflectionUtil.getMethod(ctx, t.base, t.property, types, values);
+
+        // Handle varArgs and any coercion required
+        values = convertArgs(ctx, values, m);
+
+        return new MethodReference(t.base, getMethodInfo(ctx, types), 
m.getAnnotations(), values);
+    }
+
     private Object[] convertArgs(EvaluationContext ctx, Object[] src, Method 
m) {
         Class<?>[] types = m.getParameterTypes();
         if (types.length == 0) {
diff --git a/java/org/apache/el/parser/Node.java 
b/java/org/apache/el/parser/Node.java
index 0cf4a18..ec7e86a 100644
--- a/java/org/apache/el/parser/Node.java
+++ b/java/org/apache/el/parser/Node.java
@@ -20,6 +20,7 @@ package org.apache.el.parser;
 
 import jakarta.el.ELException;
 import jakarta.el.MethodInfo;
+import jakarta.el.MethodReference;
 import jakarta.el.ValueReference;
 
 import org.apache.el.lang.EvaluationContext;
@@ -80,4 +81,9 @@ public interface Node {
    * @since EL 2.2
    */
   public boolean isParametersProvided();
+
+  /**
+   * @since EL 5.0
+   */
+  public MethodReference getMethodReference(EvaluationContext ctx);
 }
diff --git a/java/org/apache/el/parser/SimpleNode.java 
b/java/org/apache/el/parser/SimpleNode.java
index b50645a..a698515 100644
--- a/java/org/apache/el/parser/SimpleNode.java
+++ b/java/org/apache/el/parser/SimpleNode.java
@@ -21,6 +21,7 @@ import java.util.Arrays;
 
 import jakarta.el.ELException;
 import jakarta.el.MethodInfo;
+import jakarta.el.MethodReference;
 import jakarta.el.PropertyNotWritableException;
 import jakarta.el.ValueReference;
 
@@ -157,6 +158,10 @@ public abstract class SimpleNode implements Node {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public MethodReference getMethodReference(EvaluationContext ctx) {
+        throw new UnsupportedOperationException();
+    }
 
     @Override
     public int hashCode() {
diff --git a/java/org/apache/jasper/el/JspMethodExpression.java 
b/java/org/apache/jasper/el/JspMethodExpression.java
index c59b6f0..6e336f6 100644
--- a/java/org/apache/jasper/el/JspMethodExpression.java
+++ b/java/org/apache/jasper/el/JspMethodExpression.java
@@ -26,6 +26,7 @@ import jakarta.el.ELException;
 import jakarta.el.MethodExpression;
 import jakarta.el.MethodInfo;
 import jakarta.el.MethodNotFoundException;
+import jakarta.el.MethodReference;
 import jakarta.el.PropertyNotFoundException;
 
 public final class JspMethodExpression extends MethodExpression implements
@@ -99,6 +100,31 @@ public final class JspMethodExpression extends 
MethodExpression implements
     }
 
     @Override
+    public MethodReference getMethodReference(ELContext context) {
+        context.notifyBeforeEvaluation(getExpressionString());
+        try {
+            MethodReference result = this.target.getMethodReference(context);
+            context.notifyAfterEvaluation(getExpressionString());
+            return result;
+        } catch (MethodNotFoundException e) {
+            if (e instanceof JspMethodNotFoundException) {
+                throw e;
+            }
+            throw new JspMethodNotFoundException(this.mark, e);
+        } catch (PropertyNotFoundException e) {
+            if (e instanceof JspPropertyNotFoundException) {
+                throw e;
+            }
+            throw new JspPropertyNotFoundException(this.mark, e);
+        } catch (ELException e) {
+            if (e instanceof JspELException) {
+                throw e;
+            }
+            throw new JspELException(this.mark, e);
+        }
+    }
+
+    @Override
     public boolean equals(Object obj) {
         return this.target.equals(obj);
     }
diff --git a/test/jakarta/el/TestMethodReference.java 
b/test/jakarta/el/TestMethodReference.java
new file mode 100644
index 0000000..c9b524f
--- /dev/null
+++ b/test/jakarta/el/TestMethodReference.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package jakarta.el;
+
+import java.beans.BeanProperty;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.jasper.el.ELContextImpl;
+
+public class TestMethodReference {
+
+    @Test
+    public void testGetAnnotationInfo01() {
+        // None
+        ExpressionFactory factory = ExpressionFactory.newInstance();
+        ELContext context = new ELContextImpl(factory);
+
+        TesterBean bean = new TesterBean("myBean");
+
+        ValueExpression var = factory.createValueExpression(bean, 
TesterBean.class);
+        context.getVariableMapper().setVariable("bean", var);
+
+        MethodExpression me = factory.createMethodExpression(context, 
"${bean.getName()}", String.class, null);
+
+        MethodReference result = me.getMethodReference(context);
+
+        Assert.assertNotNull(result);
+        Assert.assertNotNull(result.getAnnotations());
+        Assert.assertEquals(0, result.getAnnotations().length);
+    }
+
+    @Test
+    public void testGetAnnotationInfo02() {
+        // @BeanProperty with defaults
+        ExpressionFactory factory = ExpressionFactory.newInstance();
+        ELContext context = new ELContextImpl(factory);
+
+        TesterBean bean = new TesterBean("myBean");
+
+        ValueExpression var = factory.createValueExpression(bean, 
TesterBean.class);
+        context.getVariableMapper().setVariable("bean", var);
+
+        MethodExpression me = factory.createMethodExpression(context, 
"${bean.getValueD()}", String.class, null);
+
+        MethodReference result = me.getMethodReference(context);
+
+        Assert.assertNotNull(result);
+        Assert.assertNotNull(result.getAnnotations());
+        Assert.assertEquals(1, result.getAnnotations().length);
+        Assert.assertEquals(BeanProperty.class, 
result.getAnnotations()[0].annotationType());
+    }
+}
diff --git a/test/jakarta/el/TesterBean.java b/test/jakarta/el/TesterBean.java
index e781476..8f0d1e4 100644
--- a/test/jakarta/el/TesterBean.java
+++ b/test/jakarta/el/TesterBean.java
@@ -16,6 +16,8 @@
  */
 package jakarta.el;
 
+import java.beans.BeanProperty;
+
 public class TesterBean {
 
     private String name;
@@ -62,4 +64,9 @@ public class TesterBean {
     public Integer[] getValueC() {
         return valueC;
     }
+
+    @BeanProperty
+    public String getValueD() {
+        return "";
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index be86080..86bbbc6 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -116,6 +116,11 @@
         Tomcat with recent updates in the Jakarta EL specification project.
         (markt)
       </add>
+      <add>
+        Add support for <code>MethodReference</code> and the associated getter
+        on <code>MehtodExpression</code> to align Tomcat with recent updates in
+        the Jakarta EL specification project. (markt)
+      </add>
     </changelog>
   </subsection>
 </section>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to