From 18524270fd822604724ca07acf3b578663d64f40 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 3 Jan 2024 11:12:35 +0100 Subject: [PATCH] [NETBEANS-6881][NETBEANS-6880][NETBEANS-6921] Additional fixes for the multi-file launcher: - checking for null return result for getClassPath(COMPILE) - filtering out directories that belong to a project from the multi-file launcher source path - more thoroughly prevent creating of the multi-file launcher source ClassPath for files under projects - never touch read-only filesystems (i.e. typically jars/zip) - do not register the source CP into GlobalPathRegistry by default in the IDE - the behavior can either be overriden using a localization bundle --- .../client/bindings/CustomIndexerImpl.java | 7 +- java/java.file.launcher/nbproject/project.xml | 18 +++++ .../AttributeBasedSingleFileOptions.java | 6 +- .../java/file/launcher/SharedRootData.java | 10 +-- .../file/launcher/SingleSourceFileUtil.java | 13 +++- .../indexer/CompilerOptionsIndexer.java | 14 +++- .../queries/MultiSourceRootProvider.java | 72 ++++++++++++++++++- .../file/launcher/queries/Bundle.properties | 18 +++++ .../java/source/indexing/APTUtils.java | 8 ++- 9 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CustomIndexerImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CustomIndexerImpl.java index c46c3d4095c0..9e97e8c736d0 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CustomIndexerImpl.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CustomIndexerImpl.java @@ -51,8 +51,13 @@ public class CustomIndexerImpl extends CustomIndexer { @Override protected void index(Iterable files, Context context) { + FileObject root = context.getRoot(); + + if (root == null) { + return ; //ignore + } + handleStoredFiles(context, props -> { - FileObject root = context.getRoot(); for (Indexable i : files) { FileObject file = root.getFileObject(i.getRelativePath()); if (file != null) { diff --git a/java/java.file.launcher/nbproject/project.xml b/java/java.file.launcher/nbproject/project.xml index d5520075de76..195981d25ec1 100644 --- a/java/java.file.launcher/nbproject/project.xml +++ b/java/java.file.launcher/nbproject/project.xml @@ -140,6 +140,15 @@ 1.84 + + org.netbeans.modules.parsing.api + + + + 1 + 9.30 + + org.netbeans.modules.parsing.indexing @@ -166,6 +175,15 @@ 1.63 + + org.netbeans.spi.editor.hints + + + + 0 + 1.65 + + org.openide.filesystems diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java index 586415f3c414..cfa6c4d209aa 100644 --- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java +++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/AttributeBasedSingleFileOptions.java @@ -19,8 +19,6 @@ package org.netbeans.modules.java.file.launcher; import javax.swing.event.ChangeListener; -import org.netbeans.api.project.FileOwnerQuery; -import org.netbeans.api.project.Project; import org.netbeans.modules.java.file.launcher.queries.MultiSourceRootProvider; import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; import org.openide.filesystems.FileAttributeEvent; @@ -38,9 +36,7 @@ public class AttributeBasedSingleFileOptions implements SingleFileOptionsQueryIm @Override public Result optionsFor(FileObject file) { - Project p = FileOwnerQuery.getOwner(file); - - if (p != null) { + if (!SingleSourceFileUtil.isSupportedFile(file)) { return null; } diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java index 3c9d9099224a..98530378dd60 100644 --- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java +++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/SharedRootData.java @@ -18,16 +18,13 @@ */ package org.netbeans.modules.java.file.launcher; -import java.io.File; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.modules.java.file.launcher.api.SourceLauncher; import org.openide.filesystems.FileAttributeEvent; @@ -36,8 +33,6 @@ import org.openide.filesystems.FileEvent; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; -import org.openide.modules.SpecificationVersion; -import org.openide.util.Exceptions; /** * @@ -108,10 +103,7 @@ private synchronized void setNewProperties(Map newProperties) { } String joinedCommandLine = SourceLauncher.joinCommandLines(options.values()); try { - if ( - !root.getFileSystem().isReadOnly() // Skip read-only FSes (like JarFileSystem) - && !joinedCommandLine.equals(root.getAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS)) - ) { + if (!joinedCommandLine.equals(root.getAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS))) { root.setAttribute(SingleSourceFileUtil.FILE_VM_OPTIONS, joinedCommandLine); } } catch (IOException ex) { 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 65b6fc08fd36..0d18a7b12c3b 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 @@ -34,6 +34,7 @@ import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation.Result; import org.netbeans.spi.java.queries.CompilerOptionsQueryImplementation; import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileStateInvalidException; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; import org.openide.util.Lookup; @@ -74,13 +75,21 @@ public static FileObject getJavaFileWithoutProjectFromLookup(Lookup lookup) { } public static boolean isSingleSourceFile(FileObject fObj) { - Project p = FileOwnerQuery.getOwner(fObj); - if (p != null || !fObj.getExt().equalsIgnoreCase("java")) { //NOI18N + if (!isSupportedFile(fObj) || !fObj.getExt().equalsIgnoreCase("java")) { //NOI18N return false; } return true; } + public static boolean isSupportedFile(FileObject file) { + try { + return !MultiSourceRootProvider.DISABLE_MULTI_SOURCE_ROOT && + FileOwnerQuery.getOwner(file) == null && + !file.getFileSystem().isReadOnly(); + } catch (FileStateInvalidException ex) { + return false; + } + } public static Process compileJavaSource(FileObject fileObject) { FileObject javac = JavaPlatformManager.getDefault().getDefaultPlatform().findTool("javac"); //NOI18N File javacFile = FileUtil.toFile(javac); diff --git a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/indexer/CompilerOptionsIndexer.java b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/indexer/CompilerOptionsIndexer.java index d541b4c77600..b8fe279be40d 100644 --- a/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/indexer/CompilerOptionsIndexer.java +++ b/java/java.file.launcher/src/org/netbeans/modules/java/file/launcher/indexer/CompilerOptionsIndexer.java @@ -20,11 +20,12 @@ import org.netbeans.modules.java.file.launcher.SharedRootData; import org.netbeans.api.editor.mimelookup.MimeRegistration; -import org.netbeans.api.project.FileOwnerQuery; +import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; import org.netbeans.modules.parsing.spi.indexing.Context; import org.netbeans.modules.parsing.spi.indexing.CustomIndexer; import org.netbeans.modules.parsing.spi.indexing.CustomIndexerFactory; import org.netbeans.modules.parsing.spi.indexing.Indexable; +import org.openide.filesystems.FileObject; /** * @@ -35,10 +36,17 @@ public class CompilerOptionsIndexer extends CustomIndexer { @Override protected void index(Iterable files, Context context) { - if (FileOwnerQuery.getOwner(context.getRoot()) != null) { + FileObject root = context.getRoot(); + + if (root == null) { + return ; //ignore + } + + if (!SingleSourceFileUtil.isSupportedFile(root)) { return ; //ignore roots under projects } - SharedRootData.ensureRootRegistered(context.getRoot()); + + SharedRootData.ensureRootRegistered(root); } 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 7f5061d3fac4..ad76218b3e4a 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 @@ -21,6 +21,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -32,6 +34,7 @@ import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicReference; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.netbeans.api.java.classpath.ClassPath; @@ -41,6 +44,7 @@ import org.netbeans.api.java.platform.JavaPlatformManager; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.api.project.FileOwnerQuery; import org.netbeans.api.queries.FileEncodingQuery; import org.netbeans.modules.java.file.launcher.SingleSourceFileUtil; import org.netbeans.modules.java.file.launcher.spi.SingleFileOptionsQueryImplementation; @@ -48,11 +52,14 @@ import org.netbeans.spi.java.classpath.ClassPathImplementation; import static org.netbeans.spi.java.classpath.ClassPathImplementation.PROP_RESOURCES; import org.netbeans.spi.java.classpath.ClassPathProvider; +import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation; import org.netbeans.spi.java.classpath.PathResourceImplementation; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; import org.openide.util.Exceptions; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; @@ -89,7 +96,7 @@ public ClassPath findClassPath(FileObject file, String type) { } private ClassPath getSourcePath(FileObject file) { - if (DISABLE_MULTI_SOURCE_ROOT) return null; + if (!SingleSourceFileUtil.isSupportedFile(file)) return null; synchronized (this) { //XXX: what happens if there's a Java file in user's home??? if (file.isValid() && file.isData() && "text/x-java".equals(file.getMIMEType())) { @@ -116,8 +123,10 @@ private ClassPath getSourcePath(FileObject file) { } return root2SourceCP.computeIfAbsent(root, r -> { - ClassPath srcCP = ClassPathSupport.createClassPath(r); - GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] {srcCP}); + ClassPath srcCP = ClassPathSupport.createClassPath(Arrays.asList(new RootPathResourceImplementation(r))); + if (registerRoot(r)) { + GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] {srcCP}); + } return srcCP; }); } catch (IOException ex) { @@ -234,6 +243,13 @@ private ClassPath attributeBasedPath(FileObject file, Map } } + @Messages({ + "SETTING_AutoRegisterAsRoot=false" + }) + private static boolean registerRoot(FileObject root) { + return "true".equals(Bundle.SETTING_AutoRegisterAsRoot()); + } + private static final class AttributeBasedClassPathImplementation implements ChangeListener, ClassPathImplementation { private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final SingleFileOptionsQueryImplementation.Result delegate; @@ -299,4 +315,54 @@ public void removePropertyChangeListener(PropertyChangeListener listener) { } + private static final class RootPathResourceImplementation implements FilteringPathResourceImplementation { + + private final URL root; + private final URL[] roots; + private final AtomicReference lastCheckedAsIncluded = new AtomicReference<>(); + + public RootPathResourceImplementation(FileObject root) { + this.root = root.toURL(); + this.roots = new URL[] {this.root}; + } + + @Override + public boolean includes(URL root, String resource) { + if (!resource.endsWith("/")) { + int lastSlash = resource.lastIndexOf('/'); + if (lastSlash != (-1)) { + resource = resource.substring(0, lastSlash + 1); + } + } + if (resource.equals(lastCheckedAsIncluded.get())) { + return true; + } + FileObject fo = URLMapper.findFileObject(root); + fo = fo != null ? fo.getFileObject(resource) : null; + boolean included = fo == null || FileOwnerQuery.getOwner(fo) == null; + if (included) { + lastCheckedAsIncluded.set(resource); + } + return included; + } + + @Override + public URL[] getRoots() { + return roots; + } + + @Override + public ClassPathImplementation getContent() { + return null; + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + } + + } } diff --git a/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties b/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties new file mode 100644 index 000000000000..684c2d3a5fff --- /dev/null +++ b/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-java-file-launcher.jar/org/netbeans/modules/java/file/launcher/queries/Bundle.properties @@ -0,0 +1,18 @@ +# 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. + +SETTING_AutoRegisterAsRoot=true diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java index 5f47ca7cd445..58f01106fcb6 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java @@ -135,7 +135,9 @@ private APTUtils(@NonNull final FileObject root) { this.root = root; bootPath = ClassPath.getClassPath(root, ClassPath.BOOT); compilePath = ClassPath.getClassPath(root, ClassPath.COMPILE); - compilePath.addPropertyChangeListener(this); + if (compilePath != null) { + compilePath.addPropertyChangeListener(this); + } processorPath = new AtomicReference<>(ClassPath.getClassPath(root, JavaClassPathConstants.PROCESSOR_PATH)); processorModulePath = new AtomicReference<>(ClassPath.getClassPath(root, JavaClassPathConstants.MODULE_PROCESSOR_PATH)); aptOptions = AnnotationProcessingQuery.getAnnotationProcessingOptions(root); @@ -360,7 +362,9 @@ private ClassPath[] validatePaths() { compilePath.removePropertyChangeListener(this); } compilePath = ClassPath.getClassPath(root, ClassPath.COMPILE); - compilePath.addPropertyChangeListener(this); + if (compilePath != null) { + compilePath.addPropertyChangeListener(this); + } listenOnProcessorPath(pp, this); classLoaderCache = null; }