From 18e83a09b18fde4721af69123fd4c7799bcca00e Mon Sep 17 00:00:00 2001 From: Patrick Linskey Date: Thu, 31 Jan 2008 19:01:45 +0000 Subject: [PATCH] OPENJPA-251, OPENJPA-329. svn merge -c 616961 ../branches/1.0.x/ git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@617191 13f79535-47bb-0310-9956-ffa450edef68 --- .../enhance/TestGetDeclaredMethod.java | 93 +++++++++++++++++++ .../apache/openjpa/enhance/Reflection.java | 49 ++++++++-- .../openjpa/enhance/localizer.properties | 5 + 3 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 openjpa-kernel-5/src/test/java/org/apache/openjpa/enhance/TestGetDeclaredMethod.java diff --git a/openjpa-kernel-5/src/test/java/org/apache/openjpa/enhance/TestGetDeclaredMethod.java b/openjpa-kernel-5/src/test/java/org/apache/openjpa/enhance/TestGetDeclaredMethod.java new file mode 100644 index 000000000..1254cd79c --- /dev/null +++ b/openjpa-kernel-5/src/test/java/org/apache/openjpa/enhance/TestGetDeclaredMethod.java @@ -0,0 +1,93 @@ +/* + * 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 org.apache.openjpa.enhance; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.ArrayList; + +import junit.framework.TestCase; + +/** + * Tests that {@link Reflection#getDeclaredMethod(Class, String, Class)} + * returns the most-derived class's method when called from a type hierarchy. + * See OPENJPA-251. + */ +public class TestGetDeclaredMethod extends TestCase { + + public void testGetDeclaredMethod() { + Method meth = + Reflection.getDeclaredMethod(Impl.class, "getObject", null); + assertEquals(Impl.class, meth.getDeclaringClass()); + assertEquals(String.class, meth.getReturnType()); + } + + public void testMostDerived() throws NoSuchMethodException { + Method impl = Impl.class.getDeclaredMethod("getObject", null); + Method iface = Iface.class.getDeclaredMethod("getObject", null); + Method other = Other.class.getDeclaredMethod("getObject", null); + assertEquals(Impl.class, Reflection.mostDerived(impl, iface) + .getDeclaringClass()); + assertEquals(Impl.class, Reflection.mostDerived(iface, impl) + .getDeclaringClass()); + try { + Reflection.mostDerived(iface, other); + fail("'iface' and 'other' are not from related types"); + } catch (IllegalArgumentException e) { + // expected + } + } + + public void testGenerics() throws NoSuchMethodException { + List meths = new ArrayList(); + for (Method meth : GenericsImpl.class.getDeclaredMethods()) { + if ("getObject".equals(meth.getName())) + meths.add(meth); + } + assertEquals(2, meths.size()); + assertEquals(String.class, Reflection.mostDerived(meths.get(0), + meths.get(1)).getReturnType()); + } + + interface Iface { + Object getObject(); + } + + static class Impl implements Iface { + public String getObject() { + return "string"; + } + } + + static class Other { + public String getObject() { + return "other"; + } + } + + interface GenericsIface { + public T getObject(); + } + + static class GenericsImpl implements GenericsIface { + public String getObject() { + return null; + } + } +} diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java index 7393bd1ce..bd8996873 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java @@ -112,27 +112,62 @@ public class Reflection { * Invokes cls.getDeclaredMethods(), and returns the method * that matches the name and param arguments. * Avoids the exception thrown by Class.getDeclaredMethod() - * for performance reasons. param may be null. + * for performance reasons. param may be null. Additionally, + * if there are multiple methods with different return types, this will + * return the method defined in the least-derived class. * * @since 0.9.8 */ - private static Method getDeclaredMethod(Class cls, String name, + static Method getDeclaredMethod(Class cls, String name, Class param) { Method[] methods = (Method[]) AccessController.doPrivileged( J2DoPrivHelper.getDeclaredMethodsAction(cls)); + Method candidate = null; for (int i = 0 ; i < methods.length; i++) { if (name.equals(methods[i].getName())) { Class[] methodParams = methods[i].getParameterTypes(); if (param == null && methodParams.length == 0) - return methods[i]; - if (param != null && methodParams.length == 1 + candidate = mostDerived(methods[i], candidate); + else if (param != null && methodParams.length == 1 && param.equals(methodParams[0])) - return methods[i]; + candidate = mostDerived(methods[i], candidate); } } - return null; + return candidate; } - + + static Method mostDerived(Method meth1, Method meth2) { + if (meth1 == null) + return meth2; + if (meth2 == null) + return meth1; + + Class cls2 = meth2.getDeclaringClass(); + Class cls1 = meth1.getDeclaringClass(); + + if (cls1.equals(cls2)) { + Class ret1 = meth1.getReturnType(); + Class ret2 = meth2.getReturnType(); + if (ret1.isAssignableFrom(ret2)) + return meth2; + else if (ret2.isAssignableFrom(ret1)) + return meth1; + else + throw new IllegalArgumentException( + _loc.get("most-derived-unrelated-same-type", meth1, meth2) + .getMessage()); + } else { + if (cls1.isAssignableFrom(cls2)) + return meth2; + else if (cls2.isAssignableFrom(cls1)) + return meth1; + else + throw new IllegalArgumentException( + _loc.get("most-derived-unrelated", meth1, meth2) + .getMessage()); + } + } + /** * Return the field with the given name, optionally throwing an exception * if none. diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties index 2200b052d..3ad9b279b 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/enhance/localizer.properties @@ -199,3 +199,8 @@ unspecified-unenhanced-types: The following classes were not specified in your \ configuration: {1}. One or more of the classes that were registered with \ the system had references to these classes. Registered types: {0} enhance-error: An error occurred while enhancing {0}. Exception message: {1} +most-derived-unrelated: Methods "{0}" and "{1}" are defined in types that do \ + not have an interface or superclass inheritance relationship. +most-derived-unrelated-same-type: Methods "{0}" and "{1}" are defined in the same \ + type, but the method return types do not have an interface or superclass \ + inheritance relationship.