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