Skip to content

Commit 55cabe8

Browse files
committed
Support for nested classes
1 parent e441948 commit 55cabe8

File tree

11 files changed

+145
-45
lines changed

11 files changed

+145
-45
lines changed

ide/projectapi/apichanges.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,13 @@ is the proper place.
9696
allow running tests in parallel.
9797
<code><a href="@TOP@/org/netbeans/api/project/ContainedProjectFilter.html">ContainedProjectFilter</a></code> was added and
9898
it can be used to pass list of projects the project action should apply to.
99+
<code><a href="@TOP@/org/netbeans/spi/project/NestedClass">NestedClass</a></code> was added in order to support
100+
nested classes.
99101
</p>
100102
</description>
101103
<class package="org.netbeans.api.project" name="ContainedProjectFilter"/>
102104
<class package="org.netbeans.spi.project" name="ActionProvider"/>
105+
<class package="org.netbeans.spi.project" name="NestedClass"/>
103106
</change>
104107
<change id="project-action-context">
105108
<api name="general"/>

ide/projectapi/src/org/netbeans/spi/project/NestedClass.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,62 @@
1919
package org.netbeans.spi.project;
2020

2121
import java.util.Objects;
22+
import org.openide.filesystems.FileObject;
2223

2324
/**
24-
* Structure representing an identification of a single method/function
25-
* in a file.
25+
* Structure representing an identification of a nested class in a file.
26+
*
27+
* <p>
28+
* <code>NestedClass</code> can be used to represent nested classes within parent class
29+
* Example:
30+
* If we have following structure: ParentClass (parent-of) ChildClass1 (parent-of) ChildClass2,
31+
* for ChildClass1 className field would contain "ChildClass1", and
32+
* for ChildClass2 className field would contain "ChildClass1$ChildClass2"
33+
* </p>
2634
*
2735
* @author Dusan Petrovic
2836
*
2937
* @since 1.90
3038
*/
3139
public final class NestedClass {
3240

41+
private FileObject file;
3342
private String className;
3443

3544
/**
3645
* Creates a new instance holding the specified identification
37-
* of a nested class in a file.
46+
* of a nested class.
3847
*
3948
* @param className name of a class inside the file
49+
* @param file file to be kept in the object
4050
* @exception java.lang.IllegalArgumentException
4151
* if the file or class name is {@code null}
4252
* @since 1.90
4353
*/
44-
public NestedClass(String className) {
54+
public NestedClass(String className, FileObject file) {
4555
super();
4656
if (className == null) {
4757
throw new IllegalArgumentException("className is <null>");
4858
}
59+
if (file == null) {
60+
throw new IllegalArgumentException("file is <null>");
61+
}
4962
this.className = className;
63+
this.file = file;
64+
}
65+
66+
/**
67+
* Returns the file identification.
68+
*
69+
* @return file held by this object
70+
* @since 1.90
71+
*/
72+
public FileObject getFile() {
73+
return file;
5074
}
5175

5276
/**
53-
* Returns name of a nested class.
77+
* Returns name of a nested class within a file.
5478
*
5579
* @return class name held by this object
5680
* @since 1.90
@@ -63,6 +87,7 @@ public String getClassName() {
6387
public int hashCode() {
6488
int hash = 3;
6589
hash = 41 * hash + Objects.hashCode(this.className);
90+
hash = 41 * hash + Objects.hashCode(this.file);
6691
return hash;
6792
}
6893

@@ -75,6 +100,6 @@ public boolean equals(Object obj) {
75100
return true;
76101
}
77102
final NestedClass other = (NestedClass) obj;
78-
return other.className.equals(className);
103+
return other.file.equals(file) && other.className.equals(className);
79104
}
80105
}

ide/projectapi/src/org/netbeans/spi/project/SingleMethod.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public final class SingleMethod {
3131

3232
private FileObject file;
3333
private String methodName;
34+
private NestedClass nestedClass;
3435

3536
/**
3637
* Creates a new instance holding the specified identification
@@ -53,6 +54,37 @@ public SingleMethod(FileObject file, String methodName) {
5354
this.file = file;
5455
this.methodName = methodName;
5556
}
57+
58+
/**
59+
* Creates a new instance holding the specified identification
60+
* of a method/function in nested class in a file.
61+
*
62+
* @param file file to be kept in the object
63+
* @param methodName name of a method inside the file
64+
* @param nestedClass nested class containing the method
65+
*
66+
* @exception java.lang.IllegalArgumentException
67+
* if the nested class name is {@code null}
68+
* @since 1.90
69+
*/
70+
public SingleMethod(FileObject file, String methodName, NestedClass nestedClass) {
71+
this(file, methodName);
72+
if (nestedClass == null) {
73+
throw new IllegalArgumentException("nestedClass is <null>");
74+
}
75+
this.nestedClass = nestedClass;
76+
}
77+
78+
79+
/**
80+
* Returns the nested class containing the method.
81+
*
82+
* @return nested class containing the method
83+
* @since 1.90
84+
*/
85+
public NestedClass getNestedClass() {
86+
return nestedClass;
87+
}
5688

5789
/**
5890
* Returns the file identification.
@@ -94,14 +126,15 @@ public boolean equals(Object obj) {
94126
return false;
95127
}
96128
SingleMethod other = (SingleMethod) obj;
97-
return other.file.equals(file) && other.methodName.equals(methodName);
129+
return other.file.equals(file) && other.methodName.equals(methodName) && other.nestedClass.equals(nestedClass);
98130
}
99131

100132
@Override
101133
public int hashCode() {
102134
int hash = 7;
103135
hash = 29 * hash + this.file.hashCode();
104136
hash = 29 * hash + this.methodName.hashCode();
137+
hash = 29 * hash + this.nestedClass.hashCode();
105138
return hash;
106139
}
107140
}

java/gradle.java/src/org/netbeans/modules/gradle/java/GradleJavaTokenProvider.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.netbeans.api.java.source.ClasspathInfo;
3535
import org.netbeans.api.java.source.SourceUtils;
3636
import org.netbeans.api.project.Project;
37+
import org.netbeans.spi.project.NestedClass;
3738
import org.netbeans.spi.project.SingleMethod;
3839
import org.openide.filesystems.FileObject;
3940
import org.openide.filesystems.FileUtil;
@@ -84,8 +85,9 @@ public Map<String, String> createReplacements(String action, Lookup context) {
8485

8586
private void processSelectedPackageAndClass(final Map<String, String> map, Lookup context) {
8687
FileObject fo = RunUtils.extractFileObjectfromLookup(context);
88+
NestedClass nestedClass = context.lookup(NestedClass.class);
8789
GradleJavaProject gjp = GradleJavaProject.get(project);
88-
String className = evaluateClassName(gjp, fo);
90+
String className = evaluateClassName(gjp, fo, nestedClass);
8991
if (className != null) {
9092
map.put(SELECTED_CLASS, className);
9193
int dot = className.lastIndexOf('.');
@@ -104,7 +106,8 @@ private void processSelectedMethod(final Map<String, String> map, Lookup context
104106
FileObject fo = method != null ? method.getFile() : RunUtils.extractFileObjectfromLookup(context);
105107
if ((fo != null) && fo.isData()) {
106108
GradleJavaProject gjp = GradleJavaProject.get(project);
107-
String className = evaluateClassName(gjp, fo);
109+
NestedClass nestedClass = method != null ? method.getNestedClass() : context.lookup(NestedClass.class);
110+
String className = evaluateClassName(gjp, fo, nestedClass);
108111
String selectedMethod = method != null ? className + '.' + method.getMethodName() : className;
109112
map.put(SELECTED_METHOD, selectedMethod);
110113
}
@@ -133,7 +136,7 @@ private void processSourceSets(final Map<String, String> map, Lookup context) {
133136
}
134137
}
135138

136-
private String evaluateClassName(GradleJavaProject gjp, FileObject fo) {
139+
private String evaluateClassName(GradleJavaProject gjp, FileObject fo, NestedClass nestedClass) {
137140
String ret = null;
138141
if ((gjp != null) && (fo != null)) {
139142
File f = FileUtil.toFile(fo);
@@ -145,7 +148,7 @@ private String evaluateClassName(GradleJavaProject gjp, FileObject fo) {
145148
ret = relPath.replace('/', '.');
146149
ret = ret + '*';
147150
} else {
148-
ret = SourceUtils.classNameFor(ClasspathInfo.create(fo), relPath);
151+
ret = SourceUtils.classNameFor(ClasspathInfo.create(fo), relPath, nestedClass);
149152
}
150153
}
151154
}

java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import org.netbeans.spi.project.ActionProgress;
7979
import org.netbeans.spi.project.ActionProvider;
8080
import org.netbeans.api.project.ContainedProjectFilter;
81+
import org.netbeans.spi.project.NestedClass;
8182
import org.netbeans.spi.project.ProjectConfiguration;
8283
import org.netbeans.spi.project.ProjectConfigurationProvider;
8384
import org.netbeans.spi.project.SingleMethod;
@@ -126,14 +127,22 @@ protected void notifyFinished(DebugAdapterContext ctx, boolean success) {
126127
}
127128

128129
public final CompletableFuture<Void> nbLaunch(FileObject toRun, boolean preferProjActions, @NullAllowed File nativeImageFile,
129-
@NullAllowed String method, Map<String, Object> launchArguments, DebugAdapterContext context,
130+
@NullAllowed String method, @NullAllowed String nestedClassName, Map<String, Object> launchArguments, DebugAdapterContext context,
130131
boolean debug, boolean testRun, Consumer<NbProcessConsole.ConsoleMessage> consoleMessages,
131132
boolean testInParallel) {
132133
CompletableFuture<Void> launchFuture = new CompletableFuture<>();
133134
NbProcessConsole ioContext = new NbProcessConsole(consoleMessages);
135+
NestedClass nestedClass;
136+
if (nestedClassName != null) {
137+
nestedClass = new NestedClass(nestedClassName, toRun);
138+
} else {
139+
nestedClass = null;
140+
}
134141
SingleMethod singleMethod;
135142
if (method != null) {
136-
singleMethod = new SingleMethod(toRun, method);
143+
singleMethod = nestedClass != null ?
144+
new SingleMethod(toRun, method, nestedClass)
145+
: new SingleMethod(toRun, method);
137146
} else {
138147
singleMethod = null;
139148
}
@@ -187,7 +196,7 @@ public void close() throws IOException {
187196
}
188197
}
189198
W writer = new W();
190-
CompletableFuture<Pair<ActionProvider, String>> commandFuture = findTargetWithPossibleRebuild(prj, preferProjActions, toRun, singleMethod, debug, testRun, ioContext, testInParallel, projectFilter);
199+
CompletableFuture<Pair<ActionProvider, String>> commandFuture = findTargetWithPossibleRebuild(prj, preferProjActions, toRun, singleMethod, nestedClass, debug, testRun, ioContext, testInParallel, projectFilter);
191200
commandFuture.thenAccept((providerAndCommand) -> {
192201
ExplicitProcessParameters params = createExplicitProcessParameters(launchArguments);
193202
OperationContext ctx = OperationContext.find(Lookup.getDefault());
@@ -235,7 +244,7 @@ public void progressHandleCreated(ProgressOperationEvent e) {
235244
}
236245

237246
Lookup lookup = new ProxyLookup(
238-
createTargetLookup(prj, singleMethod, toRun, projectFilter),
247+
createTargetLookup(prj, singleMethod, nestedClass, toRun, projectFilter),
239248
Lookups.fixed(runContext.toArray(new Object[runContext.size()]))
240249
);
241250
// the execution Lookup is fully populated now. If the Project supports Configurations,
@@ -498,8 +507,8 @@ static List<String> argsToStringList(Object o) {
498507
}
499508
}
500509

501-
private static CompletableFuture<Pair<ActionProvider, String>> findTargetWithPossibleRebuild(Project proj, boolean preferProjActions, FileObject toRun, SingleMethod singleMethod, boolean debug, boolean testRun, NbProcessConsole ioContext, boolean testInParallel, ContainedProjectFilter projectFilter) throws IllegalArgumentException {
502-
Pair<ActionProvider, String> providerAndCommand = findTarget(proj, preferProjActions, toRun, singleMethod, debug, testRun, testInParallel, projectFilter);
510+
private static CompletableFuture<Pair<ActionProvider, String>> findTargetWithPossibleRebuild(Project proj, boolean preferProjActions, FileObject toRun, SingleMethod singleMethod, NestedClass nestedClass, boolean debug, boolean testRun, NbProcessConsole ioContext, boolean testInParallel, ContainedProjectFilter projectFilter) throws IllegalArgumentException {
511+
Pair<ActionProvider, String> providerAndCommand = findTarget(proj, preferProjActions, toRun, singleMethod, nestedClass, debug, testRun, testInParallel, projectFilter);
503512
if (providerAndCommand != null) {
504513
return CompletableFuture.completedFuture(providerAndCommand);
505514
}
@@ -515,7 +524,7 @@ protected void started() {
515524
@Override
516525
public void finished(boolean success) {
517526
if (success) {
518-
Pair<ActionProvider, String> providerAndCommand = findTarget(proj, preferProjActions, toRun, singleMethod, debug, testRun, testInParallel, projectFilter);
527+
Pair<ActionProvider, String> providerAndCommand = findTarget(proj, preferProjActions, toRun, singleMethod, nestedClass, debug, testRun, testInParallel, projectFilter);
519528
if (providerAndCommand != null) {
520529
afterBuild.complete(providerAndCommand);
521530
return;
@@ -548,7 +557,7 @@ public void finished(boolean success) {
548557
return afterBuild;
549558
}
550559

551-
protected static @CheckForNull Pair<ActionProvider, String> findTarget(Project prj, boolean preferProjActions, FileObject toRun, SingleMethod singleMethod, boolean debug, boolean testRun, boolean testInParallel, ContainedProjectFilter projectFilter) {
560+
protected static @CheckForNull Pair<ActionProvider, String> findTarget(Project prj, boolean preferProjActions, FileObject toRun, SingleMethod singleMethod, NestedClass nestedClass, boolean debug, boolean testRun, boolean testInParallel, ContainedProjectFilter projectFilter) {
552561
ClassPath sourceCP = ClassPath.getClassPath(toRun, ClassPath.SOURCE);
553562
FileObject fileRoot = sourceCP != null ? sourceCP.findOwnerRoot(toRun) : null;
554563
boolean mainSource;
@@ -560,7 +569,7 @@ public void finished(boolean success) {
560569
ActionProvider provider = null;
561570
String command = null;
562571
Collection<ActionProvider> actionProviders = findActionProviders(prj);
563-
Lookup testLookup = createTargetLookup(preferProjActions ? prj : null, singleMethod, toRun, projectFilter);
572+
Lookup testLookup = createTargetLookup(preferProjActions ? prj : null, singleMethod, nestedClass, toRun, projectFilter);
564573
String[] actions;
565574

566575
if (testInParallel) {
@@ -577,7 +586,7 @@ public void finished(boolean success) {
577586
if (debug && !mainSource) {
578587
// We are calling COMMAND_DEBUG_TEST_SINGLE instead of a missing COMMAND_DEBUG_TEST
579588
// This is why we need to add the file to the lookup
580-
testLookup = createTargetLookup(null, singleMethod, toRun, projectFilter);
589+
testLookup = createTargetLookup(null, singleMethod, nestedClass, toRun, projectFilter);
581590
}
582591
} else {
583592
actions = debug ? mainSource ? new String[] {ActionProvider.COMMAND_DEBUG_SINGLE}
@@ -638,7 +647,7 @@ public void invokeAction(String command, Lookup context) throws IllegalArgumentE
638647
return Pair.of(provider, command);
639648
}
640649

641-
static Lookup createTargetLookup(Project prj, SingleMethod singleMethod, FileObject toRun, ContainedProjectFilter projectFilter) {
650+
static Lookup createTargetLookup(Project prj, SingleMethod singleMethod, NestedClass nestedClass, FileObject toRun, ContainedProjectFilter projectFilter) {
642651
List<Lookup> arr = new ArrayList<>();
643652
if (prj != null) {
644653
arr.add(Lookups.singleton(prj));
@@ -647,6 +656,10 @@ static Lookup createTargetLookup(Project prj, SingleMethod singleMethod, FileObj
647656
Lookup methodLookup = Lookups.singleton(singleMethod);
648657
arr.add(methodLookup);
649658
}
659+
if (nestedClass != null) {
660+
Lookup nestedClassLookup = Lookups.singleton(nestedClass);
661+
arr.add(nestedClassLookup);
662+
}
650663
if (projectFilter != null) {
651664
Lookup projectLookup = Lookups.singleton(projectFilter);
652665
arr.add(projectLookup);

java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchRequestHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,9 @@ public CompletableFuture<Void> launch(Map<String, Object> launchArguments, Debug
245245
context.setSourcePaths((String[]) launchArguments.get("sourcePaths"));
246246
}
247247
String singleMethod = (String)launchArguments.get("methodName");
248+
String nestedClass = (String)launchArguments.get("nestedClass");
248249
boolean testInParallel = (Boolean) launchArguments.getOrDefault("testInParallel", Boolean.FALSE);
249-
activeLaunchHandler.nbLaunch(file, preferProjActions, nativeImageFile, singleMethod, launchArguments, context, !noDebug, testRun, new OutputListener(context), testInParallel).thenRun(() -> {
250+
activeLaunchHandler.nbLaunch(file, preferProjActions, nativeImageFile, singleMethod, nestedClass, launchArguments, context, !noDebug, testRun, new OutputListener(context), testInParallel).thenRun(() -> {
250251
activeLaunchHandler.postLaunch(launchArguments, context);
251252
resultFuture.complete(null);
252253
}).exceptionally(e -> {

java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegateTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public NbLaunchDelegateTest() {
4343
@Test
4444
public void testFileObjectsLookup() throws Exception {
4545
FileObject fo = FileUtil.createMemoryFileSystem().getRoot().createData("test.txt");
46-
Lookup lkp = NbLaunchDelegate.createTargetLookup(null, null, fo, null);
46+
Lookup lkp = NbLaunchDelegate.createTargetLookup(null, null, null, fo, null);
4747
assertEquals(fo, lkp.lookup(FileObject.class));
4848

4949
DataObject obj = lkp.lookup(DataObject.class);
@@ -57,7 +57,7 @@ public void testFileObjectsLookup() throws Exception {
5757
public void testFileObjectsLookupWithSingleMethod() throws Exception {
5858
FileObject fo = FileUtil.createMemoryFileSystem().getRoot().createData("test-with-method.txt");
5959
SingleMethod m = new SingleMethod(fo, "main");
60-
Lookup lkp = NbLaunchDelegate.createTargetLookup(null, m, fo, null);
60+
Lookup lkp = NbLaunchDelegate.createTargetLookup(null, m, null, fo, null);
6161
assertEquals(fo, lkp.lookup(FileObject.class));
6262

6363
DataObject obj = lkp.lookup(DataObject.class);
@@ -76,7 +76,7 @@ public void testFindsMavenProject() throws Exception {
7676
assertNotNull("Project dir recognized", prj);
7777

7878
SingleMethod m = new SingleMethod(xml, "main");
79-
Lookup lkp = NbLaunchDelegate.createTargetLookup(prj, null, null, null);
79+
Lookup lkp = NbLaunchDelegate.createTargetLookup(prj, null, null, null, null);
8080
assertNull("No file object", lkp.lookup(FileObject.class));
8181
DataObject obj = lkp.lookup(DataObject.class);
8282
assertNull("No DataObject ", obj);
@@ -92,7 +92,7 @@ public void testFindsWithFileObjectAndDataObject() throws Exception {
9292
assertNotNull("Project dir recognized", prj);
9393

9494
SingleMethod m = new SingleMethod(xml, "main");
95-
Lookup lkp = NbLaunchDelegate.createTargetLookup(prj, m, xml, null);
95+
Lookup lkp = NbLaunchDelegate.createTargetLookup(prj, m, null, xml, null);
9696
assertEquals("File object is available", xml, lkp.lookup(FileObject.class));
9797
DataObject obj = lkp.lookup(DataObject.class);
9898
assertNotNull("DataObject is available", obj);
@@ -108,7 +108,7 @@ public void testFindsWithFileObjectAndDataObjectNoMethod() throws Exception {
108108
Project prj = ProjectManager.getDefault().findProject(dir);
109109
assertNotNull("Project dir recognized", prj);
110110

111-
Lookup lkp = NbLaunchDelegate.createTargetLookup(prj, null, xml, null);
111+
Lookup lkp = NbLaunchDelegate.createTargetLookup(prj, null, null, xml, null);
112112
assertEquals("File object is available", xml, lkp.lookup(FileObject.class));
113113
DataObject obj = lkp.lookup(DataObject.class);
114114
assertNotNull("DataObject is available", obj);

0 commit comments

Comments
 (0)