IDEA-140550 gradle selective import support
authorVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Tue, 14 Jul 2015 10:08:34 +0000 (13:08 +0300)
committerVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Tue, 14 Jul 2015 10:11:12 +0000 (13:11 +0300)
64 files changed:
platform/external-system-api/resources/i18n/ExternalSystemBundle.properties
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalProjectInfo.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/Key.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ProjectData.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/PlatformFacade.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemConstants.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/view/ExternalProjectsViewState.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/DetachExternalProjectAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemNodeAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemOpenProjectStructureAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemToggleAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/IgnoreExternalProjectAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RefreshExternalProjectAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/AssignShortcutAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ShowIgnoredAction.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ToggleTaskActivationAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizer.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizerImpl.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/model/internal/InternalExternalProjectInfo.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/PlatformFacadeImpl.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/autoimport/ExternalSystemAutoImporter.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractDependencyDataService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractProjectDataService.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsManager.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsState.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemTaskActivator.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDependencyDataService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDependencyDataService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataServiceEx.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataServiceImpl.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/wizard/AbstractExternalProjectImportBuilder.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ToolWindowModuleService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ToolWindowTaskService.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/task/ui/ConfigureTasksActivationDialog.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectStructureDialog.form [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectStructureDialog.java [new file with mode: 0644]
platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsStructure.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsView.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsViewAdapter.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsViewImpl.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemNode.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ModuleNode.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ProjectNode.java
platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/AbstractExternalSystemTest.groovy
platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java
platform/platform-resources/src/META-INF/ExternalSystemExtensionPoints.xml
platform/platform-resources/src/idea/ExternalSystemActions.xml
plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataService.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy

index ca2f1e3403c7397eaf6378993224fbb5579dfe60..86cc1e0171ce9a7e09f19763c9f6882ba0a482b3 100644 (file)
@@ -46,6 +46,8 @@ action.attach.external.project.text=Attach {0} project
 action.attach.external.project.description=Attach {0} project to the current ide project
 action.detach.external.project.text=Detach {0} project
 action.detach.external.project.description=Detach selected external project
+action.ignore.external.project.text=Ignore {0} project
+action.ignore.external.project.description=Ignore selected external project
 action.open.config.text=Open {0} config
 action.open.config.description=Allows to open project file of the linked {0} project at the editor
 
index edfd3059c0d1748f5e6bd1058da86f5cfd61f3f3..86e35d1d5aa4095883abed6bc223f3a09ea02437 100644 (file)
 package com.intellij.openapi.externalSystem.model;
 
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.util.UserDataHolderEx;
 import com.intellij.openapi.util.io.StreamUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
 
 import java.io.*;
 import java.lang.reflect.Modifier;
@@ -39,17 +42,19 @@ import java.util.*;
  * @author Denis Zhdanov
  * @since 4/12/13 11:53 AM
  */
-public class DataNode<T> implements Serializable {
+public class DataNode<T> implements Serializable, UserDataHolderEx {
 
   private static final long serialVersionUID = 1L;
   private static final Logger LOG = Logger.getInstance(DataNode.class);
 
   @NotNull private final List<DataNode<?>> myChildren = ContainerUtilRt.newArrayList();
   @NotNull private transient List<DataNode<?>> myChildrenView = Collections.unmodifiableList(myChildren);
+  @NotNull private transient UserDataHolderBase myUserData = new UserDataHolderBase();
 
   @NotNull private final Key<T> myKey;
   private transient T myData;
   private byte[] myRawData;
+  private boolean myIgnored;
 
   @Nullable private DataNode<?> myParent;
 
@@ -88,6 +93,14 @@ public class DataNode<T> implements Serializable {
     return myData;
   }
 
+  public boolean isIgnored() {
+    return myIgnored;
+  }
+
+  public void setIgnored(boolean ignored) {
+    myIgnored = ignored;
+  }
+
   /**
    * This class is a generic holder for any kind of project data. That project data might originate from different locations, e.g.
    * core ide plugins, non-core ide plugins, third-party plugins etc. That means that when a service from a core plugin needs to
@@ -245,6 +258,7 @@ public class DataNode<T> implements Serializable {
     throws IOException, ClassNotFoundException {
     in.defaultReadObject();
     myChildrenView = Collections.unmodifiableList(myChildren);
+    myUserData = new UserDataHolderBase();
   }
 
   public byte[] getDataBytes() throws IOException {
@@ -313,17 +327,70 @@ public class DataNode<T> implements Serializable {
     myChildren.clear();
   }
 
+  @NotNull
   public DataNode<T> graphCopy() {
-    return nodeCopy(this, null);
+    return сopy(this, null);
   }
 
-  private static <T> DataNode<T> nodeCopy(@NotNull DataNode<T> dataNode, @Nullable DataNode<?> newParent) {
+  @NotNull
+  public DataNode<T> nodeCopy() {
+    return nodeCopy(this);
+  }
+
+  @Nullable
+  @Override
+  public <U> U getUserData(@NotNull com.intellij.openapi.util.Key<U> key) {
+    return (U)myUserData.getUserData(key);
+  }
+
+  @Override
+  public <U> void putUserData(@NotNull com.intellij.openapi.util.Key<U> key, U value) {
+    myUserData.putUserData(key, value);
+  }
+
+  public <U> void removeUserData(@NotNull com.intellij.openapi.util.Key<U> key) {
+    myUserData.putUserData(key, null);
+  }
+
+  @NotNull
+  @Override
+  public <T> T putUserDataIfAbsent(@NotNull com.intellij.openapi.util.Key<T> key, @NotNull T value) {
+    return myUserData.putUserDataIfAbsent(key, value);
+  }
+
+  @Override
+  public <T> boolean replace(@NotNull com.intellij.openapi.util.Key<T> key, @Nullable T oldValue, @Nullable T newValue) {
+    return myUserData.replace(key, oldValue, newValue);
+  }
+
+  public <T> void putCopyableUserData(@NotNull com.intellij.openapi.util.Key<T> key, T value) {
+    myUserData.putCopyableUserData(key, value);
+  }
+
+  public boolean isUserDataEmpty() {
+    return myUserData.isUserDataEmpty();
+  }
+
+  public <T> T getCopyableUserData(@NotNull com.intellij.openapi.util.Key<T> key) {
+    return myUserData.getCopyableUserData(key);
+  }
+
+  @NotNull
+  public static <T> DataNode<T> nodeCopy(@NotNull DataNode<T> dataNode) {
     DataNode<T> copy = new DataNode<T>(dataNode.myKey);
-    copy.myParent = newParent;
     copy.myData = dataNode.myData;
     copy.myRawData = dataNode.myRawData;
+    copy.myIgnored = dataNode.myIgnored;
+    dataNode.myUserData.copyCopyableDataTo(copy.myUserData);
+    return copy;
+  }
+
+  @NotNull
+  private static <T> DataNode<T> сopy(@NotNull DataNode<T> dataNode, @Nullable DataNode<?> newParent) {
+    DataNode<T> copy = nodeCopy(dataNode);
+    copy.myParent = newParent;
     for (DataNode<?> child : dataNode.myChildren) {
-      copy.addChild(nodeCopy(child, copy));
+      copy.addChild(сopy(child, copy));
     }
     return copy;
   }
index 27d20b83be64ea19a837be5720a8869aa8991411..3481e26236433227a927f259592ce8702db9cb9d 100644 (file)
@@ -36,4 +36,6 @@ public interface ExternalProjectInfo {
   long getLastSuccessfulImportTimestamp();
 
   long getLastImportTimestamp();
+
+  ExternalProjectInfo copy();
 }
index 3e25602cb54632f931998446c3db61e84cf9e4f4..32f76849c68dfc6e4821d83c0c0efe04963ab917 100644 (file)
@@ -60,6 +60,10 @@ public class Key<T> implements Serializable, Comparable<Key<?>> {
     return new Key<T>(dataClass.getName(), processingWeight);
   }
 
+  public String getDataType() {
+    return myDataClass;
+  }
+
   /**
    * There is a possible case that when a {@link DataNode} object has children of more than on type (children with more than
    * one different {@link Key} we might want to process one type of children before another. That's why we need a way to define
index 212396c586c0e9164a9d6a2e35d2f8f35bf5fab8..1d8af229bebf57967c6fd1b917fa3bc9d34f5464 100644 (file)
@@ -27,6 +27,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
   @NotNull private String myModuleFilePath;
   @Nullable private String group;
   @Nullable private String version;
+  @Nullable private String description;
   @NotNull private List<File> myArtifacts;
 
   private boolean myInheritProjectCompileOutputPath = true;
@@ -127,6 +128,15 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
     this.version = version;
   }
 
+  @Nullable
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(@Nullable String description) {
+    this.description = description;
+  }
+
   @NotNull
   public List<File> getArtifacts() {
     return myArtifacts;
@@ -146,6 +156,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
     if (group != null ? !group.equals(that.group) : that.group != null) return false;
     if (!myModuleTypeId.equals(that.myModuleTypeId)) return false;
     if (version != null ? !version.equals(that.version) : that.version != null) return false;
+    if (description != null ? !description.equals(that.description) : that.description != null) return false;
 
     return true;
   }
@@ -156,6 +167,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
     result = 31 * result + myModuleTypeId.hashCode();
     result = 31 * result + (group != null ? group.hashCode() : 0);
     result = 31 * result + (version != null ? version.hashCode() : 0);
+    result = 31 * result + (description != null ? description.hashCode() : 0);
     return result;
   }
 
index 43a723bb966a4af4b917a4948016a7e643c07bcc..fdda074d516a1663ee6b2b09b8f8753e9e195c49 100644 (file)
@@ -3,6 +3,7 @@ package com.intellij.openapi.externalSystem.model.project;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Not thread-safe.
@@ -17,6 +18,7 @@ public class ProjectData extends AbstractNamedData implements ExternalConfigPath
   @NotNull private final String myLinkedExternalProjectPath;
 
   @NotNull private String myIdeProjectFileDirectoryPath;
+  @Nullable private String myDescription;
   private String myGroup;
   private String myVersion;
 
@@ -105,4 +107,13 @@ public class ProjectData extends AbstractNamedData implements ExternalConfigPath
   public void setVersion(String version) {
     myVersion = version;
   }
+
+  @Nullable
+  public String getDescription() {
+    return myDescription;
+  }
+
+  public void setDescription(@Nullable String description) {
+    myDescription = description;
+  }
 }
index 8407c432904eff614bb5e320a1f59f840707bef1..1ac0444b75a1f7cdb77f78097fafe3426f87d3a6 100644 (file)
@@ -1,9 +1,7 @@
 package com.intellij.openapi.externalSystem.service.project;
 
-import com.intellij.openapi.externalSystem.model.project.LibraryData;
-import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData;
-import com.intellij.openapi.externalSystem.model.project.ModuleData;
-import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.*;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ModifiableRootModel;
@@ -43,6 +41,9 @@ public interface PlatformFacade {
   Collection<Module> getModules(@NotNull Project project);
 
   @NotNull
+  Collection<Module> getModules(@NotNull Project project, @NotNull ProjectData projectData);
+
+  @NotNull
   Collection<OrderEntry> getOrderEntries(@NotNull Module module);
 
   /**
index b0aa9e3375b7f908e56bb5a01aa17fa365c81ce0..15854c86b6443fbd01139a16880a1c3cca500117 100644 (file)
@@ -48,17 +48,15 @@ import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.JarFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.*;
-import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.*;
 import com.intellij.util.containers.Stack;
-import com.intellij.util.containers.TransferToEDTQueue;
 import com.intellij.util.lang.UrlClassLoader;
-import com.intellij.util.messages.MessageBusConnection;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.Contract;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import javax.swing.*;
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -66,6 +64,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.*;
+import java.util.Queue;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -245,13 +244,27 @@ public class ExternalSystemApiUtil {
     return result;
   }
 
+  public static MultiMap<Key<?>, DataNode<?>> recursiveGroup(@NotNull Collection<DataNode<?>> nodes) {
+    MultiMap<Key<?>, DataNode<?>> result = MultiMap.createLinked();
+    Queue<Collection<DataNode<?>>> queue = ContainerUtil.newLinkedList();
+    queue.add(nodes);
+    while (!queue.isEmpty()) {
+      Collection<DataNode<?>> _nodes = queue.remove();
+      result.putAllValues(group(_nodes));
+      for (DataNode<?> _node : _nodes) {
+        queue.add(_node.getChildren());
+      }
+    }
+    return result;
+  }
+
   @NotNull
-  public static Map<Key<?>, List<DataNode<?>>> group(@NotNull Collection<DataNode<?>> nodes) {
+  public static MultiMap<Key<?>, DataNode<?>> group(@NotNull Collection<DataNode<?>> nodes) {
     return groupBy(nodes, GROUPER);
   }
 
   @NotNull
-  public static <K, V> Map<DataNode<K>, List<DataNode<V>>> groupBy(@NotNull Collection<DataNode<V>> nodes, @NotNull final Key<K> key) {
+  public static <K, V> MultiMap<DataNode<K>, DataNode<V>> groupBy(@NotNull Collection<DataNode<V>> nodes, @NotNull final Key<K> key) {
     return groupBy(nodes, new NullableFunction<DataNode<V>, DataNode<K>>() {
       @Nullable
       @Override
@@ -262,8 +275,8 @@ public class ExternalSystemApiUtil {
   }
 
   @NotNull
-  public static <K, V> Map<K, List<V>> groupBy(@NotNull Collection<V> nodes, @NotNull NullableFunction<V, K> grouper) {
-    Map<K, List<V>> result = ContainerUtilRt.newHashMap();
+  public static <K, V> MultiMap<K, V> groupBy(@NotNull Collection<V> nodes, @NotNull NullableFunction<V, K> grouper) {
+    MultiMap<K, V> result = MultiMap.createLinked();
     for (V data : nodes) {
       K key = grouper.fun(data);
       if (key == null) {
@@ -275,17 +288,13 @@ public class ExternalSystemApiUtil {
           nodes));
         continue;
       }
-      List<V> grouped = result.get(key);
-      if (grouped == null) {
-        result.put(key, grouped = ContainerUtilRt.newArrayList());
-      }
-      grouped.add(data);
+      result.putValue(key, data);
     }
 
     if (!result.isEmpty() && result.keySet().iterator().next() instanceof Comparable) {
       List<K> ordered = ContainerUtilRt.newArrayList(result.keySet());
       Collections.sort(ordered, COMPARABLE_GLUE);
-      Map<K, List<V>> orderedResult = ContainerUtilRt.newLinkedHashMap();
+      MultiMap<K, V> orderedResult = MultiMap.createLinked();
       for (K k : ordered) {
         orderedResult.put(k, result.get(k));
       }
@@ -366,7 +375,7 @@ public class ExternalSystemApiUtil {
     return result == null ? Collections.<DataNode<T>>emptyList() : result;
   }
 
-  public static void visit(@Nullable DataNode node, @NotNull Consumer<DataNode> consumer) {
+  public static void visit(@Nullable DataNode node, @NotNull Consumer<DataNode<?>> consumer) {
     if(node == null) return;
 
     Stack<DataNode> toProcess = ContainerUtil.newStack(node);
index 476f863388266cd9774fa5e81ccd1b59eea00350..b02ae52faf341fa2595a0a1c9be3004a7cc5bac3 100644 (file)
@@ -52,9 +52,12 @@ public class ExternalSystemConstants {
   public static final char PATH_SEPARATOR = '/';
 
   // Order.
-  public static final int BUILTIN_SERVICE_ORDER             = 42;
-  public static final int BUILTIN_TOOL_WINDOW_SERVICE_ORDER = 62;
-  public static final int UNORDERED                         = 1000;
+  public static final int BUILTIN_PROJECT_DATA_SERVICE_ORDER = Integer.MIN_VALUE;
+  public static final int BUILTIN_MODULE_DATA_SERVICE_ORDER = BUILTIN_PROJECT_DATA_SERVICE_ORDER + 1;
+  public static final int BUILTIN_LIBRARY_DATA_SERVICE_ORDER = BUILTIN_MODULE_DATA_SERVICE_ORDER + 1;
+  public static final int BUILTIN_SERVICE_ORDER = BUILTIN_LIBRARY_DATA_SERVICE_ORDER + 1;
+  public static final int BUILTIN_TOOL_WINDOW_SERVICE_ORDER = BUILTIN_SERVICE_ORDER + 1;
+  public static final int UNORDERED = 1000;
 
   public static final int TEXT_FIELD_WIDTH_IN_COLUMNS = 20;
 }
index 6e05b60178cc980efa75559c87a13c7dfc18fb97..15271afbfd9a688f25eae2c4f50845dad07c16ce 100644 (file)
@@ -26,6 +26,7 @@ import org.jdom.Element;
 public class ExternalProjectsViewState {
   public boolean groupTasks = true;
   public boolean showInheritedTasks = true;
+  public boolean showIgnored = true;
   @Tag("tree_state")
   public Element treeState;
 }
index 80e1e0f3a9da9e6eb6b06add5159261f4faf079d..467f94d299b69b2aa6ed30cc42f437fcba0d48a3 100644 (file)
@@ -18,24 +18,22 @@ package com.intellij.openapi.externalSystem.action;
 import com.intellij.icons.AllIcons;
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.ProjectKeys;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
-import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
-import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
 import com.intellij.openapi.externalSystem.view.ProjectNode;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.SystemInfoRt;
-import com.intellij.util.Consumer;
-import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
 
@@ -46,10 +44,10 @@ import java.util.List;
  * @author Denis Zhdanov
  * @since 6/13/13 5:42 PM
  */
-public class DetachExternalProjectAction extends ExternalSystemNodeAction<AbstractExternalEntityData> implements DumbAware {
+public class DetachExternalProjectAction extends ExternalSystemNodeAction<ProjectData> implements DumbAware {
 
   public DetachExternalProjectAction() {
-    super(AbstractExternalEntityData.class);
+    super(ProjectData.class);
     getTemplatePresentation().setText(ExternalSystemBundle.message("action.detach.external.project.text", "external"));
     getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.detach.external.project.description"));
     getTemplatePresentation().setIcon(SystemInfoRt.isMac ? AllIcons.ToolbarDecorator.Mac.Remove : AllIcons.ToolbarDecorator.Remove);
@@ -58,32 +56,22 @@ public class DetachExternalProjectAction extends ExternalSystemNodeAction<Abstra
   @Override
   protected boolean isEnabled(AnActionEvent e) {
     if (!super.isEnabled(e)) return false;
-    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
-    if (selectedNodes == null || selectedNodes.size() != 1) return false;
-    final Object externalData = selectedNodes.get(0).getData();
-    return (externalData instanceof ProjectData || externalData instanceof ModuleData);
+    return ExternalSystemDataKeys.SELECTED_PROJECT_NODE.getData(e.getDataContext()) != null;
   }
 
   @Override
   public void perform(@NotNull final Project project,
                       @NotNull ProjectSystemId projectSystemId,
-                      @NotNull AbstractExternalEntityData entityData,
+                      @NotNull ProjectData projectData,
                       @NotNull AnActionEvent e) {
 
     e.getPresentation().setText(
       ExternalSystemBundle.message("action.detach.external.project.text", projectSystemId.getReadableName())
     );
 
-
-    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
-    final ExternalSystemNode<?> externalSystemNode = ContainerUtil.getFirstItem(selectedNodes);
-    assert externalSystemNode != null;
-    final ProjectNode projectNode =
-      externalSystemNode instanceof ProjectNode ? (ProjectNode)externalSystemNode : externalSystemNode.findParent(ProjectNode.class);
+    final ProjectNode projectNode = ExternalSystemDataKeys.SELECTED_PROJECT_NODE.getData(e.getDataContext());
     assert projectNode != null;
 
-    final ProjectData projectData = projectNode.getData();
-    assert projectData != null;
     ExternalSystemApiUtil.getLocalSettings(project, projectSystemId).
       forgetExternalProjects(Collections.singleton(projectData.getLinkedExternalProjectPath()));
     ExternalSystemApiUtil.getSettings(project, projectSystemId).unlinkExternalProject(projectData.getLinkedExternalProjectPath());
@@ -103,14 +91,9 @@ public class DetachExternalProjectAction extends ExternalSystemNodeAction<Abstra
     }
 
     if (!orphanModules.isEmpty()) {
-      ExternalSystemUtil.ruleOrphanModules(orphanModules, project, projectSystemId, new Consumer<Boolean>() {
-        @Override
-        public void consume(Boolean result) {
-          if (result != null && result) {
-            projectNode.getGroup().remove(projectNode);
-          }
-        }
-      });
+      projectNode.getGroup().remove(projectNode);
+      ProjectDataManager.getInstance().removeData(
+        ProjectKeys.MODULE, orphanModules, Collections.<DataNode<ModuleData>>emptyList(), projectData, project, false);
     }
   }
 }
index 2d13ff88fedc6b760b329369522fe8fdbd97168a..c8f77d2182703ab40d1026ff261e8546dfb5fb1c 100644 (file)
@@ -59,4 +59,16 @@ public abstract class ExternalSystemAction extends AnAction implements DumbAware
   protected boolean hasProject(AnActionEvent e) {
     return getProject(e) != null;
   }
+
+  protected void setText(String message) {
+    getTemplatePresentation().setText(message);
+  }
+
+  protected void setDescription(String message) {
+    getTemplatePresentation().setDescription(message);
+  }
+
+  protected void setText(AnActionEvent e, String message) {
+    e.getPresentation().setText(message);
+  }
 }
\ No newline at end of file
index 94fb31f34c1036dc521a5226ebe9df16716cf1c2..c531bdb088db40de9b2e0bf3cc1922dff9d64173 100644 (file)
@@ -77,6 +77,12 @@ public abstract class ExternalSystemNodeAction<T> extends ExternalSystemAction {
     return node != null && dataClass.isInstance(node.getData()) ? (T)node.getData() : null;
   }
 
+  @SuppressWarnings("unchecked")
+  protected boolean isIgnoredNode(AnActionEvent e) {
+    ExternalSystemNode node = ContainerUtil.getFirstItem(ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext()));
+    return node != null && myExternalDataClazz.isInstance(node.getData()) && node.isIgnored();
+  }
+
   @Nullable
   protected VirtualFile getExternalConfig(@NotNull ExternalConfigPathAware data, ProjectSystemId externalSystemId) {
     String path = data.getLinkedExternalProjectPath();
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemOpenProjectStructureAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/ExternalSystemOpenProjectStructureAction.java
new file mode 100644 (file)
index 0000000..9c83c49
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2000-2015 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
+import com.intellij.openapi.externalSystem.service.ui.ExternalProjectStructureDialog;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.ProjectNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.containers.ContainerUtil;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 5/12/2015
+ */
+public class ExternalSystemOpenProjectStructureAction extends ExternalSystemAction {
+
+  public ExternalSystemOpenProjectStructureAction() {
+    //super(AbstractExternalEntityData.class);
+    //getTemplatePresentation().setText(ExternalSystemBundle.message("action.detach.external.project.text", "external"));
+    //getTemplatePresentation().setDescription(ExternalSystemBundle.message("action.detach.external.project.description"));
+    //getTemplatePresentation().setIcon(SystemInfoRt.isMac ? AllIcons.ToolbarDecorator.Mac.Remove : AllIcons.ToolbarDecorator.Remove);
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final Project project = getProject(e);
+    final ProjectSystemId projectSystemId = getSystemId(e);
+
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+
+    final ExternalProjectInfo projectInfo;
+    final ExternalSystemNode<?> externalSystemNode = ContainerUtil.getFirstItem(selectedNodes);
+    if (externalSystemNode == null) {
+      projectInfo = ContainerUtil.getFirstItem(ProjectDataManager.getInstance().getExternalProjectsData(project, projectSystemId));
+    }
+    else {
+      final ProjectNode projectNode =
+        externalSystemNode instanceof ProjectNode ? (ProjectNode)externalSystemNode : externalSystemNode.findParent(ProjectNode.class);
+      assert projectNode != null;
+
+      final ProjectData projectData = projectNode.getData();
+      assert projectData != null;
+      projectInfo =
+        ProjectDataManager.getInstance().getExternalProjectData(project, projectSystemId, projectData.getLinkedExternalProjectPath());
+    }
+
+    final ExternalProjectStructureDialog dialog;
+    if (projectInfo != null) {
+      dialog = new ExternalProjectStructureDialog(project, projectInfo, externalSystemNode != null ? externalSystemNode.getData() : null);
+      dialog.showAndGet();
+    }
+  }
+}
\ No newline at end of file
index c2ea887ecc0de4ebb4665d7bc67b92bebba7bc50..2409fbc0cfdf56a43ff7ee3d39cd4174008684ef 100644 (file)
@@ -65,4 +65,16 @@ public abstract class ExternalSystemToggleAction extends ToggleAction implements
   protected ProjectSystemId getSystemId(AnActionEvent e) {
     return ExternalSystemDataKeys.EXTERNAL_SYSTEM_ID.getData(e.getDataContext());
   }
+
+  protected void setText(String message) {
+    getTemplatePresentation().setText(message);
+  }
+
+  protected void setDescription(String message) {
+    getTemplatePresentation().setDescription(message);
+  }
+
+  protected void setText(AnActionEvent e, String message) {
+    e.getPresentation().setText(message);
+  }
 }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/IgnoreExternalProjectAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/IgnoreExternalProjectAction.java
new file mode 100644 (file)
index 0000000..32db13b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2015 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.action;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
+import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
+import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.ModuleNode;
+import com.intellij.openapi.externalSystem.view.ProjectNode;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 3/21/2015
+ */
+public class IgnoreExternalProjectAction extends ExternalSystemToggleAction {
+
+  private static final Logger LOG = Logger.getInstance(IgnoreExternalProjectAction.class);
+
+  public IgnoreExternalProjectAction() {
+    setText(ExternalSystemBundle.message("action.ignore.external.project.text", "external"));
+    setDescription(ExternalSystemBundle.message("action.ignore.external.project.description"));
+  }
+
+  @Override
+  public void setSelected(AnActionEvent e, boolean state) {
+    final ProjectSystemId projectSystemId = getSystemId(e);
+    setText(e, ExternalSystemBundle.message("action.ignore.external.project.text", projectSystemId.getReadableName()));
+
+    final ExternalSystemNode<ExternalConfigPathAware> projectNode = getProjectNode(e);
+    if (projectNode == null || projectNode.getData() == null) return;
+
+    projectNode.setIgnored(state);
+
+    final Project project = getProject(e);
+
+    final String externalProjectPath = projectNode.getData().getLinkedExternalProjectPath();
+    final ExternalProjectInfo externalProjectInfo =
+      ExternalSystemUtil.getExternalProjectInfo(project, projectSystemId, externalProjectPath);
+    if (externalProjectInfo == null || externalProjectInfo.getExternalProjectStructure() == null) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug(String.format("external project data not found, path: %s, data: %s", externalProjectPath, externalProjectInfo));
+      }
+      return;
+    }
+
+    ExternalSystemApiUtil.executeProjectChangeAction(true, new DisposeAwareProjectChange(project) {
+      @Override
+      public void execute() {
+        ProjectRootManagerEx.getInstanceEx(project).mergeRootsChangesDuring(new Runnable() {
+          @Override
+          public void run() {
+            final DataNode<ProjectData> projectDataNode = externalProjectInfo.getExternalProjectStructure();
+            ServiceManager.getService(ProjectDataManager.class).importData(projectDataNode, project, true);
+          }
+        });
+      }
+    });
+  }
+
+  @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    if (!super.isEnabled(e)) return false;
+    return getProjectNode(e) != null;
+  }
+
+  @Override
+  protected boolean doIsSelected(AnActionEvent e) {
+    final ExternalSystemNode projectNode = getProjectNode(e);
+    if (projectNode == null) return false;
+    return projectNode.isIgnored();
+  }
+
+  @Nullable
+  private static ExternalSystemNode<ExternalConfigPathAware> getProjectNode(AnActionEvent e) {
+    final List<ExternalSystemNode> selectedNodes = ExternalSystemDataKeys.SELECTED_NODES.getData(e.getDataContext());
+    if (selectedNodes == null || selectedNodes.size() != 1) return null;
+    final ExternalSystemNode<?> node = selectedNodes.get(0);
+    //noinspection unchecked
+    return (node instanceof ModuleNode || node instanceof ProjectNode) ? (ExternalSystemNode<ExternalConfigPathAware>)node : null;
+  }
+}
index fe3e6c8c2609a952e5e9b8cc17a1f1aa37718a32..712f0628ec3c6ace84272b22f7464afdac389b5f 100644 (file)
@@ -8,6 +8,8 @@ import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
+import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
 import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
 import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
