blob: 3e1c95ad37669c33ff18c72d3e04762d495b6197 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed 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 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.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.project.ContentRootData;
import com.intellij.openapi.externalSystem.model.project.ContentRootData.SourceRoot;
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.service.project.ProjectStructureHelper;
import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
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.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
import org.jetbrains.jps.model.java.JavaResourceRootType;
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Thread-safe.
*
* @author Denis Zhdanov
* @since 2/7/12 3:20 PM
*/
@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
public class ContentRootDataService implements ProjectDataService<ContentRootData, ContentEntry> {
private static final Logger LOG = Logger.getInstance("#" + ContentRootDataService.class.getName());
@NotNull private final ProjectStructureHelper myProjectStructureHelper;
public ContentRootDataService(@NotNull ProjectStructureHelper helper) {
myProjectStructureHelper = helper;
}
@NotNull
@Override
public Key<ContentRootData> getTargetDataKey() {
return ProjectKeys.CONTENT_ROOT;
}
@Override
public void importData(@NotNull final Collection<DataNode<ContentRootData>> toImport,
@NotNull final Project project,
boolean synchronous) {
if (toImport.isEmpty()) {
return;
}
Map<DataNode<ModuleData>, List<DataNode<ContentRootData>>> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE);
for (Map.Entry<DataNode<ModuleData>, List<DataNode<ContentRootData>>> entry : byModule.entrySet()) {
final Module module = myProjectStructureHelper.findIdeModule(entry.getKey().getData(), project);
if (module == null) {
LOG.warn(String.format(
"Can't import content roots. Reason: target module (%s) is not found at the ide. Content roots: %s",
entry.getKey(), entry.getValue()
));
continue;
}
importData(entry.getValue(), module, synchronous);
}
}
private static void importData(@NotNull final Collection<DataNode<ContentRootData>> data,
@NotNull final Module module,
boolean synchronous) {
ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) {
@Override
public void execute() {
ModuleRootModificationUtil.updateModel(module, new Consumer<ModifiableRootModel>() {
@Override
public void consume(ModifiableRootModel model) {
final ContentEntry[] contentEntries = model.getContentEntries();
final Map<String, ContentEntry> contentEntriesMap = ContainerUtilRt.newHashMap();
for(ContentEntry contentEntry : contentEntries) {
contentEntriesMap.put(contentEntry.getUrl(), contentEntry);
}
boolean createEmptyContentRootDirectories = false;
if (!data.isEmpty()) {
ProjectSystemId projectSystemId = data.iterator().next().getData().getOwner();
AbstractExternalSystemSettings externalSystemSettings =
ExternalSystemApiUtil.getSettings(module.getProject(), projectSystemId);
String path = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY);
if (path != null) {
ExternalProjectSettings projectSettings = externalSystemSettings.getLinkedProjectSettings(path);
createEmptyContentRootDirectories = projectSettings != null && projectSettings.isCreateEmptyContentRootDirectories();
}
}
for (final DataNode<ContentRootData> node : data) {
final ContentRootData contentRoot = node.getData();
final ContentEntry contentEntry = findOrCreateContentRoot(model, contentRoot.getRootPath());
contentEntry.clearExcludeFolders();
contentEntry.clearSourceFolders();
LOG.info(String.format("Importing content root '%s' for module '%s'", contentRoot.getRootPath(), module.getName()));
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.RESOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaResourceRootType.RESOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_RESOURCE)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaResourceRootType.TEST_RESOURCE, false, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE_GENERATED)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, true, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_GENERATED)) {
createSourceRootIfAbsent(contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, true, createEmptyContentRootDirectories);
}
for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.EXCLUDED)) {
createExcludedRootIfAbsent(contentEntry, path, module.getName(), module.getProject());
}
contentEntriesMap.remove(contentEntry.getUrl());
}
for(ContentEntry contentEntry : contentEntriesMap.values()) {
model.removeContentEntry(contentEntry);
}
}
});
}
});
}
@NotNull
private static ContentEntry findOrCreateContentRoot(@NotNull ModifiableRootModel model, @NotNull String path) {
ContentEntry[] entries = model.getContentEntries();
for (ContentEntry entry : entries) {
VirtualFile file = entry.getFile();
if (file == null) {
continue;
}
if (ExternalSystemApiUtil.getLocalFileSystemPath(file).equals(path)) {
return entry;
}
}
return model.addContentEntry(toVfsUrl(path));
}
private static void createSourceRootIfAbsent(
@NotNull ContentEntry entry, @NotNull SourceRoot root, @NotNull String moduleName,
@NotNull JpsModuleSourceRootType sourceRootType, boolean generated, boolean createEmptyContentRootDirectories) {
List<SourceFolder> folders = entry.getSourceFolders(sourceRootType);
for (SourceFolder folder : folders) {
VirtualFile file = folder.getFile();
if (file == null) {
continue;
}
if (ExternalSystemApiUtil.getLocalFileSystemPath(file).equals(root.getPath())) {
return;
}
}
LOG.info(String.format("Importing %s for content root '%s' of module '%s'", root, entry.getUrl(), moduleName));
SourceFolder sourceFolder = entry.addSourceFolder(toVfsUrl(root.getPath()), sourceRootType);
if (!StringUtil.isEmpty(root.getPackagePrefix())) {
sourceFolder.setPackagePrefix(root.getPackagePrefix());
}
if (generated) {
JavaSourceRootProperties properties = sourceFolder.getJpsElement().getProperties(JavaModuleSourceRootTypes.SOURCES);
if(properties != null) {
properties.setForGeneratedSources(true);
}
}
if(createEmptyContentRootDirectories) {
try {
VfsUtil.createDirectoryIfMissing(root.getPath());
}
catch (IOException e) {
LOG.warn(String.format("Unable to create directory for the path: %s", root.getPath()), e);
}
}
}
private static void createExcludedRootIfAbsent(@NotNull ContentEntry entry, @NotNull SourceRoot root, @NotNull String moduleName, @NotNull Project project) {
String rootPath = root.getPath();
for (VirtualFile file : entry.getExcludeFolderFiles()) {
if (ExternalSystemApiUtil.getLocalFileSystemPath(file).equals(rootPath)) {
return;
}
}
LOG.info(String.format("Importing excluded root '%s' for content root '%s' of module '%s'", root, entry.getUrl(), moduleName));
entry.addExcludeFolder(toVfsUrl(rootPath));
if (!Registry.is("ide.hide.excluded.files")) {
ChangeListManager.getInstance(project).addDirectoryToIgnoreImplicitly(rootPath);
}
}
@Override
public void removeData(@NotNull Collection<? extends ContentEntry> toRemove, @NotNull Project project, boolean synchronous) {
}
private static String toVfsUrl(@NotNull String path) {
return LocalFileSystem.PROTOCOL_PREFIX + path;
}
}