diff options
author | Brian de Alwis | 2016-03-18 02:52:58 +0000 |
---|---|---|
committer | Vikas Chandra | 2016-04-03 05:01:16 +0000 |
commit | 24f00d7ad2beed8ce21d56a9c7e251e2dea399c6 (patch) | |
tree | bede8cd830e4baea3d7243e4b4ac99defc65a11d | |
parent | 8614d2d261367ebde0523ba822664aa7817b00d8 (diff) | |
download | eclipse.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
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<QE;>;", {"E" → "Ljava.lang.Object;"}) = "QList;" + * expand("QE;", {"E" → "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 |