IDEA-111342 Gradle: if root project is renamed by means of gradle, it is shown twice in tool window - as root project and as sub-project

1. Correctly update cached data at ExternalSystemFacadeManager on project rename;
2. Minor refactoring (method rename);
3. Drop obsolete data from 'local external system settings' on project rename;
4. Update external tool window project node representation on project rename;
This commit is contained in:
Denis.Zhdanov
2013-08-02 21:14:23 +04:00
parent 24b76168dc
commit 22861a0f32
8 changed files with 93 additions and 38 deletions
@@ -73,11 +73,11 @@ public abstract class AbstractExternalSystemLocalSettings {
}
/**
* Asks current settings to drop all information related to external project which root config is located at the given path.
* Asks current settings to drop all information related to external projects which root configs are located at the given paths.
*
* @param linkedProjectPathsToForget target root external project's path
* @param linkedProjectPathsToForget target root external project paths
*/
public void forgetExternalProject(@NotNull Set<String> linkedProjectPathsToForget) {
public void forgetExternalProjects(@NotNull Set<String> linkedProjectPathsToForget) {
Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> projects = myAvailableProjects.get();
for (Iterator<Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>>> it = projects.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> entry = it.next();
@@ -184,24 +184,24 @@ public abstract class AbstractExternalSystemLocalSettings {
private void pruneOutdatedEntries() {
ExternalSystemManager<?,?,?,?,?> manager = ExternalSystemApiUtil.getManager(myExternalSystemId);
assert manager != null;
Set<String> toForget = ContainerUtilRt.newHashSet();
Set<String> pathsToForget = ContainerUtilRt.newHashSet();
for (ExternalProjectPojo pojo : myAvailableProjects.get().keySet()) {
toForget.add(pojo.getPath());
pathsToForget.add(pojo.getPath());
}
for (String path : myAvailableTasks.get().keySet()) {
toForget.add(path);
pathsToForget.add(path);
}
for (ExternalTaskExecutionInfo taskInfo : myRecentTasks.get()) {
toForget.add(taskInfo.getSettings().getExternalProjectPath());
pathsToForget.add(taskInfo.getSettings().getExternalProjectPath());
}
AbstractExternalSystemSettings<?, ?, ?> settings = manager.getSettingsProvider().fun(myProject);
for (ExternalProjectSettings projectSettings : settings.getLinkedProjectsSettings()) {
toForget.remove(projectSettings.getExternalProjectPath());
pathsToForget.remove(projectSettings.getExternalProjectPath());
}
if (!toForget.isEmpty()) {
forgetExternalProject(toForget);
if (!pathsToForget.isEmpty()) {
forgetExternalProjects(pathsToForget);
}
}
@@ -86,7 +86,7 @@ public class DetachExternalProjectAction extends AnAction implements DumbAware {
recentTasksList.getModel().forgetTasksFrom(info.externalProject.getPath());
}
info.localSettings.forgetExternalProject(Collections.singleton(info.externalProject.getPath()));
info.localSettings.forgetExternalProjects(Collections.singleton(info.externalProject.getPath()));
info.settings.unlinkExternalProject(info.externalProject.getPath());
// Process orphan modules.
@@ -45,6 +45,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.psi.PsiBundle;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.PathUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
@@ -244,8 +245,13 @@ public class ExternalSystemFacadeManager {
key.getIdeProjectLocationHash(),
key.getExternalSystemId(),
key.getExternalProjectConfigPath());
data.put(newKey, data.get(key));
V value = data.get(key);
data.put(newKey, value);
data.remove(key);
if (value instanceof Consumer) {
//noinspection unchecked
((Consumer)value).consume(newKey);
}
}
}
@@ -265,7 +271,7 @@ public class ExternalSystemFacadeManager {
final RemoteExternalSystemFacade facade = myFacadeWrappers.get(key);
if (facade == null) {
final RemoteExternalSystemFacade newFacade = (RemoteExternalSystemFacade)Proxy.newProxyInstance(
ExternalSystemFacadeManager.class.getClassLoader(), new Class[]{RemoteExternalSystemFacade.class}, new MyHandler(key)
ExternalSystemFacadeManager.class.getClassLoader(), new Class[]{RemoteExternalSystemFacade.class, Consumer.class}, new MyHandler(key)
);
myFacadeWrappers.putIfAbsent(key, newFacade);
}
@@ -408,16 +414,22 @@ public class ExternalSystemFacadeManager {
}
private class MyHandler implements InvocationHandler {
@NotNull private final IntegrationKey myKey;
@NotNull private final AtomicReference<IntegrationKey> myKey = new AtomicReference<IntegrationKey>();
MyHandler(@NotNull IntegrationKey key) {
myKey = key;
myKey.set(key);
}
@Nullable
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return doInvoke(myKey, findProject(myKey), method, args, REMOTE_FAIL_RECOVERY_ATTEMPTS_NUMBER);
if ("consume".equals(method.getName())) {
myKey.set((IntegrationKey)args[0]);
return null;
}
Project project = findProject(myKey.get());
return doInvoke(myKey.get(), project, method, args, REMOTE_FAIL_RECOVERY_ATTEMPTS_NUMBER);
}
}
}
@@ -34,10 +34,7 @@ import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Ensures that all external system sub-projects are correctly represented at the external system tool window.
@@ -82,8 +79,40 @@ public class ToolWindowModuleService extends AbstractToolWindowService<ModuleDat
}
AbstractExternalSystemLocalSettings settings = manager.getLocalSettingsProvider().fun(project);
HashMap<ExternalProjectPojo,Collection<ExternalProjectPojo>> projects = ContainerUtilRt.newHashMap(settings.getAvailableProjects());
Set<String> pathsToForget = detectRenamedProjects(data, settings.getAvailableProjects());
if (!pathsToForget.isEmpty()) {
settings.forgetExternalProjects(pathsToForget);
}
Map<ExternalProjectPojo,Collection<ExternalProjectPojo>> projects = ContainerUtilRt.newHashMap(settings.getAvailableProjects());
projects.putAll(data);
settings.setAvailableProjects(projects);
}
@NotNull
private static Set<String> detectRenamedProjects(@NotNull Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> currentInfo,
@NotNull Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> oldInfo)
{
Map<String/* external config path */, String/* project name */> map = ContainerUtilRt.newHashMap();
for (Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> entry : currentInfo.entrySet()) {
map.put(entry.getKey().getPath(), entry.getKey().getName());
for (ExternalProjectPojo pojo : entry.getValue()) {
map.put(pojo.getPath(), pojo.getName());
}
}
Set<String> result = ContainerUtilRt.newHashSet();
for (Map.Entry<ExternalProjectPojo, Collection<ExternalProjectPojo>> entry : oldInfo.entrySet()) {
String newName = map.get(entry.getKey().getPath());
if (newName != null && !newName.equals(entry.getKey().getName())) {
result.add(entry.getKey().getPath());
}
for (ExternalProjectPojo pojo : entry.getValue()) {
newName = map.get(pojo.getPath());
if (newName != null && !newName.equals(pojo.getName())) {
result.add(pojo.getPath());
}
}
}
return result;
}
}
@@ -37,6 +37,10 @@ public class ExternalSystemNodeDescriptor<T> extends PresentableNodeDescriptor<T
setIcon(icon);
}
public void setName(@NotNull String name) {
myName = name;
}
@Override
protected void update(PresentationData presentation) {
presentation.setPresentableText(myName);
@@ -16,7 +16,6 @@
package com.intellij.openapi.externalSystem.service.task.ui;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings;
@@ -40,8 +39,6 @@ import java.util.*;
*/
public class ExternalSystemTasksTreeModel extends DefaultTreeModel {
private static final Logger LOG = Logger.getInstance("#" + ExternalSystemTasksTreeModel.class.getName());
@NotNull private static final Comparator<TreeNode> NODE_COMPARATOR = new Comparator<TreeNode>() {
@Override
public int compare(TreeNode t1, TreeNode t2) {
@@ -95,11 +92,18 @@ public class ExternalSystemTasksTreeModel extends DefaultTreeModel {
// Remove outdated projects.
for (int i = root.getChildCount() - 1; i >= 0; i--) {
ExternalSystemNode<?> child = root.getChildAt(i);
Object element = child.getDescriptor().getElement();
if (element instanceof ExternalProjectPojo
&& ((ExternalProjectPojo)element).getPath().equals(project.getPath()))
{
return (ExternalSystemNode<ExternalProjectPojo>)child;
ExternalSystemNodeDescriptor<?> descriptor = child.getDescriptor();
Object element = descriptor.getElement();
if (element instanceof ExternalProjectPojo) {
ExternalProjectPojo pojo = (ExternalProjectPojo)element;
if (pojo.getPath().equals(project.getPath())) {
if (!pojo.getName().equals(project.getName())) {
pojo.setName(project.getName());
descriptor.setName(project.getName());
nodeChanged(child);
}
return (ExternalSystemNode<ExternalProjectPojo>)child;
}
}
}
ExternalProjectPojo element = new ExternalProjectPojo(project.getName(), project.getPath());
@@ -206,7 +206,7 @@ public class ExternalSystemUtil {
ExternalProjectRefreshCallback callback = new ExternalProjectRefreshCallback() {
@NotNull
private final Set<String> myExternalModuleNames = ContainerUtilRt.newHashSet();
private final Set<String> myExternalModulePaths = ContainerUtilRt.newHashSet();
@Override
public void onSuccess(@Nullable final DataNode<ProjectData> externalProject) {
@@ -215,7 +215,7 @@ public class ExternalSystemUtil {
}
Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAll(externalProject, ProjectKeys.MODULE);
for (DataNode<ModuleData> node : moduleNodes) {
myExternalModuleNames.add(node.getData().getName());
myExternalModulePaths.add(node.getData().getLinkedExternalProjectPath());
}
ExternalSystemApiUtil.executeProjectChangeAction(true, new Runnable() {
@Override
@@ -225,7 +225,7 @@ public class ExternalSystemUtil {
public void run() {
projectDataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), project, true);
}
});
});
}
});
if (--counter[0] <= 0) {
@@ -245,7 +245,8 @@ public class ExternalSystemUtil {
for (Module module : platformFacade.getModules(project)) {
String s = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
if (externalSystemIdAsString.equals(s) && !myExternalModuleNames.contains(module.getName())) {
String p = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
if (externalSystemIdAsString.equals(s) && !myExternalModulePaths.contains(p)) {
orphanIdeModules.add(module);
}
}
@@ -436,8 +436,9 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
// Our aim is to make sub-project nodes contain corresponding TaskData nodes and add root project tasks to ProjectData node.
// The later is achieved by composing all tasks from IdeaModule which corresponds to the IdeaProject plus all tasks
// which are shared between all sub-projects.
final String rootProjectPath = rootProjectNode.getData().getLinkedExternalProjectPath();
ProjectData projectData = rootProjectNode.getData();
final String rootProjectPath = projectData.getLinkedExternalProjectPath();
Map<String/* module name */, Collection<TaskData>> tasksByModule = ContainerUtilRt.newHashMap();
TObjectIntHashMap<Pair<String/* task name */, String /* task description */>> rootProjectTaskCandidates
= new TObjectIntHashMap<Pair<String, String>>();
@@ -487,7 +488,11 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAll(rootProjectNode, ProjectKeys.MODULE);
for (DataNode<ModuleData> moduleNode : moduleNodes) {
Collection<TaskData> tasks = tasksByModule.get(moduleNode.getData().getName());
ModuleData moduleData = moduleNode.getData();
if (rootProjectPath.equals(moduleData.getLinkedExternalProjectPath()) && !projectData.getName().equals(moduleData.getName())) {
moduleData.setName(projectData.getName());
}
Collection<TaskData> tasks = tasksByModule.get(moduleData.getName());
if (tasks != null && !tasks.isEmpty()) {
for (TaskData task : tasks) {
moduleNode.createChild(ProjectKeys.TASK, task);