From 60d19e6b2e1fab7fa1ee73f9914554dfe3245671 Mon Sep 17 00:00:00 2001 From: sougandhs Date: Tue, 22 Oct 2024 14:25:01 +0530 Subject: [PATCH] Filtering unqualified Stacktraces #115 using method name to filter unqualified stacktraces generated in java stacktrace console. Fixes https://github.com/eclipse-jdt/eclipse.jdt.debug/issues/115 --- .../console/JavaStackTraceAmbiguityTest.java | 358 ++++++++++++++++++ .../AmbiguityTest/InnerClassTest.java | 32 ++ .../AmbiguityTest/SampleGenerics.java | 29 ++ .../testfiles/AmbiguityTest/a/Sample.java | 40 ++ .../testfiles/AmbiguityTest/b/Sample.java | 39 ++ .../testfiles/AmbiguityTest/c/Sample.java | 46 +++ .../jdt/debug/tests/AutomatedSuite.java | 2 + .../ui/console/JavaStackTraceHyperlink.java | 232 +++++++++++- 8 files changed, 774 insertions(+), 4 deletions(-) create mode 100644 org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/SampleGenerics.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/Sample.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/Sample.java 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..38a44d8cd1 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * 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()); + } + } + + 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..e0bf88d865 --- /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 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/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/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java new file mode 100644 index 0000000000..6e19f1dea4 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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"); + } + +} \ 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..f639ac91de --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/Sample.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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"); + } +} \ 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..06d579af05 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 @@ -25,10 +25,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,7 +60,7 @@ public class JavaStackTraceHyperlink implements IHyperlink { private final TextConsole fConsole; - + private String generatedLink; /** * Constructor * @param console the {@link TextConsole} this link detector is attached to @@ -89,6 +92,7 @@ public void linkActivated() { int lineNumber; try { String linkText = getLinkText(); + generatedLink = linkText; typeName = getTypeName(linkText); lineNumber = getLineNumber(linkText); } catch (CoreException e1) { @@ -191,8 +195,29 @@ public IStatus runInUIThread(IProgressMonitor monitor) { @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 == null) { // Handles invalid links (without line number) + return openClipboard(matches, line, typeName); + } + int methodNameStartIndex = generatedLink.indexOf('.'); + int methodNameEndIndex = generatedLink.lastIndexOf(')'); + if (methodNameStartIndex != -1 && methodNameEndIndex != -1) { + List exactMatchesFiltered = new ArrayList<>(); + String methodSignature = generatedLink.substring(methodNameStartIndex + 1, methodNameEndIndex + 1).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, lineNumber); + return Status.OK_STATUS; + } else if (exactMatchesFiltered.size() > 1) { + return openClipboard(exactMatchesFiltered, line, typeName); + } + } + + return openClipboard(matches, line, typeName); } else { processSearchResult(source, typeName, lineNumber); } @@ -203,6 +228,205 @@ public IStatus runInUIThread(IProgressMonitor monitor) { job.schedule(); } + /** + * 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.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 { + while (innerClass.length > 0) { + for (IType innerType : innerClass) { + if (innerClass.length > 0) { + innerClass = innerType.getTypes(); + } + if (extractFromResults(innerType, methodSignature, methodNameExtracted)) { + return true; + } + } + } + return false; + } + /** + * 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 { + IMethod[] methods = type.getMethods(); + for (IMethod method : methods) { + int indexOfClosing = method.toString().indexOf(')'); + int indexOfStart = method.toString().indexOf(method.getElementName()); + String methodName = method.toString().substring(indexOfStart, indexOfClosing + 1).replaceAll(" ", ""); //$NON-NLS-1$//$NON-NLS-2$ + int paramCount = methodSignature.substring(methodSignature.indexOf('(') + + 1, methodSignature.lastIndexOf(')')).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 paramters 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.concat(param); + methodSignatureInnerClass.concat(")"); //$NON-NLS-1$ + if (methodSignatureInnerClass.toString().equals(methodSignatureGen)) { + return true; + } + } + 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$ + String paramsExtracted = methodSignature.substring(methodSignature.indexOf('(') + 1, methodSignature.indexOf(')')); + 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 *