You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by lk...@apache.org on 2022/02/19 00:38:32 UTC
[netbeans] branch master updated: Bugfix: Go To source broken for nested classes.
This is an automated email from the ASF dual-hosted git repository.
lkishalmi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 7ae4452 Bugfix: Go To source broken for nested classes.
7ae4452 is described below
commit 7ae4452ecdad828497c3317f7a5d113534978f39
Author: ratcash <de...@ratcash.net>
AuthorDate: Fri Sep 24 09:02:25 2021 +0200
Bugfix: Go To source broken for nested classes.
---
java/gradle.java/apichanges.xml | 14 ++
.../modules/gradle/java/api/output/Location.java | 131 +++++++++++--
.../gradle/java/api/output/LocationOpener.java | 45 ++++-
.../gradle/java/api/output/LocationTest.java | 208 +++++++++++++++++++++
java/gradle.test/nbproject/project.xml | 2 +-
.../test/ui/nodes/GradleJUnitNodeOpener.java | 2 +-
.../gradle/test/ui/nodes/GradleTestMethodNode.java | 2 +-
7 files changed, 374 insertions(+), 30 deletions(-)
diff --git a/java/gradle.java/apichanges.xml b/java/gradle.java/apichanges.xml
index 1796f74..f77b1ba 100644
--- a/java/gradle.java/apichanges.xml
+++ b/java/gradle.java/apichanges.xml
@@ -83,6 +83,20 @@ is the proper place.
<!-- ACTUAL CHANGES BEGIN HERE: -->
<changes>
+ <change id="nested-class-locations">
+ <api name="gradle.java.api"/>
+ <summary>Location can represent nested classes</summary>
+ <version major="1" minor="17"/>
+ <date day="18" month="2" year="2022"/>
+ <author login="ratcashdev"/>
+ <compatibility semantic="compatible" deprecation="yes"/>
+ <description>
+ <code><a href="@TOP@/org/netbeans/modules/gradle/java/api/output/Location.html">Location</a></code>
+ is now capabe to represent java code location inside nested classes as well.
+ </description>
+ <class package="org.netbeans.modules.gradle.java.api.output" name="Location"/>
+ <issue number="NETBEANS-6041"/>
+ </change>
<change id="gradle-7.0-deprecation">
<api name="gradle.java.api"/>
<summary>Deprecating Gradle 7.0 removed API-s</summary>
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/Location.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/Location.java
index 175d186..d8a1844 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/Location.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/Location.java
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package org.netbeans.modules.gradle.java.api.output;
import java.util.regex.Matcher;
@@ -24,6 +23,9 @@ import java.util.regex.Pattern;
import org.openide.filesystems.FileObject;
/**
+ * This class represents a location in a Java file or Class which usually
+ * presented in Sting form as {@code a/b/c/ClassName$SubClas1$SubClass2.java:123} or
+ * {@code a/b/c/ClassName$SubClas1$SubClass2.java:methodName()}
*
* @author Laszlo Kishalmi
*/
@@ -31,32 +33,102 @@ public final class Location {
final String fileName;
final String target;
+ final String[] classNames;
private Integer lineNum = null;
- public Location(String fileName, String target) {
- this.fileName = fileName;
- this.target = target;
- try {
- lineNum = Integer.parseInt(target);
- } catch (NumberFormatException ex) {
+ /**
+ * Parses the given string in the format {@code a/b/c/ClassName$SubClas1$SubClass2.java:123} or
+ * {@code a/b/c/ClassName$SubClas1$SubClass2.java:methodName()} to a Location
+ *
+ * @param loc the location String
+ * @return the Location object represented by the location String
+ * @since 1.17
+ */
+ public static Location parseLocation(String loc) {
+ assert loc != null;
+
+ // example MyFile$NestedClass.java:123
+ // or // example MyFile$NestedClass.java:getMethod()
+ int targetDelimiterIndex = loc.lastIndexOf(':');
+ if (targetDelimiterIndex == -1) {
+ targetDelimiterIndex = loc.length();
}
- }
+ // the last dot will be before the .java extension
+ // unless there's no extension and this may be before the classname
+ // but those cases not supported, really
+ int extensionIndx = loc.lastIndexOf('.', targetDelimiterIndex);
+ if (extensionIndx == -1) {
+ extensionIndx = targetDelimiterIndex;
+ }
+ // is the dot right before the File's name (after the package's name)
+ int packageSlashIndx = loc.lastIndexOf('/', extensionIndx - 1);
- public Location(String loc) {
- int i = loc != null ? loc.indexOf(':') : 0;
- if ((i > 0) && (loc != null)) {
- fileName = loc.substring(0, i);
- target = loc.substring(i + 1);
+ String[] classNames = loc.substring(packageSlashIndx + 1, extensionIndx).split("\\$");
+ String ext = loc.substring(extensionIndx, targetDelimiterIndex);
+ String fileName = loc.substring(0, packageSlashIndx + 1) + classNames[0] + ext;
+
+ String target;
+ if (targetDelimiterIndex < loc.length() - 1) {
+ target = loc.substring(targetDelimiterIndex + 1);
} else {
- fileName = loc;
target = null;
}
+
+ return new Location(fileName, classNames, target);
+ }
+
+ /**
+ * Parses and creates a location item out of a string.
+ * @param loc the string representation of the location
+ * @deprecated in favor of {@linkplain #parseLocation(java.lang.String)}
+ */
+ @Deprecated
+ public Location(String loc) {
+ Location l = parseLocation(loc);
+ this.fileName = l.fileName;
+ this.classNames = l.classNames;
+ this.target = l.target;
+ this.lineNum = l.lineNum;
+ }
+
+ /**
+ * Parses and creates a location item out of a string as a file name and a
+ * target which can be either a method name or a line number.
+ * @param fileName the file name part of the location
+ * @param target the line number or method name.
+ *
+ * @deprecated in favor of {@linkplain #parseLocation(java.lang.String)}
+ */
+ @Deprecated
+ public Location(String fileName, String target) {
+ Location loc = parseLocation(fileName + " : " + target);
+ this.fileName = loc.fileName;
+ this.classNames = loc.classNames;
+ this.target = loc.target;
+ this.lineNum = loc.lineNum;
+ }
+
+ private Location(String fileName, String[] classNames, String target) {
+ this.fileName = fileName;
+ this.target = target;
+ this.classNames = classNames;
try {
lineNum = Integer.parseInt(target);
} catch (NumberFormatException ex) {
}
}
+ /**
+ * Returns a new location instance without the target(line number or method name)
+ * of this location.
+ *
+ * @return a Location without target information.
+ * @since 1.17
+ */
+ public Location withNoTarget() {
+ return new Location(fileName, classNames, null);
+ }
+
public String getFileName() {
return fileName;
}
@@ -77,13 +149,38 @@ public final class Location {
return (target != null) && (lineNum == null);
}
+ /**
+ * Get the classes represented in this Location, the outmost and then the
+ * inner nested classes as well.
+ *
+ * @return the class name nesting hierarchy
+ * @since 1.17
+ */
+ String[] getClassNames() {
+ return classNames;
+ }
+
@Override
public String toString() {
- return target != null ? fileName + ":" + target : fileName;
+ StringBuilder sb = new StringBuilder();
+ sb.append(fileName.substring(0, fileName.indexOf(classNames[0])));
+ for (int i = 0; i < classNames.length; i++) {
+ String className = classNames[i];
+ sb.append(className);
+ sb.append("$");
+ }
+ sb.setLength(sb.length() - 1);
+ sb.append(fileName.substring(
+ fileName.indexOf(classNames[0]) + classNames[0].length()));
+ if (target != null) {
+ sb.append(":");
+ sb.append(target);
+ }
+ return sb.toString();
}
private static final Pattern CALLSTACK_ITEM_PARSER = Pattern.compile("(.*)at (\\w[\\w\\.\\$<>]*)\\.(\\w+)\\((\\w+)\\.java\\:([0-9]+)\\)");
-
+
public static final Location locationFromCallStackItem(String item) {
Matcher m = CALLSTACK_ITEM_PARSER.matcher(item);
if (m.matches()) {
@@ -100,7 +197,7 @@ public final class Location {
ret.append(className.replace('.', '/'));
}
ret.append(".java");
- return new Location(ret.toString(), line != null ? line : methodName);
+ return Location.parseLocation(ret.toString() + ":" + line != null ? line : methodName);
} else {
return null;
}
diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/LocationOpener.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/LocationOpener.java
index b15d9bc..0301601 100644
--- a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/LocationOpener.java
+++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/output/LocationOpener.java
@@ -63,10 +63,10 @@ public final class LocationOpener {
if (location.isLine()) {
openAtLine(fo, location.getLineNum());
} else if (location.isMethod()) {
- int l = getMethodLine(fo, location.getTarget());
+ int l = getMethodLine(fo, location.getClassNames(), location.getTarget());
openAtLine(fo, l);
} else {
- int l = getTargetLine(fo);
+ int l = getTargetLine(fo, location.getClassNames());
openAtLine(fo, l);
}
}
@@ -81,24 +81,28 @@ public final class LocationOpener {
return cleanName;
}
- private int getMethodLine(final FileObject fo, final String methodNameWithParams) {
+ private int getMethodLine(final FileObject fo, final String[] classNames, final String methodNameWithParams) {
String methodName = stripMethodParams(methodNameWithParams);
final int[] line = new int[1];
JavaSource javaSource = JavaSource.forFileObject(fo);
if (javaSource != null) {
try {
javaSource.runUserActionTask((CompilationController compilationController) -> {
- compilationController.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ compilationController.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
Trees trees = compilationController.getTrees();
CompilationUnitTree compilationUnitTree = compilationController.getCompilationUnit();
List<? extends Tree> typeDecls = compilationUnitTree.getTypeDecls();
for (Tree tree : typeDecls) {
- Element element = trees.getElement(trees.getPath(compilationUnitTree, tree));
- if (element != null && element.getKind() == ElementKind.CLASS && element.getSimpleName().contentEquals(fo.getName())) {
+ Element element = getClassElement(
+ trees.getElement(trees.getPath(compilationUnitTree, tree)),
+ classNames,0);
+ if (element != null) {
List<? extends ExecutableElement> methodElements = ElementFilter.methodsIn(element.getEnclosedElements());
+ System.out.println("Looking for methodName " + methodName);
for (Element child : methodElements) {
if (child.getSimpleName().contentEquals(methodName)) {
long pos = trees.getSourcePositions().getStartPosition(compilationUnitTree, trees.getTree(child));
+ System.out.println("Found method. LINE: " + pos);
line[0] = (int) compilationUnitTree.getLineMap().getLineNumber(pos);
break;
}
@@ -114,7 +118,24 @@ public final class LocationOpener {
return 1;
}
- private int getTargetLine(final FileObject fo) {
+
+ private Element getClassElement(Element element, String[] classNames, int startIndex) {
+ if (element != null && element.getKind() == ElementKind.CLASS &&
+ element.getSimpleName().contentEquals(classNames[startIndex])) {
+ if (startIndex == classNames.length - 1) {
+ return element;
+ }
+ for (Element enclosedElement : element.getEnclosedElements()) {
+ Element matchingElement = getClassElement(enclosedElement, classNames, startIndex + 1);
+ if (matchingElement != null) {
+ return matchingElement;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int getTargetLine(final FileObject fo, String[] classNames) {
final int[] line = new int[]{0};
JavaSource javaSource = JavaSource.forFileObject(fo);
if (javaSource != null) {
@@ -125,9 +146,13 @@ public final class LocationOpener {
CompilationUnitTree compilationUnitTree = compilationController.getCompilationUnit();
List<? extends Tree> typeDecls = compilationUnitTree.getTypeDecls();
for (Tree tree : typeDecls) {
- Element element = trees.getElement(trees.getPath(compilationUnitTree, tree));
- if (element != null && element.getKind() == ElementKind.CLASS && element.getSimpleName().contentEquals(fo.getName())) {
- long pos = trees.getSourcePositions().getStartPosition(compilationUnitTree, tree);
+ Element element = getClassElement(
+ trees.getElement(trees.getPath(compilationUnitTree, tree)),
+ classNames,0);
+ if (element != null) {
+ long pos = trees.getSourcePositions().getStartPosition(
+ compilationUnitTree,
+ trees.getTree(element));
line[0] = (int) compilationUnitTree.getLineMap().getLineNumber(pos);
break;
}
diff --git a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/api/output/LocationTest.java b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/api/output/LocationTest.java
new file mode 100644
index 0000000..6d80c08
--- /dev/null
+++ b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/api/output/LocationTest.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.gradle.java.api.output;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author OmniBene, s.r.o.
+ */
+public class LocationTest {
+
+ @Test
+ public void testFilenameFromClassOnly() {
+ String src = "A";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testFilenameFromClassAndExtension() {
+ String src = "A.java";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testFilenameFromPackageAndClassOnly() {
+ String src = "my/package.A";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testFilenameFromPackageAndClassAndExtension() {
+ String src = "my/package/A.java";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testLine() {
+ String src = ":123";
+ Location cut = Location.parseLocation(src);
+ Assert.assertTrue(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testLineWithClass() {
+ String src = "A:123";
+ Location cut = Location.parseLocation(src);
+ Assert.assertTrue(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testLineWithClassAndExt() {
+ String src = "Class.java:123";
+ Location cut = Location.parseLocation(src);
+ Assert.assertTrue(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testLineWithPackageAndClassAndExt() {
+ String src = "a/Class.java:123";
+ Location cut = Location.parseLocation(src);
+ Assert.assertTrue(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testLineWithPackageAndClass() {
+ String src = "a/Class:123";
+ Location cut = Location.parseLocation(src);
+ Assert.assertTrue(cut.isLine());
+ Assert.assertFalse(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testMethod() {
+ String src = ":myMethod()";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertTrue(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testMethodWithClass() {
+ String src = "A:myMethod()";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertTrue(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testMethodWithClassAndExt() {
+ String src = "Class.java:myMethod()";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertTrue(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testMethodWithPackageAndClassAndExt() {
+ String src = "a/Class.java:myMethod()";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertTrue(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testMethodWithPackageAndClass() {
+ String src = "a/Class:myMethod()";
+ Location cut = Location.parseLocation(src);
+ Assert.assertFalse(cut.isLine());
+ Assert.assertTrue(cut.isMethod());
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testNestedClassNoPackage() {
+ String src = "A$B";
+ Location cut = Location.parseLocation(src);
+ Assert.assertEquals("A", cut.classNames[0]);
+ Assert.assertEquals("B", cut.classNames[1]);
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testNestedClassWithPackageAndExtension() {
+ String src = "x/y/A$B.java";
+ Location cut = Location.parseLocation(src);
+ Assert.assertEquals("A", cut.classNames[0]);
+ Assert.assertEquals("B", cut.classNames[1]);
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testNestedClassWithPackageAndGroovyExtension() {
+ String src = "x/y/A$B.groovy";
+ Location cut = Location.parseLocation(src);
+ Assert.assertEquals("A", cut.classNames[0]);
+ Assert.assertEquals("B", cut.classNames[1]);
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testNestedClassWithPackageAndKotlinExtension() {
+ String src = "x/y/A$B.kt";
+ Location cut = Location.parseLocation(src);
+ Assert.assertEquals("A", cut.classNames[0]);
+ Assert.assertEquals("B", cut.classNames[1]);
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testNestedClassWithPackageAndKotlinExtension2() {
+ String src = "x/y/A$B.kt";
+ Location cut = Location.parseLocation(src);
+ Assert.assertEquals("A", cut.classNames[0]);
+ Assert.assertEquals("B", cut.classNames[1]);
+ Assert.assertEquals(src, cut.toString());
+ }
+
+ @Test
+ public void testPackageWithNoExtension() {
+ String src = "x/y/A";
+ Location cut = Location.parseLocation(src);
+ Assert.assertEquals("A", cut.classNames[0]);
+ }
+
+}
diff --git a/java/gradle.test/nbproject/project.xml b/java/gradle.test/nbproject/project.xml
index 2c90eb7..38e3b00 100644
--- a/java/gradle.test/nbproject/project.xml
+++ b/java/gradle.test/nbproject/project.xml
@@ -47,7 +47,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>1.5</specification-version>
+ <specification-version>1.17</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git a/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleJUnitNodeOpener.java b/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleJUnitNodeOpener.java
index 4246c42..7656f87 100644
--- a/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleJUnitNodeOpener.java
+++ b/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleJUnitNodeOpener.java
@@ -45,7 +45,7 @@ public final class GradleJUnitNodeOpener extends NodeOpener {
Node first = children.getNodeAt(0);
if ((first != null) && (first instanceof GradleTestMethodNode)) {
GradleTestMethodNode node = (GradleTestMethodNode) first;
- Location loc = new Location(node.getTestLocation().getFileName());
+ Location loc = node.getTestLocation().withNoTarget();
new LocationOpener(loc, node).open();
}
}
diff --git a/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleTestMethodNode.java b/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleTestMethodNode.java
index 5981b60..a9d7f50 100644
--- a/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleTestMethodNode.java
+++ b/java/gradle.test/src/org/netbeans/modules/gradle/test/ui/nodes/GradleTestMethodNode.java
@@ -93,6 +93,6 @@ public final class GradleTestMethodNode extends JUnitTestMethodNode implements L
}
Location getTestLocation() {
- return new Location(getTestcase().getLocation());
+ return Location.parseLocation(getTestcase().getLocation());
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists