diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/AbstractBindingLabelsTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/AbstractBindingLabelsTest.java new file mode 100644 index 0000000..9f705d6 --- /dev/null +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/AbstractBindingLabelsTest.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2015 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view + *******************************************************************************/ +package org.eclipse.jdt.ui.tests.core; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.testplugin.JavaProjectHelper; + +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.IBinding; + +import org.eclipse.jdt.ui.PreferenceConstants; + +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; + +public abstract class AbstractBindingLabelsTest extends CoreTests { + + public AbstractBindingLabelsTest(String name) { + super(name); + } + + protected IJavaProject fJProject1; + protected boolean fHaveSource= true; + + protected void setUp() throws Exception { + fJProject1= ProjectTestSetup.getProject(); + + IPreferenceStore store= PreferenceConstants.getPreferenceStore(); + store.setValue(PreferenceConstants.APPEARANCE_COMPRESS_PACKAGE_NAMES, false); + } + + + protected void tearDown() throws Exception { + JavaProjectHelper.clear(fJProject1, ProjectTestSetup.getDefaultClasspath()); + } + + protected String getBindingLabel(IJavaElement elem, long flags) { + ASTParser parser= ASTParser.newParser(AST.JLS8); + parser.setResolveBindings(true); + parser.setProject(fJProject1); + IBinding binding= parser.createBindings(new IJavaElement[]{elem}, null)[0]; + return JavaElementLinks.getBindingLabel(binding, elem, flags, fHaveSource); + } + + protected void assertLink(String lab, String display) { + Pattern pattern= Pattern.compile("(.*)"); + Matcher matcher= pattern.matcher(lab); + assertEquals("number of match groups", 1, matcher.groupCount()); + assertTrue("Label doesn't match against expected pattern: "+lab, matcher.matches()); + assertEquals("display label", display, matcher.group(1)); + } + + protected void assertLink(String lab, String display, String title) { + Pattern pattern= Pattern.compile("(.*)"); + Matcher matcher= pattern.matcher(lab); + assertEquals("number of match groups", 2, matcher.groupCount()); + assertTrue("Label doesn't match against expected pattern: "+lab, matcher.matches()); + assertEquals("title", title, matcher.group(1)); + assertEquals("display label", display, matcher.group(2)); + } + + /* + * expectedMarkup may contain any number of occurrences of "{{qualifier|name}}", + * which will be matched as a link with link text "name" and a link title "in qualifier". + * Optionally, {{name}} will be matched as a link with a link text but no title. + */ + protected void assertLinkMatch(String label, String expectedMarkup) { + // to avoid matching the entire unreadable label in one step we co-parse expected and actual value: + int patternPos= 0; + int labelPos= 0; + int fragmentCount= 0; + while (patternPos < expectedMarkup.length()) { + // analyze expected mark-up: + int open= expectedMarkup.indexOf("{{", patternPos); + if (open == -1) + break; + int pipe= expectedMarkup.indexOf('|', open); + if (pipe > -1) { + int nextOpen= expectedMarkup.indexOf("{{", open+2); + if (nextOpen > -1 && nextOpen < pipe) + pipe= -1; // pipe belongs to next link + } + boolean hasTitle= pipe != -1; + int close= expectedMarkup.indexOf("}}", hasTitle ? pipe : open); + if (close + 2 < expectedMarkup.length() && expectedMarkup.charAt(close+2) == '}') + close++; // position to the end of "}}}" + + if (open > patternPos) { + // matching plain text: + String expected= expectedMarkup.substring(patternPos, open); + int end= label.indexOf("LINK_TEXT" + assertTrue("link found ("+fragmentCount+")", label.substring(labelPos).startsWith( "", end) + 2; + assertTrue("link text start not found", start != -1); + end= label.indexOf("", start); + assertTrue("link text end not found", end != -1); + assertEquals("link text ("+fragmentCount+")", escape(linkText), label.substring(start, end)); + fragmentCount++; + + labelPos= end + "".length(); + } else { + // match only linkText + int start= label.indexOf("'>", labelPos) + 2; + assertTrue("link text start not found", start != -1); + int end= label.indexOf("", start+1); + assertTrue("link text end not found", end != -1); + assertEquals("link text ("+fragmentCount+")", escape(linkText), label.substring(start, end)); + fragmentCount++; + + labelPos= end + "".length(); + } + } + patternPos= close+2; + } + if (patternPos < expectedMarkup.length()) { + // matching tailing plain text: + String expected= expectedMarkup.substring(patternPos); + assertEquals("plain text ("+(fragmentCount)+")", escape(expected), label.substring(labelPos)); + } + } + + protected String escape(String element) { + return element.replace("&", "&").replace("<", "<").replace(">", ">"); + } + +} diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/BindingLabels18Test.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/BindingLabels18Test.java new file mode 100644 index 0000000..a3c8585 --- /dev/null +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/BindingLabels18Test.java @@ -0,0 +1,323 @@ +/******************************************************************************* + * Copyright (c) 2015 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view + *******************************************************************************/ +package org.eclipse.jdt.ui.tests.core; + +import org.eclipse.jdt.testplugin.JavaProjectHelper; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; + +import org.eclipse.jdt.ui.JavaElementLabels; +import org.eclipse.jdt.ui.PreferenceConstants; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class BindingLabels18Test extends AbstractBindingLabelsTest { + + private static final Class THIS= BindingLabels18Test.class; + + public BindingLabels18Test(String name) { + super(name); + } + + public static Test suite() { + return setUpTest(new TestSuite(THIS)); + } + + public static Test setUpTest(Test test) { + return new Java18ProjectTestSetup(test); + } + + protected void setUp() throws Exception { + fJProject1= Java18ProjectTestSetup.getProject(); + + IPreferenceStore store= PreferenceConstants.getPreferenceStore(); + store.setValue(PreferenceConstants.APPEARANCE_COMPRESS_PACKAGE_NAMES, false); + } + + protected void tearDown() throws Exception { + JavaProjectHelper.clear(fJProject1, Java18ProjectTestSetup.getDefaultClasspath()); + } + + + public void testMethodLabelPolymorphicSignatureDeclaration() throws Exception { + IType methodHandle= fJProject1.findType("java.lang.invoke.MethodHandle"); + IMethod invokeExact= methodHandle.getMethod("invokeExact", new String[] { + Signature.createArraySignature(Signature.createTypeSignature("java.lang.Object", true), 1) + }); + + String lab= getBindingLabel(invokeExact, JavaElementLabels.ALL_DEFAULT); + assertLinkMatch(lab, "invokeExact({{java.lang|Object}}...)"); + + lab= getBindingLabel(invokeExact, JavaElementLabels.M_PARAMETER_NAMES); + assertEqualString(lab, "invokeExact(arg0)"); + + lab= getBindingLabel(invokeExact, JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "invokeExact({{java.lang|Object}}...)"); + + lab= getBindingLabel(invokeExact, JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "invokeExact({{java.lang|Object}}... arg0)"); + + lab= getBindingLabel(invokeExact, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invokeExact({{java.lang|Object}}...)"); + } + + private IJavaElement createInvokeReference(String invocation) throws CoreException, JavaModelException { + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.lang.invoke.MethodHandle;\n"); + buf.append("public class Test {\n"); + buf.append(" void foo(MethodHandle mh) throws Throwable {\n"); + buf.append(" " + invocation + ";\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Test.java", content, false, null); + + IJavaElement elem= cu.codeSelect(content.indexOf("invoke("), 0)[0]; + return elem; + } + + private void assertInvokeUnresolved(IJavaElement elem) { + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT); + assertLinkMatch(lab, "invoke({{java.lang|Object}}...)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES); + assertEqualString(lab, "invoke(arg0)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "invoke({{java.lang|Object}}...)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PRE_RETURNTYPE); + assertLinkMatch(lab, "{{java.lang|Object}} invoke({{java.lang|Object}}... arg0)"); + } + + public void testMethodLabelPolymorphicSignatureReference0() throws Exception { + IJavaElement elem= createInvokeReference("mh.invoke()"); + + assertInvokeUnresolved(elem); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "invoke()"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "invoke()"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{java.lang|Object}} invoke()"); + } + + public void testMethodLabelPolymorphicSignatureReference0Ret() throws Exception { + IJavaElement elem= createInvokeReference("String s= (String) mh.invoke()"); + + assertInvokeUnresolved(elem); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "invoke()"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "invoke()"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{java.lang|Object}} invoke()"); + } + + public void testMethodLabelPolymorphicSignatureReference1() throws Exception { + IJavaElement elem= createInvokeReference("mh.invoke(1)"); + + assertInvokeUnresolved(elem); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "invoke(int)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "invoke(int)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{java.lang|Object}} invoke(int arg00)"); + } + + public void testMethodLabelPolymorphicSignatureReference1Array() throws Exception { + IJavaElement elem= createInvokeReference("mh.invoke(new Object[42])"); + + assertInvokeUnresolved(elem); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invoke({{java.lang|Object}}[])"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invoke({{java.lang|Object}}[])"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{java.lang|Object}} invoke({{java.lang|Object}}[] arg00)"); + } + + public void testMethodLabelPolymorphicSignatureReference2() throws Exception { + IJavaElement elem= createInvokeReference("mh.invoke('a', new Integer[0][])"); + + assertInvokeUnresolved(elem); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invoke(char, {{java.lang|Integer}}[][])"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invoke(char, {{java.lang|Integer}}[][])"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{java.lang|Object}} invoke(char arg00, {{java.lang|Integer}}[][] arg01)"); + } + + public void testMethodLabelPolymorphicSignatureReference3Ret() throws Exception { + IJavaElement elem= createInvokeReference("long l= (long) mh.invoke('a', new java.util.ArrayList(), null)"); + + assertInvokeUnresolved(elem); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invoke(char, {{java.util|ArrayList}}<{{java.util.ArrayList|E}}>, {{java.lang|Void}})"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "invoke(char, {{java.util|ArrayList}}, {{java.lang|Void}})"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{java.lang|Object}} invoke(char arg00, {{java.util|ArrayList}} arg01, {{java.lang|Void}} arg02)"); + } + + public void testTypeLabelLambda1() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.function.IntConsumer;\n"); + buf.append("public class C {\n"); + buf.append(" IntConsumer c = (i) -> { };\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", content, false, null); + + IJavaElement[] elems= cu.codeSelect(content.lastIndexOf("i"), 1); + IJavaElement i= elems[0]; + String lab= getBindingLabel(i, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.C}}.{{org.test.C|c}}.() -> {...} {{IntConsumer}}.{{java.util.function.IntConsumer|accept}}(int).i"); + + IJavaElement lambdaMethod= i.getParent(); + lab= getBindingLabel(lambdaMethod, JavaElementLabels.T_FULLY_QUALIFIED + | JavaElementLabels.M_POST_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES); + assertLinkMatch(lab, "accept(int i) - {{org.test.C}}.{{org.test.C|c}}.() -> {...} {{java.util.function|IntConsumer}}"); + + IJavaElement lambdaType= lambdaMethod.getParent(); + lab= getBindingLabel(lambdaType, JavaElementLabels.T_POST_QUALIFIED); +// Bindings don't have the split identity of a lambda as expected from JavaElementLabelsTest18 + assertLinkMatch(lab, "accept(...) - {{org.test.C}}.{{org.test.C|c}}.() -> {...} {{java.util.function|IntConsumer}}"); +// assertLinkMatch(lab, "() -> {...} IntConsumer - org.test.C.c"); + } + + public void testTypeLabelLambda2() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.function.Consumer;\n"); + buf.append("public class C {\n"); + buf.append(" Consumer c = (s) -> { };\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", content, false, null); + + IJavaElement[] elems= cu.codeSelect(content.lastIndexOf("s"), 1); + IJavaElement i= elems[0]; + String lab= getBindingLabel(i, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.C}}.{{org.test.C|c}}.() -> {...} {{Consumer}}.{{java.util.function.Consumer|accept}}({{java.util.function.Consumer|T}}).s"); + + lab= getBindingLabel(i, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{org.test.C}}.{{org.test.C|c}}.() -> {...} {{Consumer}}.{{java.util.function.Consumer|accept}}({{java.lang|String}}).s"); + + IJavaElement lambdaMethod= i.getParent(); + lab= getBindingLabel(lambdaMethod, JavaElementLabels.T_FULLY_QUALIFIED + | JavaElementLabels.M_POST_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES); + assertLinkMatch(lab, "accept({{java.util.function.Consumer|T}} arg0) - {{org.test.C}}.{{org.test.C|c}}.() -> {...} {{java.util.function|Consumer}}"); + + IJavaElement lambdaType= lambdaMethod.getParent(); + lab= getBindingLabel(lambdaType, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "accept(...) - {{org.test.C}}.{{org.test.C|c}}.() -> {...} {{java.util.function|Consumer}}"); + } + + public void testAnonymousClassInLambda1() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.function.Consumer;\n"); + buf.append("public class C {\n"); + buf.append(" Consumer c = (s) -> {\n"); + buf.append(" new Thread() { public void run() { } }.start();\n"); + buf.append(" };\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", content, false, null); + + IJavaElement thread= cu.getElementAt(content.lastIndexOf("Thread")); + String lab= getBindingLabel(thread, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.C}}.{{org.test.C|c}}.() -> {...}.new Thread() {...}"); + + lab= getBindingLabel(thread, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_POST_QUALIFIED); + assertLinkMatch(lab, "new Thread() {...} - {{org.test.C}}.{{org.test.C|c}}.() -> {...}"); + } + + public void testLambdaInAnonymousClass1() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.function.Consumer;\n"); + buf.append("public class C {\n"); + buf.append(" Thread t= new Thread() {\n"); + buf.append(" public void run() {\n"); + buf.append(" Consumer c = (s) -> { };\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", content, false, null); + + IJavaElement[] elems= cu.codeSelect(content.lastIndexOf("s)"), 1); + IJavaElement thread= elems[0]; + String lab= getBindingLabel(thread, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "{{org.test.C}}.{{org.test.C|t}}.{{org.test.C.t|new Thread() {...}}}.{{org.test.C.t.new Thread() {...}|run}}()." + + "() -> {...} {{java.util.function|Consumer}}.{{java.util.function.Consumer|accept}}({{java.lang|String}}).s"); + + lab= getBindingLabel(thread, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.F_POST_QUALIFIED); + assertLinkMatch(lab, "{{java.lang|String}} s - {{org.test.C}}.{{org.test.C|t}}.{{org.test.C.t|new Thread() {...}}}.{{org.test.C.t.new Thread() {...}|run}}()." + + "() -> {...} {{java.util.function|Consumer}}.{{java.util.function.Consumer|accept}}({{java.util.function.Consumer|T}})"); + } + +} diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/BindingLabelsTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/BindingLabelsTest.java new file mode 100644 index 0000000..08f9f6d --- /dev/null +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/BindingLabelsTest.java @@ -0,0 +1,702 @@ +/******************************************************************************* + * Copyright (c) 2015 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view + *******************************************************************************/ +package org.eclipse.jdt.ui.tests.core; + +import org.eclipse.jdt.testplugin.JavaProjectHelper; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeParameter; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; + +import org.eclipse.jdt.ui.JavaElementLabels; + +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; + +import junit.framework.Test; +import junit.framework.TestSuite; + + +/** + * This test suite is copied and adjusted from {@link JavaElementLabelsTest} + */ +public class BindingLabelsTest extends AbstractBindingLabelsTest { + + private static final Class THIS= BindingLabelsTest.class; + + + public BindingLabelsTest(String name) { + super(name); + } + + public static Test suite() { + return setUpTest(new TestSuite(THIS)); + } + + public static Test setUpTest(Test test) { + return new ProjectTestSetup(test); + } + + public void testTypeLabelOuter() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("public class Outer {\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Outer")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertEqualString(lab, "org.test.Outer"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertEqualString(lab, "Outer"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "Outer - {{org.test}}"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer"); + */ + } + + public void testTypeLabelInner() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Vector;\n"); + buf.append("public class Outer {\n"); + buf.append(" public void foo(Vector vec) {\n"); + buf.append(" }\n"); + buf.append(" public class Inner {\n"); + buf.append(" public int inner(Vector vec) {\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Inner")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertEqualString(lab, "org.test.Outer.Inner"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertEqualString(lab, "Outer.Inner"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "Inner - {{org.test.Outer}}"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer.Inner - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer.Inner"); + */ + } + + public void testTypeLabelLocal() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Vector;\n"); + buf.append("public class Outer {\n"); + buf.append(" public void foo(Vector vec) {\n"); + buf.append(" public class Local {\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Local")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertEqualString(lab, "org.test.Outer.foo(...).Local"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertEqualString(lab, "Outer.foo(...).Local"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "Local - {{org.test.Outer}}.{{foo}}(...)"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer.foo(...).Local - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer.foo(...).Local"); + */ + } + + public void testTypeParameterLabelType() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.*;\n"); + buf.append("import java.io.Serializable;\n"); + buf.append("public class TypeParams, Element extends Map & Serializable, NoBound> {\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("TypeParams.java", content, false, null); + + IType typeParams= (IType)cu.getElementAt(content.indexOf("TypeParams")); + ITypeParameter[] typeParameters= typeParams.getTypeParameters(); + ITypeParameter q= typeParameters[0]; + ITypeParameter element= typeParameters[1]; + ITypeParameter nobound= typeParameters[2]; + + String lab= getBindingLabel(q, 0); + assertLinkMatch(lab, "Q extends {{java.util|ArrayList}}"); + lab= getBindingLabel(q, JavaElementLabels.ALL_POST_QUALIFIED); + assertLinkMatch(lab, "Q extends {{java.util|ArrayList}} - {{org.test.TypeParams}}"); + + lab= getBindingLabel(element, 0); + assertLinkMatch(lab, "Element extends {{java.util|Map}}<{{java.lang|String}}, {{java.lang|Integer}}> & {{java.io|Serializable}}"); + lab= getBindingLabel(element, JavaElementLabels.DEFAULT_POST_QUALIFIED); + assertLinkMatch(lab, "Element extends {{java.util|Map}}<{{java.lang|String}}, {{java.lang|Integer}}> & {{java.io|Serializable}} - {{org.test.TypeParams}}"); + + lab= getBindingLabel(nobound, 0); + assertEqualString(lab, "NoBound"); + lab= getBindingLabel(nobound, JavaElementLabels.TP_POST_QUALIFIED); + assertLinkMatch(lab, "NoBound - {{org.test.TypeParams}}"); + + +/* cannot select 'E' from cu. + IType al= (IType)cu.codeSelect(content.indexOf("ArrayList"), 0)[0]; + ITypeParameter[] alTypeParameters= al.getTypeParameters(); + ITypeParameter e= alTypeParameters[0]; + + lab= JavaElementLabels.getTextLabel(e, 0); + assertEqualString(lab, "E"); // no " extends java.lang.Object"! + */ + +/* + lab= JavaElementLabels.getTextLabel(e, JavaElementLabels.ALL_POST_QUALIFIED); + assertEqualString(lab, "E - java.util.ArrayList"); + */ + + + lab= getBindingLabel(typeParams, 0); + assertEqualString(lab, "TypeParams"); + lab= getBindingLabel(typeParams, JavaElementLabels.ALL_DEFAULT); + assertLinkMatch(lab, "TypeParams<{{org.test.TypeParams|Q}} extends {{java.util|ArrayList}}, {{org.test.TypeParams|Element}} extends {{java.util|Map}}<{{java.lang|String}}, {{java.lang|Integer}}> & {{java.io|Serializable}}, {{org.test.TypeParams|NoBound}}>"); +/* + lab= JavaElementLabels.getTextLabel(typeParams, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_POST_QUALIFIED); + assertEqualString(lab, "TypeParams, Element extends Map & Serializable, NoBound> - org.test"); + */ + } + + public void testTypeParameterLabelMethod() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.*;\n"); + buf.append("public class X {\n"); + buf.append(" , Element extends Map, NoBound> Q method(Element e, NoBound n) {\n"); + buf.append(" return null;\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("X.java", content, false, null); + + IMethod method= (IMethod)cu.getElementAt(content.indexOf("method")); + ITypeParameter[] typeParameters= method.getTypeParameters(); + ITypeParameter q= typeParameters[0]; + ITypeParameter element= typeParameters[1]; + ITypeParameter nobound= typeParameters[2]; + + String lab= getBindingLabel(q, 0); + assertLinkMatch(lab, "Q extends {{java.util|ArrayList}}"); + lab= getBindingLabel(q, JavaElementLabels.ALL_POST_QUALIFIED); + assertLinkMatch(lab, "Q extends {{java.util|ArrayList}} - {{org.test.X}}.{{org.test.X|method}}({{org.test.X.method(...)|Element}}, {{org.test.X.method(...)|NoBound}})"); + + lab= getBindingLabel(element, 0); + assertLinkMatch(lab, "Element extends {{java.util|Map}}<{{java.lang|String}}, {{java.lang|Integer}}>"); + lab= getBindingLabel(element, JavaElementLabels.DEFAULT_POST_QUALIFIED); + assertLinkMatch(lab, "Element extends {{java.util|Map}}<{{java.lang|String}}, {{java.lang|Integer}}> - {{org.test.X}}.{{org.test.X|method}}(Element, {{org.test.X.method(...)|NoBound}})"); + + lab= getBindingLabel(nobound, 0); + assertEqualString(lab, "NoBound"); + lab= getBindingLabel(nobound, JavaElementLabels.TP_POST_QUALIFIED); + assertLinkMatch(lab, "NoBound - {{org.test.X}}.{{org.test.X|method}}({{org.test.X.method(...)|Element}}, NoBound)"); + } + + public void testTypeLabelAnonymous() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Vector;\n"); + buf.append("public class Outer {\n"); + buf.append(" public void foo(Vector vec) {\n"); + buf.append(" new Object() {\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Object")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer}}.{{org.test.Outer|foo}}(...).new Object() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer|foo}}(...).new Object() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "new Object() {...} - {{org.test.Outer}}.{{org.test.Outer|foo}}(...)"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer.foo(...).new Object() {...} - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer.foo(...).new Object() {...}"); + */ + } + + public void testTypeLabelAnonymousInAnonymous() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Vector;\n"); + buf.append("import java.io.Serializable;\n"); + buf.append("public class Outer {\n"); + buf.append(" public void foo(Vector vec) {\n"); + buf.append(" new Object() {\n"); + buf.append(" public void xoo() {\n"); + buf.append(" new Serializable() {\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Serializable()")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer}}.{{org.test.Outer|foo}}(...).{{org.test.Outer.foo(...)|new Object() {...}}}." + + "{{org.test.Outer.foo(...).new Object() {...}|xoo}}()" + + ".new Serializable() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer.foo(...).new Object() {...}|xoo}}().new Serializable() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "new Serializable() {...} - " + + "{{org.test.Outer}}.{{org.test.Outer|foo}}(...).{{org.test.Outer.foo(...)|new Object() {...}}}." + + "{{org.test.Outer.foo(...).new Object() {...}|xoo}}()"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer.foo(...).new Object() {...}.xoo().new Serializable() {...} - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer.foo(...).new Object() {...}.xoo().new Serializable() {...}"); + */ + } + + public void testTypeLabelAnonymousInFieldInitializer() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Vector;\n"); + buf.append("public class Outer {\n"); + buf.append(" Object o= new Thread() {\n"); + buf.append(" };\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Thread")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer}}.{{org.test.Outer|o}}.new Thread() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer|o}}.new Thread() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "new Thread() {...} - {{org.test.Outer}}.{{o}}"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer.o.new Thread() {...} - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer.o.new Thread() {...}"); + */ + } + + public void testTypeLabelAnonymousInInitializer() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Vector;\n"); + buf.append("public class Outer {\n"); + buf.append(" static {\n"); + buf.append(" new Object() {\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Outer.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("Object")); + String lab= getBindingLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED); + assertLinkMatch(lab, "{{org.test.Outer}}.{...}.new Object() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_CONTAINER_QUALIFIED); + assertLinkMatch(lab, "{...}.new Object() {...}"); + + lab= getBindingLabel(elem, JavaElementLabels.T_POST_QUALIFIED); + assertLinkMatch(lab, "new Object() {...} - {{org.test.Outer}}.{...}"); + +/* *_ROOT_PATH is not relevant for javadoc hover/view + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.APPEND_ROOT_PATH); + assertEqualString(lab, "org.test.Outer.{...}.new Object() {...} - TestSetupProject/src"); + + lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.PREPEND_ROOT_PATH); + assertEqualString(lab, "TestSetupProject/src - org.test.Outer.{...}.new Object() {...}"); + */ + } + + public void testTypeLabelWildcards() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("public class Wildcards {\n"); + buf.append(" Wildcards upper;\n"); + buf.append(" Wildcards lower;\n"); + buf.append(" Wildcards wild;\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Wildcards.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("upper")); + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.F_PRE_TYPE_SIGNATURE); + assertLinkMatch(lab, "{{org.test|Wildcards}} upper"); + + elem= cu.getElementAt(content.indexOf("lower")); + lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.F_PRE_TYPE_SIGNATURE); + assertLinkMatch(lab, "{{org.test|Wildcards}} lower"); + + elem= cu.getElementAt(content.indexOf("wild")); + lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.F_PRE_TYPE_SIGNATURE); + assertLinkMatch(lab, "{{org.test|Wildcards}} wild"); + + } + + public void testPackageLabels() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment packDefault= sourceFolder.getPackageFragment(""); + IPackageFragment packOrg= sourceFolder.createPackageFragment("org", false, null); +// IPackageFragment packOrgTest= sourceFolder.createPackageFragment("org.test", false, null); + IPackageFragment packOrgTestLongname= sourceFolder.createPackageFragment("org.test.longname", false, null); + + // to obtain an IPackageBinding go via imported types to their #package: + packOrg.createCompilationUnit("T1.java", "package org;\npublic class T1 {}\n", false, null); + packOrgTestLongname.createCompilationUnit("T2.java", "package org.test.longname;\npublic class T2 {}\n", false, null); + + StringBuffer buf= new StringBuffer(); + buf.append("import org.T1;\n"); + buf.append("import org.test.longname.T2;\n"); + buf.append("public class Main {\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= packDefault.createCompilationUnit("Main.java", content, false, null); + + IJavaElement main= cu.getElementAt(content.indexOf("Main")); + IJavaElement t1= cu.getElementAt(content.indexOf("T1")); + IJavaElement t2= cu.getElementAt(content.indexOf("T2")); + + ASTParser parser= ASTParser.newParser(AST.JLS8); + parser.setResolveBindings(true); + parser.setProject(fJProject1); + IBinding[] bindings= parser.createBindings(new IJavaElement[]{main, t1, t2}, null); + + String lab= JavaElementLinks.getBindingLabel(((ITypeBinding)bindings[0]).getPackage(), main.getAncestor(IJavaElement.PACKAGE_FRAGMENT), JavaElementLabels.ALL_DEFAULT, true); + assertEqualString(lab, "(default package)"); + lab= JavaElementLinks.getBindingLabel(((ITypeBinding)bindings[1]).getPackage(), t1.getAncestor(IJavaElement.PACKAGE_FRAGMENT), JavaElementLabels.ALL_DEFAULT, true); + assertLink(lab, "org"); + lab= JavaElementLinks.getBindingLabel(((ITypeBinding)bindings[2]).getPackage(), t2.getAncestor(IJavaElement.PACKAGE_FRAGMENT), JavaElementLabels.ALL_DEFAULT, true); + assertLink(lab, "org.test.longname"); + +/* P_COMPRESSED is not relevant for hovers / javadoc view: + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "org", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "org.test.longname", JavaElementLabels.P_COMPRESSED); + + assertExpectedLabel(packDefault, "(default package) - TestSetupProject/src", JavaElementLabels.P_POST_QUALIFIED); + assertExpectedLabel(packOrg, "org - TestSetupProject/src", JavaElementLabels.P_POST_QUALIFIED); + assertExpectedLabel(packOrgTestLongname, "org.test.longname - TestSetupProject/src", JavaElementLabels.P_POST_QUALIFIED); + + IPreferenceStore store= PreferenceConstants.getPreferenceStore(); + store.setValue(PreferenceConstants.APPEARANCE_COMPRESS_PACKAGE_NAMES, true); + + try { + store.setValue(PreferenceConstants.APPEARANCE_PKG_NAME_PATTERN_FOR_PKG_VIEW, "0"); + + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "org", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTest, "test", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "longname", JavaElementLabels.P_COMPRESSED); + + store.setValue(PreferenceConstants.APPEARANCE_PKG_NAME_PATTERN_FOR_PKG_VIEW, "."); + + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "org", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTest, ".test", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "..longname", JavaElementLabels.P_COMPRESSED); + + store.setValue(PreferenceConstants.APPEARANCE_PKG_NAME_PATTERN_FOR_PKG_VIEW, "1~."); + + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "org", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTest, "o~.test", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "o~.t~.longname", JavaElementLabels.P_COMPRESSED); + + store.setValue(PreferenceConstants.APPEARANCE_PKG_NAME_PATTERN_FOR_PKG_VIEW, "2*."); + + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "org", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTest, "org.test", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "org.te*.longname", JavaElementLabels.P_COMPRESSED); + + + store.setValue(PreferenceConstants.APPEARANCE_ABBREVIATE_PACKAGE_NAMES, true); + + assertExpectedLabel(packOrgTestLongname, "org.te*.longname", JavaElementLabels.P_COMPRESSED); + + store.setValue(PreferenceConstants.APPEARANCE_PKG_NAME_ABBREVIATION_PATTERN_FOR_PKG_VIEW, "#com=@C\norg=@O"); + + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "@O", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTest, "@O.test", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "@O.te*.longname", JavaElementLabels.P_COMPRESSED); + + store.setValue(PreferenceConstants.APPEARANCE_PKG_NAME_ABBREVIATION_PATTERN_FOR_PKG_VIEW, "org=@O\n\norg.test=@OT\n"); + + assertExpectedLabel(packDefault, "(default package)", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrg, "@O", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTest, "@OT", JavaElementLabels.P_COMPRESSED); + assertExpectedLabel(packOrgTestLongname, "@OT.longname", JavaElementLabels.P_COMPRESSED); + + } finally { + store.setToDefault(PreferenceConstants.APPEARANCE_PKG_NAME_PATTERN_FOR_PKG_VIEW); + store.setValue(PreferenceConstants.APPEARANCE_COMPRESS_PACKAGE_NAMES, false); + store.setToDefault(PreferenceConstants.APPEARANCE_PKG_NAME_ABBREVIATION_PATTERN_FOR_PKG_VIEW); + store.setValue(PreferenceConstants.APPEARANCE_ABBREVIATE_PACKAGE_NAMES, false); + } + */ + } + + public void testMethodLabelVarargsDeclaration() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("public class Varargs {\n"); + buf.append(" public void foo(int i, String... varargs) {\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Varargs.java", content, false, null); + + IJavaElement elem= cu.getElementAt(content.indexOf("foo")); + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT); + assertLinkMatch(lab, "foo(int, {{java.lang|String}}...)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES); + assertEqualString(lab, "foo(i, varargs)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "foo(int, {{java.lang|String}}...)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "foo(int i, {{java.lang|String}}... varargs)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "foo(int, {{java.lang|String}}...)"); + } + + public void testMethodLabelVarargsReference0() throws Exception { + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Arrays;\n"); + buf.append("public class Varargs {\n"); + buf.append(" void foo() {\n"); + buf.append(" Arrays.asList();\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Varargs.java", content, false, null); + + IJavaElement elem= cu.codeSelect(content.indexOf("asList"), 0)[0]; + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT); + assertLinkMatch(lab, "asList({{java.util.Arrays.asList(...)|T}}...) <{{java.util.Arrays.asList(...)|T}}>"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES); + assertEqualString(lab, "asList(arg0)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "asList({{java.util.Arrays.asList(...)|T}}...)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "asList({{java.util.Arrays.asList(...)|T}}... arg0)"); + + lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "asList({{java.lang|Object}}...) <{{java.lang|Object}}>"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "asList({{java.lang|Object}}...)"); + } + + public void testMethodLabelVarargsReference1() throws Exception { + assertMethodLabelVarargsReference("1"); + } + + public void testMethodLabelVarargsReference2() throws Exception { + assertMethodLabelVarargsReference("1, 2"); + } + + public void testMethodLabelVarargsReference3() throws Exception { + assertMethodLabelVarargsReference("1, 2, new Integer(3)"); + } + + private void assertMethodLabelVarargsReference(String args) throws CoreException, JavaModelException { + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.Arrays;\n"); + buf.append("public class Varargs {\n"); + buf.append(" void foo() {\n"); + buf.append(" Arrays.asList(" + args + ");\n"); + buf.append(" }\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Varargs.java", content, false, null); + + IJavaElement elem= cu.codeSelect(content.indexOf("asList"), 0)[0]; + + String lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT); + assertLinkMatch(lab, "asList({{java.util.Arrays.asList(...)|T}}...) <{{java.util.Arrays.asList(...)|T}}>"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES); + assertEqualString(lab, "asList(arg0)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "asList({{java.util.Arrays.asList(...)|T}}...)"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_PARAMETER_TYPES); + assertLinkMatch(lab, "asList({{java.util.Arrays.asList(...)|T}}... arg0)"); + + lab= getBindingLabel(elem, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "asList({{java.lang|Integer}}...) <{{java.lang|Integer}}>"); + + lab= getBindingLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED); + assertLinkMatch(lab, "asList({{java.lang|Integer}}...)"); + } + + + public void testMethodLabelAnnotatedParameters() throws Exception { + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("\n"); + buf.append("import java.lang.annotation.Retention;\n"); + buf.append("import java.lang.annotation.RetentionPolicy;\n"); + buf.append("\n"); + buf.append("public class Annotations {\n"); + buf.append(" void foo(@Outer(a=@Ann(\"Hello world\\r\\n\\t\\\"<'#@%^&\")) String param) { }\n"); + buf.append(" \n"); + buf.append(" void foo2(@Ann(value=\"\", cl=Annotations.class, ints={1, 2, -19},\n"); + buf.append(" ch='\\0', sh= 0x7FFF, r= @Retention(RetentionPolicy.SOURCE)) String param) { }\n"); + buf.append("}\n"); + buf.append("@interface Ann {\n"); + buf.append(" String value();\n"); + buf.append(" Class cl() default Ann.class;\n"); + buf.append(" int[] ints() default {1, 2};\n"); + buf.append(" char ch() default 'a';\n"); + buf.append(" short sh() default 1;\n"); + buf.append(" Retention r() default @Retention(RetentionPolicy.CLASS);\n"); + buf.append("}\n"); + buf.append("@interface Outer {\n"); + buf.append(" Ann a();\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("Annotations.java", content, false, null); + + IJavaElement foo= cu.getElementAt(content.indexOf("foo")); + String lab= getBindingLabel(foo, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_ANNOTATIONS); + assertLinkMatch(lab, "{{org.test.Annotations}}.foo(@{{Outer}}({{a}}=@{{Ann}}({{value}}=\"Hello world\\r\\n\\t\\\"<'#@%^&\")) {{String}})"); + + IJavaElement foo2= cu.getElementAt(content.indexOf("foo2")); + lab= getBindingLabel(foo2, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_ANNOTATIONS); + assertLinkMatch(lab, "{{org.test.Annotations}}.foo2(@{{Ann}}({{value}}=\"\", {{cl}}={{Annotations}}.class, {{ints}}={1, 2, -19}, {{ch}}='\\u0000', {{sh}}=32767, {{r}}=@{{Retention}}({{value}}={{RetentionPolicy}}.{{SOURCE}})) {{String}})"); + } +} diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/CoreTests.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/CoreTests.java index 99ec2c5..d4fc7ad 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/CoreTests.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/CoreTests.java @@ -47,6 +47,8 @@ suite.addTest(ImportOrganizeTest18.suite()); suite.addTest(JavaElementLabelsTest.suite()); suite.addTest(JavaElementLabelsTest18.suite()); + suite.addTest(BindingLabelsTest.suite()); + suite.addTest(BindingLabels18Test.suite()); suite.addTest(JavaElementPropertyTesterTest.suite()); suite.addTest(JavaModelUtilTest.suite()); suite.addTest(MethodOverrideTest.suite()); diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest18.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest18.java index 481d5b2..7765431 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest18.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest18.java @@ -237,4 +237,36 @@ assertEqualString(lab, "() -> {...} IntConsumer - org.test.C.c"); } + public void testTypeLabelLambda2() throws Exception { + + IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package org.test;\n"); + buf.append("import java.util.function.Consumer;\n"); + buf.append("public class C {\n"); + buf.append(" Consumer c = (s) -> { };\n"); + buf.append("}\n"); + String content= buf.toString(); + ICompilationUnit cu= pack1.createCompilationUnit("C.java", content, false, null); + + IJavaElement[] elems= cu.codeSelect(content.lastIndexOf("s"), 1); + IJavaElement i= elems[0]; + String lab= JavaElementLabels.getTextLabel(i, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED); + assertEqualString(lab, "org.test.C.c.() -> {...} Consumer.accept(String).s"); + + lab= JavaElementLabels.getTextLabel(i, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.USE_RESOLVED); + assertEqualString(lab, "org.test.C.c.() -> {...} Consumer.accept(String).s"); + + IJavaElement lambdaMethod= i.getParent(); + lab= JavaElementLabels.getTextLabel(lambdaMethod, JavaElementLabels.T_FULLY_QUALIFIED + | JavaElementLabels.M_POST_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES); + assertEqualString(lab, "accept(String s) - org.test.C.c.() -> {...} Consumer"); + + IJavaElement lambdaType= lambdaMethod.getParent(); + lab= JavaElementLabels.getTextLabel(lambdaType, JavaElementLabels.T_POST_QUALIFIED); + assertEqualString(lab, "() -> {...} Consumer - org.test.C.c"); + } + } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java index 6757de4..109ac7c 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java @@ -10,6 +10,7 @@ * Genady Beryozkin - [misc] Display values for constant fields in the Javadoc view - https://bugs.eclipse.org/bugs/show_bug.cgi?id=204914 * Brock Janiczak - [implementation] Streams not being closed in Javadoc views - https://bugs.eclipse.org/bugs/show_bug.cgi?id=214854 * Benjamin Muskalla - [javadoc view] NPE on enumerations - https://bugs.eclipse.org/bugs/show_bug.cgi?id=223586 + * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view *******************************************************************************/ package org.eclipse.jdt.internal.ui.infoviews; @@ -150,6 +151,7 @@ import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.FallbackInformationPresenter; import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; +import org.eclipse.jdt.internal.ui.viewsupport.BindingLinkedLabelComposer; import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; @@ -1155,7 +1157,18 @@ * @return a string containing the member's label */ private String getInfoText(IJavaElement member, String constantValue, boolean allowImage) { - StringBuffer label= new StringBuffer(JavaElementLinks.getElementLabel(member, JavadocHover.getHeaderFlags(member))); + long flags= JavadocHover.getHeaderFlags(member); + IBinding binding= JavadocHover.getHoverBinding(member, null); + StringBuffer label; + if (binding != null) { + label= new StringBuffer(); + // setting haveSource to false lets the JavadocView *always* show qualified type names, + // would need to track the source of our input to distinguish classfile/compilationUnit: + boolean haveSource= false; + new BindingLinkedLabelComposer(member, label, haveSource).appendBindingLabel(binding, flags); + } else { + label= new StringBuffer(JavaElementLinks.getElementLabel(member, flags)); + } if (member.getElementType() == IJavaElement.FIELD && constantValue != null) { label.append(constantValue); } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java index 5288fe2..859bdd5 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Genady Beryozkin - [hovering] tooltip for constant string does not show constant value - https://bugs.eclipse.org/bugs/show_bug.cgi?id=85382 + * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view *******************************************************************************/ package org.eclipse.jdt.internal.ui.text.java.hover; @@ -73,6 +74,7 @@ import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; @@ -88,6 +90,7 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; @@ -759,8 +762,18 @@ private static String getInfoText(IJavaElement element, ITypeRoot editorInputElement, IRegion hoverRegion, boolean allowImage) { long flags= getHeaderFlags(element); - StringBuffer label= new StringBuffer(JavaElementLinks.getElementLabel(element, flags)); + boolean haveSource= editorInputElement instanceof ICompilationUnit; + ASTNode node= haveSource ? getHoveredASTNode(editorInputElement, hoverRegion) : null; + IBinding binding= getHoverBinding(element, node); + + StringBuffer label; + if (binding != null) { + label= new StringBuffer(JavaElementLinks.getBindingLabel(binding, element, flags, haveSource)); + } else { + label= new StringBuffer(JavaElementLinks.getElementLabel(element, flags)); + } + if (element.getElementType() == IJavaElement.FIELD) { String constantValue= getConstantValue((IField) element, editorInputElement, hoverRegion); if (constantValue != null) { @@ -777,6 +790,37 @@ // } return getImageAndLabel(element, allowImage, label.toString()); + } + + /** + * Try to acquire a binding corresponding to the given element + * for more precise information about (type) annotations. + * + * Currently this lookup is only enabled when null-annotations are enabled for the project. + * + * @param element the element being rendered + * @param node the AST node corresponding to the given element, or null, if no AST node is available. + * @return either a binding corresponding to the given element or null. + */ + public static IBinding getHoverBinding(IJavaElement element, ASTNode node) { + + if (element.getJavaProject().getOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, true).equals(JavaCore.ENABLED)) { + if (node == null) { + if (element instanceof ISourceReference) { + ASTParser p= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL); + p.setProject(element.getJavaProject()); + p.setBindingsRecovery(true); + try { + return p.createBindings(new IJavaElement[] { element }, null)[0]; + } catch (OperationCanceledException e) { + return null; + } + } + } else { + return resolveBinding(node); + } + } + return null; } private static String getImageURL(IJavaElement element) { @@ -1080,6 +1124,8 @@ return ((SuperConstructorInvocation) node).resolveConstructorBinding(); } else if (node instanceof ConstructorInvocation) { return ((ConstructorInvocation) node).resolveConstructorBinding(); + } else if (node instanceof LambdaExpression) { + return ((LambdaExpression) node).resolveMethodBinding(); } else { return null; } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java new file mode 100644 index 0000000..cc8651e --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java @@ -0,0 +1,771 @@ +/******************************************************************************* + * Copyright (c) 2015 GK Software AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stephan Herrmann - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport; + +import static org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.JAVADOC_SCHEME; +import static org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.createHeaderLink; +import static org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.createURI; + +import java.net.URISyntaxException; + +import org.eclipse.jface.viewers.StyledString; + +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.IAnnotationBinding; +import org.eclipse.jdt.core.dom.IAnonymousImplementation; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMemberValuePairBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.IPackageBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; + +import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; +import org.eclipse.jdt.internal.corext.util.Messages; + +import org.eclipse.jdt.ui.JavaElementLabels; + +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jdt.internal.ui.JavaUIMessages; +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.JavaElementLinkedLabelComposer; + +public class BindingLinkedLabelComposer extends JavaElementLinkedLabelComposer { + + private static final String PARAMETER_ELLIPSIS_LABEL= "(...)"; //$NON-NLS-1$ + private static final String ANON_TYPE_TAIL= "() {...}"; //$NON-NLS-1$ + private static final String LAMBDA_LABEL= "() -> {...}"; //$NON-NLS-1$ + private static final String MISSING_LABEL= "MISSING"; //$NON-NLS-1$ + + private static final String CLINIT_NAME= ""; //$NON-NLS-1$ + private static final String INIT_NAME= ""; //$NON-NLS-1$ + + private static final long M_ALL_QUALIFIED= JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_FULLY_QUALIFIED; + + private static final long IS_POST_QUALIFICATION= 1L << 63; + + private IJavaElement fEnclosingElement; + private boolean fIsFromSource; + + public BindingLinkedLabelComposer(IJavaElement enclosingElement, StringBuffer buffer, boolean isFromSource) { + super(enclosingElement, buffer); + fEnclosingElement= enclosingElement; + fIsFromSource= isFromSource; + } + + /** + *

Supported flags: + *

    + *
  • {@link JavaElementLabels#ALL_FULLY_QUALIFIED} (set)
  • + *
  • {@link JavaElementLabels#M_PRE_RETURNTYPE}
  • + *
  • {@link JavaElementLabels#M_PARAMETER_ANNOTATIONS}
  • + *
  • {@link JavaElementLabels#M_PARAMETER_TYPES}
  • + *
  • {@link JavaElementLabels#M_PARAMETER_NAMES}
  • + *
  • {@link JavaElementLabels#M_EXCEPTIONS}
  • + *
  • {@link JavaElementLabels#M_PRE_TYPE_PARAMETERS}
  • + *
  • {@link JavaElementLabels#F_PRE_TYPE_SIGNATURE}
  • + *
  • {@link JavaElementLabels#T_TYPE_PARAMETERS}
  • + *
  • {@link JavaElementLabels#USE_RESOLVED}
  • + *
  • {@link JavaElementLabels#M_POST_QUALIFIED}
  • + *
  • {@link JavaElementLabels#F_POST_QUALIFIED}
  • + *
  • {@link JavaElementLabels#T_POST_QUALIFIED}
  • + *
  • {@link JavaElementLabels#TP_POST_QUALIFIED}
  • + *
+ * @param binding a binding to be rendered + * @param flags rendering flags, see above for supported values. + */ + public void appendBindingLabel(IBinding binding, long flags) { + switch (binding.getKind()) { + case IBinding.METHOD: + appendMethodBindingLabel((IMethodBinding) binding, flags); + return; + case IBinding.TYPE: + appendTypeBindingLabel((ITypeBinding) binding, flags); + return; + case IBinding.VARIABLE: + appendVariableLabel((IVariableBinding) binding, flags); + return; + case IBinding.PACKAGE: + appendPackageLabel((IPackageBinding) binding, flags); + break; + case IBinding.ANNOTATION: + case IBinding.MEMBER_VALUE_PAIR: + // not used for hovers + } + } + + private void appendMethodBindingLabel(IMethodBinding method, long flags) { + long qualificationFlags = flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED); + IMethodBinding origMethod= method; + if (method instanceof IAnonymousImplementation) { + if (isEnclosingElement(method.getJavaElement())) { + // don't render top-level as lambda + if (getFlag(flags, JavaElementLabels.T_POST_QUALIFIED)) + flags |= JavaElementLabels.M_POST_QUALIFIED; + method= method.getMethodDeclaration(); // for the main part show the SAM instead + } else { + appendLambdaLabel((IAnonymousImplementation) method, flags); + return; + } + } + + if (!getFlag(flags, JavaElementLabels.USE_RESOLVED)) { + method= method.getMethodDeclaration(); + } + if (fIsFromSource) { + flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; + } + + // type parameters + if (getFlag(flags, JavaElementLabels.M_PRE_TYPE_PARAMETERS)) { + ITypeBinding[] typeParameters= method.getTypeParameters(); + if (typeParameters.length > 0) { + appendTypeArgumentsBindingLabel(typeParameters, null, flags); + fBuffer.append(' '); + } + } + + // return type + if (getFlag(flags, JavaElementLabels.M_PRE_RETURNTYPE) && !method.isConstructor()) { + appendTypeBindingLabel(method.getReturnType(), flags); + fBuffer.append(' '); + } + + // qualification + if (getFlag(flags, JavaElementLabels.M_FULLY_QUALIFIED)) { + appendTypeBindingLabel(method.getDeclaringClass(), qualificationFlags); + fBuffer.append('.'); + } + + boolean isInitializer= method.getName().equals(CLINIT_NAME) || method.getName().equals(INIT_NAME); + if (isInitializer) { + fBuffer.append(JavaUIMessages.JavaElementLabels_initializer); + } else { + appendNameLink(method, origMethod); + } + + if (!isInitializer) { + // constructor type arguments + if (getFlag(flags, JavaElementLabels.T_TYPE_PARAMETERS) && method.isConstructor()) { + appendTypeArgumentsBindingLabel(method.getTypeArguments(), null, flags); + } + + // parameters + fBuffer.append('('); + if (getFlag(flags, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES)) { + ITypeBinding[] types= null; + int nParams= 0; + boolean renderVarargs= false; + boolean isPolymorphic= false; + IMethod iMethod= getIMethod(method); + if (getFlag(flags, JavaElementLabels.M_PARAMETER_TYPES)) { + types= method.getParameterTypes(); + nParams= types.length; + renderVarargs= method.isVarargs(); + // retrieval of flag isPolymorphic uses strategy from JavaElementLabelComposer: + if (getFlag(flags, JavaElementLabels.USE_RESOLVED) + && iMethod.isResolved() + && iMethod.getParameterTypes().length == 1 + && JavaModelUtil.isPolymorphicSignature(iMethod)) { + renderVarargs= false; + isPolymorphic= true; + } + } + String[] names= null; + if (getFlag(flags, JavaElementLabels.M_PARAMETER_NAMES)) { + // mostly from JavaElementLabelComposer: + try { + names= iMethod.getParameterNames(); + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + if (isPolymorphic) { + // handled specially below + } else if (types == null) { + nParams= names.length; + } else { // types != null + if (nParams != names.length) { + if (types.length > names.length) { + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=99137 + nParams= names.length; + ITypeBinding[] typesWithoutSyntheticParams= new ITypeBinding[nParams]; + System.arraycopy(types, types.length - nParams, typesWithoutSyntheticParams, 0, nParams); + types= typesWithoutSyntheticParams; + } else { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=101029 + // JavaPlugin.logErrorMessage("JavaElementLabels: Number of param types(" + nParams + ") != number of names(" + names.length + "): " + method.getElementName()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + names= null; // no names rendered + } + } + } + } + IAnnotationBinding[][] parameterAnnotations= null; + if (nParams > 0 && getFlag(flags, JavaElementLabels.M_PARAMETER_ANNOTATIONS)) { + parameterAnnotations= new IAnnotationBinding[nParams][]; + for (int i= 0; i < nParams; i++) { + parameterAnnotations[i]= method.getParameterAnnotations(i); + } + } + + for (int i= 0; i < nParams; i++) { + if (i > 0) + fBuffer.append(JavaElementLabels.COMMA_STRING); + if (parameterAnnotations != null && i < parameterAnnotations.length) { + appendAnnotationLabels(parameterAnnotations[i], flags); + } + + if (types != null) { + ITypeBinding paramSig= types[i]; + if (renderVarargs && (i == nParams - 1)) { + int newDim= paramSig.getDimensions() - 1; + appendTypeBindingLabel(paramSig.getElementType(), flags); + for (int k= 0; k < newDim; k++) { + fBuffer.append('[').append(']'); + } + fBuffer.append(JavaElementLabels.ELLIPSIS_STRING); + } else { + appendTypeBindingLabel(paramSig, flags); + } + } + if (names != null) { + if (types != null) { + fBuffer.append(' '); + } + if (isPolymorphic) { + fBuffer.append(names[0] + i); + } else { + fBuffer.append(names[i]); + } + } + } + } else { + if (method.getParameterTypes().length > 0) { + fBuffer.append(JavaElementLabels.ELLIPSIS_STRING); + } + } + fBuffer.append(')'); + + + if (getFlag(flags, JavaElementLabels.M_EXCEPTIONS)) { + ITypeBinding[] types= method.getExceptionTypes(); + if (types.length > 0) { + fBuffer.append(" throws "); //$NON-NLS-1$ + for (int i= 0; i < types.length; i++) { + if (i > 0) + fBuffer.append(JavaElementLabels.COMMA_STRING); + appendTypeBindingLabel(types[i], flags); + } + } + } + + if (getFlag(flags, JavaElementLabels.M_APP_TYPE_PARAMETERS)) { + int offset= fBuffer.length(); + ITypeBinding[] typeParameters= method.isParameterizedMethod() + ? method.getTypeArguments() + : method.getTypeParameters(); + appendTypeArgumentsBindingLabel(typeParameters, String.valueOf(' '), flags); + if (getFlag(flags, JavaElementLabels.COLORIZE) && offset != fBuffer.length()) { + fBuffer.setStyle(offset, fBuffer.length() - offset, StyledString.DECORATIONS_STYLER); + } + } + } + + // post qualification + if (getFlag(flags, JavaElementLabels.M_POST_QUALIFIED)) { + fBuffer.append(JavaElementLabels.CONCAT_STRING); + if (origMethod instanceof IAnonymousImplementation && origMethod != method) { + // show lambda as qualification of a SAM + appendLambdaLabel((IAnonymousImplementation) origMethod, qualificationFlags | JavaElementLabels.ALL_FULLY_QUALIFIED); + fBuffer.append(' '); + appendTypeBindingLabel(origMethod.getMethodDeclaration().getDeclaringClass(), M_ALL_QUALIFIED | IS_POST_QUALIFICATION); + } else { + appendTypeBindingLabel(method.getDeclaringClass(), getPostQualificationFlags(flags)); + } + } + } + + private void appendVariableLabel(IVariableBinding variable, long flags) { + long qualificationFlags = flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED); + if (fIsFromSource) { + flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; + } + if (getFlag(flags, JavaElementLabels.F_PRE_TYPE_SIGNATURE) && !Flags.isEnum(variable.getModifiers())) { + appendTypeBindingLabel(variable.getType(), flags); + fBuffer.append(' '); + } + + // qualification + if (getFlag(flags, JavaElementLabels.F_FULLY_QUALIFIED)) { + appendVariableQualification(variable, qualificationFlags); + fBuffer.append('.'); + } + if (variable.isField()) { + appendNameLink(variable, variable); + } else { + fBuffer.append(variable.getName()); + } + + if (getFlag(flags, JavaElementLabels.F_POST_QUALIFIED)) { + fBuffer.append(JavaElementLabels.CONCAT_STRING); + appendVariableQualification(variable, getPostQualificationFlags(qualificationFlags)); + } + } + + private void appendVariableQualification(IVariableBinding variable, long flags) { + if (variable.isField()) { + appendTypeBindingLabel(variable.getDeclaringClass(), flags); + } else { + IMethodBinding declaringMethod= variable.getDeclaringMethod(); + if (declaringMethod != null) { + if (declaringMethod instanceof IAnonymousImplementation) { + appendLambdaLabel((IAnonymousImplementation) declaringMethod, flags); + fBuffer.append(' '); + appendMethodBindingLabel(declaringMethod.getMethodDeclaration(), (flags & QUALIFIER_FLAGS) | M_ALL_QUALIFIED); + } else if (variable.isParameter()) { + appendMethodBindingLabel(declaringMethod, flags | M_ALL_QUALIFIED); + } + } else { + fBuffer.append(MISSING_LABEL); + } + } + } + + private void appendLambdaLabel(IAnonymousImplementation lambdaBinding, long flags) { + long qualificationFlags = flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED); + if (fIsFromSource) { + flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; + } + if (getFlag(flags, JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.T_FULLY_QUALIFIED)) { + appendBindingLabel(lambdaBinding.getDeclaringMember(), qualificationFlags); + fBuffer.append('.'); + fBuffer.append(LAMBDA_LABEL); + } else { + IMethodBinding sam= ((IMethodBinding) lambdaBinding).getMethodDeclaration(); + appendMethodBindingLabel(sam, flags & ~JavaElementLabels.ALL_POST_QUALIFIED); + } + if (getFlag(flags, JavaElementLabels.M_POST_QUALIFIED|JavaElementLabels.T_POST_QUALIFIED)) { + fBuffer.append(JavaElementLabels.CONCAT_STRING); + qualificationFlags |= JavaElementLabels.ALL_FULLY_QUALIFIED; + appendBindingLabel(lambdaBinding.getDeclaringMember(), qualificationFlags); + fBuffer.append('.'); + fBuffer.append(LAMBDA_LABEL); + fBuffer.append(' '); + appendTypeBindingLabel(((IMethodBinding) lambdaBinding).getDeclaringClass(), flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED) | IS_POST_QUALIFICATION); + } + } + + private void appendTypeBindingLabel(ITypeBinding typeBinding, long flags) { + long typeRefFlags= flags & ~JavaElementLabels.ALL_POST_QUALIFIED; + if (fIsFromSource) { + typeRefFlags &= ~JavaElementLabels.ALL_FULLY_QUALIFIED; + } + // qualification of anonymous (class or lambda): + if (typeBinding instanceof IAnonymousImplementation + && getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED)) + { + long anonFlags= getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED) + ? flags | JavaElementLabels.ALL_FULLY_QUALIFIED + : flags; + appendBindingLabel(((IAnonymousImplementation)typeBinding).getDeclaringMember(), anonFlags); + fBuffer.append('.'); + flags &= ~(JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED); // qualification is done + } + if (!typeBinding.isArray()) { // different textual order for type annotations on array types + appendAnnotationLabels(typeBinding.getTypeAnnotations(), typeRefFlags); + } + if (typeBinding.isPrimitive()) { + fBuffer.append(typeBinding.getName()); + } else if (typeBinding.isArray()) { + appendTypeBindingLabel(typeBinding.getElementType(), flags); + IAnnotationBinding[] typeAnnotations= typeBinding.getTypeAnnotations(); + if (typeAnnotations.length > 0) { + fBuffer.append(' '); + appendAnnotationLabels(typeAnnotations, typeRefFlags); + } + for (int dim= typeBinding.getDimensions(); dim > 0; dim--) { + fBuffer.append('[').append(']'); + } + } else if (typeBinding.isClass() || typeBinding.isInterface() || typeBinding.isEnum()) { + fBuffer.append(getTypeLink(typeBinding, flags)); + if (getFlag(flags, JavaElementLabels.T_TYPE_PARAMETERS)) { + ITypeBinding[] typeArguments= typeBinding.getTypeArguments(); + if (typeArguments.length > 0) { + appendTypeArgumentsBindingLabel(typeArguments, null, typeRefFlags); + } else { + ITypeBinding[] typeParameters= typeBinding.getTypeParameters(); + appendTypeArgumentsBindingLabel(typeParameters, null, typeRefFlags); + } + } + } else if (typeBinding.isParameterizedType()) { + fBuffer.append(getTypeLink(typeBinding.getTypeDeclaration(), flags)); + fBuffer.append(getLT()); + ITypeBinding[] typeArguments= typeBinding.getTypeArguments(); + for (int i= 0; i < typeArguments.length; i++) { + if (i > 0) + fBuffer.append(JavaElementLabels.COMMA_STRING); + appendTypeBindingLabel(typeArguments[i], typeRefFlags); + } + fBuffer.append(getGT()); + } else if (typeBinding.isTypeVariable()) { + appendNameLink(typeBinding, typeBinding); + // heuristically include type bounds only when not composing a post-qualification + if (!getFlag(flags, IS_POST_QUALIFICATION)) { + ITypeBinding[] bounds= typeBinding.getTypeBounds(); + if (hasRelevantBound(bounds)) { + fBuffer.append(" extends "); //$NON-NLS-1$ + for (int i= 0; i < bounds.length; i++) { + if (i > 0) + fBuffer.append(" & "); //$NON-NLS-1$ + appendTypeBindingLabel(bounds[i], typeRefFlags | JavaElementLabels.T_TYPE_PARAMETERS); + } + } + } + // post qualification + if (getFlag(flags, JavaElementLabels.TP_POST_QUALIFIED)) { + fBuffer.append(JavaElementLabels.CONCAT_STRING); + if (typeBinding.getDeclaringClass() != null) + appendTypeBindingLabel(typeBinding.getDeclaringClass(), getPostQualificationFlags(flags)); + else + appendMethodBindingLabel(typeBinding.getDeclaringMethod(), getPostQualificationFlags(flags)); + } + return; + } else if (typeBinding.isAnnotation()) { + fBuffer.append(getTypeLink(typeBinding, flags)); + } else if (typeBinding.isWildcardType()) { + ITypeBinding bound= typeBinding.getBound(); + if (bound == null || bound.getSuperclass() == null) { // no relevant bound / only j.l.Object + fBuffer.append('?'); + } else { + if (typeBinding.isUpperbound()) + fBuffer.append("? extends "); //$NON-NLS-1$ + else + fBuffer.append("? super "); //$NON-NLS-1$ + appendTypeBindingLabel(bound, typeRefFlags); + } + } else if (typeBinding.isCapture()) { + appendTypeBindingLabel(typeBinding.getWildcard(), flags); + } + // post qualification + if (getFlag(flags, JavaElementLabels.T_POST_QUALIFIED)) { + IBinding declaringBinding= null; + // search the innermost declaring thing: + if (typeBinding instanceof IAnonymousImplementation) { + declaringBinding= ((IAnonymousImplementation) typeBinding).getDeclaringMember(); + } else { + declaringBinding= typeBinding.getDeclaringMethod(); + if (declaringBinding == null) + declaringBinding= typeBinding.getDeclaringClass(); + if (declaringBinding == null) + declaringBinding= typeBinding.getPackage(); + } + if (declaringBinding != null) { + fBuffer.append(JavaElementLabels.CONCAT_STRING); + // heuristics: JavaElementsLabelComposer does not include method parameters in post qualification of local type: + appendBindingLabel(declaringBinding, getPostQualificationFlags(flags) & ~JavaElementLabels.M_PARAMETER_TYPES); + } + } + } + + private void appendTypeArgumentsBindingLabel(ITypeBinding[] parameters, String separator, long flags) { + if (parameters.length > 0) { + if (separator != null) + fBuffer.append(separator); + fBuffer.append(getLT()); + for (int i = 0; i < parameters.length; i++) { + if (i > 0) + fBuffer.append(JavaElementLabels.COMMA_STRING); + appendTypeBindingLabel(parameters[i], flags); + } + fBuffer.append(getGT()); + } + } + + private void appendAnnotationLabels(IAnnotationBinding[] annotationBindings, long flags) { + for (int i= 0; i < annotationBindings.length; i++) { + appendAnnotationLabel(annotationBindings[i], flags); + fBuffer.append(' '); + } + } + + private void appendAnnotationLabel(IAnnotationBinding annotation, long flags) { + fBuffer.append('@'); + appendTypeBindingLabel(annotation.getAnnotationType(), flags); + IMemberValuePairBinding[] memberValuePairs= annotation.getDeclaredMemberValuePairs(); + if (memberValuePairs.length == 0) + return; + if (fIsFromSource) { + flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; + } + fBuffer.append('('); + for (int i= 0; i < memberValuePairs.length; i++) { + if (i > 0) + fBuffer.append(JavaElementLabels.COMMA_STRING); + IMemberValuePairBinding memberValuePair= memberValuePairs[i]; + fBuffer.append(getMemberName(fEnclosingElement, annotation.getName(), memberValuePair.getName())); + fBuffer.append('='); + long valueFlags= flags & ~(JavaElementLabels.F_PRE_TYPE_SIGNATURE|JavaElementLabels.M_PRE_RETURNTYPE|JavaElementLabels.ALL_POST_QUALIFIED); + appendAnnotationValue(annotation, memberValuePair.getValue(), valueFlags); + } + fBuffer.append(')'); + } + + private void appendAnnotationValue(IAnnotationBinding annotation, Object value, long flags) { + // Note: To be bug-compatible with Javadoc from Java 5/6/7, we currently don't escape HTML tags in String-valued annotations. + if (value instanceof Object[]) { + fBuffer.append('{'); + Object[] values= (Object[]) value; + for (int j= 0; j < values.length; j++) { + if (j > 0) + fBuffer.append(JavaElementLabels.COMMA_STRING); + value= values[j]; + appendAnnotationValue(annotation, value, flags); + } + fBuffer.append('}'); + } else { + if (value instanceof ITypeBinding) { + appendTypeBindingLabel((ITypeBinding) value, flags); + fBuffer.append(".class"); //$NON-NLS-1$ + } else if (value instanceof String) { + fBuffer.append(htmlEscape(ASTNodes.getEscapedStringLiteral((String) value))); + } else if (value instanceof IVariableBinding) { + appendVariableLabel((IVariableBinding) value, flags); + } else if (value instanceof IAnnotationBinding) { + appendAnnotationLabel((IAnnotationBinding) value, flags); + } else if (value instanceof Character) { + fBuffer.append(ASTNodes.getEscapedCharacterLiteral(((Character) value).charValue())); + } else { // other primitive literals + fBuffer.append(String.valueOf(value)); + } + } + } + + private void appendPackageLabel(IPackageBinding binding, long flags) { + appendAnnotationLabels(binding.getAnnotations(), flags); + String qualifiedName= binding.getName(); + if (qualifiedName.length() > 0) { + IJavaElement packageElement= binding.getJavaElement(); + try { + String uri= createURI(JAVADOC_SCHEME, packageElement); + fBuffer.append(createHeaderLink(uri, qualifiedName)); + } catch (URISyntaxException e) { + JavaPlugin.log(e); + fBuffer.append(qualifiedName); + } + } else { + fBuffer.append("(default package)"); //$NON-NLS-1$ + } + } + + // consider only relevant bounds / ignore j.l.Object + private boolean hasRelevantBound(ITypeBinding[] bounds) { + if (bounds != null) { + for (int i= 0; i < bounds.length; i++) + if (bounds[i].isInterface() || bounds[i].getSuperclass() != null) + return true; + } + return false; + } + + private long getPostQualificationFlags(long flags) { + flags |= JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES | IS_POST_QUALIFICATION; + flags &= ~JavaElementLabels.ALL_POST_QUALIFIED; + flags &= ~(JavaElementLabels.T_TYPE_PARAMETERS | + JavaElementLabels.M_PRE_TYPE_PARAMETERS | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_NAMES); + return flags; + } + + // applicable for types, methods & fields + private void appendNameLink(IBinding binding, IBinding origBinding) { + try { + if (!isEnclosingElement(origBinding.getJavaElement())) { + IJavaElement element= binding.getJavaElement(); + String uri= createURI(JAVADOC_SCHEME, element); + String title= getLinkTitle(binding); + if (title != null) { + title= Messages.format(JavaUIMessages.JavaElementLinks_title, title); + } else { + title= ""; //$NON-NLS-1$ + } + fBuffer.append(createHeaderLink(uri, binding.getName(), title)); + return; + } + } catch (URISyntaxException e) { + JavaPlugin.log(e); + } + fBuffer.append(binding.getName()); + } + + private String getLinkTitle(IBinding binding) { + if (binding instanceof ITypeBinding) { + IMethodBinding declaringMethod= ((ITypeBinding) binding).getDeclaringMethod(); + String title= null; + ITypeBinding declaringClass= null; + if (declaringMethod != null) { + title= '.'+declaringMethod.getName()+PARAMETER_ELLIPSIS_LABEL; + declaringClass= declaringMethod.getDeclaringClass(); + } else { + declaringClass= ((ITypeBinding) binding).getDeclaringClass(); + } + if (declaringClass != null) { + String typeName= getTypeName(declaringClass, JavaElementLabels.ALL_FULLY_QUALIFIED); + return title != null ? typeName+title : typeName; + } + } else if (binding instanceof IMethodBinding) { + return getTypeName(((IMethodBinding) binding).getDeclaringClass(), JavaElementLabels.ALL_FULLY_QUALIFIED); + } else if (binding instanceof IVariableBinding) { + return getTypeName(((IVariableBinding) binding).getDeclaringClass(), JavaElementLabels.ALL_FULLY_QUALIFIED); + } + return null; + } + + private String getTypeLink(ITypeBinding typeBinding, long flags) { + if (isEnclosingElement(typeBinding.getJavaElement())) { + return getTypeName(typeBinding, flags); + } + typeBinding= typeBinding.getTypeDeclaration(); + String typeName = getTypeName(typeBinding, flags); + String qualifiedName = getTypeName(typeBinding, JavaElementLabels.T_FULLY_QUALIFIED); + String title= ""; //$NON-NLS-1$ + int qualifierLength= qualifiedName.length() - typeName.length() - 1; + if (qualifierLength > 0) { + if (qualifiedName.endsWith(typeName)) { + title= qualifiedName.substring(0, qualifierLength); + title= Messages.format(JavaUIMessages.JavaElementLinks_title, title); + } else { + title= qualifiedName; // Not expected. Just show the whole qualifiedName. + } + } + + try { + String uri= createURI(JAVADOC_SCHEME, fEnclosingElement, qualifiedName, null, null); + return createHeaderLink(uri, typeName, title); + } catch (URISyntaxException e) { + JavaPlugin.log(e); + return typeName; + } + } + + private String getTypeName(ITypeBinding typeBinding, long flags) { + if (typeBinding.isLocal()) { + StringBuilder buf= new StringBuilder(); + IMethodBinding declaringMethod= typeBinding.getDeclaringMethod(); + if (getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED|JavaElementLabels.T_CONTAINER_QUALIFIED)) { + if (declaringMethod != null) { + buf.append(getTypeName(declaringMethod.getDeclaringClass(), flags)); + buf.append('.'); + buf.append(declaringMethod.getName()); + if (declaringMethod.getParameterTypes().length > 0) { + buf.append(PARAMETER_ELLIPSIS_LABEL); + } else { + buf.append("()"); //$NON-NLS-1$ + } + } else { + buf.append(getTypeName(typeBinding.getDeclaringClass(), flags)); + if (typeBinding instanceof IAnonymousImplementation) { + buf.append('.'); + IBinding declaringMember= ((IAnonymousImplementation) typeBinding).getDeclaringMember(); + if (declaringMember instanceof ITypeBinding) { + buf.append(getTypeName((ITypeBinding) declaringMember, flags)); + } else { // field or method + String name= declaringMember.getName(); + if (name.equals(CLINIT_NAME) || name.endsWith(INIT_NAME)) + buf.append(JavaUIMessages.JavaElementLabels_initializer); + else + buf.append(name); + } + } + } + buf.append('.'); + } + if (typeBinding.isAnonymous()) { + buf.append("new "); //$NON-NLS-1$ + ITypeBinding[] interfaces= typeBinding.getInterfaces(); + if (interfaces.length > 0) { + buf.append(interfaces[0].getName()); + } else if (typeBinding.getSuperclass() != null){ + buf.append(typeBinding.getSuperclass().getName()); + } else { + buf.append(MISSING_LABEL); + } + buf.append(ANON_TYPE_TAIL); + } else { + buf.append(typeBinding.getName()); + } + return buf.toString(); + } + if (getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED)) { + return typeBinding.getQualifiedName(); + } else if (getFlag(flags, JavaElementLabels.T_CONTAINER_QUALIFIED)) { + ITypeBinding declaringClass= typeBinding.getDeclaringClass(); + if (declaringClass != null) { + StringBuilder buf= new StringBuilder(getTypeName(declaringClass, flags)); + buf.append('.'); + buf.append(typeBinding.getName()); + return buf.toString(); + } + } + return typeBinding.getName(); + } + + private IMethod getIMethod(IMethodBinding method) { + IMethod iMethod= (IMethod) method.getJavaElement(); + if (isEnclosingElement(iMethod)) { + return (IMethod) fEnclosingElement; // benefit from better details on LambdaMethod + } + return iMethod; + } + + private boolean isEnclosingElement(IJavaElement element) { + if (element.equals(fEnclosingElement)) + return true; + String enclosingKey= null; + // unify different representations of a lambda: LambdaMethod, LambdaExpression, IMethod (SAM) + switch (fEnclosingElement.getElementType()) { + case IJavaElement.TYPE: + if (((IType)fEnclosingElement).isLambda() && element.getElementType() == IJavaElement.METHOD) { + // navigate from LambdaExpression to LambdaMethod to be comparable to IMethod: + try { + IJavaElement[] children= ((IType) fEnclosingElement).getChildren(); + if (children != null && children.length == 1) { + if (children[0].getElementType() == IJavaElement.METHOD) { + enclosingKey= ((IMethod) children[0]).getKey(); + } + } + } catch (JavaModelException e) { + // continue without key + } + } else { + enclosingKey= ((IType) fEnclosingElement).getKey(); + } + break; + case IJavaElement.METHOD: + enclosingKey= ((IMethod) fEnclosingElement).getKey(); + break; + } + if (enclosingKey != null) { + switch (element.getElementType()) { + case IJavaElement.TYPE: + return enclosingKey.equals(((IType) element).getKey()); + case IJavaElement.METHOD: + return enclosingKey.equals(((IMethod) element).getKey()); + } + } + return false; + } + + private String htmlEscape(String escaped) { + return escaped.replace("&", "&").replace("<", "<").replace(">", ">");//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + } +} diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java index 02b7554..d38cc44 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java @@ -196,7 +196,7 @@ } - private final static long QUALIFIER_FLAGS= JavaElementLabels.P_COMPRESSED | JavaElementLabels.USE_RESOLVED; + final static long QUALIFIER_FLAGS= JavaElementLabels.P_COMPRESSED | JavaElementLabels.USE_RESOLVED; private static final Styler QUALIFIER_STYLE= StyledString.QUALIFIER_STYLER; private static final Styler COUNTER_STYLE= StyledString.COUNTER_STYLER; @@ -219,7 +219,7 @@ protected final FlexibleBuffer fBuffer; - private static final boolean getFlag(long flags, long flag) { + protected static final boolean getFlag(long flags, long flag) { return (flags & flag) != 0; } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java index 43add76..c944125 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2014 IBM Corporation and others. + * Copyright (c) 2008, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view *******************************************************************************/ package org.eclipse.jdt.internal.ui.viewsupport; @@ -39,6 +40,7 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.eclipse.jdt.internal.corext.util.Messages; @@ -102,7 +104,7 @@ void handleTextSet(); } - private static final class JavaElementLinkedLabelComposer extends JavaElementLabelComposer { + static class JavaElementLinkedLabelComposer extends JavaElementLabelComposer { private final IJavaElement fElement; public JavaElementLinkedLabelComposer(IJavaElement member, StringBuffer buf) { @@ -665,7 +667,7 @@ * @return the HTML link * @since 3.10 */ - private static String createHeaderLink(String uri, String label, String title) { + public static String createHeaderLink(String uri, String label, String title) { if (title.length() > 0) { title= " title='" + title + "'"; //$NON-NLS-1$ //$NON-NLS-2$ } @@ -708,4 +710,27 @@ return label.replaceAll("<", "<").replaceAll(">", ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } + + /** + * Returns the label for a binding with the flags as defined by {@link JavaElementLabels}. + * Referenced element names in the label are rendered as header links. + * + * @param binding the binding to render + * @param element the corresponding Java element, used for javadoc hyperlinks + * @param flags the rendering flags + * @param haveSource true when looking at an ICompilationUnit which enables the use of short type names + * @return the label of the binding + * @since 3.11 + */ + public static String getBindingLabel(IBinding binding, IJavaElement element, long flags, boolean haveSource) { + StringBuffer buf= new StringBuffer(); + + if (!Strings.USE_TEXT_PROCESSOR) { + new BindingLinkedLabelComposer(element, buf, haveSource).appendBindingLabel(binding, flags); + return Strings.markJavaElementLabelLTR(buf.toString()); + } else { + String label= JavaElementLabels.getElementLabel(element, flags); + return label.replaceAll("<", "<").replaceAll(">", ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + } }