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 <develo...@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-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to