@@ -60,7 +62,12 @@ public class RefreshExternalProjectAction extends ExternalSystemNodeAction<Abstr
     // We save all documents because there is a possible case that there is an external system config file changed inside the ide.
     FileDocumentManager.getInstance().saveAllDocuments();
 
-    ExternalSystemUtil.refreshProject(
-      project, projectSystemId, externalConfigPathAware.getLinkedExternalProjectPath(), false, ProgressExecutionMode.IN_BACKGROUND_ASYNC);
+    final ExternalProjectSettings linkedProjectSettings = ExternalSystemApiUtil.getSettings(
+      project, projectSystemId).getLinkedProjectSettings(externalConfigPathAware.getLinkedExternalProjectPath());
+    final String externalProjectPath = linkedProjectSettings == null
+                                       ? externalConfigPathAware.getLinkedExternalProjectPath()
+                                       : linkedProjectSettings.getExternalProjectPath();
+
+    ExternalSystemUtil.refreshProject(project, projectSystemId, externalProjectPath, false, ProgressExecutionMode.IN_BACKGROUND_ASYNC);
   }
 }
index 76208ed0eb94d5d1d5a40f550ddb2f5b02cdd2fa..b292bfe96a802611a27426d7140561159d0080f0 100644 (file)
@@ -36,6 +36,11 @@ public class AssignShortcutAction extends ExternalSystemNodeAction<TaskData> {
   }
 
   @Override
