blob: 873a12fe8eb37c106b1966334503b5c172adbfd0 [file] [log] [blame]
package com.intellij.openapi.externalSystem.service.project.manage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.Key;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.LibraryData;
import com.intellij.openapi.externalSystem.model.project.LibraryPathType;
import com.intellij.openapi.externalSystem.service.project.ExternalLibraryPathTypeMapper;
import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
import com.intellij.openapi.externalSystem.service.project.ProjectStructureHelper;
import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
import com.intellij.openapi.externalSystem.util.Order;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.NotNullFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Denis Zhdanov
* @since 2/15/12 11:32 AM
*/
@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
public class LibraryDataService implements ProjectDataService<LibraryData, Library> {
private static final Logger LOG = Logger.getInstance("#" + LibraryDataService.class.getName());
@NotNull public static final NotNullFunction<String, File> PATH_TO_FILE = new NotNullFunction<String, File>() {
@NotNull
@Override
public File fun(String path) {
return new File(path);
}
};
@NotNull private final PlatformFacade myPlatformFacade;
@NotNull private final ProjectStructureHelper myProjectStructureHelper;
@NotNull private final ExternalLibraryPathTypeMapper myLibraryPathTypeMapper;
public LibraryDataService(@NotNull PlatformFacade platformFacade,
@NotNull ProjectStructureHelper helper,
@NotNull ExternalLibraryPathTypeMapper mapper)
{
myPlatformFacade = platformFacade;
myProjectStructureHelper = helper;
myLibraryPathTypeMapper = mapper;
}
@NotNull
@Override
public Key<LibraryData> getTargetDataKey() {
return ProjectKeys.LIBRARY;
}
@Override
public void importData(@NotNull Collection<DataNode<LibraryData>> toImport, @NotNull Project project, boolean synchronous) {
for (DataNode<LibraryData> dataNode : toImport) {
importLibrary(dataNode.getData(), project, synchronous);
}
}
public void importLibrary(@NotNull final LibraryData toImport, @NotNull final Project project, boolean synchronous) {
Map<OrderRootType, Collection<File>> libraryFiles = prepareLibraryFiles(toImport);
Library library = myProjectStructureHelper.findIdeLibrary(toImport, project);
if (library != null) {
syncPaths(toImport, library, project, synchronous);
return;
}
importLibrary(toImport.getInternalName(), libraryFiles, project, synchronous);
}
@NotNull
public Map<OrderRootType, Collection<File>> prepareLibraryFiles(@NotNull LibraryData data) {
Map<OrderRootType, Collection<File>> result = ContainerUtilRt.newHashMap();
for (LibraryPathType pathType : LibraryPathType.values()) {
final Set<String> paths = data.getPaths(pathType);
if (paths.isEmpty()) {
continue;
}
result.put(myLibraryPathTypeMapper.map(pathType), ContainerUtil.map(paths, PATH_TO_FILE));
}
return result;
}
public void importLibrary(@NotNull final String libraryName,
@NotNull final Map<OrderRootType, Collection<File>> libraryFiles,
@NotNull final Project project,
boolean synchronous)
{
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
// Is assumed to be called from the EDT.
final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(project);
final LibraryTable.ModifiableModel projectLibraryModel = libraryTable.getModifiableModel();
final Library intellijLibrary;
try {
intellijLibrary = projectLibraryModel.createLibrary(libraryName);
}
finally {
projectLibraryModel.commit();
}
final Library.ModifiableModel libraryModel = intellijLibrary.getModifiableModel();
try {
registerPaths(libraryFiles, libraryModel, libraryName);
}
finally {
libraryModel.commit();
}
}
});
}
@SuppressWarnings("MethodMayBeStatic")
public void registerPaths(@NotNull final Map<OrderRootType, Collection<File>> libraryFiles,
@NotNull Library.ModifiableModel model,
@NotNull String libraryName)
{
for (Map.Entry<OrderRootType, Collection<File>> entry : libraryFiles.entrySet()) {
for (File file : entry.getValue()) {
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
if (virtualFile == null) {
if (ExternalSystemConstants.VERBOSE_PROCESSING && entry.getKey() == OrderRootType.CLASSES) {
LOG.warn(
String.format("Can't find %s of the library '%s' at path '%s'", entry.getKey(), libraryName, file.getAbsolutePath())
);
}
String url = VfsUtil.getUrlForLibraryRoot(file);
model.addRoot(url, entry.getKey());
continue;
}
if (virtualFile.isDirectory()) {
model.addRoot(virtualFile, entry.getKey());
}
else {
VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile);
if (jarRoot == null) {
LOG.warn(String.format(
"Can't parse contents of the JAR file at path '%s' for the library '%s''", file.getAbsolutePath(), libraryName
));
continue;
}
model.addRoot(jarRoot, entry.getKey());
}
}
}
}
public void removeData(@NotNull final Collection<? extends Library> libraries, @NotNull final Project project, boolean synchronous) {
if (libraries.isEmpty()) {
return;
}
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(project);
final LibraryTable.ModifiableModel model = libraryTable.getModifiableModel();
try {
for (Library library : libraries) {
String libraryName = library.getName();
if (libraryName != null) {
Library libraryToRemove = model.getLibraryByName(libraryName);
if (libraryToRemove != null) {
model.removeLibrary(libraryToRemove);
}
}
}
}
finally {
model.commit();
}
}
});
}
public void syncPaths(@NotNull final LibraryData externalLibrary, @NotNull final Library ideLibrary, @NotNull final Project project, boolean synchronous) {
if (externalLibrary.isUnresolved()) {
return;
}
final Map<OrderRootType, Set<String>> toRemove = ContainerUtilRt.newHashMap();
final Map<OrderRootType, Set<String>> toAdd = ContainerUtilRt.newHashMap();
for (LibraryPathType pathType : LibraryPathType.values()) {
OrderRootType ideType = myLibraryPathTypeMapper.map(pathType);
HashSet<String> toAddPerType = ContainerUtilRt.newHashSet(externalLibrary.getPaths(pathType));
toAdd.put(ideType, toAddPerType);
HashSet<String> toRemovePerType = ContainerUtilRt.newHashSet();
toRemove.put(ideType, toRemovePerType);
for (VirtualFile ideFile : ideLibrary.getFiles(ideType)) {
String idePath = ExternalSystemApiUtil.getLocalFileSystemPath(ideFile);
if (!toAddPerType.remove(idePath)) {
toRemovePerType.add(ideFile.getUrl());
}
}
}
if (toRemove.isEmpty() && toAdd.isEmpty()) {
return;
}
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@Override
public void execute() {
Library.ModifiableModel model = ideLibrary.getModifiableModel();
try {
for (Map.Entry<OrderRootType, Set<String>> entry : toRemove.entrySet()) {
for (String path : entry.getValue()) {
model.removeRoot(path, entry.getKey());
}
}
for (Map.Entry<OrderRootType, Set<String>> entry : toAdd.entrySet()) {
Map<OrderRootType, Collection<File>> roots = ContainerUtilRt.newHashMap();
roots.put(entry.getKey(), ContainerUtil.map(entry.getValue(), PATH_TO_FILE));
registerPaths(roots, model, externalLibrary.getInternalName());
}
}
finally {
model.commit();
}
}
});
}
}