From f89ed19f425a7b901ba3541cf6041ede56d17eed Mon Sep 17 00:00:00 2001 From: Mickael Istria Date: Tue, 16 May 2023 13:16:31 +0200 Subject: [PATCH] Use linked resource instead of filesystem For all metadata files, except the .project (which may be harder to move), use linked resources instead of filesystem hack which can return invalid locations through standard API. This copies some logic from the org.eclipse.jdt.ls.filesystem to ProjectsManager, because we cannot have the filesystem bundle requiring and referencing the main jdt.ls bundle as this would cause bundle/class cycle and loading error. This also remove the InvisibleProjectMetadataTest.testMetadataFileLocation() test because creating a new project in Eclipse workspace now enforces creation of a .settings/org.eclipse.core.resources.prefs file. Where the metadata for the invisible project is stored doesn't matter as it is internal anyway. Also removes some usage of gson types which can cause conflicts when multiple versions are loaded. --- .../managers/GradleProjectImporter.java | 17 +++ .../managers/MavenProjectImporter.java | 13 +- .../internal/managers/ProjectsManager.java | 98 +++++++++++++++ .../managers/WorkspaceOperationsWithLink.java | 113 ++++++++++++++++++ .../gradle/checksums/WrapperValidator.java | 24 +--- .../ls/core/internal/filesystem/JLSFile.java | 9 +- .../internal/filesystem/JLSFileSystem.java | 5 +- .../core/internal/filesystem/JLSFsUtils.java | 63 +++------- .../ls/core/internal/filesystem/AllTests.java | 14 +++ .../EclipseProjectMetadataFileTest.java | 4 +- .../GradleProjectMetadataFileTest.java | 13 +- .../InvisibleProjectMetadataFileTest.java | 32 +---- .../internal/filesystem/JLSFsUtilsTest.java | 11 +- .../MavenProjectMetadataFileTest.java | 21 ++-- 14 files changed, 309 insertions(+), 128 deletions(-) create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/WorkspaceOperationsWithLink.java create mode 100644 org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/AllTests.java diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java index 11fea843ae..dbea58a70a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java @@ -28,7 +28,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Dictionary; import java.util.HashSet; +import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -48,6 +50,7 @@ import org.eclipse.buildship.core.internal.DefaultGradleBuild; import org.eclipse.buildship.core.internal.preferences.PersistentModel; import org.eclipse.buildship.core.internal.util.gradle.GradleVersion; +import org.eclipse.buildship.core.internal.workspace.WorkspaceOperations; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; @@ -83,6 +86,9 @@ import org.eclipse.lsp4j.MessageType; import org.gradle.tooling.model.build.BuildEnvironment; import org.gradle.tooling.model.build.GradleEnvironment; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; /** * @author Fred Bricon @@ -127,6 +133,17 @@ public class GradleProjectImporter extends AbstractProjectImporter { .replaceAll("\n", System.lineSeparator()); //@formatter:on + + public GradleProjectImporter() { + // replace the project creation in buildship to hook in usage of linked resources + BundleContext context = CorePlugin.getInstance().getBundle().getBundleContext(); + ServiceReference serviceReference = context.getServiceReference(WorkspaceOperations.class); + Dictionary props = new Hashtable<>(1, 1); + props.put(Constants.SERVICE_RANKING, 2); + context.ungetService(serviceReference); + context.registerService(WorkspaceOperations.class, new WorkspaceOperationsWithLink(), props); + } + /* (non-Javadoc) * @see org.eclipse.jdt.ls.core.internal.managers.IProjectImporter#applies(org.eclipse.core.runtime.IProgressMonitor) */ diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java index c9d37bb2ac..514acc43da 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/MavenProjectImporter.java @@ -54,6 +54,7 @@ import org.eclipse.m2e.core.lifecyclemapping.model.PluginExecutionAction; import org.eclipse.m2e.core.project.IMavenProjectImportResult; import org.eclipse.m2e.core.project.IProjectConfigurationManager; +import org.eclipse.m2e.core.project.IProjectCreationListener; import org.eclipse.m2e.core.project.LocalProjectScanner; import org.eclipse.m2e.core.project.MavenProjectInfo; import org.eclipse.m2e.core.project.ProjectImportConfiguration; @@ -208,6 +209,14 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException, Op } if (!toImport.isEmpty()) { ProjectImportConfiguration importConfig = new ProjectImportConfiguration(); + IProjectCreationListener linkFoldersUponProjectCreation = ProjectsManager.generatesMetadataFilesAtProjectRoot() ? null : + p -> { + try { + ProjectsManager.linkResources(p); + } catch (CoreException ex) { + JavaLanguageServerPlugin.logException(ex); + } + }; if (toImport.size() > artifactIds.size()) { // Ensure project name is unique when same artifactId importConfig.setProjectNameTemplate(DUPLICATE_ARTIFACT_TEMPLATE); @@ -226,7 +235,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException, Op while (i++ < MAX_PROJECTS_TO_IMPORT && iter.hasNext()) { importPartial.add(iter.next()); } - List result = configurationManager.importProjects(importPartial, importConfig, monitor2.split(MAX_PROJECTS_TO_IMPORT)); + List result = configurationManager.importProjects(importPartial, importConfig, linkFoldersUponProjectCreation, monitor2.split(MAX_PROJECTS_TO_IMPORT)); results.addAll(result); monitor2.setWorkRemaining(toImport.size() * 2 - it * MAX_PROJECTS_TO_IMPORT); } @@ -238,7 +247,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException, Op updateProjects(imported, lastWorkspaceStateSaved, monitor2.split(projects.size())); monitor2.done(); } else { - configurationManager.importProjects(toImport, importConfig, subMonitor.split(75)); + configurationManager.importProjects(toImport, importConfig, linkFoldersUponProjectCreation, subMonitor.split(75)); } } subMonitor.setWorkRemaining(20); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/ProjectsManager.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/ProjectsManager.java index 1fec878ad3..a52eff6d77 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/ProjectsManager.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/ProjectsManager.java @@ -15,7 +15,9 @@ import static org.eclipse.jdt.ls.core.internal.JVMConfigurator.configureJVMSettings; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; @@ -29,9 +31,11 @@ import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.internal.preferences.EclipsePreferences; import org.eclipse.core.internal.resources.CharsetManager; import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.resources.FileInfoMatcherDescription; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; @@ -52,6 +56,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; @@ -343,6 +348,45 @@ public static IProject createJavaProject(IProject project, IProgressMonitor moni return createJavaProject(project, null, "src", "bin", monitor); } + /* + * ⚠ These value is duplicated in ProjectsManager as both bundles must remain independent, + * but the same dir should be used for .project or .settings/.classpath. + * So when updating one, think about updating the other. + */ + public static final String GENERATES_METADATA_FILES_AT_PROJECT_ROOT = "java.import.generatesMetadataFilesAtProjectRoot"; + public static final IPath METADATA_FOLDER_PATH = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(".projects"); + + /** + * Check whether the metadata files needs to be generated at project root. + */ + public static boolean generatesMetadataFilesAtProjectRoot() { + String property = System.getProperty(GENERATES_METADATA_FILES_AT_PROJECT_ROOT); + if (property == null) { + return true; + } + return Boolean.parseBoolean(property); + } + + /** + * Get the redirected path of the input path. The path will be redirected to + * the workspace's metadata folder ({@link JLSFsUtils#METADATA_FOLDER_PATH}). + * @param projectName name of the project. + * @param path path needs to be redirected. + * @return the redirected path. + */ + public static IPath getMetaDataFilePath(String projectName, IPath path) { + if (path.segmentCount() == 1) { + return METADATA_FOLDER_PATH.append(projectName).append(path); + } + + String lastSegment = path.lastSegment(); + if (IProjectDescription.DESCRIPTION_FILE_NAME.equals(lastSegment)) { + return METADATA_FOLDER_PATH.append(projectName).append(lastSegment); + } + + return null; + } + public static IProject createJavaProject(IProject project, IPath projectLocation, String src, String bin, IProgressMonitor monitor) throws CoreException, OperationCanceledException { if (project.exists()) { return project; @@ -355,6 +399,9 @@ public static IProject createJavaProject(IProject project, IPath projectLocation } project.create(description, monitor); project.open(monitor); + if (!generatesMetadataFilesAtProjectRoot()) { + linkResources(project); + } //Turn into Java project description = project.getDescription(); @@ -396,6 +443,49 @@ public static IProject createJavaProject(IProject project, IPath projectLocation return project; } + public static void linkResources(IProject project) throws CoreException { + { + IFolder settingsFolder = project.getFolder(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME); + if (!settingsFolder.exists()) { + File diskFolder = getMetaDataFilePath(settingsFolder.getProject().getName(), settingsFolder.getProjectRelativePath()).toFile(); + diskFolder.mkdirs(); + settingsFolder.createLink(diskFolder.toURI(), IResource.NONE, new NullProgressMonitor()); + } + } + { + IFile classpathFile = project.getFile(IJavaProject.CLASSPATH_FILE_NAME); + if (!classpathFile.exists()) { + File diskFile = getMetaDataFilePath(classpathFile.getProject().getName(), classpathFile.getProjectRelativePath()).toFile(); + if (!diskFile.exists()) { + try { + diskFile.getParentFile().mkdirs(); + diskFile.createNewFile(); + } catch (IOException ex) { + throw new CoreException(Status.error(diskFile + " cannot be created", ex)); //$NON-NLS-1$ + } + } + classpathFile.createLink(diskFile.toURI(), IResource.NONE, new NullProgressMonitor()); + classpathFile.setContents(new ByteArrayInputStream("".getBytes()), 0, null); + } + } + { + IFile factorypathFile = project.getFile(".factorypath"); + if (!factorypathFile.exists()) { + File diskFile = getMetaDataFilePath(factorypathFile.getProject().getName(), factorypathFile.getProjectRelativePath()).toFile(); + if (!diskFile.exists()) { + try { + diskFile.getParentFile().mkdirs(); + diskFile.createNewFile(); + } catch (IOException ex) { + throw new CoreException(Status.error(diskFile + " cannot be created", ex)); //$NON-NLS-1$ + } + } + factorypathFile.createLink(diskFile.toURI(), IResource.NONE, new NullProgressMonitor()); + factorypathFile.setContents(new ByteArrayInputStream("".getBytes()), 0, null); + } + } + } + @Override public Job updateProject(IProject project, boolean force) { if (project == null || ProjectUtils.isInternalBuildSupport(BuildSupportManager.find(project).orElse(null))) { @@ -426,6 +516,7 @@ public IStatus runInWorkspace(IProgressMonitor monitor) { updateEncoding(monitor); project.deleteMarkers(BUILD_FILE_MARKER_TYPE, false, IResource.DEPTH_ONE); long elapsed = System.currentTimeMillis() - start; + replaceLinkedMetadataWithLocal(project); JavaLanguageServerPlugin.logInfo("Updated " + projectName + " in " + elapsed + " ms"); } catch (Exception e) { String msg = "Error updating " + projectName; @@ -625,5 +716,12 @@ public void reportProjectsStatus() { JavaLanguageServerPlugin.sendStatus(ServiceStatus.ProjectStatus, "OK"); } } + + private void replaceLinkedMetadataWithLocal(IProject p) throws CoreException { + if (new File(p.getLocation().toFile(), IJavaProject.CLASSPATH_FILE_NAME).exists() && + p.getFile(IJavaProject.CLASSPATH_FILE_NAME).isLinked()) { + p.getFile(IJavaProject.CLASSPATH_FILE_NAME).delete(false, false, null); + } + } } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/WorkspaceOperationsWithLink.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/WorkspaceOperationsWithLink.java new file mode 100644 index 0000000000..f108b2dd16 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/WorkspaceOperationsWithLink.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.managers; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.eclipse.buildship.core.internal.workspace.DefaultWorkspaceOperations; +import org.eclipse.buildship.core.internal.workspace.WorkspaceOperations; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; + +public class WorkspaceOperationsWithLink implements WorkspaceOperations { + + private final WorkspaceOperations delegate = new DefaultWorkspaceOperations(); + + @Override + public ImmutableList getAllProjects() { + return delegate.getAllProjects(); + } + + @Override + public Optional findProjectByName(String name) { + return delegate.findProjectByName(name); + } + + @Override + public Optional findProjectByLocation(File location) { + return delegate.findProjectByLocation(location); + } + + @Override + public Optional findProjectDescriptor(File location, IProgressMonitor monitor) { + return delegate.findProjectDescriptor(location, monitor); + } + + @Override + public IProject createProject(String name, File location, List natureIds, IProgressMonitor monitor) { + IProject res = delegate.createProject(name, location, natureIds, monitor); + if (!ProjectsManager.generatesMetadataFilesAtProjectRoot()) { + try { + ProjectsManager.linkResources(res); + } catch (CoreException ex) { + JavaLanguageServerPlugin.logException(ex); + } + } + return res; + } + + @Override + public IProject includeProject(IProjectDescription projectDescription, List extraNatureIds, IProgressMonitor monitor) { + return delegate.includeProject(projectDescription, extraNatureIds, monitor); + } + + @Override + public void refreshProject(IProject project, IProgressMonitor monitor) { + delegate.refreshProject(project, monitor); + } + + @Override + public void addNature(IProject project, String natureId, IProgressMonitor monitor) { + delegate.addNature(project, natureId, monitor); + } + + @Override + public void removeNature(IProject project, String natureId, IProgressMonitor monitor) { + delegate.removeNature(project, natureId, monitor); + } + + @Override + public void addBuildCommand(IProject project, String name, Map arguments, IProgressMonitor monitor) { + delegate.addBuildCommand(project, name, arguments, monitor); + } + + @Override + public void removeBuildCommand(IProject project, String name, IProgressMonitor monitor) { + delegate.removeBuildCommand(project, name, monitor); + } + + @Override + public void validateProjectName(String name, File location) { + delegate.validateProjectName(name, location); + } + + @Override + public IProject renameProject(IProject project, String newName, IProgressMonitor monitor) { + return delegate.renameProject(project, newName, monitor); + } + + @Override + public boolean isNatureRecognizedByEclipse(String natureId) { + return delegate.isNatureRecognizedByEclipse(natureId); + } + + @Override + public boolean isWtpInstalled() { + return delegate.isWtpInstalled(); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/internal/gradle/checksums/WrapperValidator.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/internal/gradle/checksums/WrapperValidator.java index f70020340f..d8673908bb 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/internal/gradle/checksums/WrapperValidator.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/internal/gradle/checksums/WrapperValidator.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -47,10 +48,6 @@ import org.osgi.framework.Bundle; import com.google.common.base.Charsets; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; import com.google.common.io.CharStreams; import com.google.common.io.Closeables; import com.google.gson.Gson; @@ -110,21 +107,10 @@ public ValidationResult checkWrapper(String baseDir) throws CoreException { }; List> versions = gson.fromJson(json, typeToken.getType()); //@formatter:off - ImmutableList urls = FluentIterable - .from(versions) - .filter(new Predicate>() { - @Override - public boolean apply(Map input) { - return input.get(WRAPPER_CHECKSUM_URL) != null; - } - }) - .transform(new Function, String>() { - @Override - public String apply(Map input) { - return input.get(WRAPPER_CHECKSUM_URL); - } - }) - .toList(); + List urls = versions.stream() + .map(input -> input.get(WRAPPER_CHECKSUM_URL)) + .filter(Objects::nonNull) + .toList(); // @formatter:on DownloadChecksumJob downloadJob = new DownloadChecksumJob(); int count = 0; diff --git a/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFile.java b/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFile.java index f5770486f3..4875bc6c7f 100644 --- a/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFile.java +++ b/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFile.java @@ -20,6 +20,7 @@ import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.internal.filesystem.local.LocalFile; +import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; @@ -56,11 +57,9 @@ public String[] childNames(int options, IProgressMonitor monitor) { } Set childNameSet = new LinkedHashSet<>(Arrays.asList(childNames)); - for (String fileName : JLSFsUtils.METADATA_NAMES) { - if (!childNameSet.contains(fileName) && - JLSFsUtils.METADATA_FOLDER_PATH.append(projectName).append(fileName).toFile().exists()) { - childNameSet.add(fileName); - } + if (!childNameSet.contains(IProjectDescription.DESCRIPTION_FILE_NAME) && + JLSFsUtils.METADATA_FOLDER_PATH.append(projectName).append(IProjectDescription.DESCRIPTION_FILE_NAME).toFile().exists()) { + childNameSet.add(IProjectDescription.DESCRIPTION_FILE_NAME); } return childNameSet.toArray(String[]::new); diff --git a/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFileSystem.java b/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFileSystem.java index 7e0d8682c7..96562c19d2 100644 --- a/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFileSystem.java +++ b/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFileSystem.java @@ -23,8 +23,9 @@ /** * JDT.LS's own implementation of file system to handle the 'file' scheme uri. - * The purpose of this implementation is to allow the project metadata files (.project, .classpath, .settings/) - * can be persisted out of the project root. + * The purpose of this implementation is to allow the .project files can be + * persisted out of the project root, since for the moment using linked resources + * seem not possible. */ public class JLSFileSystem extends LocalFileSystem { diff --git a/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtils.java b/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtils.java index b898ccda93..8831e4621b 100644 --- a/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtils.java +++ b/org.eclipse.jdt.ls.filesystem/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtils.java @@ -13,18 +13,13 @@ package org.eclipse.jdt.ls.core.internal.filesystem; -import java.util.Arrays; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; -import org.eclipse.core.internal.preferences.EclipsePreferences; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.core.IJavaProject; /** * Utilities of the file system implementation. @@ -32,24 +27,20 @@ public class JLSFsUtils { private JLSFsUtils() {} - static final IPath METADATA_FOLDER_PATH = ResourcesPlugin.getPlugin().getStateLocation().append(".projects"); - /** - * The system property key to specify the file system mode. - */ - static final String GENERATES_METADATA_FILES_AT_PROJECT_ROOT = "java.import.generatesMetadataFilesAtProjectRoot"; - - static final String FACTORY_PATH = ".factorypath"; - + * ⚠ This value is duplicated in ProjectsManager as both bundles must remain independent, + * but the same dir should be used for .project or .settings/.classpath. + * So when updating one, think about updating the other. + **/ + static final IPath METADATA_FOLDER_PATH = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(".projects");; /** - * The metadata files + * The system property key to specify the file system mode. + * + * This value is duplicated in ProjectsManager as both bundles must remain independent, + * but the same dir should be used for .project or .settings/.classpath. + * So when updating one, think about updating the other. */ - static final Set METADATA_NAMES = new HashSet<>(Arrays.asList( - IProjectDescription.DESCRIPTION_FILE_NAME, - EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME, - IJavaProject.CLASSPATH_FILE_NAME, - FACTORY_PATH - )); + static final String GENERATES_METADATA_FILES_AT_PROJECT_ROOT = "java.import.generatesMetadataFilesAtProjectRoot"; /** * Determine whether the resource should be stored in workspace's metadata folder. @@ -77,9 +68,6 @@ static boolean shouldStoreInMetadataArea(IPath location) { // do not redirect if the file already exists on disk if (location.toFile().exists()) { return false; - } else if (location.lastSegment().endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) { - location = location.removeLastSegments(1); - return !location.toFile().exists(); } return true; @@ -91,23 +79,9 @@ static boolean shouldStoreInMetadataArea(IPath location) { * @return whether the given location points to a metadata file. */ static boolean isProjectMetadataFile(IPath location) { - if (location == null || location.segmentCount() < 2) { - return false; - } - - if (location.lastSegment().endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) { - location = location.removeLastSegments(1); - } - - if (location.segmentCount() < 2) { - return false; - } - - if (!METADATA_NAMES.contains(location.lastSegment())) { - return false; - } - - return true; + return location != null && // + location.segmentCount() != 2 && // + IProjectDescription.DESCRIPTION_FILE_NAME.equals(location.lastSegment()); } /** @@ -116,9 +90,6 @@ static boolean isProjectMetadataFile(IPath location) { * @param filePath the file path. */ static IPath getContainerPath(IPath filePath) { - if (filePath.lastSegment().endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) { - filePath = filePath.removeLastSegments(1); - } return filePath.removeLastSegments(1); } @@ -152,12 +123,8 @@ static IPath getMetaDataFilePath(String projectName, IPath path) { } String lastSegment = path.lastSegment(); - if (METADATA_NAMES.contains(lastSegment)) { + if (IProjectDescription.DESCRIPTION_FILE_NAME.equals(lastSegment)) { return METADATA_FOLDER_PATH.append(projectName).append(lastSegment); - } else if (lastSegment.endsWith(EclipsePreferences.PREFS_FILE_EXTENSION)) { - return METADATA_FOLDER_PATH.append(projectName) - .append(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME) - .append(lastSegment); } return null; diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/AllTests.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/AllTests.java new file mode 100644 index 0000000000..75f9434009 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/AllTests.java @@ -0,0 +1,14 @@ +package org.eclipse.jdt.ls.core.internal.filesystem; + +import org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ GradleProjectImporterTest.class, + + GradleProjectMetadataFileTest.class, }) +public class AllTests { + +} diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/EclipseProjectMetadataFileTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/EclipseProjectMetadataFileTest.java index 8869434fa3..73491d6ac1 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/EclipseProjectMetadataFileTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/EclipseProjectMetadataFileTest.java @@ -31,6 +31,8 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManagerTest; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager.CHANGE_TYPE; import org.junit.Before; import org.junit.Test; @@ -51,7 +53,7 @@ public static Collection data(){ @Before public void setUp() { - System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); + System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); } @Test diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/GradleProjectMetadataFileTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/GradleProjectMetadataFileTest.java index 1fd2ab9cc8..ea69d01b6c 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/GradleProjectMetadataFileTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/GradleProjectMetadataFileTest.java @@ -41,6 +41,7 @@ import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.WorkspaceHelper; import org.eclipse.jdt.ls.core.internal.managers.AbstractGradleBasedTest; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager.CHANGE_TYPE; import org.eclipse.jdt.ls.core.internal.preferences.Preferences.FeatureStatus; import org.junit.Before; @@ -62,7 +63,7 @@ public static Collection data(){ @Before public void setup() throws Exception { - System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); + System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); } @Test @@ -77,13 +78,13 @@ public void testMetadataFileLocation() throws Exception { // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath projectDescriptionPath = FileUtil.toPath(projectDescription.getLocationURI()); assertTrue(projectDescriptionPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); IFile preferencesFile = project.getFile(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath preferencesPath = FileUtil.toPath(preferencesFile.getLocationURI()); assertTrue(preferencesPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(preferencesPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(preferencesPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); // then we check the sub-module project = WorkspaceHelper.getProject("app"); @@ -91,19 +92,19 @@ public void testMetadataFileLocation() throws Exception { // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 projectDescriptionPath = FileUtil.toPath(projectDescription.getLocationURI()); assertTrue(projectDescriptionPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); IFile classpath = project.getFile(IJavaProject.CLASSPATH_FILE_NAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath classpathPath = FileUtil.toPath(classpath.getLocationURI()); assertTrue(classpathPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(classpathPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(classpathPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); preferencesFile = project.getFile(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 preferencesPath = FileUtil.toPath(preferencesFile.getLocationURI()); assertTrue(preferencesPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(preferencesPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(preferencesPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); } @Test diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/InvisibleProjectMetadataFileTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/InvisibleProjectMetadataFileTest.java index 2204ec78a3..cfc85bd527 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/InvisibleProjectMetadataFileTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/InvisibleProjectMetadataFileTest.java @@ -19,17 +19,13 @@ import java.util.Arrays; import java.util.Collection; -import org.eclipse.core.internal.preferences.EclipsePreferences; -import org.eclipse.core.internal.utils.FileUtil; -import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectDescription; -import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.ls.core.internal.TestVMType; import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,31 +45,7 @@ public static Collection data(){ @Before public void setUp() { - System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); - } - - @Test - public void testMetadataFileLocation() throws Exception { - IProject project = copyAndImportFolder("singlefile/simple", "src/App.java"); - assertTrue(project.exists()); - - IFile projectDescription = project.getFile(IProjectDescription.DESCRIPTION_FILE_NAME); - // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 - IPath projectDescriptionPath = FileUtil.toPath(projectDescription.getLocationURI()); - assertTrue(projectDescriptionPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); - - IFile classpath = project.getFile(IJavaProject.CLASSPATH_FILE_NAME); - // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 - IPath classpathPath = FileUtil.toPath(classpath.getLocationURI()); - assertTrue(classpathPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(classpathPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); - - IFile preferencesFile = project.getFile(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME); - // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 - IPath preferencesPath = FileUtil.toPath(preferencesFile.getLocationURI()); - assertTrue(preferencesPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(preferencesPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); } @Test diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtilsTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtilsTest.java index 51699f796a..afb55e0ca8 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtilsTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/JLSFsUtilsTest.java @@ -16,23 +16,24 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.junit.Test; public class JLSFsUtilsTest { @Test public void testGeneratesMetadataFilesAtProjectRoot() { - System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, "true"); - assertTrue(JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, "true"); + assertTrue(ProjectsManager.generatesMetadataFilesAtProjectRoot()); } @Test public void testNotGeneratesMetadataFilesAtProjectRoot() { - System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, "false"); - assertFalse(JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, "false"); + assertFalse(ProjectsManager.generatesMetadataFilesAtProjectRoot()); } @Test public void testGeneratesMetadataFilesAtProjectRootWhenNotSet() { - assertTrue(JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertTrue(ProjectsManager.generatesMetadataFilesAtProjectRoot()); } } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/MavenProjectMetadataFileTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/MavenProjectMetadataFileTest.java index e424d27507..31242d7ba6 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/MavenProjectMetadataFileTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/filesystem/MavenProjectMetadataFileTest.java @@ -35,6 +35,7 @@ import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.WorkspaceHelper; import org.eclipse.jdt.ls.core.internal.managers.AbstractMavenBasedTest; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager.CHANGE_TYPE; import org.junit.Before; import org.junit.Test; @@ -59,7 +60,7 @@ public static Collection data(){ @Before public void setup() throws Exception { - System.setProperty(JLSFsUtils.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); + System.setProperty(ProjectsManager.GENERATES_METADATA_FILES_AT_PROJECT_ROOT, fsMode); } @Test @@ -73,19 +74,19 @@ public void testMetadataFileLocation() throws Exception { // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath projectDescriptionPath = FileUtil.toPath(projectDescription.getLocationURI()); assertTrue(projectDescriptionPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); IFile classpath = project.getFile(IJavaProject.CLASSPATH_FILE_NAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath classpathPath = FileUtil.toPath(classpath.getLocationURI()); assertTrue(classpathPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(classpathPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(classpathPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); IFile preferencesFile = project.getFile(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath preferencesPath = FileUtil.toPath(preferencesFile.getLocationURI()); assertTrue(preferencesPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(preferencesPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(preferencesPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); } @Test @@ -157,30 +158,30 @@ public void testFactoryPathFileLocation() throws Exception { // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath projectDescriptionPath = FileUtil.toPath(projectDescription.getLocationURI()); assertTrue(projectDescriptionPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(projectDescriptionPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); IFile classpath = project.getFile(IJavaProject.CLASSPATH_FILE_NAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath classpathPath = FileUtil.toPath(classpath.getLocationURI()); assertTrue(classpathPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(classpathPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(classpathPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); IFile preferencesFile = project.getFile(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath preferencesPath = FileUtil.toPath(preferencesFile.getLocationURI()); assertTrue(preferencesPath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(preferencesPath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(preferencesPath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); - IFile factoryPathFile = project.getFile(JLSFsUtils.FACTORY_PATH); + IFile factoryPathFile = project.getFile(".factorypath"); // workaround to get the correct path, see: https://github.com/eclipse/eclipse.jdt.ls/pull/1900 IPath factoryPathFilePath = FileUtil.toPath(factoryPathFile.getLocationURI()); assertTrue(factoryPathFilePath.toFile().exists()); - assertEquals(project.getLocation().isPrefixOf(factoryPathFilePath), JLSFsUtils.generatesMetadataFilesAtProjectRoot()); + assertEquals(project.getLocation().isPrefixOf(factoryPathFilePath), ProjectsManager.generatesMetadataFilesAtProjectRoot()); } @Test public void testMultipleMetadataFile() throws Exception { - if (JLSFsUtils.generatesMetadataFilesAtProjectRoot()) { + if (ProjectsManager.generatesMetadataFilesAtProjectRoot()) { return; } String name = "quickstart2";