+  protected boolean isEnabled(AnActionEvent e) {
+    return super.isEnabled(e) && !isIgnoredNode(e);
+  }
+
+  @Override
   protected void perform(@NotNull Project project,
                          @NotNull ProjectSystemId projectSystemId,
                          @NotNull TaskData taskData,
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ShowIgnoredAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/task/ShowIgnoredAction.java
new file mode 100644 (file)
index 0000000..4a5472c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2015 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.action.task;
+
+import com.intellij.openapi.externalSystem.action.ExternalSystemViewGearAction;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsViewImpl;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 3/21/2015
+ */
+public class ShowIgnoredAction extends ExternalSystemViewGearAction {
+  @Override
+  protected boolean isSelected(@NotNull ExternalProjectsViewImpl view) {
+    return view.getShowIgnored();
+  }
+
+  @Override
+  protected void setSelected(@NotNull ExternalProjectsViewImpl view, boolean value) {
+    view.setShowIgnored(value);
+  }
+}
index 1ca9732fdb036b6c66920b990fcc576babdb4fc6..dda28b4c52ead434d44d4a8954d51f3bd1aeba1e 100644 (file)
@@ -75,7 +75,7 @@ public abstract class ToggleTaskActivationAction extends ExternalSystemToggleAct
 
     List<TaskData> tasks = new SmartList<TaskData>();
     for (ExternalSystemNode node : selectedNodes) {
-      if (node instanceof TaskNode) {
+      if (node instanceof TaskNode && !node.isIgnored()) {
         tasks.add((TaskData)node.getData());
       }
       else if (node instanceof RunConfigurationNode) {
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizer.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizer.java
new file mode 100644 (file)
index 0000000..a4633a3
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2015 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.importing;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.util.Couple;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 5/13/2015
+ */
+public abstract class ExternalProjectStructureCustomizer {
+  public static final ExtensionPointName<ExternalProjectStructureCustomizer> EP_NAME =
+    ExtensionPointName.create("com.intellij.externalProjectStructureCustomizer");
+
+  /**
+   * Set of data keys, which respective data can be marked as ignored in External Project Structure Dialog
+   * @return data keys
+   */
+  @NotNull
+  public abstract Set<? extends Key<?>> getIgnorableDataKeys();
+
+  /**
+   * Set of data keys, which respective data can be represented in External Project Structure Dialog
+   * @return data keys
+   */
+  @NotNull
+  public Set<? extends Key<?>> getPublicDataKeys() {
+    return Collections.emptySet();
+  }
+
+  @Nullable
+  public abstract Icon suggestIcon(@NotNull DataNode node, @NotNull ExternalSystemUiAware uiAware);
+
+  @NotNull
+  public Couple<String> getRepresentationName(@NotNull DataNode node) {
+    return Couple.of(node.getKey().toString(), null);
+  }
+}
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizerImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizerImpl.java
new file mode 100644 (file)
index 0000000..09def58
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2015 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.importing;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+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.AbstractNamedData;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.util.Couple;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Set;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 5/21/2015
+ */
+public class ExternalProjectStructureCustomizerImpl extends ExternalProjectStructureCustomizer {
+  private final Set<? extends Key<? extends AbstractNamedData>> myKeys = ContainerUtil.set(ProjectKeys.PROJECT, ProjectKeys.MODULE);
+
+  @NotNull
+  @Override
+  public Set<? extends Key<?>> getIgnorableDataKeys() {
+    return myKeys;
+  }
+
+  @NotNull
+  @Override
+  public Set<? extends Key<?>> getPublicDataKeys() {
+    return myKeys;
+  }
+
+  @Nullable
+  @Override
+  public Icon suggestIcon(@NotNull DataNode node, @NotNull ExternalSystemUiAware uiAware) {
+    if(ProjectKeys.PROJECT.equals(node.getKey())) {
+      return uiAware.getProjectIcon();
+    } else if(ProjectKeys.MODULE.equals(node.getKey())) {
+      return uiAware.getProjectIcon();
+    }
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public Couple<String> getRepresentationName(@NotNull DataNode node) {
+    if(ProjectKeys.PROJECT.equals(node.getKey())) {
+      ProjectData projectData = (ProjectData)node.getData();
+      return Couple.of("Project: " + projectData.getExternalName(), projectData.getDescription());
+    } else if(ProjectKeys.MODULE.equals(node.getKey())) {
+      ModuleData moduleData = (ModuleData)node.getData();
+      return Couple.of(moduleData.getId(), moduleData.getDescription());
+    }
+    return super.getRepresentationName(node);
+  }
+}
index ad43261a91beb1e9b7465bf816ee5cabfd1c0866..3d2a1cf3f87cc6196a9a25f34f81ac4ca116aae8 100644 (file)
@@ -88,4 +88,27 @@ public class InternalExternalProjectInfo implements ExternalProjectInfo, Seriali
   public void setLastImportTimestamp(long lastImportTimestamp) {
     this.lastImportTimestamp = lastImportTimestamp;
   }
+
+  @Override
+  public ExternalProjectInfo copy() {
+    InternalExternalProjectInfo copy = new InternalExternalProjectInfo(
+      myProjectSystemId,
+      myExternalProjectPath,
+      myExternalProjectStructure != null ? myExternalProjectStructure.graphCopy() : null
+    );
+    copy.setLastImportTimestamp(lastImportTimestamp);
+    copy.setLastSuccessfulImportTimestamp(lastSuccessfulImportTimestamp);
+    return copy;
+  }
+
+  @Override
+  public String toString() {
+    return "InternalExternalProjectInfo{" +
+           "myProjectSystemId=" + myProjectSystemId +
+           ", myExternalProjectPath='" + myExternalProjectPath + '\'' +
+           ", myExternalProjectStructure=" + myExternalProjectStructure +
+           ", lastSuccessfulImportTimestamp=" + lastSuccessfulImportTimestamp +
+           ", lastImportTimestamp=" + lastImportTimestamp +
+           '}';
+  }
 }
index b2f0b9ac7e2e0722721453774f762d687d6b43c6..65b60a42625d76e78e573a879c9086cf9a172a22 100644 (file)
@@ -9,7 +9,10 @@ import com.intellij.openapi.roots.*;
 import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable;
 import com.intellij.openapi.roots.libraries.Library;
 import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -37,6 +40,18 @@ public class PlatformFacadeImpl implements PlatformFacade {
 
   @NotNull
   @Override
+  public Collection<Module> getModules(@NotNull Project project, @NotNull final ProjectData projectData) {
+    return ContainerUtil.filter(getModules(project), new Condition<Module>() {
+      @Override
+      public boolean value(Module module) {
+        return ExternalSystemApiUtil.isExternalSystemAwareModule(projectData.getOwner(), module) &&
+               StringUtil.equals(projectData.getLinkedExternalProjectPath(), ExternalSystemApiUtil.getExternalRootProjectPath(module));
+      }
+    });
+  }
+
+  @NotNull
+  @Override
   public Collection<OrderEntry> getOrderEntries(@NotNull Module module) {
     return Arrays.asList(ModuleRootManager.getInstance(module).getOrderEntries());
   }
@@ -62,7 +77,8 @@ public class PlatformFacadeImpl implements PlatformFacade {
   @Nullable
   @Override
   public Module findIdeModule(@NotNull ModuleData module, @NotNull Project ideProject) {
-    return findIdeModule(module.getInternalName(), ideProject);
+    final Module ideModule = findIdeModule(module.getInternalName(), ideProject);
+    return ExternalSystemApiUtil.isExternalSystemAwareModule(module.getOwner(), ideModule) ? ideModule : null;
   }
 
   @Nullable
index 1da394bff80d22273809f4a6d6afc14669e74ad9..0b9bd9995028d66c1346fe37c5aad86872dbecdb 100644 (file)
@@ -98,7 +98,7 @@ public class ExternalSystemAutoImporter implements BulkFileListener, DocumentLis
             ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() {
               @Override
               public void run() {
-                myProjectDataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), myProject, true);
+                myProjectDataManager.importData(externalProject, myProject, true);
               }
             });
           }
index 3cf66f3e7a4f5de2037e0b704097aa4337ef031a..50373bf5d063524f3b1c723e73915251b6b18157 100644 (file)
@@ -15,9 +15,9 @@
  */
 package com.intellij.openapi.externalSystem.service.project.manage;
 
-import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.project.AbstractDependencyData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
@@ -26,11 +26,15 @@ import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.*;
+import com.intellij.openapi.util.Computable;
 import com.intellij.util.Consumer;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -39,16 +43,9 @@ import java.util.Map;
  */
 @Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
 public abstract class AbstractDependencyDataService<E extends AbstractDependencyData<?>, I extends ExportableOrderEntry>
-  implements ProjectDataServiceEx<E, I>
+  extends AbstractProjectDataService<E, I>
 {
 
-  public void importData(@NotNull final Collection<DataNode<E>> toImport,
-                         @NotNull final Project project,
-                         final boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    importData(toImport, project, platformFacade, synchronous);
-  }
-
   public void setScope(@NotNull final DependencyScope scope, @NotNull final ExportableOrderEntry dependency, boolean synchronous) {
     ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(dependency.getOwnerModule()) {
       @Override
@@ -97,22 +94,52 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
     }
   }
 
+
+  @NotNull
   @Override
-  public void removeData(@NotNull Collection<? extends I> toRemove, @NotNull Project project, boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    removeData(toRemove, project, platformFacade, synchronous);
+  public Computable<Collection<I>> computeOrphanData(@NotNull final Collection<DataNode<E>> toImport,
+                                                     @NotNull final ProjectData projectData,
+                                                     @NotNull final Project project,
+                                                     @NotNull final PlatformFacade platformFacade) {
+    return new Computable<Collection<I>>() {
+      @Override
+      public Collection<I> compute() {
+        MultiMap<String /*module name*/, String /*dep name*/> byModuleName = MultiMap.create();
+        for (DataNode<E> node : toImport) {
+          final AbstractDependencyData data = node.getData();
+          byModuleName.putValue(data.getOwnerModule().getInternalName(), data.getInternalName());
+        }
+
+        List<I> orphanEntries = ContainerUtil.newSmartList();
+        for (Module module : platformFacade.getModules(project, projectData)) {
+          for (OrderEntry entry : platformFacade.getOrderEntries(module)) {
+            if (getOrderEntryType().isInstance(entry) &&
+                !byModuleName.get(entry.getOwnerModule().getName()).contains(getOrderEntryName((I)entry))) {
+              orphanEntries.add((I)entry);
+            }
+          }
+        }
+
+        return orphanEntries;
+      }
+    };
+  }
+
+  @NotNull
+  public abstract Class<I> getOrderEntryType();
+
+  protected String getOrderEntryName(@NotNull I orderEntry) {
+    return orderEntry.getPresentableName();
   }
 
   @Override
-  public void removeData(@NotNull Collection<? extends I> toRemove,
-                         @NotNull Project project,
+  public void removeData(@NotNull final Computable<Collection<I>> toRemoveComputable,
+                         @NotNull final Collection<DataNode<E>> toIgnore,
+                         @NotNull final ProjectData projectData,
+                         @NotNull final Project project,
                          @NotNull final PlatformFacade platformFacade,
-                         boolean synchronous) {
-    if (toRemove.isEmpty()) {
-      return;
-    }
-
-    Map<Module, Collection<ExportableOrderEntry>> byModule = groupByModule(toRemove);
+                         final boolean synchronous) {
+    Map<Module, Collection<ExportableOrderEntry>> byModule = groupByModule(toRemoveComputable.compute());
     for (Map.Entry<Module, Collection<ExportableOrderEntry>> entry : byModule.entrySet()) {
       removeData(entry.getValue(), entry.getKey(), platformFacade, synchronous);
     }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractProjectDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractProjectDataService.java
new file mode 100644 (file)
index 0000000..2507d4f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000-2015 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.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 5/5/2015
+ */
+public abstract class AbstractProjectDataService<E, I> implements ProjectDataServiceEx<E, I> {
+
+  public final Computable.PredefinedValueComputable<Collection<I>> EMPTY_LIST =
+    new Computable.PredefinedValueComputable<Collection<I>>(Collections.<I>emptyList());
+
+  @NotNull
+  @Override
+  public abstract Key<E> getTargetDataKey();
+
+  @Override
+  public void importData(@NotNull Collection<DataNode<E>> toImport,
+                         @Nullable ProjectData projectData,
+                         @NotNull Project project,
+                         @NotNull PlatformFacade platformFacade,
+                         boolean synchronous) {
+  }
+
+  @NotNull
+  @Override
+  public Computable<Collection<I>> computeOrphanData(@NotNull Collection<DataNode<E>> toImport,
+                                                     @NotNull ProjectData projectData,
+                                                     @NotNull Project project,
+                                                     @NotNull PlatformFacade platformFacade) {
+    return EMPTY_LIST;
+  }
+
+  @Override
+  public void removeData(@NotNull Computable<Collection<I>> toRemoveComputable,
+                         @NotNull Collection<DataNode<E>> toIgnore,
+                         @NotNull ProjectData projectData,
+                         @NotNull Project project,
+                         @NotNull PlatformFacade platformFacade,
+                         boolean synchronous) {
+  }
+
+  /**
+   * @deprecated to be removed in v15
+   */
+  @Deprecated
+  @Override
+  public void importData(@NotNull Collection<DataNode<E>> toImport, @NotNull Project project, boolean synchronous) {
+  }
+
+  /**
+   * @deprecated to be removed in v15
+   */
+  @Deprecated
+  @Override
+  public void removeData(@NotNull Collection<? extends I> toRemove, @NotNull Project project, boolean synchronous) {
+  }
+}
index 28d284d3d1fd7e3cfdb6972e8b3234564723dcb6..7e04d02d405576bdee97c5b13e39fdb52557fff4 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.intellij.openapi.externalSystem.service.project.manage;
 
-import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.Key;
@@ -25,6 +24,7 @@ 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.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
 import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
@@ -46,7 +46,9 @@ import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.Consumer;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
 import org.jetbrains.jps.model.java.JavaResourceRootType;
 import org.jetbrains.jps.model.java.JavaSourceRootProperties;
@@ -65,7 +67,7 @@ import java.util.Map;
  * @since 2/7/12 3:20 PM
  */
 @Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
-public class ContentRootDataService implements ProjectDataServiceEx<ContentRootData, ContentEntry> {
+public class ContentRootDataService extends AbstractProjectDataService<ContentRootData, ContentEntry> {
 
   private static final Logger LOG = Logger.getInstance("#" + ContentRootDataService.class.getName());
 
@@ -75,15 +77,9 @@ public class ContentRootDataService implements ProjectDataServiceEx<ContentRootD
     return ProjectKeys.CONTENT_ROOT;
   }
 
-  public void importData(@NotNull final Collection<DataNode<ContentRootData>> toImport,
-                         @NotNull final Project project,
-                         final boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    importData(toImport, project, platformFacade, synchronous);
-  }
-
   @Override
   public void importData(@NotNull final Collection<DataNode<ContentRootData>> toImport,
+                         @Nullable ProjectData projectData,
                          @NotNull final Project project,
                          @NotNull final PlatformFacade platformFacade,
                          final boolean synchronous) {
@@ -91,8 +87,8 @@ public class ContentRootDataService implements ProjectDataServiceEx<ContentRootD
       return;
     }
 
-    Map<DataNode<ModuleData>, List<DataNode<ContentRootData>>> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE);
-    for (Map.Entry<DataNode<ModuleData>, List<DataNode<ContentRootData>>> entry : byModule.entrySet()) {
+    MultiMap<DataNode<ModuleData>, DataNode<ContentRootData>> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE);
+    for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ContentRootData>>> entry : byModule.entrySet()) {
       final Module module = platformFacade.findIdeModule(entry.getKey().getData(), project);
       if (module == null) {
         LOG.warn(String.format(
@@ -236,16 +232,6 @@ public class ContentRootDataService implements ProjectDataServiceEx<ContentRootD
     }
   }
 
-  @Override
-  public void removeData(@NotNull Collection<? extends ContentEntry> toRemove, @NotNull Project project, boolean synchronous) {
-  }
-
-  @Override
-  public void removeData(@NotNull Collection<? extends ContentEntry> toRemove,
-                         @NotNull Project project,
-                         @NotNull PlatformFacade platformFacade,
-                         boolean synchronous) {
-  }
 
   private static String toVfsUrl(@NotNull String path) {
     return LocalFileSystem.PROTOCOL_PREFIX + path;
index cc0451103bb0d102c565a5416081b14ce81dbb99..7dafb49d2099297ce80d214bd48e1840406034ac 100644 (file)
 package com.intellij.openapi.externalSystem.service.project.manage;
 
 import com.intellij.openapi.application.PathManager;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.components.SettingsSavingComponent;
+import com.intellij.openapi.components.*;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.ExternalSystemManager;
-import com.intellij.openapi.externalSystem.model.DataNode;
-import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
-import com.intellij.openapi.externalSystem.model.ProjectKeys;
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.*;
 import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
 import com.intellij.openapi.externalSystem.model.internal.InternalExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware;
 import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
@@ -42,6 +39,10 @@ import com.intellij.util.Consumer;
 import com.intellij.util.Function;
 import com.intellij.util.SmartList;
 import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.MapAnnotation;
+import com.intellij.util.xmlb.annotations.Property;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -52,11 +53,14 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static com.intellij.openapi.externalSystem.model.ProjectKeys.*;
+
 /**
  * @author Vladislav.Soroka
  * @since 9/18/2014
  */
-public class ExternalProjectsDataStorage implements SettingsSavingComponent {
+@State(name = "ExternalProjectsData", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)})
+public class ExternalProjectsDataStorage implements SettingsSavingComponent, PersistentStateComponent<ExternalProjectsDataStorage.State> {
   private static final Logger LOG = Logger.getInstance(ExternalProjectsDataStorage.class);
 
   private static final String STORAGE_VERSION = ExternalProjectsDataStorage.class.getSimpleName() + ".1";
@@ -68,6 +72,7 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
     ContainerUtil.newConcurrentMap(ExternalSystemUtil.HASHING_STRATEGY);
 
   private final AtomicBoolean changed = new AtomicBoolean();
+  private State myState = new State();
 
   public static ExternalProjectsDataStorage getInstance(@NotNull Project project) {
     return ServiceManager.getService(project, ExternalProjectsDataStorage.class);
@@ -122,6 +127,8 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
   }
 
   synchronized void update(@NotNull ExternalProjectInfo externalProjectInfo) {
+    restoreInclusionSettings(externalProjectInfo.getExternalProjectStructure());
+
     final ProjectSystemId projectSystemId = externalProjectInfo.getProjectSystemId();
     final String projectPath = externalProjectInfo.getExternalProjectPath();
     DataNode<ProjectData> externalProjectStructure = externalProjectInfo.getExternalProjectStructure();
@@ -138,12 +145,18 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
       if (externalProjectInfo.getExternalProjectStructure() == null) {
         externalProjectStructure = old.getExternalProjectStructure();
       }
+      else {
+        externalProjectStructure = externalProjectInfo.getExternalProjectStructure().graphCopy();
+      }
+    }
+    else {
+      externalProjectStructure = externalProjectStructure != null ? externalProjectStructure.graphCopy() : null;
     }
 
     InternalExternalProjectInfo merged = new InternalExternalProjectInfo(
       projectSystemId,
       projectPath,
-      externalProjectStructure != null ? externalProjectStructure.graphCopy() : null
+      externalProjectStructure
     );
     merged.setLastImportTimestamp(lastImportTimestamp);
     merged.setLastSuccessfulImportTimestamp(lastSuccessfulImportTimestamp);
@@ -152,6 +165,68 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
     changed.set(true);
   }
 
+  synchronized void restoreInclusionSettings(@Nullable DataNode<ProjectData> projectDataNode) {
+    if (projectDataNode == null) return;
+    final String rootProjectPath = projectDataNode.getData().getLinkedExternalProjectPath();
+    final ProjectState projectState = myState.map.get(rootProjectPath);
+    if (projectState == null) return;
+
+    ExternalSystemApiUtil.visit(projectDataNode, new Consumer<DataNode<?>>() {
+      @Override
+      public void consume(DataNode node) {
+        final DataNode<ExternalConfigPathAware> projectOrModuleNode = resolveProjectNode(node);
+        assert projectOrModuleNode != null;
+        final ModuleState moduleState = projectState.map.get(projectOrModuleNode.getData().getLinkedExternalProjectPath());
+        node.setIgnored(isIgnored(projectState, moduleState, node.getKey()));
+      }
+    });
+  }
+
+  synchronized void saveInclusionSettings(@Nullable DataNode<ProjectData> projectDataNode) {
+    if (projectDataNode == null) return;
+
+    final MultiMap<String, String> inclusionMap = MultiMap.create();
+    final MultiMap<String, String> exclusionMap = MultiMap.create();
+    ExternalSystemApiUtil.visit(projectDataNode, new Consumer<DataNode<?>>() {
+      @Override
+      public void consume(DataNode dataNode) {
+        try {
+          dataNode.getDataBytes();
+          DataNode<ExternalConfigPathAware> projectNode = resolveProjectNode(dataNode);
+          if (projectNode != null) {
+            final String projectPath = projectNode.getData().getLinkedExternalProjectPath();
+            if (projectNode.isIgnored() || dataNode.isIgnored()) {
+              exclusionMap.putValue(projectPath, dataNode.getKey().getDataType());
+            }
+            else {
+              inclusionMap.putValue(projectPath, dataNode.getKey().getDataType());
+            }
+          }
+        }
+        catch (IOException e) {
+          dataNode.clear(true);
+        }
+      }
+    });
+    final MultiMap<String, String> map;
+    ProjectState projectState = new ProjectState();
+    if (inclusionMap.size() < exclusionMap.size()) {
+      projectState.isInclusion = true;
+      map = inclusionMap;
+    }
+    else {
+      projectState.isInclusion = false;
+      map = exclusionMap;
+    }
+
+    for (String path : map.keySet()) {
+      projectState.map.put(path, new ModuleState(map.get(path)));
+    }
+
+    myState.map.put(projectDataNode.getData().getLinkedExternalProjectPath(), projectState);
+    changed.set(true);
+  }
+
   @Nullable
   synchronized ExternalProjectInfo get(@NotNull ProjectSystemId projectSystemId, @NotNull String externalProjectPath) {
     return myExternalRootProjects.get(Pair.create(projectSystemId, new File(externalProjectPath)));
@@ -219,20 +294,20 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
                                                @NotNull Collection<ExternalProjectPojo> childProjects,
                                                @NotNull Map<String, Collection<ExternalTaskPojo>> availableTasks) {
     ProjectData projectData = new ProjectData(systemId, rootProject.getName(), rootProject.getPath(), rootProject.getPath());
-    DataNode<ProjectData> projectDataNode = new DataNode<ProjectData>(ProjectKeys.PROJECT, projectData, null);
+    DataNode<ProjectData> projectDataNode = new DataNode<ProjectData>(PROJECT, projectData, null);
 
     for (ExternalProjectPojo childProject : childProjects) {
       String moduleConfigPath = childProject.getPath();
       ModuleData moduleData = new ModuleData(childProject.getName(), systemId,
                                              ModuleTypeId.JAVA_MODULE, childProject.getName(),
                                              moduleConfigPath, moduleConfigPath);
-      final DataNode<ModuleData> moduleDataNode = projectDataNode.createChild(ProjectKeys.MODULE, moduleData);
+      final DataNode<ModuleData> moduleDataNode = projectDataNode.createChild(MODULE, moduleData);
 
       final Collection<ExternalTaskPojo> moduleTasks = availableTasks.get(moduleConfigPath);
       if (moduleTasks != null) {
         for (ExternalTaskPojo moduleTask : moduleTasks) {
           TaskData taskData = new TaskData(systemId, moduleTask.getName(), moduleConfigPath, moduleTask.getDescription());
-          moduleDataNode.createChild(ProjectKeys.TASK, taskData);
+          moduleDataNode.createChild(TASK, taskData);
         }
       }
     }
@@ -240,7 +315,8 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
     return projectDataNode;
   }
 
-  private static void doSave(@NotNull Project project, @NotNull Collection<InternalExternalProjectInfo> externalProjects) throws IOException {
+  private static void doSave(@NotNull final Project project, @NotNull Collection<InternalExternalProjectInfo> externalProjects)
+    throws IOException {
     final File projectConfigurationFile = getProjectConfigurationFile(project);
     if (!FileUtil.createParentDirs(projectConfigurationFile)) {
       throw new IOException("Unable to save " + projectConfigurationFile);
@@ -253,7 +329,7 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
         continue;
       }
 
-      ExternalSystemApiUtil.visit(externalProject.getExternalProjectStructure(), new Consumer<DataNode>() {
+      ExternalSystemApiUtil.visit(externalProject.getExternalProjectStructure(), new Consumer<DataNode<?>>() {
         @Override
         public void consume(DataNode dataNode) {
           try {
@@ -285,10 +361,23 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
     }
   }
 
+  @SuppressWarnings("unchecked")
+  @Nullable
+  private static DataNode<ExternalConfigPathAware> resolveProjectNode(@NotNull DataNode node) {
+    if ((MODULE.equals(node.getKey()) || PROJECT.equals(node.getKey())) && node.getData() instanceof ExternalConfigPathAware) {
+      return (DataNode<ExternalConfigPathAware>)node;
+    }
+    DataNode parent = ExternalSystemApiUtil.findParent(node, MODULE);
+    if (parent == null) {
+      parent = ExternalSystemApiUtil.findParent(node, PROJECT);
+    }
+    return parent;
+  }
+
   @NotNull
   private static Collection<InternalExternalProjectInfo> load(@NotNull Project project) throws IOException {
     SmartList<InternalExternalProjectInfo> projects = new SmartList<InternalExternalProjectInfo>();
-    final File configurationFile = getProjectConfigurationFile(project);
+    @SuppressWarnings("unchecked") final File configurationFile = getProjectConfigurationFile(project);
     if (!configurationFile.isFile()) return projects;
 
     DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(configurationFile)));
@@ -332,4 +421,73 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponent {
   private static File getExternalBuildSystemDir(String folder) {
     return new File(PathManager.getSystemPath(), "external_build_system" + "/" + folder).getAbsoluteFile();
   }
+
+  @Nullable
+  @Override
+  public synchronized State getState() {
+    return myState;
+  }
+
+  @Override
+  public synchronized void loadState(State state) {
+    myState = state == null ? new State() : state;
+  }
+
+  synchronized void setIgnored(@NotNull final DataNode<?> dataNode, final boolean isIgnored) {
+    //noinspection unchecked
+    final DataNode<ProjectData> projectDataNode =
+      PROJECT.equals(dataNode.getKey()) ? (DataNode<ProjectData>)dataNode : ExternalSystemApiUtil.findParent(dataNode, PROJECT);
+    if (projectDataNode == null) {
+      return;
+    }
+
+    ExternalSystemApiUtil.visit(dataNode, new Consumer<DataNode<?>>() {
+      @Override
+      public void consume(DataNode node) {
+        node.setIgnored(isIgnored);
+      }
+    });
+
+    saveInclusionSettings(projectDataNode);
+  }
+
+  synchronized boolean isIgnored(@NotNull String rootProjectPath, @NotNull String modulePath, @NotNull Key key) {
+    final ProjectState projectState = myState.map.get(rootProjectPath);
+    if (projectState == null) return false;
+
+    final ModuleState moduleState = projectState.map.get(modulePath);
+    return isIgnored(projectState, moduleState, key);
+  }
+
+  private static boolean isIgnored(@NotNull ProjectState projectState, @Nullable ModuleState moduleState, @NotNull Key<?> key) {
+    return projectState.isInclusion ^ (moduleState != null && moduleState.set.contains(key.getDataType()));
+  }
+
+  static class State {
+    @Property(surroundWithTag = false)
+    @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
+      keyAttributeName = "path", entryTagName = "projectState")
+    public Map<String, ProjectState> map = ContainerUtil.newConcurrentMap();
+  }
+
+  static class ProjectState {
+    @Property(surroundWithTag = false)
+    @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
+      keyAttributeName = "path", entryTagName = "dataType")
+    public Map<String, ModuleState> map = ContainerUtil.newConcurrentMap();
+    public boolean isInclusion;
+  }
+
+  static class ModuleState {
+    @Property(surroundWithTag = false)
+    @AbstractCollection(surroundWithTag = false, elementTag = "id")
+    public Set<String> set = ContainerUtil.newConcurrentSet();
+
+    public ModuleState() {
+    }
+
+    public ModuleState(Collection<String> values) {
+      set.addAll(values);
+    }
+  }
 }
index d8fc4056208176537db5b4146bce41e9560a7d7e..e71d0ce715e1f36f798e81c64f7138fd990aceff 100644 (file)
@@ -18,18 +18,21 @@ package com.intellij.openapi.externalSystem.service.project.manage;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.*;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.ExternalSystemManager;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
 import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
 import com.intellij.openapi.externalSystem.view.ExternalProjectsViewImpl;
 import com.intellij.openapi.externalSystem.view.ExternalProjectsViewState;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.util.Function;
 import com.intellij.util.SmartList;
@@ -43,6 +46,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static com.intellij.openapi.externalSystem.model.ProjectKeys.MODULE;
 import static com.intellij.openapi.externalSystem.model.ProjectKeys.TASK;
 
 /**
@@ -51,6 +55,7 @@ import static com.intellij.openapi.externalSystem.model.ProjectKeys.TASK;
  */
 @State(name = "ExternalProjectsManager", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)})
 public class ExternalProjectsManager implements PersistentStateComponent<ExternalProjectsState>, Disposable {
+  private static final Logger LOG = Logger.getInstance(ExternalProjectsManager.class);
 
   private final AtomicBoolean isInitialized = new AtomicBoolean();
   @NotNull
@@ -211,12 +216,24 @@ public class ExternalProjectsManager implements PersistentStateComponent<Externa
       }
 
       @Override
-      public  Map<String, TaskActivationState> getProjectsTasksActivationMap(@NotNull final ProjectSystemId systemId) {
-          return myState.getExternalSystemsState().get(systemId.getId()).getExternalSystemsTaskActivation();
+      public Map<String, TaskActivationState> getProjectsTasksActivationMap(@NotNull final ProjectSystemId systemId) {
+        return myState.getExternalSystemsState().get(systemId.getId()).getExternalSystemsTaskActivation();
       }
     };
   }
 
+  public boolean isIgnored(@NotNull ProjectSystemId systemId, @NotNull String projectPath) {
+    final ExternalProjectInfo projectInfo = ExternalSystemUtil.getExternalProjectInfo(myProject, systemId, projectPath);
+    if (projectInfo == null) return true;
+
+    return ExternalProjectsDataStorage.getInstance(myProject).isIgnored(projectInfo.getExternalProjectPath(), projectPath, MODULE);
+  }
+
+  public void setIgnored(@NotNull DataNode<?> dataNode, boolean isIgnored) {
+    ExternalProjectsDataStorage.getInstance(myProject).setIgnored(dataNode, isIgnored);
+    ExternalSystemKeymapExtension.updateActions(myProject, ExternalSystemApiUtil.findAllRecursively(dataNode, TASK));
+  }
+
   @Override
   public void loadState(ExternalProjectsState state) {
     myState = state == null ? new ExternalProjectsState() : state;
index 7821268fd60f8c47afec7d26b6603d1458c8da7b..8802aff0b143a657e03761b08a07f4cc75970317 100644 (file)
@@ -56,7 +56,7 @@ public class ExternalProjectsState {
   public static class State {
     private ExternalProjectsViewState projectsViewState = new ExternalProjectsViewState();
 
-    private final Map<String, TaskActivationState> myExternalSystemsTaskActivation = new FactoryMap<String, TaskActivationState>() {
+    private final Map<String, TaskActivationState> myExternalSystemsTaskActivation = new NullSafeMap<String, TaskActivationState>() {
       @Nullable
       @Override
       protected TaskActivationState create(String key) {
@@ -64,20 +64,14 @@ public class ExternalProjectsState {
       }
 
       @Override
-      public TaskActivationState put(String key, TaskActivationState value) {
-        if(value == null) return null;
-        return super.put(key, value);
-      }
-
-      @Override
       protected Map<String, TaskActivationState> createMap() {
         return new LinkedHashMap<String, TaskActivationState>();
       }
     };
 
     @Property(surroundWithTag = false)
-    @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false,
-      keyAttributeName = "path", entryTagName = "task", sortBeforeSave = false)
+    @MapAnnotation(keyAttributeName = "path", entryTagName = "task",
+      surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false, sortBeforeSave = false)
     public Map<String, TaskActivationState> getExternalSystemsTaskActivation() {
       return myExternalSystemsTaskActivation;
     }
index 6e5a8c848883d0a11088fd5656480fd700af1ad5..419f6a00053a476a6dc6a59dfc191e79e864dd98 100644 (file)
@@ -124,7 +124,7 @@ public class ExternalSystemKeymapExtension implements KeymapExtension {
     if (manager != null) {
       for (DataNode<TaskData> each : taskData) {
         final DataNode<ModuleData> moduleData = ExternalSystemApiUtil.findParent(each, ProjectKeys.MODULE);
-        if (moduleData == null) continue;
+        if (moduleData == null || moduleData.isIgnored()) continue;
         ExternalSystemTaskAction eachAction = new ExternalSystemTaskAction(project, moduleData.getData().getInternalName(), each.getData());
 
         manager.unregisterAction(eachAction.getId());
index 539f215de43d0af305985da05d5ef9fd7acde3e1..641c7024f5e4f6f7162de1f0be4dc92ba5c65a9b 100644 (file)
@@ -193,6 +193,8 @@ public class ExternalSystemTaskActivator {
 
       if (tasks.isEmpty()) continue;
 
+      if (ExternalProjectsManager.getInstance(myProject).isIgnored(activation.systemId, activation.projectPath)) continue;
+
       ExternalSystemTaskExecutionSettings executionSettings = new ExternalSystemTaskExecutionSettings();
       executionSettings.setExternalSystemIdString(activation.systemId.toString());
       executionSettings.setExternalProjectPath(activation.projectPath);
index e4a8a7c5c9c2bd029047eddcc51fc4402ec37f2f..ab16cfc045c46c9508147622c32d06c5d8c6b63c 100644 (file)
@@ -8,6 +8,8 @@ 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.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.ExternalLibraryPathTypeMapper;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
@@ -18,6 +20,7 @@ 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.util.Computable;
 import com.intellij.openapi.vfs.JarFileSystem;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VfsUtil;
@@ -27,6 +30,7 @@ import com.intellij.util.NotNullFunction;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.util.Collection;
@@ -38,8 +42,8 @@ import java.util.Set;
  * @author Denis Zhdanov
  * @since 2/15/12 11:32 AM
  */
-@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
-public class LibraryDataService implements ProjectDataServiceEx<LibraryData, Library> {
+@Order(ExternalSystemConstants.BUILTIN_LIBRARY_DATA_SERVICE_ORDER)
+public class LibraryDataService extends AbstractProjectDataService<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>() {
@@ -62,15 +66,9 @@ public class LibraryDataService implements ProjectDataServiceEx<LibraryData, Lib
     return ProjectKeys.LIBRARY;
   }
 
-  public void importData(@NotNull final Collection<DataNode<LibraryData>> toImport,
-                         @NotNull final Project project,
-                         final boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    importData(toImport, project, platformFacade, synchronous);
-  }
-
   @Override
   public void importData(@NotNull final Collection<DataNode<LibraryData>> toImport,
+                         @Nullable final ProjectData projectData,
                          @NotNull final Project project,
                          @NotNull final PlatformFacade platformFacade,
                          final boolean synchronous) {
@@ -185,17 +183,14 @@ public class LibraryDataService implements ProjectDataServiceEx<LibraryData, Lib
   }
 
   @Override
-  public void removeData(@NotNull final Collection<? extends Library> libraries, @NotNull final Project project, boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    removeData(libraries, project, platformFacade, synchronous);
-  }
-
-  @Override
-  public void removeData(@NotNull final Collection<? extends Library> libraries,
+  public void removeData(@NotNull final Computable<Collection<Library>> toRemoveComputable,
+                         @NotNull Collection<DataNode<LibraryData>> toIgnore,
+                         @NotNull ProjectData projectData,
                          @NotNull final Project project,
                          @NotNull final PlatformFacade platformFacade,
                          boolean synchronous) {
-    if (libraries.isEmpty()) {
+    final Collection<Library> toRemove = toRemoveComputable.compute();
+    if (toRemove.isEmpty()) {
       return;
     }
     ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@@ -204,7 +199,7 @@ public class LibraryDataService implements ProjectDataServiceEx<LibraryData, Lib
         final LibraryTable libraryTable = platformFacade.getProjectLibraryTable(project);
         final LibraryTable.ModifiableModel model = libraryTable.getModifiableModel();
         try {
-          for (Library library : libraries) {
+          for (Library library : toRemove) {
             String libraryName = library.getName();
             if (libraryName != null) {
               Library libraryToRemove = model.getLibraryByName(libraryName);
index a7844acf3b253dfa855ac780e2a0ce18f5c3967f..184dc77296282b57db290d80925c2bfbb870e6f8 100644 (file)
@@ -27,17 +27,23 @@ 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.*;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.OrderRootType;
 import com.intellij.openapi.roots.impl.ModuleLibraryOrderEntryImpl;
 import com.intellij.openapi.roots.libraries.Library;
 import com.intellij.openapi.roots.libraries.LibraryTable;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.BooleanFunction;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
-import java.util.*;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
 
 import static com.intellij.openapi.externalSystem.model.ProjectKeys.MODULE;
 
@@ -50,13 +56,9 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
 
   private static final Logger LOG = Logger.getInstance("#" + LibraryDependencyDataService.class.getName());
 
-  @NotNull private final ModuleDataService      myModuleManager;
-  @NotNull private final LibraryDataService     myLibraryManager;
+  @NotNull private final LibraryDataService myLibraryManager;
 
-  public LibraryDependencyDataService(@NotNull ModuleDataService moduleManager,
-                                      @NotNull LibraryDataService libraryManager)
-  {
-    myModuleManager = moduleManager;
+  public LibraryDependencyDataService(@NotNull LibraryDataService libraryManager) {
     myLibraryManager = libraryManager;
   }
 
@@ -68,6 +70,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
 
   @Override
   public void importData(@NotNull Collection<DataNode<LibraryDependencyData>> toImport,
+                         @Nullable ProjectData projectData,
                          @NotNull Project project,
                          @NotNull PlatformFacade platformFacade,
                          boolean synchronous) {
@@ -75,34 +78,38 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
       return;
     }
 
-    Map<DataNode<ModuleData>, List<DataNode<LibraryDependencyData>>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
-    for (Map.Entry<DataNode<ModuleData>, List<DataNode<LibraryDependencyData>>> entry : byModule.entrySet()) {
+    MultiMap<DataNode<ModuleData>, DataNode<LibraryDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
+    for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<LibraryDependencyData>>> entry : byModule.entrySet()) {
       Module module = platformFacade.findIdeModule(entry.getKey().getData(), project);
       if (module == null) {
-        myModuleManager.importData(Collections.singleton(entry.getKey()), project, true);
-        module = platformFacade.findIdeModule(entry.getKey().getData(), project);
-        if (module == null) {
-          LOG.warn(String.format(
-            "Can't import library dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
-            entry.getValue(), entry.getKey()
-          ));
-          continue;
-        }
+        LOG.warn(String.format(
+          "Can't import library dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
+          entry.getValue(), entry.getKey()
+        ));
+        continue;
       }
       importData(entry.getValue(), module, platformFacade, synchronous);
     }
   }
 
-  public void importData(@NotNull final Collection<DataNode<LibraryDependencyData>> nodesToImport,
+  @NotNull
+  @Override
+  public Class<LibraryOrderEntry> getOrderEntryType() {
+    return LibraryOrderEntry.class;
+  }
+
+  @Override
+  protected String getOrderEntryName(@NotNull LibraryOrderEntry orderEntry) {
+    return orderEntry.getLibraryName();
+  }
+
+  private void importData(@NotNull final Collection<DataNode<LibraryDependencyData>> nodesToImport,
                          @NotNull final Module module,
                          @NotNull final PlatformFacade platformFacade,
-                         final boolean synchronous)
-  {
+                          final boolean synchronous) {
     ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(module) {
       @Override
       public void execute() {
-        importMissingProjectLibraries(module, platformFacade, nodesToImport, synchronous);
-        
         // The general idea is to import all external project library dependencies and module libraries which don't present at the
         // ide side yet and remove all project library dependencies and module libraries which present at the ide but not at
         // the given collection.
@@ -111,7 +118,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
         Map<Set<String>/* library paths */, LibraryDependencyData> moduleLibrariesToImport = ContainerUtilRt.newHashMap();
         Map<String/* library name + scope */, LibraryDependencyData> projectLibrariesToImport = ContainerUtilRt.newHashMap();
         Set<LibraryDependencyData> toImport = ContainerUtilRt.newLinkedHashSet();
-        
+
         boolean hasUnresolved = false;
         for (DataNode<LibraryDependencyData> dependencyNode : nodesToImport) {
           LibraryDependencyData dependencyData = dependencyNode.getData();
@@ -134,9 +141,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
           }
         }
 
-        //ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
-        //final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel();
-        final ModifiableRootModel moduleRootModel = ModifiableModelsProvider.SERVICE.getInstance().getModuleModifiableModel(module);
+        final ModifiableRootModel moduleRootModel = platformFacade.getModuleModifiableModel(module);
         LibraryTable moduleLibraryTable = moduleRootModel.getModuleLibraryTable();
         LibraryTable libraryTable = platformFacade.getProjectLibraryTable(module.getProject());
         try {
@@ -158,8 +163,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
                              @NotNull ModifiableRootModel moduleRootModel,
                              @NotNull LibraryTable moduleLibraryTable,
                              @NotNull LibraryTable libraryTable,
-                             @NotNull Module module)
-  {
+                             @NotNull Module module) {
     for (final LibraryDependencyData dependencyData : toImport) {
       final LibraryData libraryData = dependencyData.getTarget();
       final String libraryName = libraryData.getInternalName();
@@ -171,8 +175,8 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
         case PROJECT:
           final Library projectLib = libraryTable.getLibraryByName(libraryName);
           if (projectLib == null) {
-            assert false;
-            continue;
+            syncExistingLibraryDependency(dependencyData, moduleLibraryTable.createLibrary(libraryName), moduleRootModel, module);
+            break;
           }
           LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(projectLib);
           setLibraryScope(orderEntry, projectLib, module, dependencyData);
@@ -197,8 +201,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
                                              @NotNull Map<String, LibraryDependencyData> projectLibrariesToImport,
                                              @NotNull Set<LibraryDependencyData> toImport,
                                              @NotNull ModifiableRootModel moduleRootModel,
-                                             boolean hasUnresolvedLibraries)
-  {
+                                             boolean hasUnresolvedLibraries) {
     Set<String> moduleLibraryKey = ContainerUtilRt.newHashSet();
     for (OrderEntry entry : moduleRootModel.getOrderEntries()) {
       if (entry instanceof ModuleLibraryOrderEntryImpl) {
@@ -254,38 +257,4 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
       libModel.commit();
     }
   }
-
-  private void importMissingProjectLibraries(@NotNull Module module,
-                                             @NotNull PlatformFacade platformFacade,
-                                             @NotNull Collection<DataNode<LibraryDependencyData>> nodesToImport,
-                                             boolean synchronous)
-  {
-    LibraryTable libraryTable = platformFacade.getProjectLibraryTable(module.getProject());
-    List<DataNode<LibraryData>> librariesToImport = ContainerUtilRt.newArrayList();
-    for (DataNode<LibraryDependencyData> dataNode : nodesToImport) {
-      final LibraryDependencyData dependencyData = dataNode.getData();
-      if (dependencyData.getLevel() != LibraryLevel.PROJECT) {
-        continue;
-      }
-      final Library library = libraryTable.getLibraryByName(dependencyData.getInternalName());
-      if (library == null) {
-        DataNode<ProjectData> projectNode = dataNode.getDataNode(ProjectKeys.PROJECT);
-        if (projectNode != null) {
-          DataNode<LibraryData> libraryNode =
-            ExternalSystemApiUtil.find(projectNode, ProjectKeys.LIBRARY, new BooleanFunction<DataNode<LibraryData>>() {
-              @Override
-              public boolean fun(DataNode<LibraryData> node) {
-                return node.getData().equals(dependencyData.getTarget());
-              }
-            });
-          if (libraryNode != null) {
-            librariesToImport.add(libraryNode);
-          }
-        }
-      }
-    }
-    if (!librariesToImport.isEmpty()) {
-      myLibraryManager.importData(librariesToImport, module.getProject(), synchronous);
-    }
-  }
 }
index cd37c81d10a6322f09c7d4dcfb7d6603ae32757c..a5024b071f03d307f5fce755a4f339bc7c9f888b 100644 (file)
@@ -2,33 +2,45 @@ package com.intellij.openapi.externalSystem.service.project.manage;
 
 import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.components.ServiceManager;
 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.ExternalSystemSourceType;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
-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.externalSystem.util.*;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.*;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VfsUtilCore;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.CheckBoxList;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.components.JBScrollPane;
 import com.intellij.util.Alarm;
+import com.intellij.util.Consumer;
+import com.intellij.util.Function;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
+import javax.swing.*;
+import java.awt.*;
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -38,8 +50,8 @@ import java.util.concurrent.TimeUnit;
  * @author Denis Zhdanov
  * @since 2/7/12 2:49 PM
  */
-@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
-public class ModuleDataService implements ProjectDataServiceEx<ModuleData, Module> {
+@Order(ExternalSystemConstants.BUILTIN_MODULE_DATA_SERVICE_ORDER)
+public class ModuleDataService extends AbstractProjectDataService<ModuleData, Module> {
 
   public static final com.intellij.openapi.util.Key<ModuleData> MODULE_DATA_KEY = com.intellij.openapi.util.Key.create("MODULE_DATA_KEY");
 
@@ -59,15 +71,9 @@ public class ModuleDataService implements ProjectDataServiceEx<ModuleData, Modul
     return ProjectKeys.MODULE;
   }
 
-  public void importData(@NotNull final Collection<DataNode<ModuleData>> toImport,
-                         @NotNull final Project project,
-                         final boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    importData(toImport, project, platformFacade, synchronous);
-  }
-
   @Override
   public void importData(@NotNull final Collection<DataNode<ModuleData>> toImport,
+                         @Nullable ProjectData projectData,
                          @NotNull final Project project,
                          @NotNull final PlatformFacade platformFacade,
                          final boolean synchronous) {
@@ -75,7 +81,9 @@ public class ModuleDataService implements ProjectDataServiceEx<ModuleData, Modul
       return;
     }
     if (!project.isInitialized()) {
-      myAlarm.addRequest(new ImportModulesTask(project, toImport, synchronous), PROJECT_INITIALISATION_DELAY_MS);
+      myAlarm.addRequest(
+        new ImportModulesTask(project, toImport, projectData, platformFacade, synchronous), PROJECT_INITIALISATION_DELAY_MS
+      );
       return;
     }
     ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
@@ -213,59 +221,198 @@ public class ModuleDataService implements ProjectDataServiceEx<ModuleData, Modul
     }
   }
 
+  @NotNull
   @Override
-  public void removeData(@NotNull Collection<? extends Module> toRemove,
-                         @NotNull Project project,
-                         boolean synchronous) {
-    final PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-    removeData(toRemove, project, platformFacade, synchronous);
-  }
+  public Computable<Collection<Module>> computeOrphanData(@NotNull final Collection<DataNode<ModuleData>> toImport,
+                                                          @NotNull final ProjectData projectData,
+                                                          @NotNull final Project project,
+                                                          @NotNull final PlatformFacade platformFacade) {
+    return new Computable<Collection<Module>>() {
+      @Override
+      public Collection<Module> compute() {
+        List<Module> orphanIdeModules = ContainerUtil.newSmartList();
+
+        for (Module module : platformFacade.getModules(project)) {
+          if (!ExternalSystemApiUtil.isExternalSystemAwareModule(projectData.getOwner(), module)) continue;
+          final String rootProjectPath = ExternalSystemApiUtil.getExternalRootProjectPath(module);
+          if (projectData.getLinkedExternalProjectPath().equals(rootProjectPath)) {
+            final String projectPath = ExternalSystemApiUtil.getExternalProjectPath(module);
+            final String projectId = ExternalSystemApiUtil.getExternalProjectId(module);
+
+            final DataNode<ModuleData> found = ContainerUtil.find(toImport, new Condition<DataNode<ModuleData>>() {
+              @Override
+              public boolean value(DataNode<ModuleData> node) {
+                final ModuleData moduleData = node.getData();
+                return moduleData.getId().equals(projectId) && moduleData.getLinkedExternalProjectPath().equals(projectPath);
+              }
+            });
+
+            if (found == null) {
+              orphanIdeModules.add(module);
+            }
+          }
+        }
 
+        return orphanIdeModules;
+      }
+    };
+  }
 
   @Override
-  public void removeData(@NotNull final Collection<? extends Module> modules,
-                         @NotNull Project project,
-                         @NotNull PlatformFacade platformFacade,
-                         boolean synchronous) {
+  public void removeData(@NotNull final Computable<Collection<Module>> toRemoveComputable,
+                         @NotNull final Collection<DataNode<ModuleData>> toIgnore,
+                         @NotNull final ProjectData projectData,
+                         @NotNull final Project project,
+                         @NotNull final PlatformFacade platformFacade,
+                         final boolean synchronous) {
+    final Collection<Module> toRemove = toRemoveComputable.compute();
+    final List<Module> modules = new SmartList<Module>(toRemove);
+    for (DataNode<ModuleData> moduleDataNode : toIgnore) {
+      final Module module = platformFacade.findIdeModule(moduleDataNode.getData(), project);
+      ContainerUtil.addIfNotNull(modules, module);
+    }
+
     if (modules.isEmpty()) {
       return;
     }
+
+    ContainerUtil.removeDuplicates(modules);
+
     ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
       @Override
       public void execute() {
         for (Module module : modules) {
           if (module.isDisposed()) continue;
+          unlinkModuleFromExternalSystem(module);
+        }
+      }
+    });
 
-          ModuleManager moduleManager = ModuleManager.getInstance(module.getProject());
-          String path = module.getModuleFilePath();
-          moduleManager.disposeModule(module);
-          File file = new File(path);
-          if (file.isFile()) {
-            boolean success = file.delete();
-            if (!success) {
-              LOG.warn("Can't remove module file at '" + path + "'");
+    ruleOrphanModules(modules, project, projectData.getOwner(), new Consumer<List<Module>>() {
+      @Override
+      public void consume(final List<Module> modules) {
+        ExternalSystemApiUtil.executeProjectChangeAction(synchronous, new DisposeAwareProjectChange(project) {
+          @Override
+          public void execute() {
+            for (Module module : modules) {
+              if (module.isDisposed()) continue;
+
+              ModuleManager moduleManager = ModuleManager.getInstance(module.getProject());
+              String path = module.getModuleFilePath();
+              moduleManager.disposeModule(module);
+              File file = new File(path);
+              if (file.isFile()) {
+                boolean success = file.delete();
+                if (!success) {
+                  LOG.warn("Can't remove module file at '" + path + "'");
+                }
+              }
+            }
+          }
+        });
+      }
+    });
+  }
+
+  /**
+   * There is a possible case that an external module has been un-linked from ide project. There are two ways to process
+   * ide modules which correspond to that external project:
+   * <pre>
+   * <ol>
+   *   <li>Remove them from ide project as well;</li>
+   *   <li>Keep them at ide project as well;</li>
+   * </ol>
+   * </pre>
+   * This method handles that situation, i.e. it asks a user what should be done and acts accordingly.
+   *
+   * @param orphanModules    modules which correspond to the un-linked external project
+   * @param project          current ide project
+   * @param externalSystemId id of the external system which project has been un-linked from ide project
+   */
+  private static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
+                                        @NotNull final Project project,
+                                        @NotNull final ProjectSystemId externalSystemId,
+                                        @NotNull final Consumer<List<Module>> result) {
+    UIUtil.invokeLaterIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        List<Module> toRemove = ContainerUtil.newSmartList();
+        if(ApplicationManager.getApplication().isHeadlessEnvironment()) {
+          toRemove.addAll(orphanModules);
+        } else {
+          final JPanel content = new JPanel(new GridBagLayout());
+          content.add(new JLabel(ExternalSystemBundle.message("orphan.modules.text", externalSystemId.getReadableName())),
+                      ExternalSystemUiUtil.getFillLineConstraints(0));
+
+          final CheckBoxList<Module> orphanModulesList = new CheckBoxList<Module>();
+          orphanModulesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+          orphanModulesList.setItems(orphanModules, new Function<Module, String>() {
+            @Override
+            public String fun(Module module) {
+              return module.getName();
+            }
+          });
+          for (Module module : orphanModules) {
+            orphanModulesList.setItemSelected(module, true);
+          }
+          orphanModulesList.setBorder(IdeBorderFactory.createEmptyBorder(8));
+          content.add(orphanModulesList, ExternalSystemUiUtil.getFillLineConstraints(0));
+          content.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 8, 0));
+
+          DialogWrapper dialog = new DialogWrapper(project) {
+            {
+              setTitle(ExternalSystemBundle.message("import.title", externalSystemId.getReadableName()));
+              init();
+            }
+
+            @Nullable
+            @Override
+            protected JComponent createCenterPanel() {
+              return new JBScrollPane(content);
+            }
+          };
+
+          dialog.showAndGet();
+
+          for (int i = 0; i < orphanModules.size(); i++) {
+            Module module = orphanModules.get(i);
+            if (orphanModulesList.isItemSelected(i)) {
+              toRemove.add(module);
             }
           }
         }
+        result.consume(toRemove);
       }
     });
   }
 
   public static void unlinkModuleFromExternalSystem(@NotNull Module module) {
     module.clearOption(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
+    module.clearOption(ExternalSystemConstants.LINKED_PROJECT_ID_KEY);
     module.clearOption(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
     module.clearOption(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY);
+    module.clearOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_GROUP_KEY);
+    module.clearOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_VERSION_KEY);
   }
 
   private class ImportModulesTask implements Runnable {
 
-    private final Project                          myProject;
+    private final Project myProject;
     private final Collection<DataNode<ModuleData>> myModules;
-    private final boolean                          mySynchronous;
-
-    ImportModulesTask(@NotNull Project project, @NotNull Collection<DataNode<ModuleData>> modules, boolean synchronous) {
+    @Nullable
+    private final ProjectData myProjectData;
+    private final PlatformFacade myPlatformFacade;
+    private final boolean mySynchronous;
+
+    ImportModulesTask(@NotNull Project project,
+                      @NotNull Collection<DataNode<ModuleData>> modules,
+                      @Nullable ProjectData projectData,
+                      @NotNull PlatformFacade platformFacade,
+                      boolean synchronous) {
       myProject = project;
       myModules = modules;
+      myProjectData = projectData;
+      myPlatformFacade = platformFacade;
       mySynchronous = synchronous;
     }
 
@@ -274,13 +421,13 @@ public class ModuleDataService implements ProjectDataServiceEx<ModuleData, Modul
       myAlarm.cancelAllRequests();
       if (!myProject.isInitialized()) {
         myAlarm.addRequest(
-          new ImportModulesTask(myProject, myModules, mySynchronous),
+          new ImportModulesTask(myProject, myModules, myProjectData, myPlatformFacade, mySynchronous),
           PROJECT_INITIALISATION_DELAY_MS
         );
         return;
       }
 
-      importData(myModules, myProject, mySynchronous);
+      importData(myModules, myProjectData, myProject, myPlatformFacade, mySynchronous);
     }
   }
 
index d9ee6c4aca982eea6d8dba5246f3a9763560bfb2..edaf068b52225fb74d446319735bb1fadd0037bd 100644 (file)
@@ -19,6 +19,7 @@ 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.LibraryDependencyData;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
@@ -30,13 +31,17 @@ import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.*;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.Pair;
-import com.intellij.util.BooleanFunction;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -51,12 +56,6 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
 
   private static final Logger LOG = Logger.getInstance("#" + ModuleDependencyDataService.class.getName());
 
-  @NotNull private final ModuleDataService      myModuleDataManager;
-
-  public ModuleDependencyDataService(@NotNull ModuleDataService manager) {
-    myModuleDataManager = manager;
-  }
-
   @NotNull
   @Override
   public Key<ModuleDependencyData> getTargetDataKey() {
@@ -65,17 +64,14 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
 
   @Override
   public void importData(@NotNull Collection<DataNode<ModuleDependencyData>> toImport,
+                         @Nullable ProjectData projectData,
                          @NotNull Project project,
                          @NotNull PlatformFacade platformFacade,
                          boolean synchronous) {
-    Map<DataNode<ModuleData>, List<DataNode<ModuleDependencyData>>> byModule= ExternalSystemApiUtil.groupBy(toImport, MODULE);
-    for (Map.Entry<DataNode<ModuleData>, List<DataNode<ModuleDependencyData>>> entry : byModule.entrySet()) {
+    MultiMap<DataNode<ModuleData>, DataNode<ModuleDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
+    for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ModuleDependencyData>>> entry : byModule.entrySet()) {
       Module ideModule = platformFacade.findIdeModule(entry.getKey().getData(), project);
       if (ideModule == null) {
-        myModuleDataManager.importData(Collections.singleton(entry.getKey()), project, true);
-        ideModule = platformFacade.findIdeModule(entry.getKey().getData(), project);
-      }
-      if (ideModule == null) {
         LOG.warn(String.format(
           "Can't import module dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
           entry.getValue(), entry.getKey()
@@ -86,6 +82,17 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
     }
   }
 
+  @NotNull
+  @Override
+  public Class<ModuleOrderEntry> getOrderEntryType() {
+    return ModuleOrderEntry.class;
+  }
+
+  @Override
+  protected String getOrderEntryName(@NotNull ModuleOrderEntry orderEntry) {
+    return orderEntry.getModuleName();
+  }
+
   private void importData(@NotNull final Collection<DataNode<ModuleDependencyData>> toImport,
                           @NotNull final Module module,
                           @NotNull final PlatformFacade platformFacade,
@@ -110,35 +117,26 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
             toRemove.remove(Pair.create(dependencyData.getInternalName(), dependencyData.getScope()));
             final String moduleName = dependencyData.getInternalName();
             Module ideDependencyModule = platformFacade.findIdeModule(moduleName, module.getProject());
-            if (ideDependencyModule == null) {
-              DataNode<ProjectData> projectNode = dependencyNode.getDataNode(ProjectKeys.PROJECT);
-              if (projectNode != null) {
-                DataNode<ModuleData> n = ExternalSystemApiUtil.find(projectNode, MODULE, new BooleanFunction<DataNode<ModuleData>>() {
-                  @Override
-                  public boolean fun(DataNode<ModuleData> node) {
-                    return node.getData().equals(dependencyData.getTarget());
-                  }
-                });
-                if (n != null) {
-                  myModuleDataManager.importData(Collections.singleton(n), module.getProject(), true);
-                  ideDependencyModule = platformFacade.findIdeModule(moduleName, module.getProject());
-                }
-              }
-            }
 
-            if (ideDependencyModule == null) {
-              assert false;
-              return;
-            }
-            else if (ideDependencyModule.equals(module)) {
-              // Gradle api returns recursive module dependencies (a module depends on itself) for 'gradle' project.
+            ModuleOrderEntry orderEntry;
+            if (module.equals(ideDependencyModule)) {
+              // skip recursive module dependency check
               continue;
+            } else  {
+              if(ideDependencyModule == null) {
+                LOG.warn(String.format(
+                  "Can't import module dependency for '%s' module. Reason: target module (%s) is not found at the ide",
+                  module.getName(), dependencyData
+                ));
+              }
+              orderEntry = platformFacade.findIdeModuleDependency(dependencyData, moduleRootModel);
+              if (orderEntry == null) {
+                orderEntry = ideDependencyModule == null
+                             ? moduleRootModel.addInvalidModuleEntry(moduleName)
+                             : moduleRootModel.addModuleOrderEntry(ideDependencyModule);
+              }
             }
 
-            ModuleOrderEntry orderEntry = platformFacade.findIdeModuleDependency(dependencyData, moduleRootModel);
-            if (orderEntry == null) {
-              orderEntry = moduleRootModel.addModuleOrderEntry(ideDependencyModule);
-            }
             orderEntry.setScope(dependencyData.getScope());
             orderEntry.setExported(dependencyData.isExported());
           }
index 6036d641185fade78ca81c44a96ab1f63e3dd0a9..9c23ca8577c8dd4d9dbcc7acaf8799e2afcf50cd 100644 (file)
@@ -17,22 +17,25 @@ package com.intellij.openapi.externalSystem.service.project.manage;
 
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.externalSystem.model.DataNode;
-import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
-import com.intellij.openapi.externalSystem.model.Key;
-import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.*;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.NotNullLazyValue;
 import com.intellij.util.Consumer;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -47,6 +50,8 @@ import static com.intellij.util.containers.ContainerUtil.map2Array;
 public class ProjectDataManager {
 
   private static final Logger LOG = Logger.getInstance("#" + ProjectDataManager.class.getName());
+  private static final com.intellij.openapi.util.Key<Boolean> DATA_READY =
+    com.intellij.openapi.util.Key.create("externalSystem.data.ready");
 
   @NotNull private final NotNullLazyValue<Map<Key<?>, List<ProjectDataService<?, ?>>>> myServices;
   private final PlatformFacade myPlatformFacade;
@@ -86,106 +91,185 @@ public class ProjectDataManager {
   }
 
   @SuppressWarnings("unchecked")
-  public <T> void importData(@NotNull Collection<DataNode<?>> nodes,
-                             @NotNull Project project,
-                             @NotNull PlatformFacade platformFacade,
-                             boolean synchronous) {
+  public void importData(@NotNull Collection<DataNode<?>> nodes,
+                         @NotNull final Project project,
+                         @NotNull PlatformFacade platformFacade,
+                         boolean synchronous) {
     if (project.isDisposed()) return;
 
-    Map<Key<?>, List<DataNode<?>>> grouped = ExternalSystemApiUtil.group(nodes);
-    for (Map.Entry<Key<?>, List<DataNode<?>>> entry : grouped.entrySet()) {
-      // Simple class cast makes ide happy but compiler fails.
-      Collection<DataNode<T>> dummy = ContainerUtilRt.newArrayList();
-      for (DataNode<?> node : entry.getValue()) {
-        dummy.add((DataNode<T>)node);
+    MultiMap<Key<?>, DataNode<?>> grouped = ExternalSystemApiUtil.recursiveGroup(nodes);
+    for (Key<?> key : myServices.getValue().keySet()) {
+      if (!grouped.containsKey(key)) {
+        grouped.put(key, Collections.<DataNode<?>>emptyList());
       }
-      importData((Key<T>)entry.getKey(), dummy, project, platformFacade, synchronous);
+    }
+
+    final Collection<DataNode<?>> projects = grouped.get(ProjectKeys.PROJECT);
+    // only one project(can be multi-module project) expected for per single import
+    assert projects.size() == 1 || projects.isEmpty();
+
+    final DataNode<ProjectData> projectNode = (DataNode<ProjectData>)ContainerUtil.getFirstItem(projects);
+    final ProjectData projectData;
+    ProjectSystemId projectSystemId;
+    if (projectNode != null) {
+      projectData = projectNode.getData();
+      projectSystemId = projectNode.getData().getOwner();
+      ExternalProjectsDataStorage.getInstance(project).saveInclusionSettings(projectNode);
+    }
+    else {
+      projectData = null;
+      DataNode<ModuleData> aModuleNode = (DataNode<ModuleData>)ContainerUtil.getFirstItem(grouped.get(ProjectKeys.MODULE));
+      projectSystemId = aModuleNode != null ? aModuleNode.getData().getOwner() : null;
+    }
+
+    if (projectSystemId != null) {
+      ExternalSystemUtil.scheduleExternalViewStructureUpdate(project, projectSystemId);
+    }
+
+    for (Map.Entry<Key<?>, Collection<DataNode<?>>> entry : grouped.entrySet()) {
+      doImportData(entry.getKey(), entry.getValue(), projectData, project, platformFacade, synchronous);
     }
   }
 
-  public <T> void importData(@NotNull Collection<DataNode<?>> nodes, @NotNull Project project, boolean synchronous) {
-    importData(nodes, project, myPlatformFacade, synchronous);
+  /**
+   * @deprecated to be removed in v15, use {@link #importData(Collection, Project, boolean)}
+   */
+  @Deprecated
+  public <T> void importData(@NotNull Key<T> key, @NotNull Collection<DataNode<T>> nodes, @NotNull Project project, boolean synchronous) {
+    importData(nodes, project, synchronous);
   }
 
-  @SuppressWarnings("unchecked")
-  public <T> void importData(@NotNull Key<T> key,
-                             @NotNull Collection<DataNode<T>> nodes,
+  public <T> void importData(@NotNull Collection<DataNode<T>> nodes, @NotNull Project project, boolean synchronous) {
+    Collection<DataNode<?>> dummy = ContainerUtil.newSmartList();
+    for (DataNode<T> node : nodes) {
+      dummy.add(node);
+    }
+    importData(dummy, project, myPlatformFacade, synchronous);
+  }
+
+  public <T> void importData(@NotNull DataNode<T> node,
                              @NotNull Project project,
                              @NotNull PlatformFacade platformFacade,
                              boolean synchronous) {
+    Collection<DataNode<?>> dummy = ContainerUtil.newSmartList();
+    dummy.add(node);
+    importData(dummy, project, platformFacade, synchronous);
+  }
+
+  public <T> void importData(@NotNull DataNode<T> node,
+                             @NotNull Project project,
+                             boolean synchronous) {
+    importData(node, project, myPlatformFacade, synchronous);
+  }
+
+  @SuppressWarnings("unchecked")
+  private <T> void doImportData(@NotNull Key<T> key,
+                                @NotNull Collection<DataNode<?>> nodes,
+                                @Nullable ProjectData projectData,
+                                @NotNull Project project,
+                                @NotNull PlatformFacade platformFacade,
+                                boolean synchronous) {
     if (project.isDisposed()) return;
 
-    ensureTheDataIsReadyToUse((Collection)nodes);
+    final List<DataNode<T>> toImport = ContainerUtil.newSmartList();
+    final List<DataNode<T>> toIgnore = ContainerUtil.newSmartList();
+
+    for (DataNode node : nodes) {
+      if (!key.equals(node.getKey())) continue;
+
+      if (node.isIgnored()) {
+        toIgnore.add(node);
+      }
+      else {
+        toImport.add(node);
+      }
+    }
+
+    ensureTheDataIsReadyToUse((Collection)toImport);
+
     List<ProjectDataService<?, ?>> services = myServices.getValue().get(key);
     if (services == null) {
       LOG.warn(String.format(
         "Can't import data nodes '%s'. Reason: no service is registered for key %s. Available services for %s",
-        nodes, key, myServices.getValue().keySet()
+        toImport, key, myServices.getValue().keySet()
       ));
     }
     else {
       for (ProjectDataService<?, ?> service : services) {
         if (service instanceof ProjectDataServiceEx) {
-          ((ProjectDataServiceEx<T, ?>)service).importData(nodes, project, platformFacade, synchronous);
+          ((ProjectDataServiceEx<T, ?>)service).importData(toImport, projectData, project, platformFacade, synchronous);
         }
         else {
-          ((ProjectDataService<T, ?>)service).importData(nodes, project, synchronous);
+          ((ProjectDataService<T, ?>)service).importData(toImport, project, synchronous);
         }
       }
     }
 
-    Collection<DataNode<?>> children = ContainerUtilRt.newArrayList();
-    for (DataNode<T> node : nodes) {
-      children.addAll(node.getChildren());
+    ensureTheDataIsReadyToUse((Collection)toIgnore);
+
+    if (services != null && projectData != null) {
+      for (ProjectDataService<?, ?> service : services) {
+        if (service instanceof ProjectDataServiceEx) {
+          final ProjectDataServiceEx dataServiceEx = (ProjectDataServiceEx)service;
+          final Computable<Collection<?>> orphanIdeDataComputable =
+            dataServiceEx.computeOrphanData(toImport, projectData, project, platformFacade);
+          dataServiceEx.removeData(orphanIdeDataComputable, toIgnore, projectData, project, platformFacade, synchronous);
+        }
+      }
     }
-    importData(children, project, platformFacade, synchronous);
   }
 
-  public <T> void importData(@NotNull Key<T> key,
-                             @NotNull Collection<DataNode<T>> nodes,
-                             @NotNull Project project,
-                             boolean synchronous) {
-    importData(key, nodes, project, myPlatformFacade, synchronous);
-  }
+  public void ensureTheDataIsReadyToUse(@Nullable DataNode dataNode) {
+    if (dataNode == null) return;
+    if (Boolean.TRUE.equals(dataNode.getUserData(DATA_READY))) return;
 
-  public void ensureTheDataIsReadyToUse(DataNode dataNode) {
-    final Map<Key<?>, List<ProjectDataService<?, ?>>> servicesByKey = myServices.getValue();
-    ExternalSystemApiUtil.visit(dataNode, new Consumer<DataNode>() {
+    ExternalSystemApiUtil.visit(dataNode, new Consumer<DataNode<?>>() {
       @Override
       public void consume(DataNode dataNode) {
-        List<ProjectDataService<?, ?>> services = servicesByKey.get(dataNode.getKey());
-        if (services != null) {
-          try {
-            dataNode.prepareData(map2Array(services, ClassLoader.class, new Function<ProjectDataService<?, ?>, ClassLoader>() {
-              @Override
-              public ClassLoader fun(ProjectDataService<?, ?> service) {
-                return service.getClass().getClassLoader();
-              }
-            }));
-          }
-          catch (Exception e) {
-            LOG.debug(e);
-            dataNode.clear(true);
-          }
-        }
+        prepareDataToUse(dataNode);
+        dataNode.putUserData(DATA_READY, Boolean.TRUE);
       }
     });
   }
 
-  private void ensureTheDataIsReadyToUse(@NotNull Collection<DataNode<?>> nodes) {
-    for (DataNode<?> node : nodes) {
-      ensureTheDataIsReadyToUse(node);
+  @SuppressWarnings("unchecked")
+  @Deprecated
+  public <E, I> void removeData(@NotNull Key<E> key, @NotNull Collection<I> toRemove, @NotNull Project project, boolean synchronous) {
+    List<ProjectDataService<?, ?>> services = myServices.getValue().get(key);
+    for (ProjectDataService service : services) {
+      service.removeData(toRemove, project, synchronous);
     }
   }
 
   @SuppressWarnings("unchecked")
-  public <T> void removeData(@NotNull Key<?> key, @NotNull Collection<T> toRemove, @NotNull Project project, boolean synchronous) {
+  public <E, I> void removeData(@NotNull Key<E> key,
+                                @NotNull Collection<I> toRemove,
+                                @NotNull final Collection<DataNode<E>> toIgnore,
+                                @NotNull final ProjectData projectData,
+                                @NotNull Project project,
+                                @NotNull PlatformFacade platformFacade,
+                                boolean synchronous) {
     List<ProjectDataService<?, ?>> services = myServices.getValue().get(key);
-    for (ProjectDataService<?, ?> service : services) {
-      ((ProjectDataService<?, T>)service).removeData(toRemove, project, synchronous);
+    for (ProjectDataService service : services) {
+      if (service instanceof ProjectDataServiceEx) {
+        ((ProjectDataServiceEx)service).removeData(new Computable.PredefinedValueComputable<Collection>(toRemove),
+                                                   toIgnore, projectData, project, platformFacade, synchronous);
+      }
+      else {
+        service.removeData(toRemove, project, synchronous);
+      }
     }
   }
 
+  public <E, I> void removeData(@NotNull Key<E> key,
+                                @NotNull Collection<I> toRemove,
+                                @NotNull final Collection<DataNode<E>> toIgnore,
+                                @NotNull final ProjectData projectData,
+                                @NotNull Project project,
+                                boolean synchronous) {
+    removeData(key, toRemove, toIgnore, projectData, project, myPlatformFacade, synchronous);
+  }
+
   public void updateExternalProjectData(@NotNull Project project, @NotNull ExternalProjectInfo externalProjectInfo) {
     if (!project.isDisposed()) {
       ExternalProjectsManager.getInstance(project).updateExternalProjectData(externalProjectInfo);
@@ -201,7 +285,36 @@ public class ProjectDataManager {
 
   @NotNull
   public Collection<ExternalProjectInfo> getExternalProjectsData(@NotNull Project project, @NotNull ProjectSystemId projectSystemId) {
-    return !project.isDisposed() ?
-           ExternalProjectsDataStorage.getInstance(project).list(projectSystemId) : ContainerUtil.<ExternalProjectInfo>emptyList();
+    if (!project.isDisposed()) {
+      return ExternalProjectsDataStorage.getInstance(project).list(projectSystemId);
+    }
+    else {
+      return ContainerUtil.emptyList();
+    }
+  }
+
+  private void ensureTheDataIsReadyToUse(@NotNull Collection<DataNode<?>> nodes) {
+    for (DataNode<?> node : nodes) {
+      ensureTheDataIsReadyToUse(node);
+    }
+  }
+
+  private void prepareDataToUse(@NotNull DataNode dataNode) {
+    final Map<Key<?>, List<ProjectDataService<?, ?>>> servicesByKey = myServices.getValue();
+    List<ProjectDataService<?, ?>> services = servicesByKey.get(dataNode.getKey());
+    if (services != null) {
+      try {
+        dataNode.prepareData(map2Array(services, ClassLoader.class, new Function<ProjectDataService<?, ?>, ClassLoader>() {
+          @Override
+          public ClassLoader fun(ProjectDataService<?, ?> service) {
+            return service.getClass().getClassLoader();
+          }
+        }));
+      }
+      catch (Exception e) {
+        LOG.debug(e);
+        dataNode.clear(true);
+      }
+    }
   }
 }
index 2f76ef49936adc7bd10e82d440bcde9758d7e6ee..84689e96eab442ae0ab070388accf87f970dd243 100644 (file)
 package com.intellij.openapi.externalSystem.service.project.manage;
 
 import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.PlatformFacade;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 
@@ -29,11 +32,20 @@ import java.util.Collection;
 public interface ProjectDataServiceEx<E, I> extends ProjectDataService<E, I> {
 
   void importData(@NotNull Collection<DataNode<E>> toImport,
+                  @Nullable ProjectData projectData,
                   @NotNull Project project,
                   @NotNull PlatformFacade platformFacade,
                   boolean synchronous);
 
-  void removeData(@NotNull Collection<? extends I> toRemove,
+  @NotNull
+  Computable<Collection<I>> computeOrphanData(@NotNull Collection<DataNode<E>> toImport,
+                                              @NotNull ProjectData projectData,
+                                              @NotNull Project project,
+                                              @NotNull PlatformFacade platformFacade);
+
+  void removeData(@NotNull Computable<Collection<I>> toRemove,
+                  @NotNull Collection<DataNode<E>> toIgnore,
+                  @NotNull ProjectData projectData,
                   @NotNull Project project,
                   @NotNull PlatformFacade platformFacade,
                   boolean synchronous);
index 43b19c2cabb4926f1fb6c308e9db0364ed7f3f49..bcbaba9452aece94518f082d163a2b3a044b1a7b 100644 (file)
@@ -31,7 +31,7 @@ import java.util.Collection;
  * @author Denis Zhdanov
  * @since 2/21/13 2:40 PM
  */
-@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
+@Order(ExternalSystemConstants.BUILTIN_PROJECT_DATA_SERVICE_ORDER)
 public class ProjectDataServiceImpl implements ProjectDataService<ProjectData, Project> {
   
   @NotNull
@@ -42,6 +42,9 @@ public class ProjectDataServiceImpl implements ProjectDataService<ProjectData, P
 
   @Override
   public void importData(@NotNull Collection<DataNode<ProjectData>> toImport, @NotNull Project project, boolean synchronous) {
+    // root project can be marked as ignored
+    if(toImport.isEmpty()) return;
+
     if (toImport.size() != 1) {
       throw new IllegalArgumentException(String.format("Expected to get a single project but got %d: %s", toImport.size(), toImport));
     }
index 89bcd432bff02c232bc3e416c9681fad34ca3f6d..87e652103ed9f98891197e3915f8bfc3e492f76a 100644 (file)
@@ -9,6 +9,7 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.internal.InternalExternalProjectInfo;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
 import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
@@ -17,6 +18,7 @@ import com.intellij.openapi.externalSystem.service.project.PlatformFacadeImpl;
 import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
 import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
 import com.intellij.openapi.externalSystem.service.settings.AbstractImportFromExternalSystemControl;
+import com.intellij.openapi.externalSystem.service.ui.ExternalProjectStructureDialog;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
 import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
 import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
@@ -154,14 +156,17 @@ public abstract class AbstractExternalProjectImportBuilder<C extends AbstractImp
     systemSettings.setLinkedProjectsSettings(projects);
 
     if (externalProjectNode != null) {
+      ExternalProjectStructureDialog dialog = new ExternalProjectStructureDialog(
+        project, new InternalExternalProjectInfo(myExternalSystemId, projectSettings.getExternalProjectPath(), externalProjectNode));
+      dialog.showAndGet();
+
       ExternalSystemApiUtil.executeProjectChangeAction(new DisposeAwareProjectChange(project) {
         @Override
         public void execute() {
           ProjectRootManagerEx.getInstanceEx(project).mergeRootsChangesDuring(new Runnable() {
             @Override
             public void run() {
-              myProjectDataManager.importData(
-                externalProjectNode.getKey(), Collections.singleton(externalProjectNode), project, platformFacade, true);
+              myProjectDataManager.importData(externalProjectNode, project, platformFacade, true);
               myExternalProjectNode = null;
             }
           });
index 0e1a06efce21766d8a12ba63f0a1465b23854032..7254f4ed9937e17133527117d552ef7aeae6136a 100644 (file)
@@ -30,6 +30,7 @@ import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.project.Project;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.*;
@@ -69,9 +70,9 @@ public class ToolWindowModuleService extends AbstractToolWindowService<ModuleDat
     ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
     assert manager != null;
 
-    final Map<DataNode<ProjectData>, List<DataNode<ModuleData>>> grouped = ExternalSystemApiUtil.groupBy(nodes, ProjectKeys.PROJECT);
+    final MultiMap<DataNode<ProjectData>, DataNode<ModuleData>> grouped = ExternalSystemApiUtil.groupBy(nodes, ProjectKeys.PROJECT);
     Map<ExternalProjectPojo, Collection<ExternalProjectPojo>> data = ContainerUtilRt.newHashMap();
-    for (Map.Entry<DataNode<ProjectData>, List<DataNode<ModuleData>>> entry : grouped.entrySet()) {
+    for (Map.Entry<DataNode<ProjectData>, Collection<DataNode<ModuleData>>> entry : grouped.entrySet()) {
       data.put(ExternalProjectPojo.from(entry.getKey().getData()), ContainerUtilRt.map2List(entry.getValue(), MAPPER));
     }
 
index 6467d7ed95b41fe5d4335ae456fef5b0d3bd09b6..f87b7778feb43708f938df752b7387b1528374d7 100644 (file)
@@ -20,10 +20,11 @@ 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.execution.ExternalTaskPojo;
 import com.intellij.openapi.externalSystem.model.project.ExternalConfigPathAware;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
-import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
 import com.intellij.openapi.externalSystem.model.task.TaskData;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemKeymapExtension;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
@@ -32,13 +33,14 @@ import com.intellij.openapi.project.Project;
 import com.intellij.util.Function;
 import com.intellij.util.NullableFunction;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
 
+
 /**
  * @author Denis Zhdanov
  * @since 5/15/13 7:21 PM
@@ -81,9 +83,11 @@ public class ToolWindowTaskService extends AbstractToolWindowService<TaskData> {
     ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(externalSystemId);
     assert manager != null;
 
-    Map<ExternalConfigPathAware, List<DataNode<TaskData>>> grouped = ExternalSystemApiUtil.groupBy(nodes, TASK_HOLDER_RETRIEVAL_STRATEGY);
+    ExternalSystemKeymapExtension.updateActions(project, nodes);
+
+    MultiMap<ExternalConfigPathAware, DataNode<TaskData>> grouped = ExternalSystemApiUtil.groupBy(nodes, TASK_HOLDER_RETRIEVAL_STRATEGY);
     Map<String, Collection<ExternalTaskPojo>> data = ContainerUtilRt.newHashMap();
-    for (Map.Entry<ExternalConfigPathAware, List<DataNode<TaskData>>> entry : grouped.entrySet()) {
+    for (Map.Entry<ExternalConfigPathAware, Collection<DataNode<TaskData>>> entry : grouped.entrySet()) {
       data.put(entry.getKey().getLinkedExternalProjectPath(), ContainerUtilRt.map2List(entry.getValue(), MAPPER));
     }
 
index be8baf026529a04975bca85f98f261f6da5d7838..50850b701b206073d2c07a3f73a89319c3ee7e77 100644 (file)
@@ -165,6 +165,7 @@ public class ConfigureTasksActivationDialog extends DialogWrapper {
           final List<ProjectPopupItem> popupItems = ContainerUtil.newArrayList();
           for (DataNode<ModuleData> moduleDataNode : ExternalSystemApiUtil
             .findAllRecursively(projectData.getExternalProjectStructure(), ProjectKeys.MODULE)) {
+            if(moduleDataNode.isIgnored()) continue;
 
             final List<String> tasks = ContainerUtil.map(
               ExternalSystemApiUtil.findAll(moduleDataNode, ProjectKeys.TASK), new Function<DataNode<TaskData>, String>() {
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectStructureDialog.form b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectStructureDialog.form
new file mode 100644 (file)
index 0000000..a141152
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.externalSystem.service.ui.ExternalProjectStructureDialog">
+  <grid id="cbd77" binding="mainPanel" layout-manager="BorderLayout" hgap="5" vgap="5">
+    <constraints>
+      <xy x="48" y="54" width="793" height="309"/>
+    </constraints>
+    <properties>
+      <enabled value="true"/>
+      <minimumSize width="300" height="100"/>
+      <opaque value="false"/>
+      <preferredSize width="400" height="300"/>
+    </properties>
+    <border type="none">
+      <font/>
+    </border>
+    <children>
+      <grid id="c6ac5" binding="contentPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints border-constraint="Center"/>
+        <properties>
+          <autoscrolls value="true"/>
+          <minimumSize width="0" height="0"/>
+          <preferredSize width="0" height="0"/>
+        </properties>
+        <border type="none"/>
+        <children>
+          <component id="977a3" class="com.intellij.ui.components.JBLabel" binding="mySelectionStatusLbl">
+            <constraints border-constraint="South"/>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+      <component id="5dd5f" class="com.intellij.ui.components.JBLabel" binding="myDescriptionLbl">
+        <constraints border-constraint="North"/>
+        <properties>
+          <text value="Please select the modules/data to include in the project."/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectStructureDialog.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectStructureDialog.java
new file mode 100644 (file)
index 0000000..907cec5
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Copyright 2000-2015 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.ui;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.ActionToolbarPosition;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.importing.ExternalProjectStructureCustomizer;
+import com.intellij.openapi.externalSystem.importing.ExternalProjectStructureCustomizerImpl;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.ProjectKeys;
+import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
+import com.intellij.openapi.externalSystem.util.DisposeAwareProjectChange;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Couple;
+import com.intellij.openapi.util.SimpleModificationTracker;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.util.CachedValue;
+import com.intellij.psi.util.CachedValueProvider;
+import com.intellij.ui.*;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBLoadingPanel;
+import com.intellij.util.CachedValueImpl;
+import com.intellij.util.Consumer;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.ui.tree.TreeUtil;
+import gnu.trove.TObjectHashingStrategy;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import java.awt.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 5/12/2015
+ */
+public class ExternalProjectStructureDialog extends DialogWrapper {
+
+  private static final int MAX_PATH_LENGTH = 50;
+  private static final Set<? extends Key<?>> DATA_KEYS = ContainerUtil.set(ProjectKeys.PROJECT, ProjectKeys.MODULE);
+  private static final com.intellij.openapi.util.Key<DataNode> MODIFIED_NODE_KEY = com.intellij.openapi.util.Key.create("modifiedData");
+  private static final com.intellij.openapi.util.Key<DataNodeCheckedTreeNode> CONNECTED_UI_NODE_KEY =
+    com.intellij.openapi.util.Key.create("connectedUiNode");
+  @NotNull
+  private Project myProject;
+  private volatile boolean myDisposed = false;
+  private JBLoadingPanel loadingPanel;
+  private JPanel mainPanel;
+  private JPanel contentPanel;
+  @SuppressWarnings("unused")
+  private JBLabel myDescriptionLbl;
+  private JBLabel mySelectionStatusLbl;
+  private ExternalSystemUiAware myExternalSystemUiAware;
+  private ExternalProjectInfo myProjectInfo;
+  private final Set<Key<?>> myIgnorableKeys;
+  private final Set<Key<?>> myPublicKeys;
+  @Nullable
+  private final Object myPreselectedNodeObject;
+  private CheckboxTree myTree;
+  @SuppressWarnings("unchecked")
+  private final MultiMap<DataNode<ModuleData>, DataNode<ModuleData>> dependentNodeMap = MultiMap.create(TObjectHashingStrategy.IDENTITY);
+
+  private final SimpleModificationTracker myModificationTracker = new SimpleModificationTracker();
+  private final CachedValue<SelectionState> selectionState = new CachedValueImpl<SelectionState>(new CachedValueProvider<SelectionState>() {
+    @Nullable
+    @Override
+    public Result<SelectionState> compute() {
+      return Result.createSingleDependency(getSelectionStatus(), myModificationTracker);
+    }
+  });
+
+  private boolean myShowSelectedRowsOnly;
+  private int myModulesCount;
+
+  public ExternalProjectStructureDialog(@NotNull Project project,
+                                        @NotNull ExternalProjectInfo projectInfo) {
+    this(project, projectInfo, null);
+  }
+
+  public ExternalProjectStructureDialog(@NotNull Project project,
+                                        @NotNull ExternalProjectInfo projectInfo,
+                                        @Nullable Object preselectedNodeDataObject) {
+    super(project, true);
+    myProject = project;
+    myIgnorableKeys = getIgnorableKeys();
+    myPublicKeys = getPublicKeys();
+    myPreselectedNodeObject = preselectedNodeDataObject;
+    init(projectInfo);
+  }
+
+  private void init(@NotNull ExternalProjectInfo projectInfo) {
+    myProjectInfo = projectInfo;
+    myExternalSystemUiAware = ExternalSystemUiUtil.getUiAware(myProjectInfo.getProjectSystemId());
+    myTree = createTree();
+    updateSelectionState();
+
+    myTree.addCheckboxTreeListener(new CheckboxTreeAdapter() {
+      @Override
+      public void nodeStateChanged(@NotNull CheckedTreeNode node) {
+        updateSelectionState();
+      }
+    });
+
+    String externalSystemName = myProjectInfo.getProjectSystemId().getReadableName();
+    setTitle(String.format("%s Project Data To Import", externalSystemName));
+    init();
+  }
+
+  private void updateSelectionState() {
+    myModificationTracker.incModificationCount();
+    mySelectionStatusLbl.setText(selectionState.getValue().message);
+  }
+
+  @Nullable
+  @Override
+  protected JComponent createCenterPanel() {
+    ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTree).
+      addExtraAction(new SelectAllButton()).
+      addExtraAction(new UnselectAllButton()).
+      addExtraAction(new ShowSelectedOnlyButton()).
+      addExtraAction(new SelectRequiredButton()).
+      setToolbarPosition(ActionToolbarPosition.BOTTOM).
+      setToolbarBorder(IdeBorderFactory.createEmptyBorder());
+
+    contentPanel.add(decorator.createPanel());
+    loadingPanel = new JBLoadingPanel(new BorderLayout(), getDisposable());
+    loadingPanel.add(mainPanel, BorderLayout.CENTER);
+    return loadingPanel;
+  }
+
+  private void reloadTree() {
+    final DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel();
+    final Object root = treeModel.getRoot();
+    if (!(root instanceof CheckedTreeNode)) return;
+
+    final CheckedTreeNode rootNode = (CheckedTreeNode)root;
+
+    final Couple<CheckedTreeNode> rootAndPreselectedNode = createRoot();
+    final CheckedTreeNode rootCopy = rootAndPreselectedNode.first;
+
+    List<TreeNode> nodes = TreeUtil.childrenToArray(rootCopy);
+    rootNode.removeAllChildren();
+    TreeUtil.addChildrenTo(rootNode, nodes);
+    treeModel.reload();
+  }
+
+  @Override
+  protected void doOKAction() {
+    loadingPanel.setLoadingText("Please wait...");
+    loadingPanel.startLoading();
+
+    final DataNode<ProjectData> projectStructure = myProjectInfo.getExternalProjectStructure();
+    if (projectStructure != null) {
+      final boolean[] isModified = {false};
+      ExternalSystemApiUtil.visit(projectStructure, new Consumer<DataNode<?>>() {
+        @Override
+        public void consume(DataNode<?> node) {
+          final DataNode modifiedDataNode = node.getUserData(MODIFIED_NODE_KEY);
+          if (modifiedDataNode != null) {
+            if (node.isIgnored() != modifiedDataNode.isIgnored()) {
+              node.setIgnored(modifiedDataNode.isIgnored());
+              isModified[0] = true;
+            }
+            node.removeUserData(MODIFIED_NODE_KEY);
+            node.removeUserData(CONNECTED_UI_NODE_KEY);
+          }
+        }
+      });
+      if (isModified[0]) {
+        DataNode<?> notIgnoredNode = ContainerUtil.find(projectStructure.getChildren(), new Condition<DataNode<?>>() {
+          @Override
+          public boolean value(DataNode<?> node) {
+            return !node.isIgnored();
+          }
+        });
+        projectStructure.setIgnored(notIgnoredNode == null);
+        ExternalSystemApiUtil.executeProjectChangeAction(true, new DisposeAwareProjectChange(myProject) {
+          @Override
+          public void execute() {
+            ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() {
+              @Override
+              public void run() {
+                ServiceManager.getService(ProjectDataManager.class).importData(projectStructure, myProject, true);
+              }
+            });
+          }
+        });
+        //ExternalSystemUtil.scheduleExternalViewStructureUpdate(myProject, myProjectInfo.getProjectSystemId());
+      }
+    }
+
+    super.doOKAction();
+  }
+
+  @Override
+  public void doCancelAction() {
+    ExternalSystemApiUtil.visit(myProjectInfo.getExternalProjectStructure(), new Consumer<DataNode<?>>() {
+      @Override
+      public void consume(DataNode<?> node) {
+        node.removeUserData(MODIFIED_NODE_KEY);
+        node.removeUserData(CONNECTED_UI_NODE_KEY);
+      }
+    });
+
+    super.doCancelAction();
+  }
+
+  @Override
+  public void dispose() {
+    super.dispose();
+    myDisposed = true;
+  }
+
+  private CheckboxTree createTree() {
+    final Couple<CheckedTreeNode> rootAndPreselectedNode = createRoot();
+    final CheckedTreeNode root = rootAndPreselectedNode.first;
+
+    final CheckboxTree tree = new CheckboxTree(new CheckboxTree.CheckboxTreeCellRenderer(true, false) {
+
+      @Override
+      public void customizeRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+        if (!(value instanceof DataNodeCheckedTreeNode)) {
+          return;
+        }
+        final DataNodeCheckedTreeNode node = (DataNodeCheckedTreeNode)value;
+        ColoredTreeCellRenderer renderer = getTextRenderer();
+
+        renderer.setIcon(node.icon);
+        renderer.append(node.text);
+
+        if (!StringUtil.isEmptyOrSpaces(node.comment)) {
+          String description = node.comment;
+          if (node.comment.length() > MAX_PATH_LENGTH) {
+            description = node.comment.substring(0, MAX_PATH_LENGTH) + "...";
+          }
+
+          renderer.append(" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
+          setToolTipText(node.comment);
+        }
+        else {
+          setToolTipText(null);
+        }
+      }
+    }, root, new CheckboxTreeBase.CheckPolicy(true, true, false, false));
+
+    TreeUtil.expand(tree, 1);
+    if (rootAndPreselectedNode.second != null) {
+      TreeUtil.selectNode(tree, rootAndPreselectedNode.second);
+    }
+    else {
+      tree.setSelectionRow(0);
+    }
+    return tree;
+  }
+
+  private Couple<CheckedTreeNode> createRoot() {
+    final Map<DataNode, DataNodeCheckedTreeNode> treeNodeMap = ContainerUtil.newIdentityTroveMap();
+
+    final DataNodeCheckedTreeNode[] preselectedNode = {null};
+    final DataNodeCheckedTreeNode[] rootModuleNode = {null};
+
+    final MultiMap<String, String> moduleDependenciesMap = MultiMap.create();
+    final Map<String, DataNode<ModuleData>> modulesNodeMap = ContainerUtil.newHashMap();
+
+    for (DataNode<ModuleDependencyData> moduleDependencyDataNode : ExternalSystemApiUtil.findAllRecursively(
+      myProjectInfo.getExternalProjectStructure(), ProjectKeys.MODULE_DEPENDENCY)) {
+      final ModuleDependencyData moduleDependencyData = moduleDependencyDataNode.getData();
+      moduleDependenciesMap.putValue(
+        moduleDependencyData.getOwnerModule().getLinkedExternalProjectPath(),
+        moduleDependencyData.getTarget().getLinkedExternalProjectPath());
+    }
+
+    final int[] modulesCount = {0};
+
+    ExternalSystemApiUtil.visit(myProjectInfo.getExternalProjectStructure(), new Consumer<DataNode<?>>() {
+      @Override
+      public void consume(DataNode<?> node) {
+        final Key key = node.getKey();
+        if (!myPublicKeys.contains(key)) return;
+
+        DataNode modifiableDataNode = getModifiableDataNode(node);
+
+        if (node.getKey().equals(ProjectKeys.MODULE)) {
+          modulesCount[0]++;
+        }
+
+        if (modifiableDataNode.isIgnored() && myShowSelectedRowsOnly) return;
+
+        DataNodeCheckedTreeNode treeNode = treeNodeMap.get(node);
+        if (treeNode == null) {
+          treeNode = new DataNodeCheckedTreeNode(node);
+
+          if (node.getKey().equals(ProjectKeys.MODULE)) {
+            final ModuleData moduleData = (ModuleData)node.getData();
+            //noinspection unchecked
+            modulesNodeMap.put(moduleData.getLinkedExternalProjectPath(), (DataNode<ModuleData>)node);
+          }
+
+          if (myPreselectedNodeObject != null && myPreselectedNodeObject.equals(node.getData())) {
+            preselectedNode[0] = treeNode;
+          }
+          if (node.getData() instanceof ModuleData) {
+            if (myProjectInfo.getExternalProjectPath().equals(((ModuleData)node.getData()).getLinkedExternalProjectPath())) {
+              rootModuleNode[0] = treeNode;
+            }
+          }
+          treeNode.setEnabled(myIgnorableKeys.contains(key));
+          treeNodeMap.put(node, treeNode);
+          final DataNode parent = node.getParent();
+          if (parent != null) {
+            final CheckedTreeNode parentTreeNode = treeNodeMap.get(parent);
+            if (parentTreeNode != null) {
+              parentTreeNode.add(treeNode);
+            }
+          }
+        }
+      }
+    });
+
+    myModulesCount = modulesCount[0];
+
+    dependentNodeMap.clear();
+    for (String modulePath : moduleDependenciesMap.keySet()) {
+      final Collection<String> moduleDependencies = moduleDependenciesMap.get(modulePath);
+      final DataNode<ModuleData> moduleNode = modulesNodeMap.get(modulePath);
+      if (moduleNode != null) {
+        dependentNodeMap.putValues(moduleNode, ContainerUtil.mapNotNull(moduleDependencies, new Function<String, DataNode<ModuleData>>() {
+          @Override
+          public DataNode<ModuleData> fun(String s) {
+            return modulesNodeMap.get(s);
+          }
+        }));
+      }
+    }
+
+    final CheckedTreeNode root = new CheckedTreeNode(null);
+    final DataNodeCheckedTreeNode projectNode = treeNodeMap.get(myProjectInfo.getExternalProjectStructure());
+
+    if (rootModuleNode[0] != null) {
+      rootModuleNode[0].comment = "root module";
+      projectNode.remove(rootModuleNode[0]);
+      projectNode.insert(rootModuleNode[0], 0);
+    }
+
+    List<TreeNode> nodes = TreeUtil.childrenToArray(projectNode);
+    TreeUtil.addChildrenTo(root, nodes);
+    return Couple.of(root, preselectedNode[0]);
+  }
+
+  @NotNull
+  private static Set<Key<?>> getPublicKeys() {
+    Set<Key<?>> result = ContainerUtil.newHashSet(DATA_KEYS);
+    for (ExternalProjectStructureCustomizer customizer : ExternalProjectStructureCustomizer.EP_NAME.getExtensions()) {
+      result.addAll(customizer.getPublicDataKeys());
+    }
+    return result;
+  }
+
+  @NotNull
+  private static Set<Key<?>> getIgnorableKeys() {
+    Set<Key<?>> result = ContainerUtil.newHashSet(DATA_KEYS);
+    for (ExternalProjectStructureCustomizer customizer : ExternalProjectStructureCustomizer.EP_NAME.getExtensions()) {
+      result.addAll(customizer.getIgnorableDataKeys());
+    }
+    return result;
+  }
+
+  private class DataNodeCheckedTreeNode extends CheckedTreeNode {
+    private final DataNode myDataNode;
+    @Nullable
+    private Icon icon;
+    private String text;
+    @Nullable
+    private String comment;
+
+    private DataNodeCheckedTreeNode(DataNode node) {
+      super(node);
+      myDataNode = node;
+      node.putUserData(CONNECTED_UI_NODE_KEY, this);
+      DataNode modifiableDataNode = (DataNode)node.getUserData(MODIFIED_NODE_KEY);
+      assert modifiableDataNode != null;
+      isChecked = !modifiableDataNode.isIgnored();
+
+      Icon anIconCandidate = null;
+      boolean multipleIconCandidatesFound = false;
+      ExternalProjectStructureCustomizer projectStructureCustomizer = new ExternalProjectStructureCustomizerImpl();
+      for (ExternalProjectStructureCustomizer customizer : ExternalProjectStructureCustomizer.EP_NAME.getExtensions()) {
+        Icon icon = customizer.suggestIcon(node, myExternalSystemUiAware);
+        if (!multipleIconCandidatesFound && icon != null) {
+          if (anIconCandidate != null) {
+            multipleIconCandidatesFound = true;
+            anIconCandidate = null;
+          }
+          else {
+            anIconCandidate = icon;
+          }
+        }
+
+        if (customizer.getPublicDataKeys().contains(node.getKey())) {
+          projectStructureCustomizer = customizer;
+          break;
+        }
+      }
+
+      icon = anIconCandidate != null ? anIconCandidate : projectStructureCustomizer.suggestIcon(node, myExternalSystemUiAware);
+      final Couple<String> representationName = projectStructureCustomizer.getRepresentationName(node);
+      text = representationName.first;
+      comment = representationName.second;
+
+      if (text == null) {
+        text = node.getKey().toString();
+      }
+    }
+
+    @Override
+    public boolean isChecked() {
+      return super.isChecked();
+    }
+
+    @Override
+    public void setChecked(final boolean checked) {
+      super.setChecked(checked);
+      if (checked) {
+        DataNodeCheckedTreeNode parent = this;
+        DataNodeCheckedTreeNode moduleNode = null;
+        while (parent.parent instanceof DataNodeCheckedTreeNode) {
+          if (moduleNode == null && ProjectKeys.MODULE.equals(parent.myDataNode.getKey())) {
+            moduleNode = parent;
+          }
+          parent = (DataNodeCheckedTreeNode)parent.parent;
+        }
+        parent.isChecked = true;
+
+        final DataNode modifiedParentDataNode = getModifiableDataNode(parent.myDataNode);
+        modifiedParentDataNode.setIgnored(false);
+
+        if (moduleNode != null) {
+          moduleNode.isChecked = true;
+        }
+        ExternalSystemApiUtil.visit(moduleNode == null ? myDataNode : moduleNode.myDataNode, new Consumer<DataNode<?>>() {
+          @Override
+          public void consume(DataNode node) {
+            final DataNode modifiedDataNode = getModifiableDataNode(node);
+            modifiedDataNode.setIgnored(false);
+          }
+        });
+      }
+      else {
+        ExternalSystemApiUtil.visit(myDataNode, new Consumer<DataNode<?>>() {
+          @Override
+          public void consume(DataNode node) {
+            final DataNode modifiedDataNode = getModifiableDataNode(node);
+            modifiedDataNode.setIgnored(true);
+          }
+        });
+        if (myShowSelectedRowsOnly) {
+          final DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel();
+          treeModel.removeNodeFromParent(this);
+        }
+      }
+
+      if (!checked && parent instanceof DataNodeCheckedTreeNode) {
+        if (myDataNode.getKey().equals(ProjectKeys.MODULE) &&
+            ((DataNodeCheckedTreeNode)parent).myDataNode.getKey().equals(ProjectKeys.PROJECT)) {
+          final DataNode projectDataNode = ((DataNodeCheckedTreeNode)parent).myDataNode;
+          final ProjectData projectData = (ProjectData)projectDataNode.getData();
+          final ModuleData moduleData = (ModuleData)myDataNode.getData();
+          if (moduleData.getLinkedExternalProjectPath().equals(projectData.getLinkedExternalProjectPath())) {
+            if (ExternalSystemApiUtil.findAll(projectDataNode, ProjectKeys.MODULE).size() == 1) {
+              ((DataNodeCheckedTreeNode)parent).setChecked(false);
+            }
+          }
+        }
+      }
+
+      updateSelectionState();
+    }
+  }
+
+  @NotNull
+  private static DataNode getModifiableDataNode(@NotNull DataNode node) {
+    DataNode modifiedDataNode = (DataNode)node.getUserData(MODIFIED_NODE_KEY);
+    if (modifiedDataNode == null) {
+      modifiedDataNode = node.nodeCopy();
+      node.putUserData(MODIFIED_NODE_KEY, modifiedDataNode);
+    }
+    return modifiedDataNode;
+  }
+
+  private SelectionState getSelectionStatus() {
+    boolean isRequiredSelectionEnabled = computeRequiredSelectionStatus();
+
+    String stateMessage = "";
+    final Object root = myTree.getModel().getRoot();
+    if (root instanceof CheckedTreeNode) {
+
+      final int[] selectedModulesCount = {0};
+
+      TreeUtil.traverse((CheckedTreeNode)root, new TreeUtil.Traverse() {
+        @Override
+        public boolean accept(Object node) {
+          if (node instanceof DataNodeCheckedTreeNode &&
+              ((DataNodeCheckedTreeNode)node).isChecked() &&
+              ((DataNodeCheckedTreeNode)node).myDataNode.getKey().equals(ProjectKeys.MODULE)) {
+            selectedModulesCount[0]++;
+          }
+          return true;
+        }
+      });
+      stateMessage = String.format("%1$d Modules. %2$d selected", myModulesCount, selectedModulesCount[0]);
+    }
+
+
+    return new SelectionState(isRequiredSelectionEnabled, stateMessage);
+  }
+
+  private boolean computeRequiredSelectionStatus() {
+    for (DataNode<ModuleData> node : dependentNodeMap.keySet()) {
+      final DataNodeCheckedTreeNode uiNode = node.getUserData(CONNECTED_UI_NODE_KEY);
+      assert uiNode != null;
+      if (!uiNode.isChecked()) continue;
+
+      for (DataNode<ModuleData> depNode : dependentNodeMap.get(node)) {
+        final DataNodeCheckedTreeNode uiDependentNode = depNode.getUserData(CONNECTED_UI_NODE_KEY);
+        assert uiDependentNode != null;
+        if (!uiDependentNode.isChecked()) return true;
+      }
+    }
+    return false;
+  }
+
+  private static class SelectionState {
+    boolean isRequiredSelectionEnabled;
+    @Nullable String message;
+
+    public SelectionState(boolean isRequiredSelectionEnabled, @Nullable String message) {
+      this.isRequiredSelectionEnabled = isRequiredSelectionEnabled;
+      this.message = message;
+    }
+  }
+
+  private class SelectAllButton extends AnActionButton {
+    public SelectAllButton() {
+      super("Select All", AllIcons.Actions.Selectall);
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent e) {
+      final DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel();
+      final Object root = treeModel.getRoot();
+      if (!(root instanceof CheckedTreeNode)) return;
+
+      if (!myShowSelectedRowsOnly) {
+        myTree.setNodeState((CheckedTreeNode)root, true);
+      }
+      else {
+        myShowSelectedRowsOnly = false;
+        reloadTree();
+        myTree.setNodeState((CheckedTreeNode)root, true);
+        myShowSelectedRowsOnly = true;
+      }
+    }
+  }
+
+  private class UnselectAllButton extends AnActionButton {
+    public UnselectAllButton() {
+      super("Unselect All", AllIcons.Actions.Unselectall);
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent e) {
+      final DefaultTreeModel treeModel = (DefaultTreeModel)myTree.getModel();
+      final Object root = treeModel.getRoot();
+      if (!(root instanceof CheckedTreeNode)) return;
+
+      if (!myShowSelectedRowsOnly) {
+        myTree.setNodeState((CheckedTreeNode)root, false);
+      }
+      else {
+        myShowSelectedRowsOnly = false;
+        reloadTree();
+        myTree.setNodeState((CheckedTreeNode)root, false);
+        myShowSelectedRowsOnly = true;
+        reloadTree();
+      }
+    }
+  }
+
+  private class ShowSelectedOnlyButton extends ToggleActionButton {
+
+    public ShowSelectedOnlyButton() {
+      super("Show Selected Only", AllIcons.Actions.ShowHiddens);
+    }
+
+    @Override
+    public boolean isSelected(AnActionEvent e) {
+      return myShowSelectedRowsOnly;
+    }
+
+    @Override
+    public void setSelected(AnActionEvent e, boolean state) {
+      myShowSelectedRowsOnly = state;
+      reloadTree();
+    }
+  }
+
+  private class SelectRequiredButton extends AnActionButton {
+    public SelectRequiredButton() {
+      super("Select Required", "select required projects based on your current selection", AllIcons.Actions.IntentionBulb);
+
+      addCustomUpdater(new AnActionButtonUpdater() {
+        @Override
+        public boolean isEnabled(AnActionEvent e) {
+          return selectionState.getValue().isRequiredSelectionEnabled;
+        }
+      });
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent e) {
+      boolean showSelectedRowsOnly = myShowSelectedRowsOnly;
+      if (showSelectedRowsOnly) {
+        myShowSelectedRowsOnly = false;
+        reloadTree();
+      }
+      for (DataNode<ModuleData> node : dependentNodeMap.keySet()) {
+        final DataNodeCheckedTreeNode uiNode = node.getUserData(CONNECTED_UI_NODE_KEY);
+        assert uiNode != null;
+        if (!uiNode.isChecked()) continue;
+
+        for (DataNode<ModuleData> treeNode : dependentNodeMap.get(node)) {
+          final DataNodeCheckedTreeNode uiDependentNode = treeNode.getUserData(CONNECTED_UI_NODE_KEY);
+          assert uiDependentNode != null;
+          myTree.setNodeState(uiDependentNode, true);
+        }
+      }
+
+      if (showSelectedRowsOnly) {
+        myShowSelectedRowsOnly = true;
+        reloadTree();
+      }
+      updateSelectionState();
+    }
+
+    @Override
+    public boolean displayTextInToolbar() {
+      return true;
+    }
+  }
+}
index 5c37eff0e1ec0a1ba96e4fa4890b0a58763acee1..85f9f9cf8d2d64d27402dec6c58addd0ab0f4b60 100644 (file)
@@ -71,10 +71,7 @@ import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
 import com.intellij.openapi.roots.libraries.Library;
 import com.intellij.openapi.roots.libraries.LibraryTable;
 import com.intellij.openapi.ui.DialogWrapper;
-import com.intellij.openapi.util.Computable;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.*;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.StandardFileSystems;
@@ -91,9 +88,11 @@ import com.intellij.ui.components.JBScrollPane;
 import com.intellij.util.Consumer;
 import com.intellij.util.DisposeAwareRunnable;
 import com.intellij.util.Function;
+import com.intellij.util.SmartList;
 import com.intellij.util.concurrency.Semaphore;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
+import com.intellij.util.containers.MultiMap;
 import com.intellij.util.ui.UIUtil;
 import gnu.trove.TObjectHashingStrategy;
 import org.jetbrains.annotations.NotNull;
@@ -253,11 +252,10 @@ public class ExternalSystemUtil {
     }
 
     final ProjectDataManager projectDataManager = ServiceManager.getService(ProjectDataManager.class);
-    final int[] counter = new int[1];
 
     final ExternalProjectRefreshCallback callback;
     if (spec.getCallback() == null) {
-      callback = new MyMultiExternalProjectRefreshCallback(spec.getProject(), projectDataManager, counter, spec.getExternalSystemId());
+      callback = new MyMultiExternalProjectRefreshCallback(spec.getProject(), projectDataManager, spec.getExternalSystemId());
     }
     else {
       callback = spec.getCallback();
@@ -287,7 +285,6 @@ public class ExternalSystemUtil {
       ExternalSystemNotificationManager.getInstance(spec.getProject())
         .clearNotifications(null, NotificationSource.PROJECT_SYNC, spec.getExternalSystemId());
 
-      counter[0] = toRefresh.size();
       for (String path : toRefresh) {
         refreshProject(
           spec.getProject(), spec.getExternalSystemId(), path, callback, false, spec.getProgressExecutionMode());
@@ -308,92 +305,148 @@ public class ExternalSystemUtil {
     return timeStamp;
   }
 
-  public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
-                                       @NotNull final Project project,
-                                       @NotNull final ProjectSystemId externalSystemId) {
-    //noinspection unchecked
-    ruleOrphanModules(orphanModules, project, externalSystemId, Consumer.EMPTY_CONSUMER);
-  }
-
-  /**
-   * There is a possible case that an external module has been un-linked from ide project. There are two ways to process
-   * ide modules which correspond to that external project:
-   * <pre>
-   * <ol>
-   *   <li>Remove them from ide project as well;</li>
-   *   <li>Keep them at ide project as well;</li>
-   * </ol>
-   * </pre>
-   * This method handles that situation, i.e. it asks a user what should be done and acts accordingly.
-   *
-   * @param orphanModules     modules which correspond to the un-linked external project
-   * @param project           current ide project
-   * @param externalSystemId  id of the external system which project has been un-linked from ide project
-   */
-  public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
-                                       @NotNull final Project project,
-                                       @NotNull final ProjectSystemId externalSystemId,
-                                       @NotNull final Consumer<Boolean> result)
-  {
-    UIUtil.invokeLaterIfNeeded(new Runnable() {
-      @Override
-      public void run() {
-
-        final JPanel content = new JPanel(new GridBagLayout());
-        content.add(new JLabel(ExternalSystemBundle.message("orphan.modules.text", externalSystemId.getReadableName())),
-                    ExternalSystemUiUtil.getFillLineConstraints(0));
-
-        final CheckBoxList<Module> orphanModulesList = new CheckBoxList<Module>();
-        orphanModulesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-        orphanModulesList.setItems(orphanModules, new Function<Module, String>() {
-          @Override
-          public String fun(Module module) {
-            return module.getName();
-          }
-        });
-        for (Module module : orphanModules) {
-          orphanModulesList.setItemSelected(module, true);
-        }
-        orphanModulesList.setBorder(IdeBorderFactory.createEmptyBorder(8));
-        content.add(orphanModulesList, ExternalSystemUiUtil.getFillLineConstraints(0));
-        content.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 8, 0));
-
-        DialogWrapper dialog = new DialogWrapper(project) {
-
-          {
-            setTitle(ExternalSystemBundle.message("import.title", externalSystemId.getReadableName()));
-            init();
-          }
-
-          @Nullable
-          @Override
-          protected JComponent createCenterPanel() {
-            return new JBScrollPane(content);
-          }
-        };
-        boolean ok = dialog.showAndGet();
-        result.consume(ok);
-        if (!ok) {
-          return;
-        }
-
-        List<Module> toRemove = ContainerUtilRt.newArrayList();
-        for (int i = 0; i < orphanModules.size(); i++) {
-          Module module = orphanModules.get(i);
-          if (orphanModulesList.isItemSelected(i)) {
-            toRemove.add(module);
-          }
-          else {
-            ModuleDataService.unlinkModuleFromExternalSystem(module);
-          }
-        }
-
-        if (!toRemove.isEmpty()) {
-          ServiceManager.getService(ProjectDataManager.class).removeData(ProjectKeys.MODULE, toRemove, project, true);
-        }
-      }
-    });
-  }
+  //public static void processOrphanModules(@NotNull final Project project,
+  //                                        @NotNull final ProjectSystemId projectSystemId,
+  //                                        @NotNull final String externalProjectPath) {
+  //  final ExternalProjectInfo externalProjectInfo = getExternalProjectInfo(project, projectSystemId, externalProjectPath);
+  //  if (externalProjectInfo != null) {
+  //    Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAllRecursively(
+  //      externalProjectInfo.getExternalProjectStructure(), ProjectKeys.MODULE);
+  //
+  //    processOrphanModules(project, moduleNodes);
+  //  }
+  //}
+  //
+  //public static void processOrphanModules(@NotNull Project project, @NotNull Collection<DataNode<ModuleData>> toImport) {
+  //  if (project.isDisposed()) return;
+  //  if (ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
+  //    //LOG.info(String.format(
+  //    //  "Checking for orphan modules. External paths returned by external system: '%s'", myExternalModulePaths
+  //    //));
+  //  }
+  //  PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
+  //  MultiMap<ProjectSystemId, Module> orphanIdeModules = MultiMap.create();
+  //
+  //  final MultiMap<DataNode<ProjectData>, DataNode<ModuleData>> grouped = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.PROJECT);
+  //
+  //  for (DataNode<ProjectData> node : grouped.keySet()) {
+  //    for (Module module : platformFacade.getModules(project)) {
+  //      final ProjectData projectData = node.getData();
+  //      if (!ExternalSystemApiUtil.isExternalSystemAwareModule(projectData.getOwner(), module)) continue;
+  //
+  //      final String rootProjectPath = ExternalSystemApiUtil.getExternalRootProjectPath(module);
+  //      if (projectData.getLinkedExternalProjectPath().equals(rootProjectPath)) {
+  //        final String projectPath = ExternalSystemApiUtil.getExternalProjectPath(module);
+  //        final String projectId = ExternalSystemApiUtil.getExternalProjectId(module);
+  //        final DataNode<ModuleData> found = ContainerUtil.find(grouped.get(node), new Condition<DataNode<ModuleData>>() {
+  //          @Override
+  //          public boolean value(DataNode<ModuleData> node) {
+  //            final ModuleData moduleData = node.getData();
+  //            return moduleData.getId().equals(projectId) &&
+  //                   moduleData.getLinkedExternalProjectPath().equals(projectPath);
+  //          }
+  //        });
+  //
+  //        if (found == null || found.isIgnored()) {
+  //          orphanIdeModules.putValue(projectData.getOwner(), module);
+  //        }
+  //      }
+  //    }
+  //  }
+  //
+  //  if (!orphanIdeModules.isEmpty()) {
+  //    for (Map.Entry<ProjectSystemId, Collection<Module>> entry : orphanIdeModules.entrySet()) {
+  //      ruleOrphanModules(new SmartList<Module>(entry.getValue()), project, entry.getKey());
+  //    }
+  //  }
+  //}
+  //
+  //public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
+  //                                     @NotNull final Project project,
+  //                                     @NotNull final ProjectSystemId externalSystemId) {
+  //  //noinspection unchecked
+  //  ruleOrphanModules(orphanModules, project, externalSystemId, Consumer.EMPTY_CONSUMER);
+  //}
+
+  ///**
+  // * There is a possible case that an external module has been un-linked from ide project. There are two ways to process
+  // * ide modules which correspond to that external project:
+  // * <pre>
+  // * <ol>
+  // *   <li>Remove them from ide project as well;</li>
+  // *   <li>Keep them at ide project as well;</li>
+  // * </ol>
+  // * </pre>
+  // * This method handles that situation, i.e. it asks a user what should be done and acts accordingly.
+  // *
+  // * @param orphanModules     modules which correspond to the un-linked external project
+  // * @param project           current ide project
+  // * @param externalSystemId  id of the external system which project has been un-linked from ide project
+  // */
+  //public static void ruleOrphanModules(@NotNull final List<Module> orphanModules,
+  //                                     @NotNull final Project project,
+  //                                     @NotNull final ProjectSystemId externalSystemId,
+  //                                     @NotNull final Consumer<Boolean> result)
+  //{
+  //  UIUtil.invokeLaterIfNeeded(new Runnable() {
+  //    @Override
+  //    public void run() {
+  //
+  //      final JPanel content = new JPanel(new GridBagLayout());
+  //      content.add(new JLabel(ExternalSystemBundle.message("orphan.modules.text", externalSystemId.getReadableName())),
+  //                  ExternalSystemUiUtil.getFillLineConstraints(0));
+  //
+  //      final CheckBoxList<Module> orphanModulesList = new CheckBoxList<Module>();
+  //      orphanModulesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+  //      orphanModulesList.setItems(orphanModules, new Function<Module, String>() {
+  //        @Override
+  //        public String fun(Module module) {
+  //          return module.getName();
+  //        }
+  //      });
+  //      for (Module module : orphanModules) {
+  //        orphanModulesList.setItemSelected(module, true);
+  //      }
+  //      orphanModulesList.setBorder(IdeBorderFactory.createEmptyBorder(8));
+  //      content.add(orphanModulesList, ExternalSystemUiUtil.getFillLineConstraints(0));
+  //      content.setBorder(IdeBorderFactory.createEmptyBorder(0, 0, 8, 0));
+  //
+  //      DialogWrapper dialog = new DialogWrapper(project) {
+  //
+  //        {
+  //          setTitle(ExternalSystemBundle.message("import.title", externalSystemId.getReadableName()));
+  //          init();
+  //        }
+  //
+  //        @Nullable
+  //        @Override
+  //        protected JComponent createCenterPanel() {
+  //          return new JBScrollPane(content);
+  //        }
+  //      };
+  //      boolean ok = dialog.showAndGet();
+  //      result.consume(ok);
+  //      if (!ok) {
+  //        return;
+  //      }
+  //
+  //      List<Module> toRemove = ContainerUtilRt.newArrayList();
+  //      for (int i = 0; i < orphanModules.size(); i++) {
+  //        Module module = orphanModules.get(i);
+  //        if (orphanModulesList.isItemSelected(i)) {
+  //          toRemove.add(module);
+  //        }
+  //        else {
+  //          ModuleDataService.unlinkModuleFromExternalSystem(module);
+  //        }
+  //      }
+  //
+  //      if (!toRemove.isEmpty()) {
+  //        ServiceManager.getService(ProjectDataManager.class).removeData(ProjectKeys.MODULE, toRemove, project, true);
+  //      }
+  //    }
+  //  });
+  //}
 
   @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
   @Nullable
@@ -434,8 +487,7 @@ public class ExternalSystemUtil {
               @Override
               public void run() {
                 final ProjectDataManager projectDataManager = ServiceManager.getService(ProjectDataManager.class);
-                projectDataManager
-                  .importData(externalProject.getKey(), Collections.singleton(externalProject), project, platformFacade, synchronous);
+                projectDataManager.importData(externalProject, project, platformFacade, synchronous);
               }
             });
           }
@@ -913,7 +965,7 @@ public class ExternalSystemUtil {
               @Override
               public void run() {
                 ProjectDataManager dataManager = ServiceManager.getService(ProjectDataManager.class);
-                dataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), project, true);
+                dataManager.importData(externalProject, project, true);
               }
             });
           }
@@ -963,7 +1015,7 @@ public class ExternalSystemUtil {
     });
   }
 
-  public static void scheduleExternalViewStructureUpdate(final Project project, final ProjectSystemId systemId) {
+  public static void scheduleExternalViewStructureUpdate(@NotNull final Project project, @NotNull final ProjectSystemId systemId) {
     ExternalProjectsView externalProjectsView = ExternalProjectsManager.getInstance(project).getExternalProjectsView(systemId);
     if (externalProjectsView instanceof ExternalProjectsViewImpl) {
       ((ExternalProjectsViewImpl)externalProjectsView).scheduleStructureUpdate();
@@ -1011,16 +1063,16 @@ public class ExternalSystemUtil {
     private final Set<String> myExternalModulePaths;
     private final Project myProject;
     private final ProjectDataManager myProjectDataManager;
-    private final int[] myCounter;
+    //private final int[] myCounter;
     private final ProjectSystemId myExternalSystemId;
 
     public MyMultiExternalProjectRefreshCallback(Project project,
                                                  ProjectDataManager projectDataManager,
-                                                 int[] counter,
+                                                 //int[] counter,
                                                  ProjectSystemId externalSystemId) {
       myProject = project;
       myProjectDataManager = projectDataManager;
-      myCounter = counter;
+      //myCounter = counter;
       myExternalSystemId = externalSystemId;
       myExternalModulePaths = ContainerUtilRt.newHashSet();
     }
@@ -1030,7 +1082,7 @@ public class ExternalSystemUtil {
       if (externalProject == null) {
         return;
       }
-      Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAll(externalProject, ProjectKeys.MODULE);
+      Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAllRecursively(externalProject, ProjectKeys.MODULE);
       for (DataNode<ModuleData> node : moduleNodes) {
         myExternalModulePaths.add(node.getData().getLinkedExternalProjectPath());
       }
@@ -1040,54 +1092,55 @@ public class ExternalSystemUtil {
           ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() {
             @Override
             public void run() {
-              myProjectDataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), myProject, true);
+              myProjectDataManager.importData(externalProject, myProject, true);
             }
           });
 
           processOrphanProjectLibraries();
         }
       });
-      if (--myCounter[0] <= 0) {
-        processOrphanModules();
-      }
+      //if (--myCounter[0] <= 0) {
+      //  //processOrphanModules(myProject, moduleNodes);
+      //}
     }
 
     @Override
     public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) {
-      myCounter[0] = Integer.MAX_VALUE; // Don't process orphan modules if there was an error on refresh.
     }
 
-    private void processOrphanModules() {
-      if(myProject.isDisposed()) return;
-      if(ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
-        LOG.info(String.format(
-          "Checking for orphan modules. External paths returned by external system: '%s'", myExternalModulePaths
-        ));
-      }
-      PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
-      List<Module> orphanIdeModules = ContainerUtilRt.newArrayList();
-      String externalSystemIdAsString = myExternalSystemId.toString();
-
-      for (Module module : platformFacade.getModules(myProject)) {
-        String s = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
-        String p = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
-        if(ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
-          LOG.info(String.format(
-            "IDE module: EXTERNAL_SYSTEM_ID_KEY - '%s', LINKED_PROJECT_PATH_KEY - '%s'.", s, p
-          ));
-        }
-        if (externalSystemIdAsString.equals(s) && !myExternalModulePaths.contains(p)) {
-          orphanIdeModules.add(module);
-          if(ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
-            LOG.info("External paths doesn't contain IDE module LINKED_PROJECT_PATH_KEY anymore => add to orphan IDE modules.");
-          }
-        }
-      }
-
-      if (!orphanIdeModules.isEmpty()) {
-        ruleOrphanModules(orphanIdeModules, myProject, myExternalSystemId);
-      }
-    }
+    //private void processOrphanModules() {
+    //  if(myProject.isDisposed()) return;
+    //  if(ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
+    //    LOG.info(String.format(
+    //      "Checking for orphan modules. External paths returned by external system: '%s'", myExternalModulePaths
+    //    ));
+    //  }
+    //  PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
+    //  List<Module> orphanIdeModules = ContainerUtilRt.newArrayList();
+    //  String externalSystemIdAsString = myExternalSystemId.toString();
+    //
+    //  for (Module module : platformFacade.getModules(myProject)) {
+    //    String s = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY);
+    //    String p = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY);
+    //    if(ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
+    //      LOG.info(String.format(
+    //        "IDE module: EXTERNAL_SYSTEM_ID_KEY - '%s', LINKED_PROJECT_PATH_KEY - '%s'.", s, p
+    //      ));
+    //    }
+    //    if (externalSystemIdAsString.equals(s) && !myExternalModulePaths.contains(p)) {
+    //      orphanIdeModules.add(module);
+    //      if(ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
+    //        LOG.info(String.format(
+    //          "External paths doesn't contain IDE module LINKED_PROJECT_PATH_KEY anymore => add to orphan IDE modules."
+    //        ));
+    //      }
+    //    }
+    //  }
+    //
+    //  if (!orphanIdeModules.isEmpty()) {
+    //    ruleOrphanModules(orphanIdeModules, myProject, myExternalSystemId);
+    //  }
+    //}
 
     private void processOrphanProjectLibraries() {
       PlatformFacade platformFacade = ServiceManager.getService(PlatformFacade.class);
index 9e99af4d9836873b432ce8d727545aa18289c93c..984478ab879b145a35eade76be2952238a607f8a 100644 (file)
@@ -191,8 +191,13 @@ public class ExternalProjectsStructure extends SimpleTreeStructure {
 
   private void doUpdateProject(ProjectNode node) {
     ExternalSystemNode newParentNode = myRoot;
-    node.updateProject();
-    reconnectNode(node, newParentNode);
+    if (!node.isVisible()) {
+      newParentNode.remove(node);
+    }
+    else {
+      node.updateProject();
+      reconnectNode(node, newParentNode);
+    }
   }
 
   private static void reconnectNode(ProjectNode node, ExternalSystemNode newParentNode) {
index b10f41f615d6137afcf3a54de0c5a1ddf473829c..938919d396db8078571ec2147325f38de547922c 100644 (file)
@@ -57,6 +57,8 @@ public interface ExternalProjectsView {
 
   void addListener(@NotNull ExternalProjectsView.Listener listener);
 
+  boolean getShowIgnored();
+
   interface Listener {
     void onDoubleClickOrEnter(@NotNull ExternalSystemNode node, InputEvent inputEvent);
   }
index 9d8d0c409dde5b904c10f44d1b693fba528648e3..c52eaac1cc0d88a6725e8beddb55f79cd49d8082 100644 (file)
@@ -101,4 +101,9 @@ public class ExternalProjectsViewAdapter implements ExternalProjectsView {
   public void addListener(@NotNull Listener listener) {
     delegate.addListener(listener);
   }
+
+  @Override
+  public boolean getShowIgnored() {
+    return delegate.getShowIgnored();
+  }
 }
index 716116a0229a95c95baade83c402b6c0cd7d105e..521938d30ff1a63cc99c40919c696987f7dddb94 100644 (file)
@@ -299,7 +299,7 @@ public class ExternalProjectsViewImpl extends SimpleToolWindowPanel implements D
   private ActionGroup createAdditionalGearActionsGroup() {
     ActionManager actionManager = ActionManager.getInstance();
     DefaultActionGroup group = new DefaultActionGroup();
-    String[] ids = new String[]{"ExternalSystem.GroupTasks", "ExternalSystem.ShowInheritedTasks"};
+    String[] ids = new String[]{"ExternalSystem.GroupTasks", "ExternalSystem.ShowInheritedTasks", "ExternalSystem.ShowIgnored"};
     for (String id : ids) {
       final AnAction gearAction = actionManager.getAction(id);
       if (gearAction instanceof ExternalSystemViewGearAction) {
@@ -418,14 +418,14 @@ public class ExternalProjectsViewImpl extends SimpleToolWindowPanel implements D
                                                  @Nullable ExternalSystemNode<?> parent,
                                                  @NotNull DataNode<?> dataNode) {
     final List<ExternalSystemNode<?>> result = new SmartList<ExternalSystemNode<?>>();
-    final Map<Key<?>, List<DataNode<?>>> groups = ExternalSystemApiUtil.group(dataNode.getChildren());
+    final MultiMap<Key<?>, DataNode<?>> groups = ExternalSystemApiUtil.group(dataNode.getChildren());
     for (ExternalSystemViewContributor contributor : ExternalSystemViewContributor.EP_NAME.getExtensions()) {
       List<Key<?>> keys = contributor.getKeys();
 
       final MultiMap<Key<?>, DataNode<?>> dataNodes = MultiMap.create();
       for (Key<?> key : keys) {
-        final List<DataNode<?>> values = groups.get(key);
-        if(key != null && values != null) {
+        final Collection<DataNode<?>> values = groups.get(key);
+        if(key != null) {
           dataNodes.put(key, values);
         }
       }
@@ -464,6 +464,17 @@ public class ExternalProjectsViewImpl extends SimpleToolWindowPanel implements D
     myState = state;
   }
 
+  public boolean getShowIgnored() {
+    return myState.showIgnored;
+  }
+
+  public void setShowIgnored(boolean value) {
+    if (myState.showIgnored != value) {
+      myState.showIgnored = value;
+      scheduleStructureUpdate();
+    }
+  }
+
   public boolean getGroupTasks() {
     return myState.groupTasks;
   }
index 43b9b4c67d4d183138ef345f53c02cf88a31b92a..5351a2a5017092a8a58b8b6bba9652ad7418230a 100644 (file)
@@ -17,8 +17,8 @@ package com.intellij.openapi.externalSystem.view;
 
 import com.intellij.ide.util.treeView.NodeDescriptor;
 import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
-import com.intellij.openapi.externalSystem.action.ExternalSystemActionUtil;
 import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManager;
 import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemShortcutsManager;
 import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
@@ -128,13 +128,13 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
   }
 
   @Nullable
-  public <T extends ExternalSystemNode> T findParent(Class<T> parentClass) {
+  public <DataType extends ExternalSystemNode> DataType findParent(Class<DataType> parentClass) {
     ExternalSystemNode node = this;
     while (true) {
       node = node.myParent;
       if (node == null || parentClass.isInstance(node)) {
         //noinspection unchecked
-        return (T)node;
+        return (DataType)node;
       }
     }
   }
@@ -153,7 +153,21 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
   }
 
   public boolean isVisible() {
-    return getDisplayKind() != ExternalProjectsStructure.DisplayKind.NEVER;
+    return getDisplayKind() != ExternalProjectsStructure.DisplayKind.NEVER && !(isIgnored() && !myExternalProjectsView.getShowIgnored());
+  }
+
+  public boolean isIgnored() {
+    if (myDataNode != null) {
+      return myDataNode.isIgnored();
+    }
+    final SimpleNode parent = getParent();
+    return parent instanceof ExternalSystemNode && ((ExternalSystemNode)parent).isIgnored();
+  }
+
+  public void setIgnored(final boolean ignored) {
+    if (myDataNode != null) {
+      ExternalProjectsManager.getInstance(myExternalProjectsView.getProject()).setIgnored(myDataNode, ignored);
+    }
   }
 
   public ExternalProjectsStructure.DisplayKind getDisplayKind() {
@@ -283,7 +297,7 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
     }
   }
 
-  public void setDataNode(DataNode<T> dataNode) {
+  protected void setDataNode(DataNode<T> dataNode) {
     myDataNode = dataNode;
   }
 
@@ -324,9 +338,11 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
   }
 
   protected void setNameAndTooltip(String name, @Nullable String tooltip, @Nullable String hint) {
-    setNameAndTooltip(name, tooltip, getPlainAttributes());
+    final boolean ignored = isIgnored();
+    final SimpleTextAttributes textAttributes = ignored ? SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES : getPlainAttributes();
+    setNameAndTooltip(name, tooltip, textAttributes);
     if (!StringUtil.isEmptyOrSpaces(hint)) {
-      addColoredFragment(" (" + hint + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
+      addColoredFragment(" (" + hint + ")", ignored ? SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES);
     }
   }
 
@@ -357,7 +373,7 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
   }
 
   protected String message(@NotNull String key, @NotNull Object... params) {
-    return ExternalSystemBundle.message(key);
+    return ExternalSystemBundle.message(key, params);
   }
 
   @Nullable
index b70a68d7854a9a4bcc0e668e59a34b5a215539ae..3a9ce2be6d714dc14ee08300cb6787105c437ff5 100644 (file)
@@ -55,7 +55,8 @@ public class ModuleNode extends ExternalSystemNode<ModuleData> {
       hint = "root";
     }
 
-    setNameAndTooltip(getName(), myData.toString(), hint);
+    final String tooltip = myData.toString() + (myData.getDescription() != null ? "<br>" + myData.getDescription() : "");
+    setNameAndTooltip(getName(), tooltip, hint);
   }
 
   @NotNull
@@ -76,7 +77,7 @@ public class ModuleNode extends ExternalSystemNode<ModuleData> {
   @Nullable
   @Override
   protected String getMenuId() {
-    return "ExternalSystemView.ProjectMenu";
+    return "ExternalSystemView.ModuleMenu";
   }
 
   @Override
index 78bd31e920a2cf7275a9bc998b9f8c07d380e788..8ec528d3f1323d28e2b447e9b32c4d9f4617ae46 100644 (file)
@@ -22,6 +22,7 @@ import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettin
 import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.SimpleTextAttributes;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NonNls;
@@ -62,8 +63,13 @@ public class ProjectNode extends ExternalSystemNode<ProjectData> {
         return node.isVisible();
       }
     });
-    //noinspection unchecked
-    return visibleChildren.size() == 1 ? children.get(0).doBuildChildren() : children;
+    if (visibleChildren.size() == 1 && visibleChildren.get(0).getName().equals(getName())) {
+      //noinspection unchecked
+      return visibleChildren.get(0).doBuildChildren();
+    }
+    else {
+      return visibleChildren;
+    }
   }
 
   void updateProject() {
@@ -100,30 +106,22 @@ public class ProjectNode extends ExternalSystemNode<ProjectData> {
   private String makeDescription() {
     StringBuilder desc = new StringBuilder();
     final ProjectData projectData = getData();
-    desc.append("<html>" +
-                "<table>" +
-                "<tr>" +
-                "<td nowrap>" +
-                "<table>" +
-                "<tr>" +
-                "<td nowrap>Project:</td>" +
-                "<td nowrap>").append(getName())
-      .append("</td>" +
-              "</tr>")
-      .append(projectData != null ?
+    desc
+      .append("<table>" +
               "<tr>" +
-              "<td nowrap>Location:</td>" +
-              "<td nowrap>" + projectData.getLinkedExternalProjectPath() : "")
-      .append("</td>" +
-              "</tr>" +
+              "<td nowrap>" +
+              "<table>" +
+              "<tr><td nowrap>Project:</td><td nowrap>").append(getName()).append("</td></tr>")
+      .append(projectData != null ?
+              "<tr><td nowrap>Location:</td><td nowrap>" + projectData.getLinkedExternalProjectPath() + "</td></tr>" : "")
+      .append(projectData != null && !StringUtil.isEmptyOrSpaces(projectData.getDescription()) ?
+              "<tr><td colspan='2' nowrap><hr align='center' width='90%' />" + projectData.getDescription() + "</td></tr>" : "")
+      .append("</td></tr>" +
               "</table>" +
               "</td>" +
               "</tr>");
-
     appendProblems(desc);
-
-    desc.append("</table></html>");
-
+    desc.append("</table>");
     return desc.toString();
   }
 
index 5f5d122ba3185a17c1331882168e74a9cd4a65c2..b79decb2e81ad8e594cc65d3e29fff08b0c4bda5 100644 (file)
@@ -163,7 +163,7 @@ abstract class AbstractExternalSystemTest extends UsefulTestCase {
         @Override
         void execute() {
           ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring {
-            dataManager.importData(node.key, [node], myProject, true)
+            dataManager.importData(node, myProject, true)
           }
         }})
     }
index 1a2323cabba2efa5f074d265705121f2b5fce67e..05e503edcd2031dcb063ea46c488a75df24a1042 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder;
 import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
@@ -46,7 +47,11 @@ import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
 import com.intellij.testFramework.IdeaTestUtil;
+import com.intellij.util.BooleanFunction;
+import com.intellij.util.Consumer;
 import com.intellij.util.Function;
 import com.intellij.util.PathUtil;
 import com.intellij.util.containers.ContainerUtil;
@@ -338,6 +343,18 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
     }
   }
 
+  protected void assertArtifacts(String... expectedNames) {
+    final List<String> actualNames = ContainerUtil.map(
+      ArtifactManager.getInstance(myProject).getAllArtifactsIncludingInvalid(), new Function<Artifact, String>() {
+        @Override
+        public String fun(Artifact artifact) {
+          return artifact.getName();
+        }
+      });
+
+    assertUnorderedElementsAreEqual(actualNames, expectedNames);
+  }
+
   protected Module getModule(final String name) {
     AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
     try {
@@ -378,6 +395,26 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
     return ModuleRootManager.getInstance(getModule(module));
   }
 
+  protected void ignoreData(BooleanFunction<DataNode<?>> booleanFunction, final boolean ignored) {
+    final ExternalProjectInfo externalProjectInfo = ProjectDataManager.getInstance().getExternalProjectData(
+      myProject, getExternalSystemId(), getCurrentExternalProjectSettings().getExternalProjectPath());
+    assertNotNull(externalProjectInfo);
+
+    final DataNode<ProjectData> projectDataNode = externalProjectInfo.getExternalProjectStructure();
+    assertNotNull(projectDataNode);
+
+    final Collection<DataNode<?>> nodes = ExternalSystemApiUtil.findAllRecursively(projectDataNode, booleanFunction);
+    for (DataNode<?> node : nodes) {
+      ExternalSystemApiUtil.visit(node, new Consumer<DataNode<?>>() {
+        @Override
+        public void consume(DataNode dataNode) {
+          dataNode.setIgnored(ignored);
+        }
+      });
+    }
+    ServiceManager.getService(ProjectDataManager.class).importData(projectDataNode, myProject, true);
+  }
+
   protected void importProject(@NonNls String config) throws IOException {
     createProjectConfig(config);
     importProject();
@@ -413,8 +450,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
                 ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() {
                   @Override
                   public void run() {
-                    ServiceManager.getService(ProjectDataManager.class).importData(
-                      externalProject.getKey(), Collections.singleton(externalProject), myProject, true);
+                    ServiceManager.getService(ProjectDataManager.class).importData(externalProject, myProject, true);
                   }
                 });
               }
index ce1ce7c746fa6cd4e0fc4b64ad82da5e1ce5c4a9..447d800c24a2f26e4c9c165aa6337ff512625adf 100644 (file)
@@ -12,5 +12,6 @@
     <extensionPoint name="externalSystemExecutionConsoleManager"
                     interface="com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager"/>
     <extensionPoint name="externalSystemViewContributor" interface="com.intellij.openapi.externalSystem.view.ExternalSystemViewContributor"/>
+    <extensionPoint name="externalProjectStructureCustomizer" interface="com.intellij.openapi.externalSystem.importing.ExternalProjectStructureCustomizer"/>
   </extensionPoints>
 </idea-plugin>
\ No newline at end of file
index d96ac2ac4310ddc80ac003991fbdf69c830fb4f4..db393ffb18707938a4903caadcaf00d93813678a 100644 (file)
@@ -12,6 +12,8 @@
     <action id="ExternalSystem.DetachProject"
             class="com.intellij.openapi.externalSystem.action.DetachExternalProjectAction"
             use-shortcut-of="$Delete"/>
+    <action id="ExternalSystem.IgnoreProject"
+            class="com.intellij.openapi.externalSystem.action.IgnoreExternalProjectAction"/>
     <action id="ExternalSystem.OpenConfig"
             class="com.intellij.openapi.externalSystem.action.OpenExternalConfigAction"
             use-shortcut-of="EditSource"/>
@@ -36,6 +38,9 @@
             text="Collapse All"
             icon="AllIcons.Actions.Collapseall"
             use-shortcut-of="CollapseAll"/>
+    <action id="ExternalSystem.OpenProjectStructure" class="com.intellij.openapi.externalSystem.action.ExternalSystemOpenProjectStructureAction"
+            text="Open Project Structure"
+            icon="AllIcons.Modules.ModulesNode"/>
 
     <action id="ExternalSystem.RunTask"
             class="com.intellij.openapi.externalSystem.action.task.RunExternalSystemTaskAction"
             class="com.intellij.openapi.externalSystem.action.task.ShowInheritedTasksAction" text="Display tasks inherited from sub-projects">
     </action>
 
+    <action id="ExternalSystem.ShowIgnored"
+            class="com.intellij.openapi.externalSystem.action.task.ShowIgnoredAction" text="Show Ignored">
+    </action>
+
     <group id="ExternalSystemView.BaseProjectMenu">
       <reference ref="ExternalSystem.OpenConfig"/>
       <separator/>
       <reference id="ExternalSystem.RefreshProject"/>
       <reference id="ExternalSystem.DetachProject"/>
-
+      <reference id="ExternalSystem.IgnoreProject"/>
     </group>
 
     <group id="ExternalSystemView.ProjectMenu" popup="true">
       <reference id="ExternalSystem.OpenTasksActivationManager"/>
     </group>
 
+    <group id="ExternalSystemView.ModuleMenu" popup="true">
+      <reference ref="ExternalSystem.OpenConfig"/>
+      <separator/>
+      <reference id="ExternalSystem.RefreshProject"/>
+      <reference id="ExternalSystem.IgnoreProject"/>
+      <separator/>
+      <reference id="ExternalSystem.OpenTasksActivationManager"/>
+    </group>
+
     <group id="ExternalSystemView.ActionsToolbar.LeftPanel">
       <reference id="ExternalSystem.RefreshAllProjects"/>
       <reference id="ExternalSystem.AttachProject"/>
       <separator/>
       <reference id="ExternalSystem.ExpandAll"/>
       <reference id="ExternalSystem.CollapseAll"/>
+      <reference id="ExternalSystem.OpenProjectStructure"/>
     </group>
     <group id="ExternalSystemView.ActionsToolbar.RightPanel">
     </group>
index b01499cd6f3f4c8a0d740c2d95efbb79a06cb900..f6dbe149c6871376bd489a828cb9055346192489 100644 (file)
@@ -51,12 +51,10 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve
     final List<War> warModels;
     final WebConfiguration webConfiguration = resolverCtx.getExtraProject(gradleModule, WebConfiguration.class);
     if (webConfiguration != null) {
-      warModels =
-        ContainerUtil.map(webConfiguration.getWarModels(), new Function<WebConfiguration.WarModel, War>() {
+      warModels = ContainerUtil.map(webConfiguration.getWarModels(), new Function<WebConfiguration.WarModel, War>() {
           @Override
           public War fun(WebConfiguration.WarModel model) {
-            War war =
-              new War(model.getWarName(), model.getWebAppDirName(), model.getWebAppDir());
+            War war = new War(model.getWarName(), model.getWebAppDirName(), model.getWebAppDir());
             war.setWebXml(model.getWebXml());
             war.setWebResources(map(model.getWebResources()));
             war.setClasspath(model.getClasspath());
@@ -64,12 +62,8 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve
             return war;
           }
         });
+      ideModule.createChild(WebConfigurationModelData.KEY, new WebConfigurationModelData(GradleConstants.SYSTEM_ID, warModels));
     }
-    else {
-      // we need to create WebConfigurationModelData without war artifacts to handle removal outdated web artifacts of the project which can be previously created
-      warModels = ContainerUtil.emptyList();
-    }
-    ideModule.createChild(WebConfigurationModelData.KEY, new WebConfigurationModelData(GradleConstants.SYSTEM_ID, warModels));
     nextResolver.populateModuleExtraModels(gradleModule, ideModule);
   }
 
index a7b90ae8fdd0bebee5b6ea6970e6681c8bf15f29..9da5edfde62a325d0bea1ab6e8247813602de200 100644 (file)
@@ -136,6 +136,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
     final ExternalProject externalProject = resolverCtx.getExtraProject(ExternalProject.class);
     if (externalProject != null) {
       ideProject.createChild(ExternalProjectDataService.KEY, externalProject);
+      ideProject.getData().setDescription(externalProject.getDescription());
     }
   }
 
@@ -166,6 +167,11 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
       moduleData.setVersion(moduleExtendedModel.getVersion());
       moduleData.setArtifacts(moduleExtendedModel.getArtifacts());
     }
+
+    ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
+    if (externalProject != null) {
+      moduleData.setDescription(externalProject.getDescription());
+    }
     return moduleData;
   }
 
index 54fc2559316c615701dd6d5ebaeaf5b4fef24dfc..d9f7500e28037121bd6cbc56576cc0886d7c20fd 100644 (file)
@@ -30,9 +30,9 @@ import com.intellij.openapi.externalSystem.model.task.TaskData;
 import com.intellij.openapi.externalSystem.service.project.ExternalSystemProjectResolver;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemDebugEnvironment;
+import com.intellij.openapi.module.StdModuleTypes;
 import com.intellij.openapi.util.KeyValue;
 import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.io.StreamUtil;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.BooleanFunction;
 import com.intellij.util.Function;
@@ -288,8 +288,15 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
   private void handleBuildSrcProject(@NotNull final DataNode<ProjectData> resultProjectDataNode,
                                      @NotNull final ProjectConnectionDataNodeFunction projectConnectionDataNodeFunction) {
 
-    if (projectConnectionDataNodeFunction.myIsPreviewMode
-        || !new File(projectConnectionDataNodeFunction.myProjectPath).isDirectory()) {
+    if (!new File(projectConnectionDataNodeFunction.myProjectPath).isDirectory()) {
+      return;
+    }
+
+    if (projectConnectionDataNodeFunction.myIsPreviewMode) {
+      ModuleData buildSrcModuleData =
+        new ModuleData(":buildSrc", GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), "buildSrc",
+                       projectConnectionDataNodeFunction.myProjectPath, projectConnectionDataNodeFunction.myProjectPath);
+      resultProjectDataNode.createChild(ProjectKeys.MODULE, buildSrcModuleData);
       return;
     }
 
index f8eb3f753cc80cf2e46bcaccf3ef21eb914a6fed..71a0c9e03bd6a1716375c3c0fd85144d2ed438f0 100644 (file)
@@ -78,6 +78,7 @@ public class ExternalProjectDataService implements ProjectDataService<ExternalPr
   public void importData(@NotNull final Collection<DataNode<ExternalProject>> toImport,
                          @NotNull final Project project,
                          final boolean synchronous) {
+    if(toImport.isEmpty()) return;
     if (toImport.size() != 1) {
       throw new IllegalArgumentException(
         String.format("Expected to get a single external project but got %d: %s", toImport.size(), toImport));
index 53ecff1ccb93acd8e2e1c40314270d1906b2d5b8..7b2e40efed72a3c79b8001d575682dae8b036c6d 100644 (file)
@@ -177,7 +177,7 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes
   }
 
   @Override
-  protected void importProject(@NonNls @Language("Groovy") String config) throws IOException {
+  protected void importProject() {
     ExternalSystemApiUtil.subscribe(myProject, GradleConstants.SYSTEM_ID, new ExternalSystemSettingsListenerAdapter() {
       @Override
       public void onProjectsLinked(@NotNull Collection settings) {
@@ -187,7 +187,7 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes
         }
       }
     });
-    super.importProject(config);
+    super.importProject();
   }
 
   @Override
index 45cfd2d0bf997b937ee0f425e17ea7a10ea8783b..b768a522fddde7e7712475656d139f249f180a9c 100644 (file)
@@ -109,7 +109,7 @@ class ExternalProjectBuilderImpl implements ModelBuilderService {
         result.put(externalTask.name, externalTask)
       }
 
-      def projectTaskPath = project.path + ':' + task.name
+      def projectTaskPath = (project.path == ':' ? ':' : project.path + ':') + task.name
       if (projectTaskPath.equals(task.path)) {
         externalTask.QName = task.path
       }