diff --git a/java/java.file.launcher/nbproject/project.xml b/java/java.file.launcher/nbproject/project.xml
index 23476f52a29c..dfb3b4349ff7 100644
--- a/java/java.file.launcher/nbproject/project.xml
+++ b/java/java.file.launcher/nbproject/project.xml
@@ -299,6 +299,10 @@
org.netbeans.modules.projectapi.nb
+
+ org.netbeans.modules.maven
+
+
diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java
index 06ce702f2612..36ae95019b64 100644
--- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java
+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SingleSourceFileUtil.java
@@ -33,6 +33,9 @@
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
import org.netbeans.modules.java.file.launcher.queries.MultiSourceRootProvider;
import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation;
import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation;
@@ -88,6 +91,23 @@ public static boolean isSingleSourceFile(FileObject fObj) {
return true;
}
+ /**
+ * Find a Java-like project that owns the file.
+ *
+ * @param file the file to find project for
+ * @return the project (if it has some Java sources) or {@code null} otherwise
+ */
+ public static Project findJavaOwner(FileObject file) {
+ Project owner = FileOwnerQuery.getOwner(file);
+ if (owner != null) {
+ SourceGroup[] javaSources = ProjectUtils.getSources(owner).getSourceGroups("java"); // NOI18N
+ if (javaSources.length > 0) {
+ return owner;
+ }
+ }
+ return null;
+ }
+
public static boolean isSupportedFile(FileObject file) {
if (file == null) {
return false;
@@ -95,9 +115,15 @@ public static boolean isSupportedFile(FileObject file) {
try {
FileObject dir = file.getParent();
File dirFile = dir != null ? FileUtil.toFile(dir) : null;
- return !MultiSourceRootProvider.DISABLE_MULTI_SOURCE_ROOT
- && FileOwnerQuery.getOwner(file) == null
- && !file.getFileSystem().isReadOnly()
+ if (MultiSourceRootProvider.DISABLE_MULTI_SOURCE_ROOT) {
+ return false;
+ }
+ Project owner = findJavaOwner(file);
+ if (owner != null) {
+ // no Single Java file support in Java projects
+ return false;
+ }
+ return !file.getFileSystem().isReadOnly()
&& !(dirFile != null
&& dirFile.getName().startsWith("vcs-")
&& dirFile.getAbsolutePath().startsWith(System.getProperty("java.io.tmpdir")));
diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java
index f695acef1bce..6a1e9a2780b7 100644
--- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java
+++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProvider.java
@@ -489,7 +489,7 @@ private static final class RootPathResourceImplementation implements FilteringPa
private final URL[] roots;
private final AtomicReference lastCheckedAsIncluded = new AtomicReference<>();
- public RootPathResourceImplementation(FileObject root) {
+ RootPathResourceImplementation(FileObject root) {
this.root = root.toURL();
this.roots = new URL[] {this.root};
}
@@ -507,7 +507,7 @@ public boolean includes(URL root, String resource) {
}
FileObject fo = URLMapper.findFileObject(root);
fo = fo != null ? fo.getFileObject(resource) : null;
- boolean included = fo == null || FileOwnerQuery.getOwner(fo) == null;
+ boolean included = fo == null || SingleSourceFileUtil.findJavaOwner(fo) == null;
if (included) {
lastCheckedAsIncluded.set(resource);
}
diff --git a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/JavaFileTest.java b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/JavaFileTest.java
index 31eb0572ecbc..2da06cd44223 100644
--- a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/JavaFileTest.java
+++ b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/JavaFileTest.java
@@ -19,32 +19,43 @@
package org.netbeans.modules.java.file.launcher.actions;
import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileWriter;
import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
import static junit.framework.TestCase.assertEquals;
import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil;
+import org.netbeans.spi.project.ActionProvider;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
/**
*
* @author Sarvesh Kesharwani
*/
public class JavaFileTest extends NbTestCase {
-
+
+ private FileObject javaFO;
+
public JavaFileTest(String name) {
super(name);
}
-
- public void testSingleJavaSourceRun() throws Exception {
+
+ @Override
+ protected void setUp() throws Exception {
clearWorkDir();
- File f1 = new File(getWorkDir(), "TestSingleJavaFile.java");
- try (FileWriter w = new FileWriter(f1)) {
+ FileObject root = FileUtil.toFileObject(getWorkDir());
+ root.setAttribute("fallback", true);
+ javaFO = root.createData("TestSingleJavaFile.java");
+ try (var w = new OutputStreamWriter(javaFO.getOutputStream())) {
w.write(
- """
+ """
public class TestSingleJavaFile {
public static void main (String args[]) {
System.out.print("hello world");
@@ -53,8 +64,11 @@ public static void main (String args[]) {
"""
);
}
- FileObject javaFO = FileUtil.toFileObject(f1);
- assertNotNull("FileObject found: " + f1, javaFO);
+ }
+
+
+
+ public void testSingleJavaSourceRun() throws Exception {
SingleJavaSourceRunActionProvider runActionProvider = new SingleJavaSourceRunActionProvider();
LaunchProcess process = runActionProvider.invokeActionHelper("run.single", javaFO, ExplicitProcessParameters.empty());
BufferedReader reader
@@ -79,9 +93,64 @@ public static void main (String args[]) {
}
}
}
-
+ public void testSingleFileRunAvailableInNoProject() throws Exception {
+ FileObject fallbackDir = FileUtil.toFileObject(getWorkDir());
+
+ Lookup lkp = Lookups.fixed(javaFO);
+
+ SingleJavaSourceRunActionProvider ap = new SingleJavaSourceRunActionProvider();
+ boolean runEnabled = ap.isActionEnabled(ActionProvider.COMMAND_RUN_SINGLE, lkp);
+ assertTrue("Run is enabled in Java-less project", runEnabled);
+ boolean debugEnabled = ap.isActionEnabled(ActionProvider.COMMAND_DEBUG_SINGLE, lkp);
+ assertTrue("Run is enabled in Java-less project", debugEnabled);
+ }
+
+ public void testSingleFileRunAvailableInFallbackProject() throws Exception {
+ FileObject fallbackDir = FileUtil.toFileObject(getWorkDir());
+ Project fallback = ProjectManager.getDefault().findProject(fallbackDir);
+ assertNotNull("Fallback project found", fallback);
+ final SourceGroup[] srcGroups = ProjectUtils.getSources(fallback).getSourceGroups("java");
+ assertEquals("No Java sources in there", 0, srcGroups.length);
+
+ Lookup lkp = Lookups.fixed(javaFO);
+
+ SingleJavaSourceRunActionProvider ap = new SingleJavaSourceRunActionProvider();
+ boolean runEnabled = ap.isActionEnabled(ActionProvider.COMMAND_RUN_SINGLE, lkp);
+ assertTrue("Run is enabled in Java-less project", runEnabled);
+ boolean debugEnabled = ap.isActionEnabled(ActionProvider.COMMAND_DEBUG_SINGLE, lkp);
+ assertTrue("Run is enabled in Java-less project", debugEnabled);
+ }
+
+ public void testSingleFileRunDisableInAMavenProject() throws Exception {
+ FileObject mavenDir = FileUtil.toFileObject(getWorkDir());
+ FileObject pom = mavenDir.createData("pom.xml");
+ try (var os = pom.getOutputStream()) {
+ os.write("""
+
+ 4.0.0
+ com.example
+ my-app
+ 1.0.0
+
+ """.getBytes());
+ }
+ FileObject emptyJava = FileUtil.createData(mavenDir, "src/main/java/org/Empty.java");
+ Project mavenPrj = ProjectManager.getDefault().findProject(mavenDir);
+ assertNotNull("Found Maven project", mavenPrj);
+ final SourceGroup[] srcGroups = ProjectUtils.getSources(mavenPrj).getSourceGroups("java");
+ assertEquals("It has sources", 1, srcGroups.length);
+ assertTrue("Empty.java belongs in there", srcGroups[0].contains(emptyJava));
+
+ Lookup lkp = Lookups.fixed(javaFO);
+
+ SingleJavaSourceRunActionProvider ap = new SingleJavaSourceRunActionProvider();
+ boolean runEnabled = ap.isActionEnabled(ActionProvider.COMMAND_RUN_SINGLE, lkp);
+ assertFalse("Single File Run is disabled in Java project", runEnabled);
+ boolean debugEnabled = ap.isActionEnabled(ActionProvider.COMMAND_DEBUG_SINGLE, lkp);
+ assertFalse("Single File Debug is disabled in Java project", debugEnabled);
+ }
+
private boolean isJDK11OrNewer() {
return SingleSourceFileUtil.findJavaVersion() >= 11;
}
-
}
diff --git a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/NonJavaProject.java b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/NonJavaProject.java
new file mode 100644
index 000000000000..b8a930d6711c
--- /dev/null
+++ b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/NonJavaProject.java
@@ -0,0 +1,45 @@
+/*
+ * 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.java.file.launcher.actions;
+
+import org.netbeans.api.project.Project;
+import org.netbeans.spi.project.ProjectState;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Lookup;
+
+final class NonJavaProject implements Project {
+
+ private final FileObject dir;
+ private final ProjectState state;
+
+ public NonJavaProject(FileObject dir, ProjectState state) {
+ this.dir = dir;
+ this.state = state;
+ }
+
+ @Override
+ public FileObject getProjectDirectory() {
+ return dir;
+ }
+
+ @Override
+ public Lookup getLookup() {
+ return Lookup.EMPTY;
+ }
+}
diff --git a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/NonJavaProjectFactory.java b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/NonJavaProjectFactory.java
new file mode 100644
index 000000000000..2fbc7d034b32
--- /dev/null
+++ b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/actions/NonJavaProjectFactory.java
@@ -0,0 +1,47 @@
+/*
+ * 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.java.file.launcher.actions;
+
+import java.io.IOException;
+import org.netbeans.api.project.Project;
+import org.netbeans.spi.project.ProjectFactory;
+import org.netbeans.spi.project.ProjectState;
+import org.openide.filesystems.FileObject;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider(service = ProjectFactory.class, position = 9999999)
+public final class NonJavaProjectFactory implements ProjectFactory {
+
+ @Override
+ public boolean isProject(FileObject projectDirectory) {
+ return Boolean.TRUE.equals(projectDirectory.getAttribute("fallback"));
+ }
+
+ @Override
+ public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException {
+ if (isProject(projectDirectory)) {
+ return new NonJavaProject(projectDirectory, state);
+ }
+ return null;
+ }
+
+ @Override
+ public void saveProject(Project project) throws IOException, ClassCastException {
+ }
+}
diff --git a/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderProjectTest.java b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderProjectTest.java
new file mode 100644
index 000000000000..b6af83fd7731
--- /dev/null
+++ b/java/java.file.launcher/test/unit/src/org/netbeans/modules/java/file/launcher/queries/MultiSourceRootProviderProjectTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.java.file.launcher.queries;
+
+import java.io.OutputStreamWriter;
+import static junit.framework.TestCase.assertEquals;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.junit.NbTestCase;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+
+public class MultiSourceRootProviderProjectTest extends NbTestCase {
+
+ private FileObject javaFO;
+
+ public MultiSourceRootProviderProjectTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ clearWorkDir();
+ FileObject root = FileUtil.toFileObject(getWorkDir());
+ root.setAttribute("fallback", true);
+ javaFO = root.createData("TestSingleJavaFile.java");
+ try (var w = new OutputStreamWriter(javaFO.getOutputStream())) {
+ w.write(
+ """
+ public class TestSingleJavaFile {
+ public static void main (String args[]) {
+ System.out.print("hello world");
+ }
+ }
+ """
+ );
+ }
+ }
+
+ public void testMultiSourceRootInNoProject() throws Exception {
+ FileObject fallbackDir = FileUtil.toFileObject(getWorkDir());
+
+ Lookup lkp = Lookups.fixed(javaFO);
+
+ var provider = new MultiSourceRootProvider();
+ ClassPath path = provider.findClassPath(javaFO, ClassPath.SOURCE);
+ assertNotNull("Path found", path);
+ assertTrue("Path contains", path.contains(javaFO));
+ }
+
+ public void testMultiSourceRootInFallbackProject() throws Exception {
+ FileObject fallbackDir = FileUtil.toFileObject(getWorkDir());
+ Project fallback = ProjectManager.getDefault().findProject(fallbackDir);
+ assertNotNull("Fallback project found", fallback);
+ final SourceGroup[] srcGroups = ProjectUtils.getSources(fallback).getSourceGroups("java");
+ assertEquals("No Java sources in there", 0, srcGroups.length);
+
+ var provider = new MultiSourceRootProvider();
+ ClassPath path = provider.findClassPath(javaFO, ClassPath.SOURCE);
+ assertNotNull("Path found", path);
+ assertTrue("Path contains", path.contains(javaFO));
+ }
+
+ public void testMultiSourceRootInAMavenProject() throws Exception {
+ FileObject mavenDir = FileUtil.toFileObject(getWorkDir());
+ FileObject pom = mavenDir.createData("pom.xml");
+ try (var os = pom.getOutputStream()) {
+ os.write("""
+
+ 4.0.0
+ com.example
+ my-app
+ 1.0.0
+
+ """.getBytes());
+ }
+ FileObject emptyJava = FileUtil.createData(mavenDir, "src/main/java/org/Empty.java");
+ Project mavenPrj = ProjectManager.getDefault().findProject(mavenDir);
+ assertNotNull("Found Maven project", mavenPrj);
+ final SourceGroup[] srcGroups = ProjectUtils.getSources(mavenPrj).getSourceGroups("java");
+ assertEquals("It has sources", 1, srcGroups.length);
+ assertTrue("Empty.java belongs in there", srcGroups[0].contains(emptyJava));
+
+ var provider = new MultiSourceRootProvider();
+ ClassPath path = provider.findClassPath(javaFO, ClassPath.SOURCE);
+ assertNull("No Path for maven project", path);
+ }
+}
diff --git a/java/java.source/src/org/netbeans/modules/java/JavaNode.java b/java/java.source/src/org/netbeans/modules/java/JavaNode.java
index 96e74677be52..f37541af3bb2 100644
--- a/java/java.source/src/org/netbeans/modules/java/JavaNode.java
+++ b/java/java.source/src/org/netbeans/modules/java/JavaNode.java
@@ -64,6 +64,7 @@
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.queries.FileBuiltQuery;
import org.netbeans.api.queries.FileBuiltQuery.Status;
import org.netbeans.modules.classfile.Access;
@@ -241,8 +242,9 @@ protected final Sheet createSheet () {
// "single-file" java programs
Project parentProject = FileOwnerQuery.getOwner(super.getDataObject().getPrimaryFile());
DataObject dObj = super.getDataObject();
- // If any of the parent folders is a project, user won't have the option to specify these attributes to the java files.
- if (parentProject == null) {
+ if (parentProject == null || ProjectUtils.getSources(parentProject).getSourceGroups("java").length == 0) {
+ // only provide thise java.filelauncher project properties
+ // when the file is non in a Java-like project
Sheet.Set ss = new Sheet.Set();
ss.setName("runFileArguments"); // NOI18N
ss.setDisplayName(getMessage(JavaNode.class, "LBL_JavaNode_without_project_run")); // NOI18N