Skip to content

Commit

Permalink
Add commands to add/remove/list the project's source path
Browse files Browse the repository at this point in the history
Signed-off-by: Jinbo Wang <[email protected]>
  • Loading branch information
testforstephen committed Dec 17, 2018
1 parent 068d0b8 commit d37d038
Show file tree
Hide file tree
Showing 6 changed files with 513 additions and 7 deletions.
9 changes: 9 additions & 0 deletions org.eclipse.jdt.ls.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@
<command
id="java.project.resolveSourceAttachment">
</command>
<command
id="java.project.addToSourcePath">
</command>
<command
id="java.project.removeFromSourcePath">
</command>
<command
id="java.project.listSourcePaths">
</command>
</delegateCommandHandler>
</extension>
<extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.ls.core.internal.commands.BuildPathCommand;
import org.eclipse.jdt.ls.core.internal.commands.OrganizeImportsCommand;
import org.eclipse.jdt.ls.core.internal.commands.SourceAttachmentCommand;
import org.eclipse.lsp4j.WorkspaceEdit;
Expand Down Expand Up @@ -45,6 +46,14 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
return SourceAttachmentCommand.resolveSourceAttachment(arguments, monitor);
case "java.project.updateSourceAttachment":
return SourceAttachmentCommand.updateSourceAttachment(arguments, monitor);
case "java.project.addToSourcePath":
String sourceFolder = (String) arguments.get(0);
return BuildPathCommand.addToSourcePath(sourceFolder);
case "java.project.removeFromSourcePath":
String sourceFolder1 = (String) arguments.get(0);
return BuildPathCommand.removeFromSourcePath(sourceFolder1);
case "java.project.listSourcePaths":
return BuildPathCommand.listSourcePaths();
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ public static boolean isGradleProject(IProject project) {
return hasNature(project, GradleProjectNature.ID);
}

public static boolean isGeneralJavaProject(IProject project) {
return isJavaProject(project) && !isMavenProject(project) && !isGradleProject(project);
}

public static String getJavaSourceLevel(IProject project) {
Map<String, String> options = getJavaOptions(project);
return options == null ? null : options.get(JavaCore.COMPILER_SOURCE);
Expand Down Expand Up @@ -136,15 +140,65 @@ public static boolean addSourcePath(IPath sourcePath, IPath[] exclusionPaths, IJ
return true;
}

public static boolean removeSourcePath(IPath sourcePath, IJavaProject project) throws JavaModelException {
IClasspathEntry[] existingEntries = project.getRawClasspath();
List<IClasspathEntry> newEntries = new ArrayList<>();
boolean found = false;
for (IClasspathEntry entry : existingEntries) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
if (entry.getPath().equals(sourcePath)) {
found = true;
} else {
newEntries.add(removeFilters(entry, sourcePath));
}
} else {
newEntries.add(entry);
}
}

if (found) {
project.setRawClasspath(newEntries.toArray(new IClasspathEntry[0]), project.getOutputLocation(), null);
return true;
}

return false;
}

public static IPath[] listSourcePaths(IJavaProject project) throws JavaModelException {
List<IPath> result = new ArrayList<>();
for (IClasspathEntry entry : project.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
result.add(entry.getPath());
}
}

return result.toArray(new IPath[0]);
}

public static boolean isOnSourcePath(IPath sourcePath, IJavaProject project) throws JavaModelException {
for (IClasspathEntry entry : project.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE && entry.getPath().equals(sourcePath)) {
return true;
}
}

return false;
}

public static IPath findBelongedWorkspaceRoot(IPath filePath) {
PreferenceManager manager = JavaLanguageServerPlugin.getPreferencesManager();
Collection<IPath> rootPaths = manager.getPreferences().getRootPaths();
if (rootPaths != null) {
for (IPath rootPath : rootPaths) {
if (rootPath.isPrefixOf(filePath)) {
return rootPath;
}
}
}

return null;
}

public static String getWorkspaceInvisibleProjectName(IPath workspacePath) {
String fileName = workspacePath.toFile().getName();
String projectName = fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode());
Expand Down Expand Up @@ -193,4 +247,32 @@ public static IProject createInvisibleProjectIfNotExist(IPath workspaceRoot) thr

return invisibleProject;
}

