Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian de Alwis2016-03-18 02:52:58 +0000
committerVikas Chandra2016-04-03 05:01:16 +0000
commit24f00d7ad2beed8ce21d56a9c7e251e2dea399c6 (patch)
treebede8cd830e4baea3d7243e4b4ac99defc65a11d
parent8614d2d261367ebde0523ba822664aa7817b00d8 (diff)
downloadeclipse.pde.ui-24f00d7ad2beed8ce21d56a9c7e251e2dea399c6.tar.gz
eclipse.pde.ui-24f00d7ad2beed8ce21d56a9c7e251e2dea399c6.tar.xz
eclipse.pde.ui-24f00d7ad2beed8ce21d56a9c7e251e2dea399c6.zip
Bug 334281 - Could not locate method ... error logged
Improve handling of Util and Signatures helper classes for handling generics and parameterized types. Signed-Off-By: Brian de Alwis <bsd@mt.ca> Change-Id: Ie0dc0d3e3a96d0ef1df36f6afaaec09882b68bb6
-rw-r--r--apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/util/tests/SignaturesTests.java64
-rw-r--r--apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Signatures.java103
-rw-r--r--apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java169
3 files changed, 258 insertions, 78 deletions
diff --git a/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/util/tests/SignaturesTests.java b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/util/tests/SignaturesTests.java
index a6d516c73f..805a95c495 100644
--- a/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/util/tests/SignaturesTests.java
+++ b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/util/tests/SignaturesTests.java
@@ -10,8 +10,6 @@
*******************************************************************************/
package org.eclipse.pde.api.tools.util.tests;
-import junit.framework.TestCase;
-
import org.eclipse.jdt.core.Flags;
import org.eclipse.pde.api.tools.internal.model.ApiField;
import org.eclipse.pde.api.tools.internal.model.ApiMethod;
@@ -19,11 +17,14 @@ import org.eclipse.pde.api.tools.internal.model.ApiType;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
import org.eclipse.pde.api.tools.internal.util.Signatures;
+import junit.framework.TestCase;
+
/**
* Test class for the {@link Signatures} utility class
*
* @since 1.0.0
*/
+@SuppressWarnings("nls")
public class SignaturesTests extends TestCase {
/**
@@ -52,6 +53,7 @@ public class SignaturesTests extends TestCase {
assertEquals("wrong converstion", "(QJokes;)V;", Signatures.dequalifySignature("(Lfoo.test.Jokes;)V;")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
assertEquals("wrong conversion", "(QDiff;)Z", Signatures.dequalifySignature("(LDiff;)Z")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
assertEquals("Wrong conversion", "(QList<QString;>;)QList;", Signatures.dequalifySignature("(Ljava.util.List<Ljava.lang.String;>;)Ljava.util.List;")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertEquals("Wrong conversion", "(QList<+QCharSequence;>;)QList;", Signatures.dequalifySignature("(Ljava.util.List<+Ljava.lang.CharSequence;>;)Ljava.util.List;")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
@@ -77,6 +79,8 @@ public class SignaturesTests extends TestCase {
assertEquals("Signature processed incorrectly", "(I[QString;J)[QInteger;", Signatures.processMethodSignature(method)); //$NON-NLS-1$ //$NON-NLS-2$
method = type.addMethod("m4", "(ILjava.util.List;J)[Ljava.lang.Integer;", "(ILjava.util.List<Ljava.lang.String;>;J)[Ljava.lang.Integer;", Flags.AccPublic, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
assertEquals("Signature procesed incorrectly", "(IQList<QString;>;J)[QInteger;", Signatures.processMethodSignature(method)); //$NON-NLS-1$ //$NON-NLS-2$
+ method = type.addMethod("m5", "(ILjava.util.List;J)[Ljava.lang.Integer;", "(ILjava.util.List<+Ljava.lang.CharSequence;>;J)[Ljava.lang.Integer;", Flags.AccPublic, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertEquals("Signature procesed incorrectly", "(IQList<+QCharSequence;>;J)[QInteger;", Signatures.processMethodSignature(method)); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
@@ -105,6 +109,8 @@ public class SignaturesTests extends TestCase {
assertEquals("Wrong method signature returned", "m3(int, String[], long)", Signatures.getMethodSignature(method)); //$NON-NLS-1$ //$NON-NLS-2$
method = type.addMethod("m4", "(ILjava.util.List;J)[Ljava.lang.Integer;", "(ILjava.util.List<Ljava.lang.String;>;J)[Ljava.lang.Integer;", Flags.AccPublic, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
assertEquals("Wrong method signature returned", "m4(int, List<String>, long)", Signatures.getMethodSignature(method)); //$NON-NLS-1$ //$NON-NLS-2$
+ method = type.addMethod("m5", "(ILjava.util.List;J)[Ljava.lang.Integer;", "(ILjava.util.List<+Ljava.lang.CharSequence;>;J)[Ljava.lang.Integer;", Flags.AccPublic, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ assertEquals("Wrong method signature returned", "m5(int, List<? extends CharSequence>, long)", Signatures.getMethodSignature(method)); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
@@ -137,7 +143,7 @@ public class SignaturesTests extends TestCase {
* Tests the {@link Signatures#getQualifiedMethodSignature(org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor, boolean)} method
* @throws Exception
*/
- public void testGetQialifiedMethodSignature2() throws Exception {
+ public void testGetQualifiedMethodSignature2() throws Exception {
ApiType type = new ApiType(null, "x.y.z.Parent", "Lx.y.z.Parent;", null, Flags.AccPublic, null, null); //$NON-NLS-1$ //$NON-NLS-2$
ApiMethod method = type.addMethod("m1", "()V;", null, Flags.AccPublic, null); //$NON-NLS-1$ //$NON-NLS-2$
assertEquals("Wrong qualified method signature returned", "x.y.z.Parent.m1() : void", Signatures.getQualifiedMethodSignature((IMethodDescriptor) method.getHandle(), false, true)); //$NON-NLS-1$ //$NON-NLS-2$
@@ -264,6 +270,58 @@ public class SignaturesTests extends TestCase {
assertTrue("Signatures should not match", !Signatures.matchesSignatures("(ILjava.util.List<Ljava.lang.String;>;)V;", "(ILjava.util.List;)V;")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
+ public void testMatchesInnerClasses() {
+ assertTrue("Signatures should match", Signatures.matchesSignatures("(QInnerMost;)V", "(Lmy.package.Class$InnerMost;)V"));
+ assertTrue("Signatures should match", Signatures.matchesSignatures("(QInnerMost;)V", "(Lmy.package.Class$Inner$InnerMost;)V"));
+ }
+
+ public void testMatchesArgumentPackages() {
+ assertTrue("Same packages should match", Signatures.matchesSignatures("(Lmy.package.Class;)V", "(Lmy.package.Class;)V"));
+ assertFalse("Differing packages should not match", Signatures.matchesSignatures("(Lmy.package.Class;)V", "(Lother.package.Class;)V"));
+ assertFalse("Differing packages should not match", Signatures.matchesSignatures("(Lmy.package.Class$InnerMost;)V", "(Lother.package.Class$InnerMost;)V"));
+ }
+
+ public void testMatchesGenericsBug456945_ex1() {
+ // This is the example from bug 456945
+ assertTrue("Signatures should match", Signatures.matchesSignatures("(QIterable<+QCharSequence;>;QIAcceptor<QResult;>;)V", "(Ljava.lang.Iterable<+Ljava.lang.CharSequence;>;Lorg.eclipse.xtext.util.IAcceptor<Lorg.eclipse.xtext.xbase.compiler.CompilationTestHelper$Result;>;)V"));
+ }
+
+ public void testMatchesGenericsBug456945_ex2() {
+ // Modified example that included parameterized return type
+ assertTrue("Wildcard signatures should match", Signatures.matchesSignatures("(QIterable<+QCharSequence;>;QIAcceptor<QResult;>;)QT;", "(Ljava.lang.Iterable<+Ljava.lang.CharSequence;>;Lorg.eclipse.xtext.util.IAcceptor<Lorg.eclipse.xtext.xbase.compiler.CompilationTestHelper$Result;>;)TT;"));
+ }
+
+ public void testMatchesGenericsBug334281_c1() {
+ // Example from bug 334281 comment 1
+ assertTrue("Wildcard signatures should match", Signatures.matchesSignatures("(QClass<+QITmfEvent;>;QTmfTimeRange;JII)V", "(Ljava.lang.Class<+Lorg.eclipse.linuxtools.tmf.core.event.ITmfEvent;>;Lorg.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;JII)V"));
+ // and heck, let's ensure arrays work too
+ assertTrue("Array wildcard signatures should match", Signatures.matchesSignatures("(QClass<[+QITmfEvent;>;QTmfTimeRange;JII)V", "(Ljava.lang.Class<[+Lorg.eclipse.linuxtools.tmf.core.event.ITmfEvent;>;Lorg.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;JII)V"));
+ }
+
+ public void testMatchesMismatchedGenericTypes() {
+ assertFalse("Different generic type names should not match", Signatures.matchesSignatures("(QIterable<QCharSequence;>;)V", "(QList<QCharSequence;>;)V"));
+ }
+
+ /** Tests the {@link Signatures#matches(String, String)} method */
+ public void testTypeMatches() {
+ assertTrue("Signatures should match", Signatures.matches("I", "I"));
+ assertTrue("Signatures should match", Signatures.matches("QClass;", "Ljava.lang.Class;"));
+ assertTrue("Signatures should match", Signatures.matches("Ljava.lang.Class;", "Ljava.lang.Class;"));
+ assertTrue("Signatures should match", Signatures.matches("QClass;", "QClass;"));
+ assertTrue("Signatures should match", Signatures.matches("QClass<QITmfEvent;>;", "Ljava.lang.Class<Lorg.eclipse.linuxtools.tmf.core.event.ITmfEvent;>;"));
+ assertFalse("Signatures should not match", Signatures.matches("QIterable<QCharSequence;>;", "QList<QCharSequence;>;"));
+ assertFalse("Signatures should not match", Signatures.matches("QList<QCharSequence;>;", "Ljava.lang.Iterable<QCharSequence;>;"));
+ assertFalse("Differing packages should not match", Signatures.matches("Lmy.package.Class;", "Lother.package.Class;"));
+ assertFalse("Differing packages should not match", Signatures.matches("Lmy.package.Class$InnerMost;", "Lother.package.Class$InnerMost;"));
+ assertTrue("Inner class should match", Signatures.matches("QInnerMost;", "Lmy.package.Class$InnerMost;"));
+ assertTrue("Signatures should match", Signatures.matches("QInnerMost;", "Lmy.package.Class$Inner$InnerMost;")); //$NON-NLS-1$
+ assertTrue("Super signatures should match", Signatures.matches("QClass<-QITmfEvent;>;", "Ljava.lang.Class<-Lorg.eclipse.linuxtools.tmf.core.event.ITmfEvent;>;"));
+ assertTrue("Extends signatures should match", Signatures.matches("QClass<+QITmfEvent;>;", "Ljava.lang.Class<+Lorg.eclipse.linuxtools.tmf.core.event.ITmfEvent;>;"));
+ assertTrue("Unbound wildcard signatures should match", Signatures.matches("QClass<*>;", "Ljava.lang.Class<*>;"));
+ assertFalse("Different wildcard signatures should not match", Signatures.matches("QClass<+QITmfEvent;>;", "Ljava.lang.Class<-Lorg.eclipse.linuxtools.tmf.core.event.ITmfEvent;>;"));
+ assertFalse("Different wildcard signatures should not match", Signatures.matches("QClass<+QITmfEvent;>;", "Ljava.lang.Class<*>;"));
+ }
+
/**
* Tests the {@link Signatures#getPackageName(String)} method
*/
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Signatures.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Signatures.java
index b2d028d231..c34656415c 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Signatures.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Signatures.java
@@ -32,6 +32,7 @@ import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IFieldDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor;
@@ -618,12 +619,13 @@ public final class Signatures {
}
/**
- * Returns if the given signatures match. Where signatures are considered to
- * match iff the return type, name and parameters are the same.
+ * Returns if the given method signatures match, where method signatures are
+ * considered to match iff the return type, name and parameters are the
+ * same.
*
- * @param signature
- * @param signature2
- * @return true if the signatures are equal, false otherwise
+ * @param signature a method signature
+ * @param signature2 a method signature
+ * @return true if the method signatures are equal, false otherwise
*/
public static boolean matchesSignatures(String signature, String signature2) {
if (!matches(Signature.getReturnType(signature), Signature.getReturnType(signature2))) {
@@ -645,42 +647,67 @@ public final class Signatures {
}
/**
- * Returns if the two types match. Types are considered to match iff the
- * type name and array count (if any) are the same
+ * Returns if the given type signatures match. Type signatures are
+ * considered to match iff the type name and array count (if any) are the
+ * same
*
- * @param type
- * @param type2
- * @return true if the type names match, false otherwise
- */
- private static boolean matches(String type, String type2) {
- if (Signature.getArrayCount(type) == Signature.getArrayCount(type2)) {
- String el1 = Signature.getElementType(type);
- String el2 = Signature.getElementType(type2);
- String[] typeargs1 = Signature.getTypeArguments(el1);
- String[] typeargs2 = Signature.getTypeArguments(el2);
- if (typeargs1.length == typeargs2.length) {
- if (typeargs1.length > 0) {
- for (int i = 0; i < typeargs1.length; i++) {
- if (!matches(typeargs1[i], typeargs2[i])) {
- return false;
- }
- }
- return true;
- } else {
- String signatureSimpleName = Signature.getSignatureSimpleName(el1);
- String signatureSimpleName2 = Signature.getSignatureSimpleName(el2);
- if (signatureSimpleName.equals(signatureSimpleName2)) {
- return true;
- }
- int index = signatureSimpleName2.lastIndexOf('.');
- if (index != -1) {
- // the right side is a member type
- return signatureSimpleName.equals(signatureSimpleName2.subSequence(index + 1, signatureSimpleName2.length()));
- }
- }
+ * @param type a type signature
+ * @param type2 a type signature
+ * @return true if the type signatures match, false otherwise
+ */
+ public static boolean matches(String type, String type2) {
+ if (Signature.getArrayCount(type) != Signature.getArrayCount(type2)) {
+ return false;
+ }
+ String el1 = Signature.getElementType(type);
+ String el2 = Signature.getElementType(type2);
+ if (Signature.getTypeSignatureKind(el1) == Signature.TYPE_VARIABLE_SIGNATURE || Signature.getTypeSignatureKind(el2) == Signature.TYPE_VARIABLE_SIGNATURE) {
+ // E.g., "TT;" vs "QT;"
+ return el1.substring(1).equals(el2.substring(1));
+ } else if (Signature.getTypeSignatureKind(el1) != Signature.getTypeSignatureKind(el2)) {
+ return false;
+ }
+ // we know the type signature kinds are the same
+ if (Signature.getTypeSignatureKind(el1) == Signature.WILDCARD_TYPE_SIGNATURE) {
+ if (el1.charAt(0) != el2.charAt(0)) {
+ return false;
+ } else if (el1.charAt(0) == Signature.C_STAR) {
+ // there should be nothing else to these signatures
+ return el1.length() == 1 && el2.length() == 1;
}
+ // Strip off the Signature.C_EXTEND/C_SUPER
+ el1 = el1.substring(1);
+ el2 = el2.substring(1);
}
- return false;
+ if (Signature.getTypeSignatureKind(el1) != Signature.BASE_TYPE_SIGNATURE && Signature.getTypeSignatureKind(el1) != Signature.CLASS_TYPE_SIGNATURE) {
+ ApiPlugin.logErrorMessage("Signatures#matches: Unhandled signature kind: " + el1); //$NON-NLS-1$
+ return false;
+ }
+ String[] typeargs1 = Signature.getTypeArguments(el1);
+ String[] typeargs2 = Signature.getTypeArguments(el2);
+ if (typeargs1.length != typeargs2.length) {
+ return false;
+ }
+ String erased1 = Signature.getTypeErasure(el1);
+ String erased2 = Signature.getTypeErasure(el2);
+ if (el1.charAt(0) != Signature.C_UNRESOLVED && el2.charAt(0) != Signature.C_UNRESOLVED) {
+ if (!erased1.equals(erased2)) {
+ return false;
+ }
+ } else {
+ // If an inner type, we want just the inner type name
+ String signatureSimpleName = Signature.getSimpleName(Signature.getSignatureSimpleName(erased1));
+ String signatureSimpleName2 = Signature.getSimpleName(Signature.getSignatureSimpleName(erased2));
+ if (!signatureSimpleName.equals(signatureSimpleName2)) {
+ return false;
+ }
+ }
+ for (int i = 0; i < typeargs1.length; i++) {
+ if (!matches(typeargs1[i], typeargs2[i])) {
+ return false;
+ }
+ }
+ return true;
}
/**
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java
index 5b68a7758f..f1d7e9f176 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java
@@ -40,14 +40,17 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
@@ -74,7 +77,6 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
-import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -88,10 +90,12 @@ import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
@@ -1276,57 +1280,148 @@ public final class Util {
}
if (method.exists()) {
return method;
- } else {
- // if the method is not null and it doesn't exist, it might be the
- // default constructor
- if (selector.equals(type.getElementName()) && parameterTypes.length == 0) {
+ }
+ // if the method is not null and it doesn't exist, it might be the
+ // default constructor or a constructor in inner type
+ if (selector.equals(type.getElementName())) {
+ if (parameterTypes.length == 0) {
return null;
}
- // try to check by selector
- IMethod[] methods = null;
+ // Perhaps a constructor on an inner type?
+ IJavaElement parent = type.getParent();
+ if (parent instanceof IType) {
+ String parentTypeSig = Signature.createTypeSignature(((IType) parent).getFullyQualifiedName(), true);
+ if (Signatures.matches(parentTypeSig, parameterTypes[0])) {
+ IMethod constructor = type.getMethod(selector, Arrays.copyOfRange(parameterTypes, 1, parameterTypes.length));
+ try {
+ if (constructor.exists() && constructor.isConstructor()) {
+ return constructor;
+ }
+ String contructorSig = Signature.createMethodSignature(Arrays.copyOfRange(parameterTypes, 1, parameterTypes.length), Signature.getReturnType(signature));
+ IMethod[] methods = type.findMethods(constructor);
+ if (methods != null) {
+ if (methods.length == 1 && methods[0].isConstructor()) {
+ return methods[0];
+ }
+ // findMethods() checks simple type names, so
+ // it's possible to have multiple matches with
+ // different package names
+ for (IMethod m : methods) {
+ try {
+ if (m.isConstructor() && m.getNumberOfParameters() == parameterTypes.length - 1 && Signatures.matchesSignatures(generateBinarySignature(m), contructorSig)) {
+ return m;
+ }
+ } catch (JavaModelException e) {
+ // ignore
+ }
+ }
+ }
+ } catch (JavaModelException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ // Let JDT have a go
+ IMethod[] methods = type.findMethods(method);
+ if (methods != null && methods.length == 1) {
+ /* exact match found */
+ return methods[0];
+ }
+ if (methods == null || methods.length == 0) {
+ /* no methods found: may be due to type erasure */
try {
methods = type.getMethods();
} catch (JavaModelException e) {
- ApiPlugin.log(e);
- // do not default to the enclosing type - see bug 224713
- ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind(UtilMessages.Util_6, new String[] {
- selector, descriptor })));
+ ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind("Unable to retrieve methods for {0}", type.getFullyQualifiedName()), e)); //$NON-NLS-1$
return null;
}
- List<IMethod> list = new ArrayList<>();
- for (IMethod method2 : methods) {
- if (selector.equals(method2.getElementName())) {
- list.add(method2);
+ }
+
+ /*
+ * findMethods() checks simple type names, so it's possible to have
+ * multiple matches with different package names. Or we may need to
+ * check with type erasure.
+ */
+ for (IMethod m : methods) {
+ try {
+ if (!m.getElementName().equals(selector) || m.getNumberOfParameters() != parameterTypes.length) {
+ continue;
}
- }
- switch (list.size()) {
- case 0:
- // do not default to the enclosing type - see bug 224713
- ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind(UtilMessages.Util_6, new String[] {
- selector, descriptor })));
- return null;
- case 1:
- return list.get(0);
- default:
- // need to find a matching parameters
- for (IMethod method2 : list) {
- try {
- if (Signatures.matchesSignatures(method2.getSignature(), signature)) {
- return method2;
- }
- } catch (JavaModelException e) {
- // ignore
- }
- }
+ if (Signatures.matchesSignatures(generateBinarySignature(m), signature)) {
+ return m;
+ }
+ } catch (JavaModelException e) {
+ // ignore
}
}
- // do not default to the enclosing type - see bug 224713
+
+ /*
+ * Unclear what circumstances that this could happen, so provide more
+ * information to help understand why
+ */
+ StringBuilder sb = new StringBuilder();
+ for (IMethod m : methods) {
+ sb.append('\n').append(m.getHandleIdentifier());
+ }
ApiPlugin.log(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, NLS.bind(UtilMessages.Util_6, new String[] {
- selector, descriptor })));
+ selector, descriptor }) + sb.toString()));
+ // do not default to the enclosing type - see bug 224713
return null;
}
/**
+ * Generate the binary signature for the provided method. This is the
+ * type-erased signature written out to the .class file.
+ *
+ * @param method the method
+ * @return the method signature as would be encoded in a .class file
+ * @throws JavaModelException
+ */
+ private static String generateBinarySignature(IMethod method) throws JavaModelException {
+ ITypeParameter[] typeTPs = method.getDeclaringType().getTypeParameters();
+ ITypeParameter[] methodTPs = method.getTypeParameters();
+ if (typeTPs.length == 0 && methodTPs.length == 0) {
+ return method.getSignature();
+ }
+ Map<String, String> lookup = new HashMap<>();
+ Stream.concat(Stream.of(typeTPs), Stream.of(methodTPs)).forEach(tp -> {
+ try {
+ String sigs[] = tp.getBoundsSignatures();
+ lookup.put(tp.getElementName(), sigs.length == 1 ? sigs[0] : "Ljava.lang.Object;"); //$NON-NLS-1$
+ } catch (JavaModelException e) {
+ /* ignore */
+ }
+ });
+ String[] parameterTypes = Stream.of(method.getParameterTypes()).map(p -> expandParameterType(p, lookup)).toArray(String[]::new);
+ return Signature.createMethodSignature(parameterTypes, expandParameterType(method.getReturnType(), lookup));
+ }
+
+ /**
+ * Rewrite a parameter type signature with type erasure and using the
+ * parameterized type bounds lookup table. For example:
+ *
+ * <pre>
+ * expand("QList&lt;QE;&gt;;", {"E" &rarr; "Ljava.lang.Object;"}) = "QList;"
+ * expand("QE;", {"E" &rarr; "Ljava.lang.Object;"}) = "Ljava.lang.Object;"
+ * </pre>
+ *
+ * @param parameterTypeSig the type signature for a parameter
+ * @param bounds the type bounds as expressed on the method and class
+ * @return a rewritten parameter type signature as would be found in the .class file
+ */
+ private static String expandParameterType(String parameterTypeSig, Map<String, String> bounds) {
+ String erased = Signature.getTypeErasure(parameterTypeSig);
+ if (erased.charAt(0) == Signature.C_UNRESOLVED || erased.charAt(0) == Signature.C_TYPE_VARIABLE) {
+ String repl = bounds.get(Signature.getSignatureSimpleName(erased));
+ if (repl != null) {
+ return repl;
+ }
+ }
+ return erased;
+ }
+
+ /**
* Returns the given input stream as a byte array
*
* @param stream the stream to get as a byte array

Back to the top