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 <[email protected]>
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)");
+    }
+}

Reply via email to