private static IClasspathEntry removeFilters(IClasspathEntry entry, IPath path) {
IPath[] inclusionPatterns = entry.getInclusionPatterns();
List<IPath> inclusionList = new ArrayList<>();
if (inclusionPatterns != null) {
for (IPath pattern : inclusionPatterns) {
if (!path.equals(entry.getPath().append(pattern))) {
inclusionList.add(pattern);
}
}
}

IPath[] exclusionPatterns = entry.getExclusionPatterns();
List<IPath> exclusionList = new ArrayList<>();
if (exclusionPatterns != null) {
for (IPath pattern : exclusionPatterns) {
if (!path.equals(entry.getPath().append(pattern))) {
exclusionList.add(pattern);
}
}
}

if ((inclusionPatterns == null || inclusionPatterns.length == inclusionList.size()) && (exclusionPatterns == null || exclusionPatterns.length == exclusionList.size())) {
return entry;
} else {
return JavaCore.newSourceEntry(entry.getPath(), inclusionList.toArray(new IPath[0]), exclusionList.toArray(new IPath[0]), entry.getOutputLocation(), entry.getExtraAttributes());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*******************************************************************************
* Copyright (c) 2018 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.commands;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.Messages;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;

public class BuildPathCommand {
public static final String UNSUPPORTED_ON_MAVEN = "Unsupported operation. Please use pom.xml file to manage the source directories of maven project.";
public static final String UNSUPPORTED_ON_GRADLE = "Unsupported operation. Please use build.gradle file to manage the source directories of gradle project.";

public static Result addToSourcePath(String sourceFolderUri) {
IPath sourceFolderPath = ResourceUtils.filePathFromURI(sourceFolderUri);
IProject targetProject = findBelongedProject(sourceFolderPath);
if (targetProject != null && !ProjectUtils.isGeneralJavaProject(targetProject)) {
String message = ProjectUtils.isGradleProject(targetProject) ? UNSUPPORTED_ON_GRADLE : UNSUPPORTED_ON_MAVEN;
return new Result(false, message);
}

IPath projectLocation = null;
IContainer projectRootResource = null;
IPath[] exclusionPath = new IPath[0];
if (targetProject == null) {
try {
IPath workspaceRoot = ProjectUtils.findBelongedWorkspaceRoot(sourceFolderPath);
if (workspaceRoot == null) {
return new Result(false, Messages.format("The folder ''{0}'' doesn''t belong to any workspace.", getWorkspacePath(sourceFolderPath).toOSString()));
}

targetProject = ProjectUtils.createInvisibleProjectIfNotExist(workspaceRoot);
final IFolder workspaceLink = targetProject.getFolder(ProjectUtils.WORKSPACE_LINK);
projectLocation = workspaceRoot;
projectRootResource = workspaceLink;
List<IProject> subProjects = ProjectUtils.getVisibleProjects(workspaceRoot);
exclusionPath = subProjects.stream().map(project -> {
IPath relativePath = project.getLocation().makeRelativeTo(workspaceRoot);
return workspaceLink.getFolder(relativePath).getFullPath();
}).toArray(IPath[]::new);
} catch (OperationCanceledException | CoreException e) {
JavaLanguageServerPlugin.logException("Failed to create the invisible project.", e);
return new Result(false, "Failed to add the folder to the workspace invisible project's source path. Reason: " + e.getMessage());
}
} else {
projectLocation = targetProject.getLocation();
projectRootResource = targetProject;
}

IPath relativeSourcePath = sourceFolderPath.makeRelativeTo(projectLocation);
IPath sourcePath = relativeSourcePath.isEmpty() ? projectRootResource.getFullPath() : projectRootResource.getFolder(relativeSourcePath).getFullPath();
IJavaProject javaProject = JavaCore.create(targetProject);
try {
if (ProjectUtils.addSourcePath(sourcePath, exclusionPath, javaProject)) {
return new Result(true, Messages.format("Successfully added ''{0}'' to the project {1}''s source path.", new String[] { getWorkspacePath(sourceFolderPath).toOSString(), targetProject.getName() }));
} else {
return new Result(true, Messages.format("No need to add it to source path again, because the folder ''{0}'' is already in the project {1}''s source path.",
new String[] { getWorkspacePath(sourceFolderPath).toOSString(), targetProject.getName() }));
}
} catch (CoreException e) {
return new Result(false, e.getMessage());
}

}

public static Result removeFromSourcePath(String sourceFolderUri) {
IPath sourceFolderPath = ResourceUtils.filePathFromURI(sourceFolderUri);
IProject targetProject = findBelongedProject(sourceFolderPath);
if (targetProject != null && !ProjectUtils.isGeneralJavaProject(targetProject)) {
String message = ProjectUtils.isGradleProject(targetProject) ? UNSUPPORTED_ON_GRADLE : UNSUPPORTED_ON_MAVEN;
return new Result(false, message);
}

IPath projectLocation = null;
IContainer projectRootResource = null;
if (targetProject == null) {
IPath workspaceRoot = ProjectUtils.findBelongedWorkspaceRoot(sourceFolderPath);
if (workspaceRoot == null) {
return new Result(false, Messages.format("The folder ''{0}'' doesn''t belong to any workspace.", getWorkspacePath(sourceFolderPath).toOSString()));
}

String invisibleProjectName = ProjectUtils.getWorkspaceInvisibleProjectName(workspaceRoot);
targetProject = ResourcesPlugin.getWorkspace().getRoot().getProject(invisibleProjectName);
if (!targetProject.exists()) {
return new Result(true, Messages.format("No need to remove it from source path, because the folder ''{0}'' isn''t on any project''s source path.", getWorkspacePath(sourceFolderPath).toOSString()));
}

projectLocation = workspaceRoot;
projectRootResource = targetProject.getFolder(ProjectUtils.WORKSPACE_LINK);
} else {
projectLocation = targetProject.getLocation();
projectRootResource = targetProject;
}

IPath relativeSourcePath = sourceFolderPath.makeRelativeTo(projectLocation);
IPath sourcePath = relativeSourcePath.isEmpty() ? projectRootResource.getFullPath() : projectRootResource.getFolder(relativeSourcePath).getFullPath();
IJavaProject javaProject = JavaCore.create(targetProject);
try {
if (ProjectUtils.removeSourcePath(sourcePath, javaProject)) {
return new Result(true, Messages.format("Successfully removed ''{0}'' from the project {1}''s source path.", new String[] { getWorkspacePath(sourceFolderPath).toOSString(), targetProject.getName() }));
} else {
return new Result(true, Messages.format("No need to remove it from source path, because the folder ''{0}'' isn''t on any project''s source path.", getWorkspacePath(sourceFolderPath).toOSString()));
}
} catch (CoreException e) {
return new Result(false, e.getMessage());
}
}

public static Result listSourcePaths() {
List<SourcePath> sourcePathList = new ArrayList<>();
IProject[] projects = ProjectUtils.getAllProjects();
for (IProject project : projects) {
if (!ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) && ProjectUtils.isJavaProject(project)) {
try {
IPath[] paths = ProjectUtils.listSourcePaths(JavaCore.create(project));
for (IPath path : paths) {
IPath entryPath = path;
String projectName = project.getName();
String projectType = "General";
if (ProjectUtils.isMavenProject(project)) {
projectType = "Maven";
}

if (ProjectUtils.isGradleProject(project)) {
projectType = "Gradle";
}

IContainer projectRoot = project;
if (!ProjectUtils.isVisibleProject(project)) {
projectType = "Workspace";
IFolder workspaceLinkFolder = project.getFolder(ProjectUtils.WORKSPACE_LINK);
if (!workspaceLinkFolder.isLinked()) {
continue;
}

projectRoot = workspaceLinkFolder;
}

IPath relativePath = entryPath.makeRelativeTo(projectRoot.getFullPath());
IPath location = projectRoot.getRawLocation().append(relativePath);
IPath displayPath = getWorkspacePath(location);
sourcePathList.add(new SourcePath(location != null ? location.toOSString() : "", displayPath != null ? displayPath.toOSString() : entryPath.toOSString(), projectName, projectType));
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Failed to resolve the existing source paths in current workspace.", e);
return new ListCommandResult(false, e.getMessage());
}
}
}

return new ListCommandResult(true, null, sourcePathList.toArray(new SourcePath[0]));
}

private static IProject findBelongedProject(IPath sourceFolder) {
List<IProject> projects = Stream.of(ProjectUtils.getAllProjects()).filter(ProjectUtils::isJavaProject).sorted(new Comparator<IProject>() {
@Override
public int compare(IProject p1, IProject p2) {
return p2.getLocation().toOSString().length() - p1.getLocation().toOSString().length();
}
}).collect(Collectors.toList());

for (IProject project : projects) {
if (project.getLocation().isPrefixOf(sourceFolder)) {
return project;
}
}

return null;
}

private static IPath getWorkspacePath(IPath path) {
PreferenceManager manager = JavaLanguageServerPlugin.getPreferencesManager();
Collection<IPath> rootPaths = manager.getPreferences().getRootPaths();
if (rootPaths != null) {
for (IPath rootPath : rootPaths) {
if (rootPath.isPrefixOf(path)) {
return path.makeRelativeTo(rootPath.append(".."));
}
}
}

return path;
}

public static class Result {
public boolean status;
public String message;

Result(boolean status, String message) {
this.status = status;
this.message = message;
}
}

public static class ListCommandResult extends Result {
public SourcePath[] data;

ListCommandResult(boolean status, String message) {
super(status, message);
data = new SourcePath[0];
}

ListCommandResult(boolean status, String message, SourcePath[] data) {
super(status, message);
this.data = data;
}
}

public static class SourcePath {
public String path;
public String displayPath;
public String projectName;
public String projectType;

SourcePath(String path, String displayPath, String projectName, String projectType) {
this.path = path;
this.displayPath = displayPath;
this.projectName = projectName;
this.projectType = projectType;
}
}
}
Loading

0 comments on commit d37d038

Please sign in to comment.