You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2024/08/26 08:42:49 UTC

(velocity-engine) branch VELOCITY-952 created (now a65430e6)

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

cbrisson pushed a change to branch VELOCITY-952
in repository https://gitbox.apache.org/repos/asf/velocity-engine.git


      at a65430e6 Introspection should use the top-most declaration of methods

This branch includes the following new commits:

     new a65430e6 Introspection should use the top-most declaration of methods

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(velocity-engine) 01/01: Introspection should use the top-most declaration of methods

Posted by cb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

cbrisson pushed a commit to branch VELOCITY-952
in repository https://gitbox.apache.org/repos/asf/velocity-engine.git

commit a65430e6f81144d2773764ed03548b452393dc43
Author: Claude Brisson <cl...@renegat.net>
AuthorDate: Mon Aug 26 10:42:33 2024 +0200

    Introspection should use the top-most declaration of methods
---
 .../velocity/util/introspection/ClassMap.java      |  1 +
 .../velocity/util/introspection/MethodMap.java     | 67 +++++++++++++++++++++-
 .../velocity/test/issues/Velocity952TestCase.java  | 58 +++++++++++++++++++
 3 files changed, 125 insertions(+), 1 deletion(-)

diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
index e5fe9d7f..58176801 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
@@ -184,6 +184,7 @@ public class ClassMap
                 int modifiers = method.getModifiers();
                 if (Modifier.isPublic(modifiers))
                 {
+                    method = MethodMap.getTopMostMethodDeclaration(method);
                     methodCache.put(method);
                 }
             }
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
index 1d0408bb..b2efed98 100644
--- a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
+++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
@@ -22,6 +22,7 @@ package org.apache.velocity.util.introspection;
 import org.apache.commons.lang3.reflect.TypeUtils;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -315,11 +316,75 @@ public class MethodMap
         switch (bestMatches.size())
         {
             case 0: return null;
-            case 1: return bestMatches.get(0).method;
+            case 1: return getTopMostMethodDeclaration(bestMatches.get(0).method);
             default: throw new AmbiguousException();
         }
     }
 
+    /**
+     * Once we identified a best match of a specific call, walk up the chain of inheritance to find the top-most
+     * declaration for this method. This is needed to avoid IllegalAccessException, when a public API method
+     * is implemented by a class which is not exported.
+     * @param method
+     * @return
+     */
+    public static Method getTopMostMethodDeclaration(Method method)
+    {
+        Class<?> clazz = method.getDeclaringClass();
+        String name = method.getName();
+        Class<?>[] arguments = method.getParameterTypes();
+
+        while (clazz != null)
+        {
+            Class<?> superClass = null;
+            Method superMethod = null;
+
+            // check the super class
+            superClass = clazz.getSuperclass();
+            if (superClass != null)
+            {
+                try
+                {
+                    superMethod = superClass.getDeclaredMethod(name, arguments);
+                    if ((superMethod.getModifiers() & Modifier.PUBLIC) == 0) {
+                        superMethod = null;
+                    }
+                }
+                catch (NoSuchMethodException nsme)
+                {
+                }
+            }
+
+            if (superMethod == null)
+            {
+                // check among the interfaces
+                Class<?>[] interfaces = clazz.getInterfaces();
+                for (Class<?>  intf : interfaces)
+                {
+                    try
+                    {
+                        superMethod = intf.getDeclaredMethod(name, arguments);
+                        if (superMethod != null)
+                        {
+                            superClass = intf;
+                            break;
+                        }
+                    }
+                    catch (NoSuchMethodException nsme)
+                    {
+                    }
+                }
+            }
+
+            if (superMethod != null)
+            {
+                method = superMethod;
+            }
+            clazz = superClass;
+        }
+        return method;
+    }
+
     /**
      *  Simple distinguishable exception, used when
      *  we run across ambiguous overloading.  Caught
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity952TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity952TestCase.java
new file mode 100755
index 00000000..55dda8ae
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity952TestCase.java
@@ -0,0 +1,58 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.util.introspection.ClassMap;
+import org.apache.velocity.util.introspection.MethodMap;
+
+import java.lang.reflect.Method;
+import java.util.TimeZone;
+
+/**
+ * This class tests the fix for VELOCITY-952.
+ */
+public class Velocity952TestCase extends BaseTestCase
+{
+    public Velocity952TestCase(String name)
+    {
+       super(name);
+    }
+
+    public void testMethodMap()
+    {
+        ClassMap classMap = new ClassMap(TimeZone.getDefault().getClass(), Velocity.getLog());
+        Method getOffset = classMap.findMethod("getOffset", new Object[] { 1L });
+        assertNotNull(getOffset);
+        assertEquals(TimeZone.class, getOffset.getDeclaringClass());
+    }
+
+    protected void setUpContext(VelocityContext context)
+    {
+        context.put("tz", TimeZone.getDefault());
+    }
+
+    public void testEnd2End()
+    {
+        assertEvalEquals("3600000", "$tz.getOffset(1)");
+    }
+}