diff --git a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java new file mode 100644 index 0000000000..06d520178b --- /dev/null +++ b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java @@ -0,0 +1,465 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - Implementation + *******************************************************************************/ +package org.eclipse.jdt.debug.tests.console; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.debug.testplugin.JavaProjectHelper; +import org.eclipse.jdt.debug.tests.TestUtil; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.console.IHyperlink; +import org.eclipse.ui.texteditor.ITextEditor; + +public class JavaStackTraceAmbiguityTest extends AbstractJavaStackTraceConsoleTest { + + public JavaStackTraceAmbiguityTest(String name) { + super(name); + } + + public void testLinkNavigationTrueForNoParameters() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tes3() line: 31"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Zero_Parameter\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForSingleParameter() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tes3(int) line: 31"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Single_Parameter\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForMultipleParameters() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tes3(int, String) line: 31"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Multiple_Parameter\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForMultipleParameters_Three() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tes3(int, String,Sample) line: 34"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Multiple_Parameter_Three\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForOneNormalAndOneFullyQualifiedArguments() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tesComplex(String[], URL[]) line: 37"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_One_normal_&_One_fully_qualified\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForBothFullyQualifiedArguments() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tesComplex(Object, URL[]) line: 37"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_both_fully_qualified\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForSingleVarArgs() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.testMethod(Object...) line: 43"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_oneVarArgs\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForSingleVarArgsAndOneNormal() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.testMethod(Object,Object...) line: 40"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_oneNormal&oneVarArgs\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForGenerics() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("SampleGenerics(SampleGenerics).remove() line: 25"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.print(\"EXPECTED_GENERICS\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForInnerClass() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("InnerClassTest$innerL1.check() line: 23"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"EXPECTED_INNERCLASS\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForInnerClassMultilevel() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("InnerClassTest$innerL1$innerL2.check2() line: 27"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"EXPECTED_INNER-INNER_CLASS\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForInnerClassParameters() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("MyScheduledExecutor(Worker).doWork(MyScheduledExecutor$Wor) line: 20"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Result\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForInnerClassMultiParameters() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("MyScheduledExecutor(Worker).doWorkParams(MyScheduledExecutor$Wor,MyScheduledExecutor$Ran) line: 23"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Result\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForLinksWithNoProperMethodSignature() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.testBlank(Sample.java:40)"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 1, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_No_Signature\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForLinksWithVarArgs() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.tes2(Object, Object...) line: 40 "); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_VarArgs\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + private void waitForJobs() throws Exception { + TestUtil.waitForJobs(getName(), 250, 10_000); + } + + private static String getSelectedText(IEditorPart editor) { + ITextEditor textEditor = (ITextEditor) editor; + ISelectionProvider selectionProvider = textEditor.getSelectionProvider(); + ISelection selection = selectionProvider.getSelection(); + ITextSelection textSelection = (ITextSelection) selection; + String selectedText = textSelection.getText(); + return selectedText; + } + + private IEditorPart waitForEditorOpen() throws Exception { + waitForJobs(); + IEditorPart[] editor = new IEditorPart[1]; + sync(() -> editor[0] = getActivePage().getActiveEditor()); + long timeout = 30_000; + long start = System.currentTimeMillis(); + while (editor[0] == null && System.currentTimeMillis() - start < timeout) { + waitForJobs(); + sync(() -> editor[0] = getActivePage().getActiveEditor()); + } + if (editor[0] == null) { + throw new AssertionError("Timeout occurred while waiting for editor to open"); + } + return editor[0]; + } +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java new file mode 100644 index 0000000000..24bfe2e7a1 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * Test class + */ +class InnerClassTest { + public static void main(String[] ar) { + + } + class innerL1 { + void check2() { + System.out.println("EXPECTED_INNERCLASS"); + } + class innerL2 { + void check4() { + System.out.println("EXPECTED_INNER-INNER_CLASS"); + } + } + } +} + \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java new file mode 100644 index 0000000000..9db8c2a569 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +public class MyScheduledExecutor { + public void executeTask() { + Wor worker = new Wor(); + } + class Worker { + public void doWork2(Wor w) { + + } + public void doWorkParams(Wor w,Ran r) { + System.out.println("Expected_Result"); + } + } + class Wor { + + } + class Ran { + + } +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/SampleGenerics.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/SampleGenerics.java new file mode 100644 index 0000000000..a06e4d6414 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/SampleGenerics.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * Test class + */ +class Node { + E data; + Node next; + Node(E data){}; +} +public class SampleGenerics { + private Node head; + public E remove() { + System.out.print("EXPECTED_GENERICS"); + return null; + } + +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java new file mode 100644 index 0000000000..2af8104b36 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +public class MyScheduledExecutor { + public void executeTask() { + Wor worker = new Wor(); + } + class Worker { + public void doWork(Wor w) { + System.out.println("Expected_Result"); + } + } + class Wor { + + } +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java new file mode 100644 index 0000000000..75e6e37875 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package a; +import java.net.URL; +/** + * Test class + */ +public class Sample { + public static void main(String[] args) { + System.out.println("Main Method"); + test(); + } + public static void test() { + System.out.println("Testing.."); + } + public void testMethod() { + System.out.println("Random"); + } + public static void tes3(int x) { + System.out.println("Expected_Single_Parameter"); + } + public void tes2() { + + } + public static void tesComplex(String[] x, java.net.URL[] sx) { + System.out.println("Expected_One_normal_&_One_fully_qualified"); + } + public void testBlank() { + System.out.println("Expected_No_Signature"); + } + public void tes2(java.lang.Object... objects ) { + + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java new file mode 100644 index 0000000000..b93df0192d --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * Test class + */ +class InnerClassTest { + public static void main(String[] ar) { + + } + class innerL1 { + void check1() { + System.out.println("EXPECTED_INNERCLASS"); + } + class innerL2 { + void check23() { + System.out.println("EXPECTED_INNER-INNER_CLASS"); + } + } + } +} + \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/Sample.java new file mode 100644 index 0000000000..3c7a4fc5e7 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/Sample.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package b; + +/** + * Test class + */ +public class Sample { + public static void main(String[] args) { + System.out.println("Main Method"); + test(); + } + public static void test() { + System.out.println("Testing.."); + } + public void testMethod() { + System.out.println("Random"); + } + public static void tes3() { + System.out.println("Expected_Zero_Parameter"); + } + public void tes2() { + + } + public static void tesComplex(java.lang.Object x, java.net.URL[] sx) { + System.out.println("Expected_both_fully_qualified"); + } + public void tes2(java.lang.Object x,java.lang.Object... args ) { + System.out.println("Expected_VarArgs"); + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java new file mode 100644 index 0000000000..e0bf88d865 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * Test class + */ +class InnerClassTest { + public static void main(String[] ar) { + + } + class innerL1 { + void check() { + System.out.println("EXPECTED_INNERCLASS"); + } + class innerL2 { + void check2() { + System.out.println("EXPECTED_INNER-INNER_CLASS"); + } + } + } +} + \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/Sample.java new file mode 100644 index 0000000000..2ea28e6016 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/Sample.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package c; + +/** + * Test class + */ +public class Sample { + public static void main(String[] args) { + System.out.println("Main Method"); + test(); + } + public static void test() { + System.out.println("Testing.."); + } + public void testMethod() { + System.out.println("Random"); + } + public static void tes3(int x, String v) { + System.out.println("Expected_Multiple_Parameter"); + } + public static void tes3(int x, String v, Sample s) { + System.out.println("Expected_Multiple_Parameter_Three"); + } + public void tes2() { + + } + public void testMethod(Object s,Object... sd) { + System.out.println("Expected_oneNormal&oneVarArgs"); + } + public void testMethod(Object... sd) { + System.out.println("Expected_oneVarArgs"); + } + +} \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java index 56240f0d12..a1c4b892bd 100644 --- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java @@ -57,6 +57,7 @@ import org.eclipse.jdt.debug.tests.console.ConsoleTerminateAllActionTests; import org.eclipse.jdt.debug.tests.console.IOConsoleTests; import org.eclipse.jdt.debug.tests.console.JavaDebugStackTraceConsoleTest; +import org.eclipse.jdt.debug.tests.console.JavaStackTraceAmbiguityTest; import org.eclipse.jdt.debug.tests.console.JavaStackTraceConsoleTest; import org.eclipse.jdt.debug.tests.core.AlternateStratumTests; import org.eclipse.jdt.debug.tests.core.ArgumentTests; @@ -275,6 +276,7 @@ public AutomatedSuite() { addTest(new TestSuite(JavaDebugStackTraceConsoleTest.class)); addTest(new TestSuite(IOConsoleTests.class)); addTest(new TestSuite(ConsoleTerminateAllActionTests.class)); + addTest(new TestSuite(JavaStackTraceAmbiguityTest.class)); //Core tests addTest(new TestSuite(DebugEventTests.class)); diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java index 63d2e41fc5..8f4855d925 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2017 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,9 +14,11 @@ *******************************************************************************/ package org.eclipse.jdt.internal.debug.ui.console; - import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -25,10 +27,13 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.TypeNameMatch; @@ -57,13 +62,22 @@ public class JavaStackTraceHyperlink implements IHyperlink { private final TextConsole fConsole; - + private final AtomicReference generatedLink; + private static final String REGEX_FOR_NORMAL = "([a-zA-Z0-9\\$]+)\\.([a-zA-Z0-9]+)\\(([^)]*)\\)"; //$NON-NLS-1$ + private static final String REGEX_FOR_GENERICS = "([a-zA-Z0-9\\$]+(?:<[a-zA-Z0-9,<>]+>)?)\\.([a-zA-Z0-9]+)\\(([^)]*)\\)"; //$NON-NLS-1$ + private static final String REGEX_FOR_INNER_CLASS = "([a-zA-Z0-9\\$]+(?:\\([a-zA-Z0-9]+\\))?)\\.([a-zA-Z0-9]+)\\(([^)]*)\\)"; //$NON-NLS-1$ + private static final String METHOD_SIGNATURE_REGEX = "\\w+\\([^)]*\\)"; //$NON-NLS-1$ + private static final String METHOD_ARGUMENTS_REGEX = "\\(([^)]*)\\)"; //$NON-NLS-1$ + private static final String INNER_CLASS_ARGUMENTS_REGEX = "\\(([^)]+)\\)"; //$NON-NLS-1$ /** * Constructor - * @param console the {@link TextConsole} this link detector is attached to + * + * @param console + * the {@link TextConsole} this link detector is attached to */ public JavaStackTraceHyperlink(TextConsole console) { fConsole = console; + generatedLink = new AtomicReference<>(); } /** @@ -86,15 +100,16 @@ public void linkExited() { @Override public void linkActivated() { String typeName; - int lineNumber; - try { - String linkText = getLinkText(); - typeName = getTypeName(linkText); - lineNumber = getLineNumber(linkText); - } catch (CoreException e1) { - ErrorDialog.openError(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Error, ConsoleMessages.JavaStackTraceHyperlink_Error, e1.getStatus()); - return; - } + int lineNumber; + try { + String linkText = getLinkText(); + generatedLink.set(linkText); + typeName = getTypeName(linkText); + lineNumber = getLineNumber(linkText); + } catch (CoreException e1) { + ErrorDialog.openError(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Error, ConsoleMessages.JavaStackTraceHyperlink_Error, e1.getStatus()); + return; + } // documents start at 0 if (lineNumber > 0) { @@ -106,8 +121,10 @@ public void linkActivated() { /** * Starts a search for the type with the given name. Reports back to 'searchCompleted(...)'. * - * @param typeName the type to search for - * @param lineNumber the line number to open the editor on + * @param typeName + * the type to search for + * @param lineNumber + * the line number to open the editor on */ protected void startSourceSearch(final String typeName, final int lineNumber) { Job search = new Job(ConsoleMessages.JavaStackTraceHyperlink_2) { @@ -168,13 +185,18 @@ public void acceptTypeNameMatch(TypeNameMatch match) { searchEngine.searchAllTypeNames(qualifications, typeNames, SearchEngine.createWorkspaceScope(), requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null); return matchingTypes; } + /** * Reported back to from {@link JavaStackTraceHyperlink#startSourceSearch(String, int)} when results are found * - * @param source the source object - * @param typeName the fully qualified type name - * @param lineNumber the line number in the type - * @param status the error status or null if none + * @param source + * the source object + * @param typeName + * the fully qualified type name + * @param lineNumber + * the line number in the type + * @param status + * the error status or null if none */ protected void searchCompleted(final Object source, final String typeName, final int lineNumber, final IStatus status) { UIJob job = new UIJob("link search complete") { //$NON-NLS-1$ @@ -183,16 +205,29 @@ public IStatus runInUIThread(IProgressMonitor monitor) { if (source == null) { if (status == null) { // did not find source - MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Information_1, NLS.bind(ConsoleMessages.JavaStackTraceHyperlink_Source_not_found_for__0__2, new String[] {typeName})); + MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_Information_1, NLS.bind(ConsoleMessages.JavaStackTraceHyperlink_Source_not_found_for__0__2, new String[] { + typeName })); } else { JDIDebugUIPlugin.statusDialog(ConsoleMessages.JavaStackTraceHyperlink_3, status); } } else if (source instanceof List) { // ambiguous + @SuppressWarnings("unchecked") List matches = (List) source; int line = lineNumber + 1; // lineNumber starts with 0, but line with 1, see #linkActivated - OpenFromClipboardAction.handleMatches(matches, line, typeName, ConsoleMessages.JavaDebugStackTraceHyperlink_dialog_title); - return Status.OK_STATUS; + if (generatedLink.get() == null) { // Handles invalid links (without line number) + return openClipboard(matches, line, typeName); + } + try { + return processAmbiguousResults(matches, typeName, lineNumber); + } catch(Exception e) { + String link = generatedLink.get(); + StringBuffer temp = new StringBuffer(); + temp.append("Unable to parse \"" + link + "\" \n "); //$NON-NLS-1$ //$NON-NLS-2$ + temp.append(e.getClass().getSimpleName()); + exceptionHandler(temp.toString(), e); + } + } else { processSearchResult(source, typeName, lineNumber); } @@ -201,14 +236,326 @@ public IStatus runInUIThread(IProgressMonitor monitor) { }; job.setSystem(true); job.schedule(); + + } + + /** + * Process received classes to extract exact class by mining methods + * + * @param matches + * Initial match results + * @param typeName + * the fully qualified type name + * @param lineNumber + * the line number in the type + * @param status + * the error status or null if none + */ + public IStatus processAmbiguousResults(List matches, String typeName, int line) { + List exactMatchesFiltered = new ArrayList<>(); + Pattern pattern = Pattern.compile(REGEX_FOR_NORMAL); + Matcher matcher = pattern.matcher(generatedLink.get()); + String methodSignature = null; + if (matcher.find()) { + methodSignature = matcher.group(2) + "(" + matcher.group(3) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + if (methodSignature == null) { + pattern = Pattern.compile(REGEX_FOR_GENERICS); + matcher = pattern.matcher(generatedLink.get()); + if (matcher.find()) { + methodSignature = matcher.group(2) + "(" + matcher.group(3) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (methodSignature == null) { + pattern = Pattern.compile(REGEX_FOR_INNER_CLASS); + matcher = pattern.matcher(generatedLink.get()); + if (matcher.find()) { + methodSignature = matcher.group(2) + "(" + matcher.group(3) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (methodSignature == null) { + return openClipboard(matches, line, typeName); + } + methodSignature = methodSignature.replaceAll(" ", ""); //$NON-NLS-1$//$NON-NLS-2$ ; + String methodNameExtracted = methodSignature.substring(0, methodSignature.indexOf('(')); + for (Object obj : matches) { + if (filterClasses(obj, methodSignature, methodNameExtracted)) { + exactMatchesFiltered.add(obj); + } + } + if (exactMatchesFiltered.size() == 1) { + processSearchResult(exactMatchesFiltered.get(0), typeName, line); + return Status.OK_STATUS; + } else if (exactMatchesFiltered.size() > 0) { + return openClipboard(exactMatchesFiltered, line, typeName); + } else { + return openClipboard(matches, line, typeName); + } + } + /** + * Handles exceptions details + * + * @param message + * String regarding exception details + * @param e + * Thrown exception + */ + public void exceptionHandler(String message, Exception e) { + throw new RuntimeException(message, e); + } + + /** + * Filter classes based on matching method name + * + * @param obj + * Objects of initial results + * @param methodSignature + * entire method declaration + * @param methodNameExtracted + * method name + * @return returns true if a found an exact method inside the class, or false if there's no matching methods + */ + private boolean filterClasses(Object obj, String methodSignature, String methodNameExtracted) { + if (obj instanceof IType type) { + try { + if (extractFromResults(type, methodSignature, methodNameExtracted)) { + return true; + } else if (generatedLink.get().indexOf('$') != -1) { // checks for inner class + if (extractFromInnerClassResults(type.getTypes(), methodSignature, methodNameExtracted)) { + return true; + } + } + + } catch (Exception e) { + DebugUIPlugin.log(e); + return false; + } + } + return false; + + } + + /** + * Opens the clipboard action pop-up if there are multiple results + * + * @param results + * Search results + * @param lineNumber + * Line number from given stack trace + * @param type + * Unqualified class name + * @return Returns a standard OK status with an "ok" message. + */ + private IStatus openClipboard(List results, int lineNumber, String type) { + OpenFromClipboardAction.handleMatches(results, lineNumber, type, ConsoleMessages.JavaDebugStackTraceHyperlink_dialog_title); + return Status.OK_STATUS; + } + + /** + * Additional Filtering of classes if the IType has inner classes + * + * @param innerClass + * Array of inner classes - IType[] arrays + * @param methodSignature + * entire method declaration + * @param methodNameExtracted + * method name + * @throws JavaModelException + * @return returns true if a found an exact method inside the class, or false if there's no matching methods + */ + private boolean extractFromInnerClassResults(IType[] innerClass, String methodSignature, String methodNameExtracted) throws JavaModelException { + int innerClasslevel = innerClassLevels(generatedLink.get()); + int levelTravelled = 0; + while (innerClass.length > 0) { + for (IType innerType : innerClass) { + if (innerClass.length > 0) { + innerClass = innerType.getTypes(); + } + levelTravelled++; + if (extractFromResults(innerType, methodSignature, methodNameExtracted) && levelTravelled == innerClasslevel) { + return true; + } + } + } + return false; + } + + /** + * Checks the levels of inner class + * + * @param genLink + * Generated link + * @return returns count of levels + */ + + private int innerClassLevels(String genLink) { + int level = 0; + for (Character c : genLink.toCharArray()) { + if (c == '$') { + level++; + } + } + return level; + } + + /** + * Checks if there's any matching methods for the given IType + * + * @param type + * Type of the matches + * @param methodSignature + * entire method declaration + * @param methodNameExtracted + * method name + * @throws JavaModelException + * @return returns true if a found an exact method inside the class, or false if there's no matching methods + */ + private boolean extractFromResults(IType type, String methodSignature, String methodNameExtracted) throws JavaModelException { + if (methodSignature.indexOf('(') == -1 || methodSignature.lastIndexOf(')') == -1) { + return false; + } + boolean paramDetailsNotIncluded = methodSignature.contains(".java:"); //$NON-NLS-1$ + try { + IMethod[] methods = type.getMethods(); + for (IMethod method : methods) { + if (paramDetailsNotIncluded && method.getElementName().equals(methodNameExtracted)) { + return true; + } + String methodDetails = method.toString(); + Pattern pattern = Pattern.compile(METHOD_SIGNATURE_REGEX); + Matcher matcher = pattern.matcher(methodDetails); + if (!matcher.find()) { + return false; + } + String methodName = matcher.group(); + methodName = methodName.replaceAll(" ", ""); //$NON-NLS-1$//$NON-NLS-2$ + pattern = Pattern.compile(METHOD_ARGUMENTS_REGEX); + matcher = pattern.matcher(methodSignature); + if (!matcher.find()) { + return false; + } + int paramCount = matcher.group(1).split(",").length; //$NON-NLS-1$ + if (methodName.equals(methodSignature)) { + return true; + } else if (methodNameExtracted.equals(method.getElementName()) && paramCount == method.getNumberOfParameters()) { + // Further mining from fully qualified parameter names in method signature + String methodSignatureGen = methodSignatureGenerator(method.getElementName(), methodName); + if (methodSignatureGen.equals(methodSignature)) { + return true; + } + // If parameters includes innerclass + if (methodSignature.indexOf('$') != -1) { + String methodSignatureInnerClass = innerClassMethodSignatureGen(methodNameExtracted, methodSignature); + if (methodSignatureInnerClass.equals(methodSignatureGen)) { + return true; + } + String paramsExtracted = methodSignature.substring(methodSignature.indexOf('(')); + String param = paramsExtracted.substring(paramsExtracted.indexOf('$') + 1); + methodSignatureInnerClass = methodSignatureInnerClass.concat(param); + if (methodSignatureInnerClass.equals(methodSignatureGen)) { + return true; + } + } + return false; + } + } + } catch (Exception e) { + return false; + } + return false; + } + + /** + * Additional method signature generation for inner classes + * + * @param extractedMethodName + * Extracted method name from input + * @param methodSignature + * generated method signature + * @return returns generated String suitable for inner classes + */ + private String innerClassMethodSignatureGen(String extractedMethodName, String methodSignature) { + StringBuilder newSignature = new StringBuilder(extractedMethodName + "("); //$NON-NLS-1$ + Pattern pattern = Pattern.compile(INNER_CLASS_ARGUMENTS_REGEX); + Matcher matcher = pattern.matcher(methodSignature); + matcher.find(); + String paramsExtracted = matcher.group(1); + if (paramsExtracted.indexOf(',') != -1) { + String[] parameters = paramsExtracted.split(","); //$NON-NLS-1$ + for (String param : parameters) { + newSignature.append(param.substring(param.indexOf('$') + 1)); + newSignature.append(","); //$NON-NLS-1$ + } + newSignature.deleteCharAt(newSignature.length() - 1); + if (newSignature.charAt(newSignature.length() - 1) != ')') { + newSignature.append(')'); + } + } + return newSignature.toString(); + } + + /** + * Method signature generation for normal classes + * + * @param methodName + * Extracted method name from input + * @param targetMethod + * already extracted method signature + * @return returns generated String suitable for normal classes + */ + private String methodSignatureGenerator(String methodName, String targetMethod) { + StringBuilder methodSignatureBuilder = new StringBuilder(methodName); + methodSignatureBuilder.append('('); + String[] params = targetMethod.split(","); //$NON-NLS-1$ + for (String block : params) { + if (block.contains("...")) { //$NON-NLS-1$ Parameter is var args + if (params.length > 1) { + methodSignatureBuilder = varArgsParamBuilder(block, methodSignatureBuilder); + } + } else { + if (block.indexOf('.') == -1) { + methodSignatureBuilder.append(block.substring(block.lastIndexOf('(') + 1)); + methodSignatureBuilder.append(','); + } else { + methodSignatureBuilder.append(block.substring(block.lastIndexOf('.') + 1)); + methodSignatureBuilder.append(','); + } + } + } + methodSignatureBuilder.deleteCharAt(methodSignatureBuilder.length() - 1); + + if (methodSignatureBuilder.charAt(methodSignatureBuilder.length() - 1) != ')') { + methodSignatureBuilder.append(')'); + } + return methodSignatureBuilder.toString(); + } + + /** + * Variable argument's parameter creation + * + * @param parameter + * Input parameter + * @param current + * Initial parameters + * @return returns generated Stringbuilder for parameter + */ + private StringBuilder varArgsParamBuilder(String parameter, StringBuilder current) { + String sub1 = parameter.substring(0, parameter.indexOf("...")); //$NON-NLS-1$ + sub1 = sub1.substring(sub1.lastIndexOf('.') + 1); + current.append(sub1); + current.append("...,"); //$NON-NLS-1$ + return current; } /** * The search succeeded with the given result * - * @param source resolved source object for the search - * @param typeName type name searched for - * @param lineNumber line number on link + * @param source + * resolved source object for the search + * @param typeName + * type name searched for + * @param lineNumber + * line number on link */ protected void processSearchResult(Object source, String typeName, int lineNumber) { IDebugModelPresentation presentation = JDIDebugUIPlugin.getDefault().getModelPresentation(); @@ -219,7 +566,7 @@ protected void processSearchResult(Object source, String typeName, int lineNumbe try { IEditorPart editorPart = JDIDebugUIPlugin.getActivePage().openEditor(editorInput, editorId); if (editorPart instanceof ITextEditor && lineNumber >= 0) { - ITextEditor textEditor = (ITextEditor)editorPart; + ITextEditor textEditor = (ITextEditor) editorPart; IDocumentProvider provider = textEditor.getDocumentProvider(); provider.connect(editorInput); IDocument document = provider.getDocument(editorInput); @@ -227,7 +574,8 @@ protected void processSearchResult(Object source, String typeName, int lineNumbe IRegion line = document.getLineInformation(lineNumber); textEditor.selectAndReveal(line.getOffset(), line.getLength()); } catch (BadLocationException e) { - MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_0, NLS.bind("{0}{1}{2}", new String[] {(lineNumber+1)+"", ConsoleMessages.JavaStackTraceHyperlink_1, typeName})); //$NON-NLS-2$ //$NON-NLS-1$ + MessageDialog.openInformation(JDIDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.JavaStackTraceHyperlink_0, NLS.bind("{0}{1}{2}", new String[] { //$NON-NLS-1$ + (lineNumber + 1) + "", ConsoleMessages.JavaStackTraceHyperlink_1, typeName })); //$NON-NLS-1$ } provider.disconnect(editorInput); } @@ -239,16 +587,14 @@ protected void processSearchResult(Object source, String typeName, int lineNumbe } /** - * Returns the launch associated with this hyper-link, or - * null if none + * Returns the launch associated with this hyper-link, or null if none * - * @return the launch associated with this hyper-link, or - * null if none + * @return the launch associated with this hyper-link, or null if none */ private ILaunch getLaunch() { IProcess process = (IProcess) getConsole().getAttribute(IDebugUIConstants.ATTR_CONSOLE_PROCESS); if (process != null) { - return process.getLaunch(); + return process.getLaunch(); } return null; } @@ -264,54 +610,56 @@ private ILaunch getLaunch() { */ protected String getTypeName(String linkText) throws CoreException { int start = linkText.lastIndexOf('('); - int end = linkText.indexOf(':'); - if (start >= 0 && end > start) { - //linkText could be something like packageA.TypeB(TypeA.java:45) - //need to look in packageA.TypeA for line 45 since TypeB is defined - //in TypeA.java - //Inner classes can be ignored because we're using file and line number - - // get File name (w/o .java) - String typeName = linkText.substring(start + 1, end); - typeName = JavaCore.removeJavaLikeExtension(typeName); + int end = linkText.indexOf(':'); + if (start >= 0 && end > start) { + // linkText could be something like packageA.TypeB(TypeA.java:45) + // need to look in packageA.TypeA for line 45 since TypeB is defined + // in TypeA.java + // Inner classes can be ignored because we're using file and line number + + // get File name (w/o .java) + String typeName = linkText.substring(start + 1, end); + typeName = JavaCore.removeJavaLikeExtension(typeName); typeName = removeModuleInfo(typeName); - String qualifier = linkText.substring(0, start); - // remove the method name - start = qualifier.lastIndexOf('.'); - - if (start >= 0) { - // remove the class name - start = new String((String) qualifier.subSequence(0, start)).lastIndexOf('.'); - if (start == -1) { - start = 0; // default package - } - } - - if (start >= 0) { - qualifier = qualifier.substring(0, start); - } - - if (qualifier.length() > 0) { - typeName = qualifier + "." + typeName; //$NON-NLS-1$ - } - // Remove the module name if exists + String qualifier = linkText.substring(0, start); + // remove the method name + start = qualifier.lastIndexOf('.'); + + if (start >= 0) { + // remove the class name + start = new String((String) qualifier.subSequence(0, start)).lastIndexOf('.'); + if (start == -1) { + start = 0; // default package + } + } + + if (start >= 0) { + qualifier = qualifier.substring(0, start); + } + + if (qualifier.length() > 0) { + typeName = qualifier + "." + typeName; //$NON-NLS-1$ + } + // Remove the module name if exists int index = typeName.lastIndexOf('/'); if (index != -1) { typeName = typeName.substring(index + 1); } - return typeName; - } - IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), 0, ConsoleMessages.JavaStackTraceHyperlink_Unable_to_parse_type_name_from_hyperlink__5, null); - throw new CoreException(status); - } + return typeName; + } + IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), 0, ConsoleMessages.JavaStackTraceHyperlink_Unable_to_parse_type_name_from_hyperlink__5, null); + throw new CoreException(status); + } /** * Returns the line number associated with the stack trace or -1 if none. * - * @param linkText the complete text of the link to be parsed + * @param linkText + * the complete text of the link to be parsed * @return the line number for the stack trace or -1 if one cannot be computed or has not been provided - * @exception CoreException if unable to parse the number + * @exception CoreException + * if unable to parse the number */ protected int getLineNumber(String linkText) throws CoreException { int index = linkText.lastIndexOf(':'); @@ -345,28 +693,29 @@ protected TextConsole getConsole() { * Returns this link's text * * @return the complete text of the link, never null - * @exception CoreException if unable to retrieve the text + * @exception CoreException + * if unable to retrieve the text */ protected String getLinkText() throws CoreException { - try { - IDocument document = getConsole().getDocument(); - IRegion region = getConsole().getRegion(this); - int regionOffset = region.getOffset(); + try { + IDocument document = getConsole().getDocument(); + IRegion region = getConsole().getRegion(this); + int regionOffset = region.getOffset(); - int lineNumber = document.getLineOfOffset(regionOffset); - IRegion lineInformation = document.getLineInformation(lineNumber); - int lineOffset = lineInformation.getOffset(); - String line = document.get(lineOffset, lineInformation.getLength()); + int lineNumber = document.getLineOfOffset(regionOffset); + IRegion lineInformation = document.getLineInformation(lineNumber); + int lineOffset = lineInformation.getOffset(); + String line = document.get(lineOffset, lineInformation.getLength()); - int regionOffsetInLine = regionOffset - lineOffset; + int regionOffsetInLine = regionOffset - lineOffset; - int linkEnd = line.indexOf(')', regionOffsetInLine); - int linkStart = line.lastIndexOf(' ', regionOffsetInLine); + int linkEnd = line.indexOf(')', regionOffsetInLine); + int linkStart = line.lastIndexOf(' ', regionOffsetInLine); if (linkStart == -1) { linkStart = line.lastIndexOf('\t', regionOffsetInLine); } - return line.substring(linkStart==-1?0:linkStart+1,linkEnd+1).trim(); + return line.substring(linkStart == -1 ? 0 : linkStart + 1, linkEnd + 1).trim(); } catch (BadLocationException e) { IStatus status = new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), 0, ConsoleMessages.JavaStackTraceHyperlink_Unable_to_retrieve_hyperlink_text__8, e); throw new CoreException(status); @@ -390,4 +739,4 @@ private static String removeModuleInfo(String typeName) { } return typeName; } -} +} \ No newline at end of file