gradle: source sets support (IDEA-138076)
authorVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Wed, 14 Oct 2015 15:35:30 +0000 (18:35 +0300)
committerVladislav.Soroka <Vladislav.Soroka@jetbrains.com>
Wed, 14 Oct 2015 15:42:24 +0000 (18:42 +0300)
91 files changed:
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/AbstractDependencyData.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/LibraryData.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/ModuleDependencyData.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/OrderAware.java [new file with mode: 0644]
platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProvider.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java [new file with mode: 0644]
platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractProjectDataService.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-impl/src/com/intellij/openapi/externalSystem/action/OpenTasksActivationManagerAction.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ExternalProjectStructureCustomizer.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.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/AbstractModuleDataService.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/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/ui/ExternalProjectDataSelectorDialog.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalProjectsStructure.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/ExternalSystemViewContributor.java
platform/external-system-impl/src/com/intellij/openapi/externalSystem/view/ExternalSystemViewDefaultContributor.java
platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/project/ExternalProjectServiceTest.groovy
platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java
plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleModuleResourceConfiguration.java
plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleDependenciesEnumerationHandler.java
plugins/gradle/src/META-INF/plugin.xml
plugins/gradle/src/org/jetbrains/plugins/gradle/GradleManager.java
plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java
plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java
plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/javaee/JavaEEGradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/GradleSourceSetData.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectStructureCustomizer.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/ProjectResolverContext.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/BuildClasspathModuleGradleDataService.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataCache.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataService.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectSerializer.java
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/GradleSourceSetDataService.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/view/GradleViewContributor.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleModuleBuilder.java
plugins/gradle/src/org/jetbrains/plugins/gradle/settings/GradleExecutionSettings.java
plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceFilteringTest.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceProcessingTest.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleClassFinderTest.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/DefaultExternalDependencyId.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/ExternalDependencyId.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalLibraryDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProject.java
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProjectDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalProjectPreview.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalSourceSet.java
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/FileCollectionDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ModelsHolder.java [new file with mode: 0644]
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java
plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/UnresolvedExternalDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/AbstractExternalDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalFilter.java [moved from plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/DefaultExternalFilter.java with 100% similarity]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalLibraryDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalPlugin.java [moved from plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/DefaultExternalPlugin.java with 100% similarity]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProject.java [moved from plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/DefaultExternalProject.java with 84% similarity]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalProjectDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceDirectorySet.java [moved from plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceDirectorySet.java with 97% similarity]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceSet.java [moved from plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceSet.java with 81% similarity]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalTask.java [moved from plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/DefaultExternalTask.java with 100% similarity]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultFileCollectionDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultUnresolvedExternalDependency.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/ModelFactory.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ModelBuildScriptClasspathBuilderImpl.java
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ModuleExtendedModelBuilderImpl.java
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolver.groovy [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/ModuleComponentIdentifierImpl.java [new file with mode: 0644]
plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java

index 3e89fd8febfb0181107eee067ffbdb8eaf6cb902..433a9b4ca400656cb85cb228e27d9df9743b1740 100644 (file)
@@ -233,7 +233,22 @@ public class DataNode<T> implements Serializable, UserDataHolderEx {
     return null;
   }
 
+  @SuppressWarnings("unchecked")
+  @Nullable
+  public <P> DataNode<P> getParent(@NotNull Class<P> dataClass) {
+    if (dataClass.isInstance(myData)) {
+      return (DataNode<P>)this;
+    }
+    for (DataNode<?> p = myParent; p != null; p = p.myParent) {
+      if (dataClass.isInstance(p.myData)) {
+        return (DataNode<P>)p;
+      }
+    }
+    return null;
+  }
+
   public void addChild(@NotNull DataNode<?> child) {
+    child.myParent = this;
     myChildren.add(child);
   }
 
index 0b4078ddc8a6f42f5c25bb1e219e76392607318d..9bafe3ead239ca995bfd98671760850b192a9700 100644 (file)
@@ -11,7 +11,7 @@ import java.io.ObjectInputStream;
  * @since 8/10/11 6:41 PM
  */
 public abstract class AbstractDependencyData<T extends AbstractExternalEntityData & Named> extends AbstractExternalEntityData
-  implements DependencyData, Named
+  implements DependencyData, Named, OrderAware
 {
 
   private static final long serialVersionUID = 1L;
@@ -22,6 +22,7 @@ public abstract class AbstractDependencyData<T extends AbstractExternalEntityDat
   private DependencyScope myScope = DependencyScope.COMPILE;
 
   private boolean myExported;
+  private int myOrder;
 
   protected AbstractDependencyData(@NotNull ModuleData ownerModule, @NotNull T dependency) {
     super(ownerModule.getOwner());
@@ -100,6 +101,15 @@ public abstract class AbstractDependencyData<T extends AbstractExternalEntityDat
   }
 
 
+  @Override
+  public int getOrder() {
+    return myOrder;
+  }
+
+  public void setOrder(int order) {
+    myOrder = order;
+  }
+
   @SuppressWarnings("MethodOverridesPrivateMethodOfSuperclass")
   private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
     in.defaultReadObject();
@@ -120,7 +130,9 @@ public abstract class AbstractDependencyData<T extends AbstractExternalEntityDat
       return false;
     }
     AbstractDependencyData<?> that = (AbstractDependencyData<?>)o;
-    return  myScope.equals(that.myScope) && myOwnerModule.equals(that.myOwnerModule) && myTarget.equals(that.myTarget);
+    return myScope.equals(that.myScope) &&
+           myOwnerModule.equals(that.myOwnerModule) &&
+           myTarget.equals(that.myTarget);
   }
 
   @Override
index 9e51ae5d049fdb30649b6397f21a06101cc36407..77eac3c015b2610ee1bb3702fc491f3f6dcbcedc 100644 (file)
@@ -5,10 +5,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.util.containers.HashMap;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * Not thread-safe.
@@ -29,7 +26,7 @@ public class LibraryData extends AbstractNamedData implements Named {
   }
 
   public LibraryData(@NotNull ProjectSystemId owner, @NotNull String name, boolean unresolved) {
-    super(owner, name, String.format("%s: %s", owner.getReadableName(), name));
+    super(owner, name, name.isEmpty() ? "" : String.format("%s: %s", owner.getReadableName(), name));
     myUnresolved = unresolved;
   }
 
@@ -46,7 +43,7 @@ public class LibraryData extends AbstractNamedData implements Named {
   public void addPath(@NotNull LibraryPathType type, @NotNull String path) {
     Set<String> paths = myPaths.get(type);
     if (paths == null) {
-      myPaths.put(type, paths = new HashSet<String>());
+      myPaths.put(type, paths = new LinkedHashSet<String>());
     } 
     paths.add(ExternalSystemApiUtil.toCanonicalPath(path));
   }
index 1d8af229bebf57967c6fd1b917fa3bc9d34f5464..75e252e44b0f190a03151e29071583f9599d1c3e 100644 (file)
@@ -3,6 +3,7 @@ package com.intellij.openapi.externalSystem.model.project;
 import com.intellij.ide.highlighter.ModuleFileType;
 import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -24,23 +25,15 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
   @NotNull private final String myId;
   @NotNull private final String myModuleTypeId;
   @NotNull private final String myExternalConfigPath;
-  @NotNull private String myModuleFilePath;
+  @NotNull private String myModuleFileDirectoryPath;
   @Nullable private String group;
   @Nullable private String version;
   @Nullable private String description;
   @NotNull private List<File> myArtifacts;
+  @Nullable private String[] ideModuleGroup;
 
   private boolean myInheritProjectCompileOutputPath = true;
 
-  @Deprecated
-  public ModuleData(@NotNull ProjectSystemId owner,
-                    @NotNull String typeId,
-                    @NotNull String name,
-                    @NotNull String moduleFileDirectoryPath,
-                    @NotNull String externalConfigPath) {
-    this("", owner, typeId, name, moduleFileDirectoryPath, externalConfigPath);
-  }
-
   public ModuleData(@NotNull String id,
                     @NotNull ProjectSystemId owner,
                     @NotNull String typeId,
@@ -52,7 +45,22 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
     myModuleTypeId = typeId;
     myExternalConfigPath = externalConfigPath;
     myArtifacts = Collections.emptyList();
-    setModuleFileDirectoryPath(moduleFileDirectoryPath);
+    myModuleFileDirectoryPath = moduleFileDirectoryPath;
+  }
+
+  protected ModuleData(@NotNull String id,
+                       @NotNull ProjectSystemId owner,
+                       @NotNull String typeId,
+                       @NotNull String externalName,
+                       @NotNull String internalName,
+                       @NotNull String moduleFileDirectoryPath,
+                       @NotNull String externalConfigPath) {
+    super(owner, externalName, FileUtil.sanitizeFileName(internalName));
+    myId = id;
+    myModuleTypeId = typeId;
+    myExternalConfigPath = externalConfigPath;
+    myArtifacts = Collections.emptyList();
+    myModuleFileDirectoryPath = moduleFileDirectoryPath;
   }
 
   @NotNull
@@ -74,11 +82,12 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
 
   @NotNull
   public String getModuleFilePath() {
-    return myModuleFilePath;
+    return ExternalSystemApiUtil
+      .toCanonicalPath(myModuleFileDirectoryPath + "/" + getInternalName() + ModuleFileType.DOT_DEFAULT_EXTENSION);
   }
 
   public void setModuleFileDirectoryPath(@NotNull String path) {
-    myModuleFilePath = ExternalSystemApiUtil.toCanonicalPath(path + "/" + getInternalName() + ModuleFileType.DOT_DEFAULT_EXTENSION);
+    myModuleFileDirectoryPath = path;
   }
 
   public boolean isInheritProjectCompileOutputPath() {
@@ -146,6 +155,15 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf
     myArtifacts = artifacts;
   }
 
+  @Nullable
+  public String[] getIdeModuleGroup() {
+    return ideModuleGroup;
+  }
+
+  public void setIdeModuleGroup(@Nullable String[] ideModuleGroup) {
+    this.ideModuleGroup = ideModuleGroup;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ModuleData)) return false;
index 7aed9c06749364883b265639d37077e38356a61d..b97378d112e70df2c6e272950b356cede700feee 100644 (file)
@@ -2,8 +2,6 @@ package com.intellij.openapi.externalSystem.model.project;
 
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Comparator;
-
 /**
  * @author Denis Zhdanov
  * @since 8/10/11 6:40 PM
@@ -11,8 +9,29 @@ import java.util.Comparator;
 public class ModuleDependencyData extends AbstractDependencyData<ModuleData> {
 
   private static final long serialVersionUID = 1L;
+  private boolean myProductionOnTestDependency;
 
   public ModuleDependencyData(@NotNull ModuleData ownerModule, @NotNull ModuleData module) {
     super(ownerModule, module);
   }
+
+  public boolean isProductionOnTestDependency() {
+    return myProductionOnTestDependency;
+  }
+
+  public void setProductionOnTestDependency(boolean productionOnTestDependency) {
+    myProductionOnTestDependency = productionOnTestDependency;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!super.equals(o)) return false;
+    ModuleDependencyData that = (ModuleDependencyData)o;
+    return myProductionOnTestDependency == that.myProductionOnTestDependency;
+  }
+
+  @Override
+  public int hashCode() {
+    return 31 * super.hashCode() + (myProductionOnTestDependency ? 1 : 0);
+  }
 }
diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/OrderAware.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/OrderAware.java
new file mode 100644 (file)
index 0000000..4599acf
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.model.project;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 9/24/2015
+ */
+public interface OrderAware {
+  int getOrder();
+}
index 1935f3602f2c9f9a8fb81b5a2b5b498e46d5b0e8..d12305aa78f71d4b62ead71fb67d51a8f54b6c68 100644 (file)
@@ -52,11 +52,17 @@ public interface IdeModelsProvider {
   ModuleOrderEntry findIdeModuleDependency(@NotNull ModuleDependencyData dependency, @NotNull Module module);
 
   @Nullable
-  OrderEntry findIdeModuleOrderEntry(LibraryDependencyData data);
+  OrderEntry findIdeModuleOrderEntry(@NotNull DependencyData data);
 
   @NotNull
   VirtualFile[] getContentRoots(Module module);
 
+  @NotNull
+  VirtualFile[] getSourceRoots(Module module);
+
+  @NotNull
+  VirtualFile[] getSourceRoots(Module module, boolean includingTests);
+
   @NotNull
   Library[] getAllLibraries();
 
diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java
new file mode 100644 (file)
index 0000000..6965a28
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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;
+
+import com.intellij.openapi.externalSystem.model.project.*;
+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.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Function;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.getExternalRootProjectPath;
+import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.isExternalSystemAwareModule;
+import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.isRelated;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/6/2015
+ */
+public class IdeModelsProviderImpl implements IdeModelsProvider {
+
+  @NotNull
+  protected final Project myProject;
+
+  public IdeModelsProviderImpl(@NotNull Project project) {
+    myProject = project;
+  }
+
+  @NotNull
+  @Override
+  public Module[] getModules() {
+    return ModuleManager.getInstance(myProject).getModules();
+  }
+
+  @NotNull
+  @Override
+  public Module[] getModules(@NotNull final ProjectData projectData) {
+    final List<Module> modules = ContainerUtil.filter(getModules(), new Condition<Module>() {
+      @Override
+      public boolean value(Module module) {
+        return isExternalSystemAwareModule(projectData.getOwner(), module) &&
+               StringUtil.equals(projectData.getLinkedExternalProjectPath(), getExternalRootProjectPath(module));
+      }
+    });
+    return ContainerUtil.toArray(modules, new Module[modules.size()]);
+  }
+
+  @NotNull
+  @Override
+  public OrderEntry[] getOrderEntries(@NotNull Module module) {
+    return ModuleRootManager.getInstance(module).getOrderEntries();
+  }
+
+  @Nullable
+  @Override
+  public Module findIdeModule(@NotNull ModuleData module) {
+    final Module ideModule = findIdeModule(module.getInternalName());
+    return isExternalSystemAwareModule(module.getOwner(), ideModule) ? ideModule : null;
+  }
+
+  @Nullable
+  @Override
+  public Module findIdeModule(@NotNull String ideModuleName) {
+    for (Module module : getModules()) {
+      if (ideModuleName.equals(module.getName())) {
+        return module;
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public Library findIdeLibrary(@NotNull LibraryData libraryData) {
+    final LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(myProject);
+    for (Library ideLibrary : libraryTable.getLibraries()) {
+      if (isRelated(ideLibrary, libraryData)) return ideLibrary;
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public ModuleOrderEntry findIdeModuleDependency(@NotNull ModuleDependencyData dependency, @NotNull Module module) {
+    for (OrderEntry entry : getOrderEntries(module)) {
+      if (entry instanceof ModuleOrderEntry) {
+        ModuleOrderEntry candidate = (ModuleOrderEntry)entry;
+        if (dependency.getInternalName().equals(candidate.getModuleName()) && dependency.getScope().equals(candidate.getScope())) {
+          return candidate;
+        }
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public OrderEntry findIdeModuleOrderEntry(@NotNull DependencyData data) {
+    Module ownerIdeModule = findIdeModule(data.getOwnerModule());
+    if (ownerIdeModule == null) return null;
+
+    LibraryDependencyData libraryDependencyData = null;
+    ModuleDependencyData moduleDependencyData = null;
+    if (data instanceof LibraryDependencyData) {
+      libraryDependencyData = (LibraryDependencyData)data;
+    }
+    else if (data instanceof ModuleDependencyData) {
+      moduleDependencyData = (ModuleDependencyData)data;
+    }
+    else {
+      return null;
+    }
+
+    for (OrderEntry entry : getOrderEntries(ownerIdeModule)) {
+      if (entry instanceof LibraryOrderEntry && libraryDependencyData != null) {
+        if (((LibraryOrderEntry)entry).isModuleLevel() && libraryDependencyData.getLevel() != LibraryLevel.MODULE) continue;
+        if (StringUtil.isEmpty(((LibraryOrderEntry)entry).getLibraryName())) {
+          final Set<String> paths =
+            ContainerUtil.map2Set(libraryDependencyData.getTarget().getPaths(LibraryPathType.BINARY), new Function<String, String>() {
+              @Override
+              public String fun(String path) {
+                return PathUtil.getLocalPath(path);
+              }
+            });
+          final Set<String> entryPaths = ContainerUtil.map2Set(entry.getUrls(OrderRootType.CLASSES), new Function<String, String>() {
+            @Override
+            public String fun(String s) {
+              return PathUtil.getLocalPath(VfsUtilCore.urlToPath(s));
+            }
+          });
+          if (entryPaths.equals(paths) && ((LibraryOrderEntry)entry).getScope() == data.getScope()) return entry;
+          continue;
+        }
+      }
+
+      String entryName = libraryDependencyData != null ? libraryDependencyData.getInternalName() : moduleDependencyData.getInternalName();
+      if (entryName.equals(entry.getPresentableName()) &&
+          (!(entry instanceof ExportableOrderEntry) || ((ExportableOrderEntry)entry).getScope() == data.getScope())) {
+        return entry;
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public VirtualFile[] getContentRoots(Module module) {
+    return ModuleRootManager.getInstance(module).getContentRoots();
+  }
+
+  @NotNull
+  @Override
+  public VirtualFile[] getSourceRoots(Module module) {
+    return ModuleRootManager.getInstance(module).getSourceRoots();
+  }
+
+  @NotNull
+  @Override
+  public VirtualFile[] getSourceRoots(Module module, boolean includingTests) {
+    return ModuleRootManager.getInstance(module).getSourceRoots(includingTests);
+  }
+
+  @NotNull
+  @Override
+  public Library[] getAllLibraries() {
+    return LibraryTablesRegistrar.getInstance().getLibraryTable(myProject).getLibraries();
+  }
+
+  @Nullable
+  @Override
+  public Library getLibraryByName(String name) {
+    return LibraryTablesRegistrar.getInstance().getLibraryTable(myProject).getLibraryByName(name);
+  }
+
+  @NotNull
+  @Override
+  public String[] getLibraryUrls(@NotNull Library library, @NotNull OrderRootType type) {
+    return library.getUrls(type);
+  }
+}
index df3e862d8a07250d71391668db3d981d9eeb6ad9..49de2fb50bf427936f6ed6ee26a895170d3df988 100644 (file)
@@ -63,4 +63,13 @@ public abstract class AbstractProjectDataService<E, I> implements ProjectDataSer
                          @NotNull Project project,
                          @NotNull IdeModifiableModelsProvider modelsProvider) {
   }
+
+  public void postProcess(@NotNull Collection<DataNode<E>> toImport,
+                          @Nullable ProjectData projectData,
+                          @NotNull Project project,
+                          @NotNull IdeModifiableModelsProvider modelsProvider) {
+  }
+
+  public void onSuccessImport(@NotNull Project project) {
+  }
 }
index a0435b94f01b835c7a1b0421d482b188808d652b..0870dc0818ece5377a9ff74cccb491a29e360f16 100644 (file)
@@ -255,6 +255,17 @@ public class ExternalSystemApiUtil {
     return ContainerUtil.groupBy(nodes, GROUPER);
   }
 
+  @NotNull
+  public static <K, V> MultiMap<DataNode<K>, DataNode<V>> groupBy(@NotNull Collection<DataNode<V>> nodes, final Class<K> moduleDataClass) {
+    return ContainerUtil.groupBy(nodes, new NullableFunction<DataNode<V>, DataNode<K>>() {
+      @Nullable
+      @Override
+      public DataNode<K> fun(DataNode<V> node) {
+        return node.getParent(moduleDataClass);
+      }
+    });
+  }
+
   @NotNull
   public static <K, V> MultiMap<DataNode<K>, DataNode<V>> groupBy(@NotNull Collection<DataNode<V>> nodes, @NotNull final Key<K> key) {
     return ContainerUtil.groupBy(nodes, new NullableFunction<DataNode<V>, DataNode<K>>() {
@@ -325,17 +336,7 @@ public class ExternalSystemApiUtil {
   @SuppressWarnings("unchecked")
   @NotNull
   public static <T> Collection<DataNode<T>> findAll(@NotNull DataNode<?> parent, @NotNull Key<T> key) {
-    Collection<DataNode<T>> result = null;
-    for (DataNode<?> child : parent.getChildren()) {
-      if (!key.equals(child.getKey())) {
-        continue;
-      }
-      if (result == null) {
-        result = ContainerUtilRt.newArrayList();
-      }
-      result.add((DataNode<T>)child);
-    }
-    return result == null ? Collections.<DataNode<T>>emptyList() : result;
+    return getChildren(parent, key);
   }
 
   public static void visit(@Nullable DataNode node, @NotNull Consumer<DataNode<?>> consumer) {
@@ -893,6 +894,12 @@ public class ExternalSystemApiUtil {
     return module != null && !module.isDisposed() ? module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_VERSION_KEY) : null;
   }
 
+  @Nullable
+  @Contract(pure=true)
+  public static String getExternalModuleType(@Nullable Module module) {
+    return module != null && !module.isDisposed() ? module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_TYPE_KEY) : null;
+  }
+
   public static void subscribe(@NotNull Project project,
                                @NotNull ProjectSystemId systemId,
                                @NotNull ExternalSystemSettingsListener listener) {
index 932fbfb95d57d1bb7ba4391752692f0624880ca5..06b29a2834cee674f640a29a262f6283b179dd92 100644 (file)
@@ -30,6 +30,7 @@ public class ExternalSystemConstants {
   @NonNls @NotNull public static final String ROOT_PROJECT_PATH_KEY = "external.root.project.path";
   @NonNls @NotNull public static final String LINKED_PROJECT_ID_KEY = "external.linked.project.id";
 
+  @NonNls @NotNull public static final String EXTERNAL_SYSTEM_MODULE_TYPE_KEY = "external.system.module.type";
   @NonNls @NotNull public static final String EXTERNAL_SYSTEM_MODULE_GROUP_KEY  = "external.system.module.group";
   @NonNls @NotNull public static final String EXTERNAL_SYSTEM_MODULE_VERSION_KEY  = "external.system.module.version";
 
index 4ae88e6117e4f91b07066366309a2939bb16a13f..5e7e22a5195267d2ff6abc9d53afeb39fda3e2cb 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.externalSystem.service.task.ui.ConfigureTasksActivat
 import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
 import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
 
@@ -55,7 +56,8 @@ public class OpenTasksActivationManagerAction extends ExternalSystemNodeAction<A
     e.getPresentation().setText(ExternalSystemBundle.message("external.system.task.activation.title"));
     e.getPresentation().setDescription(
       ExternalSystemBundle.message("external.system.task.activation.description", projectSystemId.getReadableName()));
-    return (externalData instanceof ProjectData || externalData instanceof ModuleData);
+    final boolean isProjectNode = externalData instanceof ProjectData || externalData instanceof ModuleData;
+    return isProjectNode && StringUtil.isNotEmpty(((ExternalConfigPathAware) externalData).getLinkedExternalProjectPath());
   }
 
   @Override
index a4633a3cb6018ce0eb0e66e02a4334c4e911f2d8..757441d1616a6ba9e1bd44ed1acd2f7bb09de087 100644 (file)
@@ -19,6 +19,7 @@ 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.externalSystem.model.project.Identifiable;
 import com.intellij.openapi.util.Couple;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -51,6 +52,15 @@ public abstract class ExternalProjectStructureCustomizer {
     return Collections.emptySet();
   }
 
+  /**
+   * Set of data keys, which respective data can have dependencies or can depend on other data
+   * @return data keys
+   */
+  @NotNull
+  public Set<? extends Key<? extends Identifiable>> getDependencyAwareDataKeys() {
+    return Collections.emptySet();
+  }
+
   @Nullable
   public abstract Icon suggestIcon(@NotNull DataNode node, @NotNull ExternalSystemUiAware uiAware);
 
index 72dc4695a30517b8df89fbfccd83b3843e335d4f..5a8d0ed2f78038dba610218ed4fb605e94e9ddf7 100644 (file)
@@ -23,7 +23,6 @@ import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.externalSystem.model.project.*;
 import com.intellij.openapi.module.ModifiableModuleModel;
 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.roots.ex.ProjectRootManagerEx;
@@ -33,30 +32,22 @@ import com.intellij.openapi.roots.libraries.LibraryTable;
 import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
 import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
 import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
-import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.packaging.artifacts.ArtifactModel;
 import com.intellij.packaging.artifacts.ModifiableArtifactModel;
 import com.intellij.packaging.elements.ManifestFileProvider;
 import com.intellij.packaging.elements.PackagingElementResolvingContext;
 import com.intellij.packaging.impl.artifacts.DefaultManifestFileProvider;
-import com.intellij.util.containers.ContainerUtil;
 import gnu.trove.THashMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Collection;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.*;
 
-public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiableModelsProvider {
-  @NotNull
-  protected final Project myProject;
+public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProviderImpl implements IdeModifiableModelsProvider {
   private ModifiableModuleModel myModifiableModuleModel;
   private Map<Module, ModifiableRootModel> myModifiableRootModels = new THashMap<Module, ModifiableRootModel>();
   private Map<Module, ModifiableFacetModel> myModifiableFacetModels = new THashMap<Module, ModifiableFacetModel>();
@@ -66,7 +57,7 @@ public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiab
   private final ArtifactExternalDependenciesImporter myArtifactExternalDependenciesImporter;
 
   public AbstractIdeModifiableModelsProvider(@NotNull Project project) {
-    myProject = project;
+    super(project);
     myArtifactExternalDependenciesImporter = new ArtifactExternalDependenciesImporterImpl();
   }
 
@@ -87,20 +78,7 @@ public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiab
   @NotNull
   @Override
   public Module[] getModules() {
-    return myModifiableModuleModel == null ? ModuleManager.getInstance(myProject).getModules() : getModifiableModuleModel().getModules();
-  }
-
-  @NotNull
-  @Override
-  public Module[] getModules(@NotNull final ProjectData projectData) {
-    final List<Module> modules = ContainerUtil.filter(getModules(), new Condition<Module>() {
-      @Override
-      public boolean value(Module module) {
-        return isExternalSystemAwareModule(projectData.getOwner(), module) &&
-               StringUtil.equals(projectData.getLinkedExternalProjectPath(), getExternalRootProjectPath(module));
-      }
-    });
-    return ContainerUtil.toArray(modules, new Module[modules.size()]);
+    return getModifiableModuleModel().getModules();
   }
 
   protected void processExternalArtifactDependencies() {
@@ -118,7 +96,7 @@ public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiab
   @NotNull
   @Override
   public OrderEntry[] getOrderEntries(@NotNull Module module) {
-    return getRootModel(module, false).getOrderEntries();
+    return getRootModel(module).getOrderEntries();
   }
 
   @NotNull
@@ -130,24 +108,6 @@ public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiab
     return module;
   }
 
-  @Nullable
-  @Override
-  public Module findIdeModule(@NotNull ModuleData module) {
-    final Module ideModule = findIdeModule(module.getInternalName());
-    return isExternalSystemAwareModule(module.getOwner(), ideModule) ? ideModule : null;
-  }
-
-  @Nullable
-  @Override
-  public Module findIdeModule(@NotNull String ideModuleName) {
-    for (Module module : getModules()) {
-      if (ideModuleName.equals(module.getName())) {
-        return module;
-      }
-    }
-    return null;
-  }
-
   @Nullable
   @Override
   public Library findIdeLibrary(@NotNull LibraryData libraryData) {
@@ -158,41 +118,22 @@ public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiab
     return null;
   }
 
-  @Nullable
   @Override
-  public ModuleOrderEntry findIdeModuleDependency(@NotNull ModuleDependencyData dependency, @NotNull Module module) {
-    for (OrderEntry entry : getRootModel(module, false).getOrderEntries()) {
-      if (entry instanceof ModuleOrderEntry) {
-        ModuleOrderEntry candidate = (ModuleOrderEntry)entry;
-        if (dependency.getInternalName().equals(candidate.getModuleName()) && dependency.getScope().equals(candidate.getScope())) {
-          return candidate;
-        }
-      }
-    }
-    return null;
+  @NotNull
+  public VirtualFile[] getContentRoots(Module module) {
+    return getRootModel(module).getContentRoots();
   }
 
-  @Nullable
+  @NotNull
   @Override
-  public OrderEntry findIdeModuleOrderEntry(LibraryDependencyData data) {
-    Module ownerIdeModule = findIdeModule(data.getOwnerModule());
-    if (ownerIdeModule == null) return null;
-    for (OrderEntry entry : getOrderEntries(ownerIdeModule)) {
-      if (entry instanceof LibraryOrderEntry) {
-        if (((LibraryOrderEntry)entry).isModuleLevel() && data.getLevel() != LibraryLevel.MODULE) continue;
-      }
-
-      if (data.getInternalName().equals(entry.getPresentableName())) {
-        return entry;
-      }
-    }
-    return null;
+  public VirtualFile[] getSourceRoots(Module module) {
+    return getRootModel(module).getSourceRoots();
   }
 
-  @Override
   @NotNull
-  public VirtualFile[] getContentRoots(Module module) {
-    return getRootModel(module, false).getContentRoots();
+  @Override
+  public VirtualFile[] getSourceRoots(Module module, boolean includingTests) {
+    return getRootModel(module).getSourceRoots(includingTests);
   }
 
   @NotNull
@@ -207,14 +148,13 @@ public abstract class AbstractIdeModifiableModelsProvider implements IdeModifiab
   @Override
   @NotNull
   public ModifiableRootModel getModifiableRootModel(Module module) {
-    return (ModifiableRootModel)getRootModel(module, true);
+    return (ModifiableRootModel)getRootModel(module);
   }
 
   @NotNull
-  private ModuleRootModel getRootModel(Module module, boolean modifiableModelNeeded) {
+  private ModuleRootModel getRootModel(Module module) {
     ModifiableRootModel result = myModifiableRootModels.get(module);
     if (result == null) {
-      if (!modifiableModelNeeded) return ModuleRootManager.getInstance(module);
       result = doGetModifiableRootModel(module);
       myModifiableRootModels.put(module, result);
     }
index d47728543672410bfa21ce79f3cc312f19205332..b650ba7ffd4b5fa1019946cb9e4bc37dd148fd75 100644 (file)
  */
 package com.intellij.openapi.externalSystem.service.project.manage;
 
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.model.DataNode;
-import com.intellij.openapi.externalSystem.model.project.AbstractDependencyData;
-import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.model.project.*;
 import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ExportableOrderEntry;
-import com.intellij.openapi.roots.ModifiableRootModel;
-import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.*;
 import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PathUtil;
 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.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @author Denis Zhdanov
@@ -43,6 +44,45 @@ import java.util.Map;
 @Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER)
 public abstract class AbstractDependencyDataService<E extends AbstractDependencyData<?>, I extends ExportableOrderEntry>
   extends AbstractProjectDataService<E, I> {
+
+  private static final Logger LOG = Logger.getInstance(AbstractDependencyDataService.class.getName());
+
+
+  @Override
+  public void importData(@NotNull Collection<DataNode<E>> toImport,
+                         @Nullable ProjectData projectData,
+                         @NotNull Project project,
+                         @NotNull IdeModifiableModelsProvider modelsProvider) {
+    if (toImport.isEmpty()) {
+      return;
+    }
+
+    MultiMap<DataNode<ModuleData>, DataNode<E>> byModule = ExternalSystemApiUtil.groupBy(toImport, ModuleData.class);
+    for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<E>>> entry : byModule.entrySet()) {
+      final DataNode<ModuleData> moduleDataNode = entry.getKey();
+      Module module = modelsProvider.findIdeModule(moduleDataNode.getData());
+      if (module == null) {
+        LOG.warn(String.format(
+          "Can't import dependencies %s. Reason: target module (%s) is not found at the ide and can't be imported",
+          entry.getValue(), moduleDataNode
+        ));
+        continue;
+      }
+
+      final Map<OrderEntry, OrderAware> moduleDependenciesOrder = importData(entry.getValue(), module, modelsProvider);
+      final Map<OrderEntry, OrderAware> orderEntryDataMap = moduleDataNode.getUserData(AbstractModuleDataService.ORDERED_DATA_MAP_KEY);
+      if(orderEntryDataMap != null) {
+        orderEntryDataMap.putAll(moduleDependenciesOrder);
+      } else {
+        moduleDataNode.putUserData(AbstractModuleDataService.ORDERED_DATA_MAP_KEY, moduleDependenciesOrder);
+      }
+    }
+  }
+
+  protected abstract Map<OrderEntry, OrderAware> importData(@NotNull Collection<DataNode<E>> nodesToImport,
+                                                            @NotNull Module module,
+                                                            @NotNull IdeModifiableModelsProvider modelsProvider);
+
   @NotNull
   @Override
   public Computable<Collection<I>> computeOrphanData(@NotNull final Collection<DataNode<E>> toImport,
@@ -55,12 +95,18 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
         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());
+          byModuleName.putValue(data.getOwnerModule().getInternalName(), getInternalName(data));
         }
 
         List<I> orphanEntries = ContainerUtil.newSmartList();
         for (Module module : modelsProvider.getModules(projectData)) {
           for (OrderEntry entry : modelsProvider.getOrderEntries(module)) {
+            // do not remove recently created library w/o name
+            if (entry instanceof LibraryOrderEntry &&
+                ((LibraryOrderEntry)entry).getLibraryName() == null &&
+                entry.getUrls(OrderRootType.CLASSES).length == 0) {
+              continue;
+            }
             //noinspection unchecked
             if (getOrderEntryType().isInstance(entry) &&
                 !byModuleName.get(entry.getOwnerModule().getName()).contains(getOrderEntryName((I)entry))) {
@@ -76,7 +122,7 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
   }
 
   @NotNull
-  public abstract Class<I> getOrderEntryType();
+  protected abstract Class<I> getOrderEntryType();
 
   protected String getOrderEntryName(@NotNull I orderEntry) {
     return orderEntry.getPresentableName();
@@ -115,23 +161,27 @@ public abstract class AbstractDependencyDataService<E extends AbstractDependency
     }
     final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module);
     for (final ExportableOrderEntry dependency : toRemove) {
-      // The thing is that intellij created order entry objects every time new modifiable model is created,
-      // that's why we can't use target dependency object as is but need to get a reference to the current
-      // entry object from the model instead.
-      for (OrderEntry entry : modifiableRootModel.getOrderEntries()) {
-        if (entry instanceof ExportableOrderEntry) {
-          ExportableOrderEntry orderEntry = (ExportableOrderEntry)entry;
-          if (orderEntry.getPresentableName().equals(dependency.getPresentableName()) &&
-              orderEntry.getScope().equals(dependency.getScope())) {
-            modifiableRootModel.removeOrderEntry(entry);
-            break;
-          }
+      modifiableRootModel.removeOrderEntry(dependency);
+    }
+  }
+
+  private static String getInternalName(final AbstractDependencyData data) {
+    if (data instanceof LibraryDependencyData) {
+      final String name = data.getInternalName();
+      if (StringUtil.isNotEmpty(name)) {
+        return name;
+      }
+      else {
+        Set<String> paths = ((LibraryDependencyData)data).getTarget().getPaths(LibraryPathType.BINARY);
+        if (!paths.isEmpty()) {
+          String url = paths.iterator().next();
+          return PathUtil.toPresentableUrl(url);
         }
-        else if (entry.getPresentableName().equals(dependency.getPresentableName())) {
-          modifiableRootModel.removeOrderEntry(entry);
-          break;
+        else {
+          return ProjectBundle.message("library.empty.library.item");
         }
       }
     }
+    return data.getInternalName();
   }
 }
diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java
new file mode 100644 (file)
index 0000000..f36e260
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * 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.ide.util.projectWizard.ModuleBuilder;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.externalSystem.model.DataNode;
+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.OrderAware;
+import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
+import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+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.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.ui.CheckBoxList;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.components.JBScrollPane;
+import com.intellij.util.ArrayUtil;
+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 org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 8/5/2015
+ */
+public abstract class AbstractModuleDataService<E extends ModuleData> extends AbstractProjectDataService<E, Module> {
+
+  public static final Key<ModuleData> MODULE_DATA_KEY = Key.create("MODULE_DATA_KEY");
+  public static final Key<Module> MODULE_KEY = Key.create("LINKED_MODULE");
+  public static final Key<Map<OrderEntry, OrderAware>> ORDERED_DATA_MAP_KEY = Key.create("ORDER_ENTRY_DATA_MAP");
+  public static final Key<Set<String>> ORPHAN_MODULE_FILES = Key.create("ORPHAN_FILES");
+
+  private static final Logger LOG = Logger.getInstance(AbstractModuleDataService.class);
+
+  @Override
+  public void importData(@NotNull final Collection<DataNode<E>> toImport,
+                         @Nullable ProjectData projectData,
+                         @NotNull final Project project,
+                         @NotNull IdeModifiableModelsProvider modelsProvider) {
+    if (toImport.isEmpty()) {
+      return;
+    }
+
+    final Collection<DataNode<E>> toCreate = filterExistingModules(toImport, modelsProvider, project);
+    if (!toCreate.isEmpty()) {
+      createModules(toCreate, modelsProvider, project);
+    }
+    for (DataNode<E> node : toImport) {
+      Module module = node.getUserData(MODULE_KEY);
+      if (module != null) {
+        setModuleOptions(module, node);
+
+        final ModifiableModuleModel modifiableModel = modelsProvider.getModifiableModuleModel();
+        modifiableModel.setModuleGroupPath(module, node.getData().getIdeModuleGroup());
+
+        syncPaths(module, modelsProvider, node.getData());
+      }
+    }
+  }
+
+  private void createModules(@NotNull Collection<DataNode<E>> toCreate,
+                             @NotNull IdeModifiableModelsProvider modelsProvider,
+                             @NotNull Project project) {
+    for (final DataNode<E> module : toCreate) {
+      ModuleData data = module.getData();
+      final Module created = modelsProvider.newModule(data.getModuleFilePath(), data.getModuleTypeId());
+      module.putUserData(MODULE_KEY, created);
+      Set<String> orphanFiles = project.getUserData(ORPHAN_MODULE_FILES);
+      if(orphanFiles != null) {
+        orphanFiles.remove(created.getModuleFilePath());
+      }
+
+      // Ensure that the dependencies are clear (used to be not clear when manually removing the module and importing it via gradle)
+      final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(created);
+      modifiableRootModel.inheritSdk();
+
+      RootPolicy<Object> visitor = new RootPolicy<Object>() {
+        @Override
+        public Object visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Object value) {
+          modifiableRootModel.removeOrderEntry(libraryOrderEntry);
+          return value;
+        }
+
+        @Override
+        public Object visitModuleOrderEntry(ModuleOrderEntry moduleOrderEntry, Object value) {
+          modifiableRootModel.removeOrderEntry(moduleOrderEntry);
+          return value;
+        }
+      };
+
+      for (OrderEntry orderEntry : modifiableRootModel.getOrderEntries()) {
+        orderEntry.accept(visitor, null);
+      }
+    }
+  }
+
+  @NotNull
+  private Collection<DataNode<E>> filterExistingModules(@NotNull Collection<DataNode<E>> modules,
+                                                        @NotNull IdeModifiableModelsProvider modelsProvider,
+                                                        @NotNull Project project) {
+    Collection<DataNode<E>> result = ContainerUtilRt.newArrayList();
+    for (DataNode<E> node : modules) {
+      ModuleData moduleData = node.getData();
+      Module module = modelsProvider.findIdeModule(moduleData.getInternalName());
+      if (module == null) {
+        result.add(node);
+      }
+      else {
+        if (!FileUtil.pathsEqual(module.getModuleFilePath(), moduleData.getModuleFilePath())) {
+          modelsProvider.getModifiableModuleModel().disposeModule(module);
+          result.add(node);
+          Set<String> orphanFiles = project.getUserData(ORPHAN_MODULE_FILES);
+          if (orphanFiles == null) {
+            project.putUserData(ORPHAN_MODULE_FILES, orphanFiles = ContainerUtil.newHashSet());
+          }
+          orphanFiles.add(module.getModuleFilePath());
+        }
+        else {
+          node.putUserData(MODULE_KEY, module);
+        }
+      }
+    }
+    return result;
+  }
+
+  private static void syncPaths(@NotNull Module module, @NotNull IdeModifiableModelsProvider modelsProvider, @NotNull ModuleData data) {
+    ModifiableRootModel modifiableModel = modelsProvider.getModifiableRootModel(module);
+    CompilerModuleExtension extension = modifiableModel.getModuleExtension(CompilerModuleExtension.class);
+    if (extension == null) {
+      //modifiableModel.dispose();
+      LOG.warn(String.format("Can't sync paths for module '%s'. Reason: no compiler extension is found for it", module.getName()));
+      return;
+    }
+    String compileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.SOURCE);
+    extension.setCompilerOutputPath(compileOutputPath != null ? VfsUtilCore.pathToUrl(compileOutputPath) : null);
+
+    String testCompileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.TEST);
+    extension.setCompilerOutputPathForTests(testCompileOutputPath != null ? VfsUtilCore.pathToUrl(testCompileOutputPath) : null);
+
+    extension.inheritCompilerOutputPath(data.isInheritProjectCompileOutputPath());
+  }
+
+  @Override
+  public void removeData(@NotNull final Computable<Collection<Module>> toRemoveComputable,
+                         @NotNull final Collection<DataNode<E>> toIgnore,
+                         @NotNull final ProjectData projectData,
+                         @NotNull final Project project,
+                         @NotNull final IdeModifiableModelsProvider modelsProvider) {
+    final Collection<Module> toRemove = toRemoveComputable.compute();
+    final List<Module> modules = new SmartList<Module>(toRemove);
+    for (DataNode<E> moduleDataNode : toIgnore) {
+      final Module module = modelsProvider.findIdeModule(moduleDataNode.getData());
+      ContainerUtil.addIfNotNull(modules, module);
+    }
+
+    if (modules.isEmpty()) {
+      return;
+    }
+
+    ContainerUtil.removeDuplicates(modules);
+
+    for (Module module : modules) {
+      if (module.isDisposed()) continue;
+      unlinkModuleFromExternalSystem(module);
+    }
+
+    ruleOrphanModules(modules, project, projectData.getOwner(), new Consumer<List<Module>>() {
+      @Override
+      public void consume(final List<Module> modules) {
+        for (Module module : modules) {
+          if (module.isDisposed()) continue;
+          String path = module.getModuleFilePath();
+          final ModifiableModuleModel moduleModel = modelsProvider.getModifiableModuleModel();
+          moduleModel.disposeModule(module);
+          ModuleBuilder.deleteModuleFile(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) {
+    ExternalSystemApiUtil.executeOnEdt(true, 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);
+            }
+
+            @NotNull
+            protected Action[] createActions() {
+              return new Action[]{getOKAction()};
+            }
+          };
+
+          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);
+  }
+
+  protected void setModuleOptions(Module module, DataNode<E> moduleDataNode) {
+    ModuleData moduleData = moduleDataNode.getData();
+    module.putUserData(MODULE_DATA_KEY, moduleData);
+
+    module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY, moduleData.getOwner().toString());
+    module.setOption(ExternalSystemConstants.LINKED_PROJECT_ID_KEY, moduleData.getId());
+    module.setOption(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY, moduleData.getLinkedExternalProjectPath());
+    final ProjectData projectData = moduleDataNode.getData(ProjectKeys.PROJECT);
+    module.setOption(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY, projectData != null ? projectData.getLinkedExternalProjectPath() : "");
+
+    if (moduleData.getGroup() != null) {
+      module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_GROUP_KEY, moduleData.getGroup());
+    }
+    if (moduleData.getVersion() != null) {
+      module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_VERSION_KEY, moduleData.getVersion());
+    }
+
+    // clear maven option
+    module.clearOption("org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule");
+  }
+
+  @Override
+  public void postProcess(@NotNull Collection<DataNode<E>> toImport,
+                          @Nullable ProjectData projectData,
+                          @NotNull Project project,
+                          @NotNull IdeModifiableModelsProvider modelsProvider) {
+    for (DataNode<E> moduleDataNode : toImport) {
+      final Module module = moduleDataNode.getUserData(MODULE_KEY);
+      final Map<OrderEntry, OrderAware> orderAwareMap = moduleDataNode.getUserData(ORDERED_DATA_MAP_KEY);
+      if (module != null && orderAwareMap != null) {
+        rearrangeOrderEntries(orderAwareMap, modelsProvider.getModifiableRootModel(module));
+      }
+    }
+  }
+
+  @Override
+  public void onSuccessImport(@NotNull Project project) {
+    final Set<String> orphanFiles = project.getUserData(ORPHAN_MODULE_FILES);
+    if(orphanFiles != null && !orphanFiles.isEmpty()) {
+      ExternalSystemApiUtil.executeOnEdt(false, new Runnable() {
+        @Override
+        public void run() {
+          for (String orphanFile : orphanFiles) {
+            ModuleBuilder.deleteModuleFile(orphanFile);
+          }
+        }
+      });
+      project.putUserData(ORPHAN_MODULE_FILES, null);
+    }
+  }
+
+  protected void rearrangeOrderEntries(@NotNull Map<OrderEntry, OrderAware> orderEntryDataMap,
+                                       @NotNull ModifiableRootModel modifiableRootModel) {
+    final OrderEntry[] orderEntries = modifiableRootModel.getOrderEntries();
+    final int length = orderEntries.length;
+    final OrderEntry[] newOrder = new OrderEntry[length];
+    final PriorityQueue<Pair<OrderEntry, OrderAware>> priorityQueue = new PriorityQueue<Pair<OrderEntry, OrderAware>>(
+      11, new Comparator<Pair<OrderEntry, OrderAware>>() {
+      @Override
+      public int compare(Pair<OrderEntry, OrderAware> o1, Pair<OrderEntry, OrderAware> o2) {
+        int order1 = o1.second.getOrder();
+        int order2 = o2.second.getOrder();
+        return order1 != order2 ? order1 < order2 ? -1 : 1 : 0;
+      }
+    });
+
+    int shift = 0;
+    for (int i = 0; i < length; i++) {
+      OrderEntry orderEntry = orderEntries[i];
+      final OrderAware orderAware = orderEntryDataMap.get(orderEntry);
+      if (orderAware == null) {
+        newOrder[i] = orderEntry;
+        shift++;
+      }
+      else {
+        priorityQueue.add(Pair.create(orderEntry, orderAware));
+      }
+    }
+
+    Pair<OrderEntry, OrderAware> pair;
+    while ((pair = priorityQueue.poll()) != null) {
+      final OrderEntry orderEntry = pair.first;
+      final OrderAware orderAware = pair.second;
+      final int order = orderAware.getOrder() != -1 ? orderAware.getOrder() : length - 1;
+      final int newPlace = findNewPlace(newOrder, order - shift);
+      assert newPlace != -1;
+      newOrder[newPlace] = orderEntry;
+    }
+
+    if (LOG.isDebugEnabled()) {
+      final boolean changed = !ArrayUtil.equals(orderEntries, newOrder, new Comparator<OrderEntry>() {
+        @Override
+        public int compare(OrderEntry o1, OrderEntry o2) {
+          return o1.compareTo(o2);
+        }
+      });
+      LOG.debug(String.format("rearrange status (%s): %s", modifiableRootModel.getModule(), changed ? "modified" : "not modified"));
+    }
+    modifiableRootModel.rearrangeOrderEntries(newOrder);
+  }
+
+  private static int findNewPlace(OrderEntry[] newOrder, int newIndex) {
+    int idx = newIndex;
+    while (idx < 0 || (idx < newOrder.length && newOrder[idx] != null)) {
+      idx++;
+    }
+    if (idx >= newOrder.length) {
+      idx = newIndex - 1;
+      while (idx >= 0 && (idx >= newOrder.length || newOrder[idx] != null)) {
+        idx--;
+      }
+    }
+    return idx == -1 ? -1 : idx;
+  }
+}
index 765e4bdeddf118b5ae976ee9f16907d999aaffc2..d0e73a2878f8e14afb583c743deb45eb7f0be7b7 100644 (file)
@@ -42,6 +42,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManager;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
@@ -54,8 +55,8 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
 
 import java.io.IOException;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author Denis Zhdanov
@@ -81,9 +82,10 @@ public class ContentRootDataService extends AbstractProjectDataService<ContentRo
       return;
     }
 
-    MultiMap<DataNode<ModuleData>, DataNode<ContentRootData>> byModule = ExternalSystemApiUtil.groupBy(toImport, ProjectKeys.MODULE);
+    MultiMap<DataNode<ModuleData>, DataNode<ContentRootData>> byModule = ExternalSystemApiUtil.groupBy(toImport, ModuleData.class);
     for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ContentRootData>>> entry : byModule.entrySet()) {
-      Module module = modelsProvider.findIdeModule(entry.getKey().getData());
+      Module module = entry.getKey().getUserData(AbstractModuleDataService.MODULE_KEY);
+      module = module != null ? module : modelsProvider.findIdeModule(entry.getKey().getData());
       if (module == null) {
         LOG.warn(String.format(
           "Can't import content roots. Reason: target module (%s) is not found at the ide. Content roots: %s",
@@ -118,12 +120,16 @@ public class ContentRootDataService extends AbstractProjectDataService<ContentRo
       }
     }
 
+    final Set<ContentEntry> importedContentEntries = ContainerUtil.newIdentityTroveSet();
     for (final DataNode<ContentRootData> node : data) {
       final ContentRootData contentRoot = node.getData();
 
       final ContentEntry contentEntry = findOrCreateContentRoot(modifiableRootModel, contentRoot.getRootPath());
-      contentEntry.clearExcludeFolders();
-      contentEntry.clearSourceFolders();
+      if(!importedContentEntries.contains(contentEntry)) {
+        contentEntry.clearExcludeFolders();
+        contentEntry.clearSourceFolders();
+        importedContentEntries.add(contentEntry);
+      }
       LOG.debug(String.format("Importing content root '%s' for module '%s'", contentRoot.getRootPath(), module.getName()));
       for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE)) {
         createSourceRootIfAbsent(
@@ -133,14 +139,6 @@ public class ContentRootDataService extends AbstractProjectDataService<ContentRo
         createSourceRootIfAbsent(
           contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, false, createEmptyContentRootDirectories);
       }
-      for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.RESOURCE)) {
-        createSourceRootIfAbsent(
-          contentEntry, path, module.getName(), JavaResourceRootType.RESOURCE, false, createEmptyContentRootDirectories);
-      }
-      for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_RESOURCE)) {
-        createSourceRootIfAbsent(
-          contentEntry, path, module.getName(), JavaResourceRootType.TEST_RESOURCE, false, createEmptyContentRootDirectories);
-      }
       for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.SOURCE_GENERATED)) {
         createSourceRootIfAbsent(
           contentEntry, path, module.getName(), JavaSourceRootType.SOURCE, true, createEmptyContentRootDirectories);
@@ -149,6 +147,14 @@ public class ContentRootDataService extends AbstractProjectDataService<ContentRo
         createSourceRootIfAbsent(
           contentEntry, path, module.getName(), JavaSourceRootType.TEST_SOURCE, true, createEmptyContentRootDirectories);
       }
+      for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.RESOURCE)) {
+        createSourceRootIfAbsent(
+          contentEntry, path, module.getName(), JavaResourceRootType.RESOURCE, false, createEmptyContentRootDirectories);
+      }
+      for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.TEST_RESOURCE)) {
+        createSourceRootIfAbsent(
+          contentEntry, path, module.getName(), JavaResourceRootType.TEST_RESOURCE, false, createEmptyContentRootDirectories);
+      }
       for (SourceRoot path : contentRoot.getPaths(ExternalSystemSourceType.EXCLUDED)) {
         createExcludedRootIfAbsent(contentEntry, path, module.getName(), module.getProject());
       }
@@ -178,14 +184,21 @@ public class ContentRootDataService extends AbstractProjectDataService<ContentRo
   private static void createSourceRootIfAbsent(
     @NotNull ContentEntry entry, @NotNull final SourceRoot root, @NotNull String moduleName,
     @NotNull JpsModuleSourceRootType<?> sourceRootType, boolean generated, boolean createEmptyContentRootDirectories) {
-    List<SourceFolder> folders = entry.getSourceFolders(sourceRootType);
+    SourceFolder[] folders = entry.getSourceFolders();
     for (SourceFolder folder : folders) {
       VirtualFile file = folder.getFile();
       if (file == null) {
         continue;
       }
       if (ExternalSystemApiUtil.getLocalFileSystemPath(file).equals(root.getPath())) {
-        return;
+        final JpsModuleSourceRootType<?> folderRootType = folder.getRootType();
+        if(JavaSourceRootType.SOURCE.equals(folderRootType) || sourceRootType.equals(folderRootType)) {
+          return;
+        }
+        if(JavaSourceRootType.TEST_SOURCE.equals(folderRootType) && JavaResourceRootType.TEST_RESOURCE.equals(sourceRootType)) {
+          return;
+        }
+        entry.removeSourceFolder(folder);
       }
     }
     LOG.debug(String.format("Importing %s for content root '%s' of module '%s'", root, entry.getUrl(), moduleName));
index 94543d494ec8e9510df8e2724bbb2e38ce4736c4..3e714366fe3a06f830914bc5b02afe00e2d96f1a 100644 (file)
@@ -25,26 +25,20 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.LibraryOrderEntry;
-import com.intellij.openapi.roots.ModifiableRootModel;
-import com.intellij.openapi.roots.OrderEntry;
-import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.*;
 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.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+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.io.File;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-import static com.intellij.openapi.externalSystem.model.ProjectKeys.MODULE;
+import java.util.*;
 
 /**
  * @author Denis Zhdanov
@@ -67,29 +61,6 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
     return ProjectKeys.LIBRARY_DEPENDENCY;
   }
 
-  @Override
-  public void importData(@NotNull Collection<DataNode<LibraryDependencyData>> toImport,
-                         @Nullable ProjectData projectData,
-                         @NotNull Project project,
-                         @NotNull IdeModifiableModelsProvider modelsProvider) {
-    if (toImport.isEmpty()) {
-      return;
-    }
-
-    MultiMap<DataNode<ModuleData>, DataNode<LibraryDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
-    for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<LibraryDependencyData>>> entry : byModule.entrySet()) {
-      Module module = modelsProvider.findIdeModule(entry.getKey().getData());
-      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;
-      }
-      importData(entry.getValue(), module, modelsProvider);
-    }
-  }
-
   @NotNull
   @Override
   public Class<LibraryOrderEntry> getOrderEntryType() {
@@ -97,13 +68,9 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
   }
 
   @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 IdeModifiableModelsProvider modelsProvider) {
+  protected Map<OrderEntry, OrderAware> importData(@NotNull final Collection<DataNode<LibraryDependencyData>> nodesToImport,
+                                                 @NotNull final Module module,
+                                                 @NotNull final IdeModifiableModelsProvider modelsProvider) {
     // 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.
@@ -112,6 +79,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
     final Map<Set<String>/* library paths */, LibraryDependencyData> moduleLibrariesToImport = ContainerUtilRt.newHashMap();
     final Map<String/* library name + scope */, LibraryDependencyData> projectLibrariesToImport = ContainerUtilRt.newHashMap();
     final Set<LibraryDependencyData> toImport = ContainerUtilRt.newLinkedHashSet();
+    final Map<OrderEntry, OrderAware> orderEntryDataMap = ContainerUtil.newLinkedHashMap();
 
     boolean hasUnresolved = false;
     for (DataNode<LibraryDependencyData> dependencyNode : nodesToImport) {
@@ -120,14 +88,12 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
       hasUnresolved |= libraryData.isUnresolved();
       switch (dependencyData.getLevel()) {
         case MODULE:
-          if (!libraryData.isUnresolved()) {
             Set<String> paths = ContainerUtilRt.newHashSet();
             for (String path : libraryData.getPaths(LibraryPathType.BINARY)) {
               paths.add(ExternalSystemApiUtil.toCanonicalPath(path) + dependencyData.getScope().name());
             }
             moduleLibrariesToImport.put(paths, dependencyData);
             toImport.add(dependencyData);
-          }
           break;
         case PROJECT:
           projectLibrariesToImport.put(libraryData.getInternalName() + dependencyData.getScope().name(), dependencyData);
@@ -140,15 +106,18 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
     final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module);
     LibraryTable moduleLibraryTable = modifiableRootModel.getModuleLibraryTable();
     syncExistingAndRemoveObsolete(
-      modelsProvider, moduleLibrariesToImport, projectLibrariesToImport, toImport, modifiableRootModel, finalHasUnresolved);
+      modelsProvider, moduleLibrariesToImport, projectLibrariesToImport, toImport, orderEntryDataMap, modifiableRootModel,
+      finalHasUnresolved);
     // Import missing library dependencies.
     if (!toImport.isEmpty()) {
-      importMissing(modelsProvider, toImport, modifiableRootModel, moduleLibraryTable, module);
+      importMissing(modelsProvider, toImport, orderEntryDataMap, modifiableRootModel, moduleLibraryTable, module);
     }
+    return orderEntryDataMap;
   }
 
   private void importMissing(@NotNull IdeModifiableModelsProvider modelsProvider,
                              @NotNull Set<LibraryDependencyData> toImport,
+                             @NotNull Map<OrderEntry, OrderAware> orderEntryDataMap,
                              @NotNull ModifiableRootModel moduleRootModel,
                              @NotNull LibraryTable moduleLibraryTable,
                              @NotNull Module module) {
@@ -157,17 +126,27 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
       final String libraryName = libraryData.getInternalName();
       switch (dependencyData.getLevel()) {
         case MODULE:
-          final Library moduleLib = moduleLibraryTable.createLibrary(libraryName);
-          syncExistingLibraryDependency(modelsProvider, dependencyData, moduleLib, moduleRootModel, module);
+          final Library moduleLib;
+          if (libraryName.isEmpty()) {
+            moduleLib = moduleLibraryTable.createLibrary();
+          }
+          else {
+            moduleLib = moduleLibraryTable.createLibrary(libraryName);
+          }
+          final LibraryOrderEntry existingLibraryDependency =
+            syncExistingLibraryDependency(modelsProvider, dependencyData, moduleLib, moduleRootModel, module);
+          orderEntryDataMap.put(existingLibraryDependency, dependencyData);
           break;
         case PROJECT:
           final Library projectLib = modelsProvider.getLibraryByName(libraryName);
           if (projectLib == null) {
-            syncExistingLibraryDependency(modelsProvider, dependencyData, moduleLibraryTable.createLibrary(libraryName), moduleRootModel,
-                                          module);
+            final LibraryOrderEntry existingProjectLibraryDependency = syncExistingLibraryDependency(
+              modelsProvider, dependencyData, moduleLibraryTable.createLibrary(libraryName), moduleRootModel, module);
+            orderEntryDataMap.put(existingProjectLibraryDependency, dependencyData);
             break;
           }
           LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(projectLib);
+          orderEntryDataMap.put(orderEntry, dependencyData);
           setLibraryScope(orderEntry, projectLib, module, dependencyData);
       }
     }
@@ -177,12 +156,11 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
                                       @NotNull Library lib,
                                       @NotNull Module module,
                                       @NotNull LibraryDependencyData dependencyData) {
-    LOG.debug(String.format("Adding library dependency '%s' to module '%s'", lib.getName(), module.getName()));
     orderEntry.setExported(dependencyData.isExported());
     orderEntry.setScope(dependencyData.getScope());
     LOG.debug(String.format(
-      "Configuring library dependency '%s' of module '%s' to be%s exported and have scope %s",
-      lib.getName(), module.getName(), dependencyData.isExported() ? " not" : "", dependencyData.getScope()
+      "Configuring library '%s' of module '%s' to be%s exported and have scope %s",
+      lib, module.getName(), dependencyData.isExported() ? " not" : "", dependencyData.getScope()
     ));
   }
 
@@ -190,9 +168,9 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
                                              @NotNull Map<Set<String>, LibraryDependencyData> moduleLibrariesToImport,
                                              @NotNull Map<String, LibraryDependencyData> projectLibrariesToImport,
                                              @NotNull Set<LibraryDependencyData> toImport,
+                                             @NotNull Map<OrderEntry, OrderAware> orderEntryDataMap,
                                              @NotNull ModifiableRootModel moduleRootModel,
                                              boolean hasUnresolvedLibraries) {
-    Set<String> moduleLibraryKey = ContainerUtilRt.newHashSet();
     for (OrderEntry entry : moduleRootModel.getOrderEntries()) {
       if (entry instanceof ModuleLibraryOrderEntryImpl) {
         ModuleLibraryOrderEntryImpl moduleLibraryOrderEntry = (ModuleLibraryOrderEntryImpl)entry;
@@ -201,15 +179,17 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
           LOG.warn("Skipping module-level library entry because it doesn't have backing Library object. Entry: " + entry);
           continue;
         }
-        moduleLibraryKey.clear();
-        for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
+        final VirtualFile[] libraryFiles = library.getFiles(OrderRootType.CLASSES);
+        final Set<String> moduleLibraryKey = ContainerUtilRt.newHashSet(libraryFiles.length);
+        for (VirtualFile file : libraryFiles) {
           moduleLibraryKey.add(ExternalSystemApiUtil.getLocalFileSystemPath(file) + moduleLibraryOrderEntry.getScope().name());
         }
         LibraryDependencyData existing = moduleLibrariesToImport.remove(moduleLibraryKey);
-        if (existing == null) {
+        if (existing == null || !StringUtil.equals(existing.getInternalName(), library.getName())) {
           moduleRootModel.removeOrderEntry(entry);
         }
         else {
+          orderEntryDataMap.put(entry, existing);
           syncExistingLibraryDependency(modelsProvider, existing, library, moduleRootModel, moduleLibraryOrderEntry.getOwnerModule());
           toImport.remove(existing);
         }
@@ -220,6 +200,7 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
         LibraryDependencyData existing = projectLibrariesToImport.remove(libraryName + libraryOrderEntry.getScope().name());
         if (existing != null) {
           toImport.remove(existing);
+          orderEntryDataMap.put(entry, existing);
         }
         else if (!hasUnresolvedLibraries) {
           // There is a possible case that a project has been successfully imported from external model and after
@@ -230,17 +211,43 @@ public class LibraryDependencyDataService extends AbstractDependencyDataService<
     }
   }
 
-  private void syncExistingLibraryDependency(@NotNull IdeModifiableModelsProvider modelsProvider,
-                                             @NotNull final LibraryDependencyData libraryDependencyData,
-                                             @NotNull final Library library,
-                                             @NotNull final ModifiableRootModel moduleRootModel,
-                                             @NotNull final Module module) {
+  private LibraryOrderEntry syncExistingLibraryDependency(@NotNull IdeModifiableModelsProvider modelsProvider,
+                                                          @NotNull final LibraryDependencyData libraryDependencyData,
+                                                          @NotNull final Library library,
+                                                          @NotNull final ModifiableRootModel moduleRootModel,
+                                                          @NotNull final Module module) {
     final Library.ModifiableModel libraryModel = modelsProvider.getModifiableLibraryModel(library);
     final String libraryName = libraryDependencyData.getInternalName();
     Map<OrderRootType, Collection<File>> files = myLibraryManager.prepareLibraryFiles(libraryDependencyData.getTarget());
     myLibraryManager.registerPaths(files, libraryModel, libraryName);
-    LibraryOrderEntry orderEntry = moduleRootModel.findLibraryOrderEntry(library);
+    LibraryOrderEntry orderEntry = findLibraryOrderEntry(moduleRootModel, library, libraryDependencyData.getScope());
+
     assert orderEntry != null;
     setLibraryScope(orderEntry, library, module, libraryDependencyData);
+    return orderEntry;
+  }
+
+  @Nullable
+  private static LibraryOrderEntry findLibraryOrderEntry(@NotNull ModifiableRootModel moduleRootModel,
+                                                         @NotNull Library library,
+                                                         @NotNull DependencyScope scope) {
+    LibraryOrderEntry candidate = null;
+    for (OrderEntry orderEntry : moduleRootModel.getOrderEntries()) {
+      if (orderEntry instanceof LibraryOrderEntry) {
+        final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
+        if (library == libraryOrderEntry.getLibrary()) {
+          return libraryOrderEntry;
+        }
+        if (library.equals(libraryOrderEntry.getLibrary())) {
+          if (libraryOrderEntry.getScope() == scope) {
+            return libraryOrderEntry;
+          }
+          else {
+            candidate = libraryOrderEntry;
+          }
+        }
+      }
+    }
+    return candidate;
   }
 }
index bf276197f871210c780f4bb24175815b6bf9522b..74d819042e2e22d92a5272adbf530aaf21470b0b 100644 (file)
  */
 package com.intellij.openapi.externalSystem.service.project.manage;
 
-import com.intellij.ide.util.projectWizard.ModuleBuilder;
-import com.intellij.openapi.application.ApplicationManager;
-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.OrderAware;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
-import com.intellij.openapi.externalSystem.util.*;
-import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
+import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.*;
-import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Condition;
-import com.intellij.openapi.vfs.VfsUtilCore;
-import com.intellij.ui.CheckBoxList;
-import com.intellij.ui.IdeBorderFactory;
-import com.intellij.ui.components.JBScrollPane;
-import com.intellij.util.Consumer;
-import com.intellij.util.Function;
-import com.intellij.util.SmartList;
+import com.intellij.openapi.util.io.FileUtil;
 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.util.Collection;
-import java.util.List;
+import java.util.*;
 
 /**
  * Encapsulates functionality of importing external system module to the intellij project.
@@ -59,11 +42,7 @@ import java.util.List;
  * @since 2/7/12 2:49 PM
  */
 @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");
-
-  private static final Logger LOG = Logger.getInstance("#" + ModuleDataService.class.getName());
+public class ModuleDataService extends AbstractModuleDataService<ModuleData> {
 
   @NotNull
   @Override
@@ -71,100 +50,6 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
     return ProjectKeys.MODULE;
   }
 
-  @Override
-  public void importData(@NotNull Collection<DataNode<ModuleData>> toImport,
-                         @Nullable ProjectData projectData,
-                         @NotNull Project project,
-                         @NotNull IdeModifiableModelsProvider modelsProvider) {
-    if (toImport.isEmpty()) {
-      return;
-    }
-
-    final Collection<DataNode<ModuleData>> toCreate = filterExistingModules(toImport, modelsProvider);
-    if (!toCreate.isEmpty()) {
-      createModules(toCreate, modelsProvider);
-    }
-    for (DataNode<ModuleData> node : toImport) {
-      Module module = modelsProvider.findIdeModule(node.getData());
-      if (module != null) {
-        syncPaths(module, modelsProvider, node.getData());
-      }
-    }
-  }
-
-  private static void createModules(@NotNull Collection<DataNode<ModuleData>> toCreate,
-                                    @NotNull IdeModifiableModelsProvider modelsProvider) {
-    for (final DataNode<ModuleData> module : toCreate) {
-      ModuleData data = module.getData();
-      final Module created = modelsProvider.newModule(data.getModuleFilePath(), data.getModuleTypeId());
-
-      // Ensure that the dependencies are clear (used to be not clear when manually removing the module and importing it via gradle)
-      final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(created);
-
-      modifiableRootModel.inheritSdk();
-      setModuleOptions(created, module);
-
-      RootPolicy<Object> visitor = new RootPolicy<Object>() {
-        @Override
-        public Object visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Object value) {
-          modifiableRootModel.removeOrderEntry(libraryOrderEntry);
-          return value;
-        }
-
-        @Override
-        public Object visitModuleOrderEntry(ModuleOrderEntry moduleOrderEntry, Object value) {
-          modifiableRootModel.removeOrderEntry(moduleOrderEntry);
-          return value;
-        }
-      };
-
-      for (OrderEntry orderEntry : modifiableRootModel.getOrderEntries()) {
-        orderEntry.accept(visitor, null);
-      }
-    }
-  }
-
-  @NotNull
-  private static Collection<DataNode<ModuleData>> filterExistingModules(@NotNull Collection<DataNode<ModuleData>> modules,
-                                                                        @NotNull IdeModifiableModelsProvider modelsProvider)
-  {
-    Collection<DataNode<ModuleData>> result = ContainerUtilRt.newArrayList();
-    for (DataNode<ModuleData> node : modules) {
-      ModuleData moduleData = node.getData();
-      Module module = modelsProvider.findIdeModule(moduleData);
-      if (module == null) {
-        result.add(node);
-      }
-      else {
-        setModuleOptions(module, node);
-      }
-    }
-    return result;
-  }
-
-  private static void syncPaths(@NotNull final Module module, @NotNull IdeModifiableModelsProvider modelsProvider, @NotNull final ModuleData data) {
-    final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module);
-    CompilerModuleExtension extension = modifiableRootModel.getModuleExtension(CompilerModuleExtension.class);
-    if (extension == null) {
-      modifiableRootModel.dispose();
-      final String errorMsg =
-        String.format("Can't sync paths for module '%s'. Reason: no compiler extension is found for it", module.getName());
-      throw new RuntimeException(errorMsg);
-    }
-
-    String compileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.SOURCE);
-    if (compileOutputPath != null) {
-      extension.setCompilerOutputPath(VfsUtilCore.pathToUrl(compileOutputPath));
-    }
-
-    String testCompileOutputPath = data.getCompileOutputPath(ExternalSystemSourceType.TEST);
-    if (testCompileOutputPath != null) {
-      extension.setCompilerOutputPathForTests(VfsUtilCore.pathToUrl(testCompileOutputPath));
-    }
-
-    extension.inheritCompilerOutputPath(data.isInheritProjectCompileOutputPath());
-  }
-
   @NotNull
   @Override
   public Computable<Collection<Module>> computeOrphanData(@NotNull final Collection<DataNode<ModuleData>> toImport,
@@ -178,6 +63,8 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
 
         for (Module module : modelsProvider.getModules()) {
           if (!ExternalSystemApiUtil.isExternalSystemAwareModule(projectData.getOwner(), module)) continue;
+          if (ExternalSystemApiUtil.getExternalModuleType(module) != null) continue;
+
           final String rootProjectPath = ExternalSystemApiUtil.getExternalRootProjectPath(module);
           if (projectData.getLinkedExternalProjectPath().equals(rootProjectPath)) {
             final String projectPath = ExternalSystemApiUtil.getExternalProjectPath(module);
@@ -191,7 +78,7 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
               }
             });
 
-            if (found == null) {
+            if (found == null || !FileUtil.pathsEqual(module.getModuleFilePath(), found.getData().getModuleFilePath())) {
               orphanIdeModules.add(module);
             }
           }
@@ -201,149 +88,4 @@ public class ModuleDataService extends AbstractProjectDataService<ModuleData, Mo
       }
     };
   }
-
-  @Override
-  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 IdeModifiableModelsProvider modelsProvider) {
-    final Collection<Module> toRemove = toRemoveComputable.compute();
-    final List<Module> modules = new SmartList<Module>(toRemove);
-    for (DataNode<ModuleData> moduleDataNode : toIgnore) {
-      final Module module = modelsProvider.findIdeModule(moduleDataNode.getData());
-      ContainerUtil.addIfNotNull(modules, module);
-    }
-
-    if (modules.isEmpty()) {
-      return;
-    }
-
-    ContainerUtil.removeDuplicates(modules);
-
-    for (Module module : modules) {
-      if (module.isDisposed()) continue;
-      unlinkModuleFromExternalSystem(module);
-    }
-
-    ruleOrphanModules(modules, project, projectData.getOwner(), new Consumer<List<Module>>() {
-      @Override
-      public void consume(final List<Module> modules) {
-        final ModifiableModuleModel moduleModel = modelsProvider.getModifiableModuleModel();
-        for (Module module : modules) {
-          if (module.isDisposed()) continue;
-          String path = module.getModuleFilePath();
-          moduleModel.disposeModule(module);
-          ModuleBuilder.deleteModuleFile(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);
-            }
-
-            @NotNull
-            protected Action[] createActions() {
-              return new Action[]{getOKAction()};
-            }
-          };
-
-          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);
-      }
-    });
-  }
-
-  private 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 static void setModuleOptions(Module module, DataNode<ModuleData> moduleDataNode) {
-    ModuleData moduleData = moduleDataNode.getData();
-    module.putUserData(MODULE_DATA_KEY, moduleData);
-
-    module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY, moduleData.getOwner().toString());
-    module.setOption(ExternalSystemConstants.LINKED_PROJECT_ID_KEY, moduleData.getId());
-    module.setOption(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY, moduleData.getLinkedExternalProjectPath());
-    ProjectData projectData = moduleDataNode.getData(ProjectKeys.PROJECT);
-    module.setOption(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY, projectData != null ? projectData.getLinkedExternalProjectPath() : "");
-
-    if (moduleData.getGroup() != null) {
-      module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_GROUP_KEY, moduleData.getGroup());
-    }
-    if (moduleData.getVersion() != null) {
-      module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_VERSION_KEY, moduleData.getVersion());
-    }
-
-    // clear maven option
-    module.clearOption("org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule");
-  }
 }
index 10576a317012d4f6c1e07135c87f5a6b3babc50a..5e0ffdf5a00601e85a1c2169c772ce933131465e 100644 (file)
@@ -19,26 +19,21 @@ 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.ModuleData;
-import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
-import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.model.project.*;
 import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.impl.ModuleOrderEntryImpl;
 import com.intellij.openapi.util.Pair;
+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.Map;
-
-import static com.intellij.openapi.externalSystem.model.ProjectKeys.MODULE;
+import java.util.Set;
 
 /**
  * @author Denis Zhdanov
@@ -55,25 +50,6 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
     return ProjectKeys.MODULE_DEPENDENCY;
   }
 
-  @Override
-  public void importData(@NotNull Collection<DataNode<ModuleDependencyData>> toImport,
-                         @Nullable ProjectData projectData,
-                         @NotNull Project project,
-                         @NotNull IdeModifiableModelsProvider modelsProvider) {
-    MultiMap<DataNode<ModuleData>, DataNode<ModuleDependencyData>> byModule = ExternalSystemApiUtil.groupBy(toImport, MODULE);
-    for (Map.Entry<DataNode<ModuleData>, Collection<DataNode<ModuleDependencyData>>> entry : byModule.entrySet()) {
-      Module ideModule = modelsProvider.findIdeModule(entry.getKey().getData());
-      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()
-        ));
-        continue;
-      }
-      importData(entry.getValue(), ideModule, modelsProvider);
-    }
-  }
-
   @NotNull
   @Override
   public Class<ModuleOrderEntry> getOrderEntryType() {
@@ -85,21 +61,28 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
     return orderEntry.getModuleName();
   }
 
-  private void importData(@NotNull final Collection<DataNode<ModuleDependencyData>> toImport,
-                          @NotNull final Module module,
-                          @NotNull final IdeModifiableModelsProvider modelsProvider) {
+  @Override
+  protected Map<OrderEntry, OrderAware> importData(@NotNull final Collection<DataNode<ModuleDependencyData>> toImport,
+                                                 @NotNull final Module module,
+                                                 @NotNull final IdeModifiableModelsProvider modelsProvider) {
     final Map<Pair<String /* dependency module internal name */, /* dependency module scope */DependencyScope>, ModuleOrderEntry> toRemove =
       ContainerUtilRt.newHashMap();
+    final Map<OrderEntry, OrderAware> orderEntryDataMap = ContainerUtil.newLinkedHashMap();
+
     for (OrderEntry entry : modelsProvider.getOrderEntries(module)) {
       if (entry instanceof ModuleOrderEntry) {
         ModuleOrderEntry e = (ModuleOrderEntry)entry;
         toRemove.put(Pair.create(e.getModuleName(), e.getScope()), e);
       }
     }
-
+    final Set<ModuleDependencyData> processed = ContainerUtil.newHashSet();
     final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module);
     for (DataNode<ModuleDependencyData> dependencyNode : toImport) {
       final ModuleDependencyData dependencyData = dependencyNode.getData();
+
+      if (processed.contains(dependencyData)) continue;
+      processed.add(dependencyData);
+
       toRemove.remove(Pair.create(dependencyData.getInternalName(), dependencyData.getScope()));
       final String moduleName = dependencyData.getInternalName();
       Module ideDependencyModule = modelsProvider.findIdeModule(moduleName);
@@ -126,10 +109,22 @@ public class ModuleDependencyDataService extends AbstractDependencyDataService<M
 
       orderEntry.setScope(dependencyData.getScope());
       orderEntry.setExported(dependencyData.isExported());
+
+      final boolean productionOnTestDependency = dependencyData.isProductionOnTestDependency();
+      if (orderEntry instanceof ModuleOrderEntryImpl) {
+        ((ModuleOrderEntryImpl)orderEntry).setProductionOnTestDependency(productionOnTestDependency);
+      }
+      else if (productionOnTestDependency) {
+        LOG.warn("Unable to set productionOnTestDependency for entry: " + orderEntry);
+      }
+
+      orderEntryDataMap.put(orderEntry, dependencyData);
     }
 
     if (!toRemove.isEmpty()) {
       removeData(toRemove.values(), module, modelsProvider);
     }
+
+    return orderEntryDataMap;
   }
 }
index 4d0a2d811ada697a84b107d19a29b305f888916c..10f3d55e5c7a7a9144c498886a559c80828c44dc 100644 (file)
@@ -86,6 +86,7 @@ public class ProjectDataManager {
     };
   }
 
+  @Deprecated // to be removed in 15.1
   @Nullable
   public ProjectDataService<?, ?> getDataService(Key<?> key) {
     final List<ProjectDataService<?, ?>> dataServices = myServices.getValue().get(key);
@@ -129,6 +130,7 @@ public class ProjectDataManager {
       ExternalSystemUtil.scheduleExternalViewStructureUpdate(project, projectSystemId);
     }
 
+    List<Runnable> onSuccessImportTasks = ContainerUtil.newSmartList();
     try {
       final Set<Map.Entry<Key<?>, Collection<DataNode<?>>>> entries = grouped.entrySet();
       final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
@@ -146,7 +148,7 @@ public class ProjectDataManager {
           indicator.setText(message);
           indicator.setFraction((double)count++ / size);
         }
-        doImportData(entry.getKey(), entry.getValue(), projectData, project, modelsProvider, postImportTasks);
+        doImportData(entry.getKey(), entry.getValue(), projectData, project, modelsProvider, postImportTasks, onSuccessImportTasks);
       }
 
       for (Runnable postImportTask : ContainerUtil.reverse(postImportTasks)) {
@@ -162,6 +164,10 @@ public class ProjectDataManager {
       dispose(modelsProvider, project, synchronous);
       ExceptionUtil.rethrowAllAsUnchecked(t);
     }
+
+    for (Runnable onSuccessImportTask : ContainerUtil.reverse(onSuccessImportTasks)) {
+      onSuccessImportTask.run();
+    }
   }
 
   @NotNull
@@ -212,7 +218,8 @@ public class ProjectDataManager {
                                 @Nullable final ProjectData projectData,
                                 @NotNull final Project project,
                                 @NotNull final IdeModifiableModelsProvider modelsProvider,
-                                @NotNull List<Runnable> postImportTasks) {
+                                @NotNull final List<Runnable> postImportTasks,
+                                @NotNull final List<Runnable> onSuccessImportTasks) {
     if (project.isDisposed()) return;
     if (project instanceof ProjectImpl) {
       assert ((ProjectImpl)project).isComponentsCreated();
@@ -266,6 +273,33 @@ public class ProjectDataManager {
           }
         }
       });
+
+      postImportTasks.add(new Runnable() {
+        @Override
+        public void run() {
+          for (ProjectDataService<?, ?> service : services) {
+            if (service instanceof AbstractProjectDataService) {
+              final long taskStartTime = System.currentTimeMillis();
+              ((AbstractProjectDataService)service).postProcess(toImport, projectData, project, modelsProvider);
+              final long taskTimeInMs = (System.currentTimeMillis() - taskStartTime);
+              LOG.debug(String.format("Service %s run post import task in %d ms", service.getClass().getSimpleName(), taskTimeInMs));
+            }
+          }
+        }
+      });
+      onSuccessImportTasks.add(new Runnable() {
+        @Override
+        public void run() {
+          for (ProjectDataService<?, ?> service : services) {
+            if (service instanceof AbstractProjectDataService) {
+              final long taskStartTime = System.currentTimeMillis();
+              ((AbstractProjectDataService)service).onSuccessImport(project);
+              final long taskTimeInMs = (System.currentTimeMillis() - taskStartTime);
+              LOG.debug(String.format("Service %s run post import task in %d ms", service.getClass().getSimpleName(), taskTimeInMs));
+            }
+          }
+        }
+      });
     }
   }
 
index 915bdca243f03ec7b19008ade543491ecfd1de1a..cf666a0e4cbc8dc20482a6bcd682109be7ab8967 100644 (file)
@@ -27,6 +27,7 @@ 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.Identifiable;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
 import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
 import com.intellij.openapi.externalSystem.model.project.ProjectData;
@@ -66,10 +67,8 @@ import javax.swing.tree.DefaultTreeModel;
 import javax.swing.tree.TreeNode;
 import javax.swing.tree.TreePath;
 import java.awt.*;
-import java.util.Collection;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * @author Vladislav.Soroka
@@ -95,11 +94,13 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
   private ExternalProjectInfo myProjectInfo;
   private final Set<Key<?>> myIgnorableKeys;
   private final Set<Key<?>> myPublicKeys;
+  private final Set<Key<? extends Identifiable>> myDependencyAwareDataKeys;
   @Nullable
   private final Object myPreselectedNodeObject;
   private CheckboxTree myTree;
   @SuppressWarnings("unchecked")
-  private final MultiMap<DataNode<ModuleData>, DataNode<ModuleData>> dependentNodeMap = MultiMap.create(TObjectHashingStrategy.IDENTITY);
+  private final MultiMap<DataNode<Identifiable>, DataNode<Identifiable>> dependentNodeMap =
+    MultiMap.create(TObjectHashingStrategy.IDENTITY);
 
   private final SimpleModificationTracker myModificationTracker = new SimpleModificationTracker();
   private final CachedValue<SelectionState> selectionState = new CachedValueImpl<SelectionState>(new CachedValueProvider<SelectionState>() {
@@ -125,6 +126,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
     myProject = project;
     myIgnorableKeys = getIgnorableKeys();
     myPublicKeys = getPublicKeys();
+    myDependencyAwareDataKeys = getDependencyAwareDataKeys();
     myPreselectedNodeObject = preselectedNodeDataObject;
     init(projectInfo);
   }
@@ -282,19 +284,31 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
 
         String tooltip = null;
         boolean hasErrors = false;
-        if (node.isChecked() && node.myDataNode.getKey().equals(ProjectKeys.MODULE)) {
-          //noinspection unchecked
-          final String listOfUncheckedDependencies =
-            StringUtil.join(dependentNodeMap.get((DataNode<ModuleData>)node.myDataNode), new Function<DataNode<ModuleData>, String>() {
-              @Override
-              public String fun(final DataNode<ModuleData> depNode) {
-                final DataNodeCheckedTreeNode uiNode = depNode.getUserData(CONNECTED_UI_NODE_KEY);
-                return uiNode != null && !uiNode.isChecked() ? depNode.getData().getId() : null;
-              }
-            }, "<br>");
-          if (StringUtil.isNotEmpty(listOfUncheckedDependencies)) {
-            hasErrors = true;
-            tooltip = "There are not selected module dependencies of the module: <br><b>" + listOfUncheckedDependencies + "</b>";
+        if (node.isChecked()) {
+          final Enumeration children = node.children();
+          while (children.hasMoreElements()) {
+            final Object o = children.nextElement();
+            if (o instanceof DataNodeCheckedTreeNode && !((DataNodeCheckedTreeNode)o).isChecked()) {
+              myCheckbox.setEnabled(false);
+              break;
+            }
+          }
+
+          if (myDependencyAwareDataKeys.contains(node.myDataNode.getKey())) {
+            //noinspection unchecked
+            final String listOfUncheckedDependencies =
+              StringUtil
+                .join(dependentNodeMap.get((DataNode<Identifiable>)node.myDataNode), new Function<DataNode<Identifiable>, String>() {
+                  @Override
+                  public String fun(final DataNode<Identifiable> depNode) {
+                    final DataNodeCheckedTreeNode uiNode = depNode.getUserData(CONNECTED_UI_NODE_KEY);
+                    return uiNode != null && !uiNode.isChecked() ? depNode.getData().getId() : null;
+                  }
+                }, "<br>");
+            if (StringUtil.isNotEmpty(listOfUncheckedDependencies)) {
+              hasErrors = true;
+              tooltip = "There are not selected module dependencies of the module: <br><b>" + listOfUncheckedDependencies + "</b>";
+            }
           }
         }
 
@@ -335,14 +349,14 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
     final DataNodeCheckedTreeNode[] rootModuleNode = {null};
 
     final MultiMap<String, String> moduleDependenciesMap = MultiMap.create();
-    final Map<String, DataNode<ModuleData>> modulesNodeMap = ContainerUtil.newHashMap();
+    final Map<String, DataNode<Identifiable>> 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());
+        moduleDependencyData.getOwnerModule().getId(),
+        moduleDependencyData.getTarget().getId());
     }
 
     final int[] modulesCount = {0};
@@ -355,7 +369,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
 
         DataNode modifiableDataNode = getModifiableDataNode(node);
 
-        if (node.getKey().equals(ProjectKeys.MODULE)) {
+        if (myDependencyAwareDataKeys.contains(key)) {
           modulesCount[0]++;
         }
 
@@ -365,17 +379,17 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
         if (treeNode == null) {
           treeNode = new DataNodeCheckedTreeNode(node);
 
-          if (node.getKey().equals(ProjectKeys.MODULE)) {
-            final ModuleData moduleData = (ModuleData)node.getData();
+          if (myDependencyAwareDataKeys.contains(key)) {
+            final Identifiable moduleData = (Identifiable)node.getData();
             //noinspection unchecked
-            modulesNodeMap.put(moduleData.getLinkedExternalProjectPath(), (DataNode<ModuleData>)node);
+            modulesNodeMap.put(moduleData.getId(), (DataNode<Identifiable>)node);
           }
 
           if (myPreselectedNodeObject != null && myPreselectedNodeObject.equals(node.getData())) {
             preselectedNode[0] = treeNode;
           }
           if (node.getData() instanceof ModuleData) {
-            if (myProjectInfo.getExternalProjectPath().equals(((ModuleData)node.getData()).getLinkedExternalProjectPath())) {
+            if (key.equals(ProjectKeys.MODULE) && myProjectInfo.getExternalProjectPath().equals(((ModuleData)node.getData()).getLinkedExternalProjectPath())) {
               rootModuleNode[0] = treeNode;
             }
           }
@@ -395,13 +409,13 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
     myModulesCount = modulesCount[0];
 
     dependentNodeMap.clear();
-    for (String modulePath : moduleDependenciesMap.keySet()) {
-      final Collection<String> moduleDependencies = moduleDependenciesMap.get(modulePath);
-      final DataNode<ModuleData> moduleNode = modulesNodeMap.get(modulePath);
+    for (String moduleId : moduleDependenciesMap.keySet()) {
+      final Collection<String> moduleDependencies = moduleDependenciesMap.get(moduleId);
+      final DataNode<Identifiable> moduleNode = modulesNodeMap.get(moduleId);
       if (moduleNode != null) {
-        dependentNodeMap.putValues(moduleNode, ContainerUtil.mapNotNull(moduleDependencies, new Function<String, DataNode<ModuleData>>() {
+        dependentNodeMap.putValues(moduleNode, ContainerUtil.mapNotNull(moduleDependencies, new Function<String, DataNode<Identifiable>>() {
           @Override
-          public DataNode<ModuleData> fun(String s) {
+          public DataNode<Identifiable> fun(String s) {
             return modulesNodeMap.get(s);
           }
         }));
@@ -413,7 +427,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
 
     if (rootModuleNode[0] != null && projectNode != null) {
       rootModuleNode[0].comment = "root module";
-      projectNode.remove(rootModuleNode[0]);
+      if (projectNode.isNodeChild(rootModuleNode[0])) projectNode.remove(rootModuleNode[0]);
       projectNode.insert(rootModuleNode[0], 0);
     }
 
@@ -440,6 +454,16 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
     return result;
   }
 
+  @NotNull
+  private static Set<Key<? extends Identifiable>> getDependencyAwareDataKeys() {
+    Set<Key<? extends Identifiable>> result = ContainerUtil.newHashSet();
+    result.add(ProjectKeys.MODULE);
+    for (ExternalProjectStructureCustomizer customizer : ExternalProjectStructureCustomizer.EP_NAME.getExtensions()) {
+      result.addAll(customizer.getDependencyAwareDataKeys());
+    }
+    return result;
+  }
+
   private class DataNodeCheckedTreeNode extends CheckedTreeNode {
     private final DataNode myDataNode;
     @Nullable
@@ -494,7 +518,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
         DataNodeCheckedTreeNode parent = this;
         DataNodeCheckedTreeNode moduleNode = null;
         while (parent.parent instanceof DataNodeCheckedTreeNode) {
-          if (moduleNode == null && ProjectKeys.MODULE.equals(parent.myDataNode.getKey())) {
+          if (moduleNode == null && (myDependencyAwareDataKeys.contains(parent.myDataNode.getKey()))) {
             moduleNode = parent;
           }
           parent = (DataNodeCheckedTreeNode)parent.parent;
@@ -548,11 +572,11 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
         DataNodeCheckedTreeNode.class, new Tree.NodeFilter<DataNodeCheckedTreeNode>() {
           @Override
           public boolean accept(DataNodeCheckedTreeNode node) {
-            return node.myDataNode.getKey().equals(ProjectKeys.MODULE) && checked != node.isChecked();
+            return myDependencyAwareDataKeys.contains(node.myDataNode.getKey()) && checked != node.isChecked();
           }
         });
 
-      boolean isCheckCompleted = unprocessedNodes.length == 0 && myDataNode.getKey().equals(ProjectKeys.MODULE);
+      boolean isCheckCompleted = unprocessedNodes.length == 0 && myDependencyAwareDataKeys.contains(myDataNode.getKey());
 
       updateSelectionState();
 
@@ -562,31 +586,31 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
     }
 
     private void warnAboutMissedDependencies(boolean checked) {
-      List<DataNode<ModuleData>> selectedModules = ContainerUtil.newSmartList();
+      List<DataNode<Identifiable>> selectedModules = ContainerUtil.newSmartList();
       for (DataNode node : TreeUtil.collectSelectedObjectsOfType(myTree, DataNode.class)) {
-        if (node.getKey().equals(ProjectKeys.MODULE)) {
+        if (myDependencyAwareDataKeys.contains(node.getKey())) {
           //noinspection unchecked
           selectedModules.add(node);
         }
       }
 
-      final Set<DataNode<ModuleData>> deps = ContainerUtil.newHashSet();
-      for (DataNode<ModuleData> selectedModule : selectedModules) {
+      final Set<DataNode<Identifiable>> deps = ContainerUtil.newHashSet();
+      for (DataNode<Identifiable> selectedModule : selectedModules) {
         if (checked) {
-          deps.addAll(ContainerUtil.filter(dependentNodeMap.get(selectedModule), new Condition<DataNode<ModuleData>>() {
+          deps.addAll(ContainerUtil.filter(dependentNodeMap.get(selectedModule), new Condition<DataNode<Identifiable>>() {
             @Override
-            public boolean value(DataNode<ModuleData> node) {
+            public boolean value(DataNode<Identifiable> node) {
               final DataNodeCheckedTreeNode uiNode = node.getUserData(CONNECTED_UI_NODE_KEY);
               return uiNode != null && !uiNode.isChecked();
             }
           }));
         }
         else {
-          for (DataNode<ModuleData> node : dependentNodeMap.keySet()) {
+          for (DataNode<Identifiable> node : dependentNodeMap.keySet()) {
             final DataNodeCheckedTreeNode uiNode = node.getUserData(CONNECTED_UI_NODE_KEY);
             if (uiNode != null && !uiNode.isChecked()) continue;
 
-            Collection<DataNode<ModuleData>> dependencies = dependentNodeMap.get(node);
+            Collection<DataNode<Identifiable>> dependencies = dependentNodeMap.get(node);
             if (dependencies.contains(selectedModule)) {
               deps.add(node);
             }
@@ -595,16 +619,16 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
       }
 
       if (!deps.isEmpty() && !selectedModules.isEmpty()) {
-        final String listOfSelectedModules = StringUtil.join(selectedModules, new Function<DataNode<ModuleData>, String>() {
+        final String listOfSelectedModules = StringUtil.join(selectedModules, new Function<DataNode<Identifiable>, String>() {
           @Override
-          public String fun(DataNode<ModuleData> node) {
+          public String fun(DataNode<Identifiable> node) {
             return node.getData().getId();
           }
         }, ", ");
 
-        final String listOfDependencies = StringUtil.join(deps, new Function<DataNode<ModuleData>, String>() {
+        final String listOfDependencies = StringUtil.join(deps, new Function<DataNode<Identifiable>, String>() {
           @Override
-          public String fun(final DataNode<ModuleData> node) {
+          public String fun(final DataNode<Identifiable> node) {
             return node.getData().getId();
           }
         }, "<br>");
@@ -613,7 +637,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
         if (!checked) {
           message = String.format(
             "<html>The following module%s <br><b>%s</b><br>%s enabled and depend%s on selected modules. <br>Would you like to disable %s too?</html>",
-            deps.size() == 1 ? "" : "s", listOfDependencies, deps.size() == 1 ? "is" : "are", deps.size() == 1 ? "" : "s",
+            deps.size() == 1 ? "" : "s", listOfDependencies, deps.size() == 1 ? "is" : "are", deps.size() == 1 ? "s" : "",
             deps.size() == 1 ? "it" : "them");
         }
         else {
@@ -625,9 +649,9 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
         if (Messages.showOkCancelDialog(message, checked ? "Enable Dependant Modules" : "Disable Modules with Dependency on this",
                                         Messages.getQuestionIcon()) == Messages.OK) {
           List<DataNodeCheckedTreeNode> nodes =
-            ContainerUtil.mapNotNull(deps, new Function<DataNode<ModuleData>, DataNodeCheckedTreeNode>() {
+            ContainerUtil.mapNotNull(deps, new Function<DataNode<Identifiable>, DataNodeCheckedTreeNode>() {
               @Override
-              public DataNodeCheckedTreeNode fun(DataNode<ModuleData> node) {
+              public DataNodeCheckedTreeNode fun(DataNode<Identifiable> node) {
                 return node.getUserData(CONNECTED_UI_NODE_KEY);
               }
             });
@@ -668,7 +692,7 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
         public boolean accept(Object node) {
           if (node instanceof DataNodeCheckedTreeNode &&
               ((DataNodeCheckedTreeNode)node).isChecked() &&
-              ((DataNodeCheckedTreeNode)node).myDataNode.getKey().equals(ProjectKeys.MODULE)) {
+              myDependencyAwareDataKeys.contains((((DataNodeCheckedTreeNode)node).myDataNode.getKey()))) {
             selectedModulesCount[0]++;
           }
           return true;
@@ -681,12 +705,12 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
   }
 
   private boolean computeRequiredSelectionStatus() {
-    for (DataNode<ModuleData> node : dependentNodeMap.keySet()) {
+    for (DataNode<Identifiable> 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)) {
+      for (DataNode<Identifiable> depNode : dependentNodeMap.get(node)) {
         final DataNodeCheckedTreeNode uiDependentNode = depNode.getUserData(CONNECTED_UI_NODE_KEY);
         assert uiDependentNode != null;
         if (!uiDependentNode.isChecked()) return true;
@@ -791,12 +815,12 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper {
       }
 
       myTree.clearSelection();
-      for (DataNode<ModuleData> node : dependentNodeMap.keySet()) {
+      for (DataNode<Identifiable> 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)) {
+        for (DataNode<Identifiable> treeNode : dependentNodeMap.get(node)) {
           final DataNodeCheckedTreeNode uiDependentNode = treeNode.getUserData(CONNECTED_UI_NODE_KEY);
           assert uiDependentNode != null;
           myTree.setNodeState(uiDependentNode, true);
index 0d96db89231d6d63405167b698b2cbd0e58ecade..1530f40303b30f79cadced5613cbb43b1f48a4b8 100644 (file)
@@ -155,10 +155,15 @@ public class ExternalProjectsStructure extends SimpleTreeStructure implements Di
   private void doMergeChildrenChanges(ExternalSystemNode currentNode, DataNode<?> newDataNode, ExternalSystemNode newNode) {
     final ExternalSystemNode[] cached = currentNode.getCached();
     if (cached != null) {
-      Map<Object, ExternalSystemNode> oldDataMap = ContainerUtil.newLinkedHashMap();
+
+      final List<Object> duplicates = ContainerUtil.newArrayList();
+      final Map<Object, ExternalSystemNode> oldDataMap = ContainerUtil.newLinkedHashMap();
       for (ExternalSystemNode node : cached) {
         Object key = node.getData() != null ? node.getData() : node.getName();
-        oldDataMap.put(key, node);
+        final Object systemNode = oldDataMap.put(key, node);
+        if(systemNode != null) {
+          duplicates.add(key);
+        }
       }
 
       Map<Object, ExternalSystemNode> newDataMap = ContainerUtil.newLinkedHashMap();
@@ -173,6 +178,10 @@ public class ExternalProjectsStructure extends SimpleTreeStructure implements Di
         }
       }
 
+      for (Object duplicate : duplicates) {
+        newDataMap.remove(duplicate);
+      }
+
       currentNode.removeAll(oldDataMap.values());
 
       for (ExternalSystemNode node : currentNode.getChildren()) {
index 4488302e65e45ed46f3406fb3e37fe2f8134e899..d6cd609bb8cac52e5d7040d39b123598192efa65 100644 (file)
@@ -422,14 +422,14 @@ public class ExternalProjectsViewImpl extends SimpleToolWindowPanel implements D
     final List<ExternalSystemNode<?>> result = new SmartList<ExternalSystemNode<?>>();
     final MultiMap<Key<?>, DataNode<?>> groups = ExternalSystemApiUtil.group(dataNode.getChildren());
     for (ExternalSystemViewContributor contributor : ExternalSystemViewContributor.EP_NAME.getExtensions()) {
-      Set<Key<?>> keys = ContainerUtil.newTreeSet(contributor.getKeys());
+      if (!contributor.getSystemId().equals(ProjectSystemId.IDE) &&
+          !contributor.getSystemId().equals(externalProjectsView.getSystemId())) {
+        continue;
+      }
 
-      final MultiMap<Key<?>, DataNode<?>> dataNodes = MultiMap.create();
-      for (Key<?> key : keys) {
-        final Collection<DataNode<?>> values = groups.get(key);
-        if(key != null) {
-          dataNodes.put(key, values);
-        }
+      final MultiMap<Key<?>, DataNode<?>> dataNodes = new ContainerUtil.KeyOrderedMultiMap<Key<?>, DataNode<?>>();
+      for (Key<?> key : contributor.getKeys()) {
+        ContainerUtil.putIfNotNull(key, groups.get(key), dataNodes);
       }
 
       if (dataNodes.isEmpty()) continue;
index 9ffbf27494c16c383a2003889fa34c944c88885e..454d050872562e82939097ed8d0b163fb4bdfd97 100644 (file)
@@ -77,6 +77,7 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
   private ExternalSystemNode myParent;
   private ExternalSystemNode[] myChildren;
   private ExternalProjectsStructure.ErrorLevel myErrorLevel = ExternalProjectsStructure.ErrorLevel.NONE;
+  private final List<String> myErrors = ContainerUtil.newArrayList();
   private ExternalProjectsStructure.ErrorLevel myTotalErrorLevel = null;
 
   public ExternalSystemNode(@NotNull ExternalProjectsView externalProjectsView,
@@ -225,7 +226,7 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
     Collections.sort(list, ORDER_AWARE_COMPARATOR);
   }
 
-  protected boolean addAll(Collection<? extends ExternalSystemNode> externalSystemNodes) {
+  public boolean addAll(Collection<? extends ExternalSystemNode> externalSystemNodes) {
     return addAll(externalSystemNodes, false);
   }
 
@@ -243,11 +244,11 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
     return true;
   }
 
-  protected boolean add(ExternalSystemNode externalSystemNode) {
+  public boolean add(ExternalSystemNode externalSystemNode) {
     return addAll(ContainerUtil.list(externalSystemNode));
   }
 
-  protected boolean removeAll(Collection<ExternalSystemNode> externalSystemNodes) {
+  public boolean removeAll(Collection<ExternalSystemNode> externalSystemNodes) {
     return removeAll(externalSystemNodes, false);
   }
 
@@ -327,9 +328,11 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
     return result;
   }
 
-  public void setErrorLevel(ExternalProjectsStructure.ErrorLevel level) {
+  public void setErrorLevel(ExternalProjectsStructure.ErrorLevel level, String... errors) {
     if (myErrorLevel == level) return;
     myErrorLevel = level;
+    myErrors.clear();
+    Collections.addAll(myErrors, errors);
     myExternalProjectsView.updateUpTo(this);
   }
 
@@ -354,7 +357,8 @@ public abstract class ExternalSystemNode<T> extends SimpleNode implements Compar
   protected void setNameAndTooltip(String name, @Nullable String tooltip, SimpleTextAttributes attributes) {
     clearColoredText();
     addColoredFragment(name, prepareAttributes(attributes));
-    getTemplatePresentation().setTooltip(tooltip);
+    final String s = (tooltip != null ? tooltip + "\n\r" : "") + StringUtil.join(myErrors, "\n\r");
+    getTemplatePresentation().setTooltip(s);
   }
 
   private SimpleTextAttributes prepareAttributes(SimpleTextAttributes from) {
index 7fee2128429c2236b7d1b88cf6096a3fccac47bc..fc8515accd9639b57437a88a39dc69cc7ffe8ea2 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.openapi.externalSystem.view;
 import com.intellij.openapi.extensions.ExtensionPointName;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
 
@@ -31,6 +32,9 @@ public abstract class ExternalSystemViewContributor {
   public static final ExtensionPointName<ExternalSystemViewContributor> EP_NAME =
     ExtensionPointName.create("com.intellij.externalSystemViewContributor");
 
+  @NotNull
+  public abstract ProjectSystemId getSystemId();
+
   @NotNull
   public abstract List<Key<?>> getKeys();
 
index 02d569af32fc648380cb59cad0006bc23c25eef6..d6cec494d45c6bbb337f3ca9d4ca2a794a56b289 100644 (file)
@@ -20,10 +20,9 @@ import com.intellij.ide.projectView.PresentationData;
 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.service.project.IdeModifiableModelsProviderImpl;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.model.project.*;
+import com.intellij.openapi.externalSystem.service.project.IdeModelsProviderImpl;
 import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings;
 import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
@@ -31,16 +30,23 @@ import com.intellij.openapi.externalSystem.util.Order;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.OrderEntry;
 import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.pom.Navigatable;
+import com.intellij.util.ObjectUtils;
 import com.intellij.util.SmartList;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.io.File;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+
+import static com.intellij.openapi.externalSystem.model.ProjectKeys.PROJECT;
 
 /**
  * @author Vladislav.Soroka
@@ -55,6 +61,11 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
     ProjectKeys.TASK
   };
 
+  @NotNull
+  @Override
+  public ProjectSystemId getSystemId() {
+    return ProjectSystemId.IDE;
+  }
 
   @NotNull
   @Override
@@ -70,11 +81,15 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
 
     addModuleNodes(externalProjectsView, dataNodes, result);
     // add tasks
-    TasksNode tasksNode = new TasksNode(externalProjectsView, dataNodes.get(ProjectKeys.TASK));
-    if(externalProjectsView.useTasksNode()) {
-      result.add(tasksNode);
-    } else {
-      ContainerUtil.addAll(result, tasksNode.getChildren());
+    Collection<DataNode<?>> tasksNodes = dataNodes.get(ProjectKeys.TASK);
+    if (!tasksNodes.isEmpty()) {
+      TasksNode tasksNode = new TasksNode(externalProjectsView, tasksNodes);
+      if (externalProjectsView.useTasksNode()) {
+        result.add(tasksNode);
+      }
+      else {
+        ContainerUtil.addAll(result, tasksNode.getChildren());
+      }
     }
 
     addDependenciesNode(externalProjectsView, dataNodes, result);
@@ -94,7 +109,14 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
       for (DataNode<?> dataNode : moduleDeps) {
         if (!(dataNode.getData() instanceof ModuleDependencyData)) continue;
         //noinspection unchecked
-        depNode.add(new ModuleDependencyDataExternalSystemNode(externalProjectsView, (DataNode<ModuleDependencyData>)dataNode));
+        ModuleDependencyDataExternalSystemNode moduleDependencyDataExternalSystemNode =
+          new ModuleDependencyDataExternalSystemNode(externalProjectsView, (DataNode<ModuleDependencyData>)dataNode);
+        if (dataNode.getParent() != null && dataNode.getParent().getData() instanceof ModuleDependencyData) {
+          result.add(moduleDependencyDataExternalSystemNode);
+        }
+        else {
+          depNode.add(moduleDependencyDataExternalSystemNode);
+        }
       }
 
       for (DataNode<?> dataNode : libDeps) {
@@ -102,14 +124,25 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
         //noinspection unchecked
         final ExternalSystemNode<LibraryDependencyData> libraryDependencyDataExternalSystemNode =
           new LibraryDependencyDataExternalSystemNode(externalProjectsView, (DataNode<LibraryDependencyData>)dataNode);
-
-        depNode.add(libraryDependencyDataExternalSystemNode);
-        libraryDependencyDataExternalSystemNode.setErrorLevel(((LibraryDependencyData)dataNode.getData()).getTarget().isUnresolved()
-                                                              ? ExternalProjectsStructure.ErrorLevel.ERROR
-                                                              : ExternalProjectsStructure.ErrorLevel.NONE);
+        if (((LibraryDependencyData)dataNode.getData()).getTarget().isUnresolved()) {
+          libraryDependencyDataExternalSystemNode.setErrorLevel(
+            ExternalProjectsStructure.ErrorLevel.ERROR,
+            "Unable to resolve " + ((LibraryDependencyData)dataNode.getData()).getTarget().getExternalName());
+        }
+        else {
+          libraryDependencyDataExternalSystemNode.setErrorLevel(ExternalProjectsStructure.ErrorLevel.NONE);
+        }
+        if (dataNode.getParent() != null && dataNode.getParent().getData() instanceof ModuleData) {
+          depNode.add(libraryDependencyDataExternalSystemNode);
+        }
+        else {
+          result.add(libraryDependencyDataExternalSystemNode);
+        }
       }
 
-      result.add(depNode);
+      if (depNode.hasChildren()) {
+        result.add(depNode);
+      }
     }
   }
 
@@ -125,8 +158,10 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
         final ModuleData data = (ModuleData)dataNode.getData();
 
         final ExternalProjectSettings projectSettings = systemSettings.getLinkedProjectSettings(data.getLinkedExternalProjectPath());
+        DataNode<ProjectData> projectDataNode = ExternalSystemApiUtil.findParent(dataNode, PROJECT);
         final boolean isRoot =
-          projectSettings != null && data.getLinkedExternalProjectPath().equals(projectSettings.getExternalProjectPath());
+          projectSettings != null && data.getLinkedExternalProjectPath().equals(projectSettings.getExternalProjectPath()) &&
+          projectDataNode != null && projectDataNode.getData().getInternalName().equals(data.getInternalName());
         //noinspection unchecked
         final ModuleNode moduleNode = new ModuleNode(externalProjectsView, (DataNode<ModuleData>)dataNode, isRoot);
         result.add(moduleNode);
@@ -153,7 +188,66 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
     }
   }
 
-  private static class ModuleDependencyDataExternalSystemNode extends ExternalSystemNode<ModuleDependencyData> {
+  private static abstract class DependencyDataExternalSystemNode<T extends DependencyData> extends ExternalSystemNode<T> {
+
+    public DependencyDataExternalSystemNode(@NotNull ExternalProjectsView externalProjectsView,
+                                            @Nullable ExternalSystemNode parent,
+                                            @Nullable DataNode<T> dataNode) {
+      super(externalProjectsView, parent, dataNode);
+    }
+
+    @Nullable
+    @Override
+    public Navigatable getNavigatable() {
+      return new Navigatable() {
+        @Nullable
+        private OrderEntry myOrderEntry;
+
+        @Override
+        public void navigate(boolean requestFocus) {
+          if (myOrderEntry != null) {
+            ProjectSettingsService.getInstance(myProject).openModuleDependenciesSettings(myOrderEntry.getOwnerModule(), myOrderEntry);
+          }
+        }
+
+        @Override
+        public boolean canNavigate() {
+          myOrderEntry = getOrderEntry();
+          return myOrderEntry != null;
+        }
+
+        @Override
+        public boolean canNavigateToSource() {
+          return true;
+        }
+      };
+    }
+
+    @Nullable
+    private OrderEntry getOrderEntry() {
+      final T data = getData();
+      if (data == null) return null;
+      final Project project = getProject();
+      if (project == null) return null;
+      return new IdeModelsProviderImpl(project).findIdeModuleOrderEntry(data);
+    }
+
+    @Override
+    public int compareTo(@NotNull ExternalSystemNode node) {
+      final T myData = getData();
+      final Object thatData = node.getData();
+      if (myData instanceof OrderAware && thatData instanceof OrderAware) {
+        int order1 = ((OrderAware)myData).getOrder();
+        int order2 = ((OrderAware)thatData).getOrder();
+        if (order1 != order2) {
+          return order1 < order2 ? -1 : 1;
+        }
+      }
+      return super.compareTo(node);
+    }
+  }
+
+  private static class ModuleDependencyDataExternalSystemNode extends DependencyDataExternalSystemNode<ModuleDependencyData> {
 
     public ModuleDependencyDataExternalSystemNode(ExternalProjectsView externalProjectsView, DataNode<ModuleDependencyData> dataNode) {
       super(externalProjectsView, null, dataNode);
@@ -178,11 +272,11 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
 
     @Override
     public boolean isAlwaysLeaf() {
-      return true;
+      return false;
     }
   }
 
-  private static class LibraryDependencyDataExternalSystemNode extends ExternalSystemNode<LibraryDependencyData> {
+  private static class LibraryDependencyDataExternalSystemNode extends DependencyDataExternalSystemNode<LibraryDependencyData> {
 
     public LibraryDependencyDataExternalSystemNode(ExternalProjectsView externalProjectsView, DataNode<LibraryDependencyData> dataNode) {
       super(externalProjectsView, null, dataNode);
@@ -202,48 +296,32 @@ public class ExternalSystemViewDefaultContributor extends ExternalSystemViewCont
     @Override
     public String getName() {
       final LibraryDependencyData data = getData();
-      return data != null ? data.getExternalName() : "";
-    }
-
-    @Override
-    public boolean isAlwaysLeaf() {
-      return true;
-    }
-
-    @Nullable
-    @Override
-    public Navigatable getNavigatable() {
-      return new Navigatable() {
-        @Nullable
-        private OrderEntry myOrderEntry;
-
-        @Override
-        public void navigate(boolean requestFocus) {
-          if (myOrderEntry != null) {
-            ProjectSettingsService.getInstance(myProject).openLibraryOrSdkSettings(myOrderEntry);
+      if (data == null) return "";
+      String externalName = data.getExternalName();
+      if (StringUtil.isEmpty(externalName)) {
+        Set<String> paths = data.getTarget().getPaths(LibraryPathType.BINARY);
+        if (paths.size() == 1) {
+          String relativePathToRoot = null;
+          String path = ExternalSystemApiUtil.toCanonicalPath(paths.iterator().next());
+          DataNode<ProjectData> projectDataDataNode = ExternalSystemApiUtil.findParent(myDataNode, PROJECT);
+          if (projectDataDataNode != null) {
+            relativePathToRoot = FileUtil.getRelativePath(projectDataDataNode.getData().getLinkedExternalProjectPath(), path, '/');
+            relativePathToRoot = relativePathToRoot != null && StringUtil.startsWith(relativePathToRoot, "../../")
+                                 ? new File(relativePathToRoot).getName()
+                                 : relativePathToRoot;
           }
+          return ObjectUtils.notNull(relativePathToRoot, path);
         }
-
-        @Override
-        public boolean canNavigate() {
-          myOrderEntry = getOrderEntry();
-          return myOrderEntry != null;
+        else {
+          return "<file set>";
         }
-
-        @Override
-        public boolean canNavigateToSource() {
-          return true;
-        }
-      };
+      }
+      return externalName;
     }
 
-    @Nullable
-    private OrderEntry getOrderEntry() {
-      final LibraryDependencyData data = getData();
-      if (data == null) return null;
-      final Project project = getProject();
-      if (project == null) return null;
-      return new IdeModifiableModelsProviderImpl(project).findIdeModuleOrderEntry(data);
+    @Override
+    public boolean isAlwaysLeaf() {
+      return false;
     }
   }
 }
index 8695e77ce573d853c181668151ba210048659a86..88d57ff3d1bc65e4677851453a12c62ff82b588e 100644 (file)
@@ -39,7 +39,7 @@ public class ExternalProjectServiceTest extends AbstractExternalSystemTest {
 
     applyProjectState([projectNode, projectNode])
 
-    def modelsProvider = new IdeModifiableModelsProviderImpl(project);
+    def modelsProvider = new IdeModelsProviderImpl(project);
     def module = modelsProvider.findIdeModule('module')
     assertNotNull(module)
 
@@ -50,7 +50,7 @@ public class ExternalProjectServiceTest extends AbstractExternalSystemTest {
         def name = (entry as LibraryOrderEntry).libraryName
         dependencies[name]++
       }
-    }
+      }
     ExternalSystemTestUtil.assertMapsEqual(['Test_external_system_id: lib1': 1, 'Test_external_system_id: lib2': 1], dependencies)
   }
 
@@ -80,7 +80,7 @@ public class ExternalProjectServiceTest extends AbstractExternalSystemTest {
 
     applyProjectState([projectNodeInitial, projectNodeRefreshed])
 
-    def modelsProvider = new IdeModifiableModelsProviderImpl(project);
+    def modelsProvider = new IdeModelsProviderImpl(project);
     def module = modelsProvider.findIdeModule('module')
     assertNotNull(module)
     def entries = modelsProvider.getOrderEntries(module)
@@ -88,10 +88,10 @@ public class ExternalProjectServiceTest extends AbstractExternalSystemTest {
     for (OrderEntry entry : entries) {
       if (entry instanceof ModuleSourceOrderEntry) {
         def contentEntry = (entry as ModuleSourceOrderEntry).getRootModel().getContentEntries().first()
-        folders['source']+=contentEntry.sourceFolders.length
-        folders['excluded']+=contentEntry.excludeFolders.length
+        folders['source'] += contentEntry.sourceFolders.length
+        folders['excluded'] += contentEntry.excludeFolders.length
+      }
       }
-    }
     ExternalSystemTestUtil.assertMapsEqual(['source': 4, 'excluded': 2], folders)
   }
 
@@ -120,7 +120,7 @@ public class ExternalProjectServiceTest extends AbstractExternalSystemTest {
             lib('lib1', level: 'module', bin: [libBinPath.absolutePath], src: [libSrcPath.absolutePath],  doc: [libDocPath.absolutePath]) } } }
     ])
 
-    def modelsProvider = new IdeModifiableModelsProviderImpl(project);
+    def modelsProvider = new IdeModelsProviderImpl(project);
     def module = modelsProvider.findIdeModule('module')
     assertNotNull(module)
 
@@ -144,8 +144,8 @@ public class ExternalProjectServiceTest extends AbstractExternalSystemTest {
         else {
           fail()
         }
+        }
       }
-    }
     ExternalSystemTestUtil.assertMapsEqual(['Test_external_system_id: lib1': 1], dependencies)
   }
 }
index 30d92682bdba4b5dd5df9ad531c53b1c7874cda1..a0c230106cf328583f36cad140f8621ed496090f 100644 (file)
@@ -45,6 +45,7 @@ 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.openapi.vfs.VirtualFileManager;
 import com.intellij.packaging.artifacts.Artifact;
 import com.intellij.packaging.artifacts.ArtifactManager;
 import com.intellij.testFramework.IdeaTestUtil;
@@ -114,19 +115,16 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
   }
 
   private void assertGeneratedSources(String moduleName, JavaSourceRootType type, String... expectedSources) {
-    ContentEntry contentRoot = getContentRoot(moduleName);
-    List<ContentFolder> folders = new ArrayList<ContentFolder>();
-    for (SourceFolder folder : contentRoot.getSourceFolders(type)) {
+    final ContentEntry[] contentRoots = getContentRoots(moduleName);
+    final String rootUrl = contentRoots.length > 1 ? ExternalSystemApiUtil.getExternalProjectPath(getModule(moduleName)) : null;
+    List<SourceFolder> folders = doAssertContentFolders(rootUrl, contentRoots, type, expectedSources);
+    for (SourceFolder folder : folders) {
       JavaSourceRootProperties properties = folder.getJpsElement().getProperties(type);
       assertNotNull(properties);
-      if (properties.isForGeneratedSources()) {
-        folders.add(folder);
-      }
+      assertTrue("Not a generated folder: " + folder, properties.isForGeneratedSources());
     }
-    doAssertContentFolders(contentRoot, folders, expectedSources);
   }
 
-
   protected void assertResources(String moduleName, String... expectedSources) {
     doAssertContentFolders(moduleName, JavaResourceRootType.RESOURCE, expectedSources);
   }
@@ -150,8 +148,33 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
   }
 
   private void doAssertContentFolders(String moduleName, @NotNull JpsModuleSourceRootType<?> rootType, String... expected) {
-    ContentEntry contentRoot = getContentRoot(moduleName);
-    doAssertContentFolders(contentRoot, contentRoot.getSourceFolders(rootType), expected);
+    final ContentEntry[] contentRoots = getContentRoots(moduleName);
+    final String rootUrl = contentRoots.length > 1 ? ExternalSystemApiUtil.getExternalProjectPath(getModule(moduleName)) : null;
+    doAssertContentFolders(rootUrl, contentRoots, rootType, expected);
+  }
+
+  private static List<SourceFolder> doAssertContentFolders(@Nullable String rootUrl,
+                                                           ContentEntry[] contentRoots,
+                                                           @NotNull JpsModuleSourceRootType<?> rootType,
+                                                           String... expected) {
+    List<SourceFolder> result = new ArrayList<SourceFolder>();
+    List<String> actual = new ArrayList<String>();
+    for (ContentEntry contentRoot : contentRoots) {
+      for (SourceFolder f : contentRoot.getSourceFolders(rootType)) {
+        rootUrl = rootUrl == null ? VirtualFileManager.extractPath(contentRoot.getUrl()) : VirtualFileManager.extractPath(rootUrl);
+        String folderUrl = VirtualFileManager.extractPath(f.getUrl());
+        if (folderUrl.startsWith(rootUrl)) {
+          int length = rootUrl.length() + 1;
+          folderUrl = folderUrl.substring(Math.min(length, folderUrl.length()));
+        }
+
+        actual.add(folderUrl);
+        result.add(f);
+      }
+    }
+
+    assertOrderedElementsAreEqual(actual, Arrays.asList(expected));
+    return result;
   }
 
   private static void doAssertContentFolders(ContentEntry e, final List<? extends ContentFolder> folders, String... expected) {
@@ -227,6 +250,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
   }
 
   private static void assertModuleLibDepPath(LibraryOrderEntry lib, OrderRootType type, List<String> paths) {
+    assertNotNull(lib);
     if (paths == null) return;
     assertUnorderedPathsAreEqual(Arrays.asList(lib.getRootUrls(type)), paths);
     // also check the library because it may contain slight different set of urls (e.g. with duplicates)
@@ -245,7 +269,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
     }), scopes);
   }
 
-  private List<LibraryOrderEntry> getModuleLibDeps(String moduleName, String depName) {
+  protected List<LibraryOrderEntry> getModuleLibDeps(String moduleName, String depName) {
     return getModuleDep(moduleName, depName, LibraryOrderEntry.class);
   }
 
index 1202457e98a4ad8080a44266e6ef4011d41eb775..e173c8e7f7a040c5ea3fa4b7f634eec92965d7d1 100644 (file)
@@ -39,10 +39,6 @@ public class GradleModuleResourceConfiguration {
   @Tag("parentId")
   public ModuleVersion parentId;
 
-  @NotNull
-  @Tag("directory")
-  public String directory;
-
   @OptionTag
   public boolean overwrite;
 
@@ -82,7 +78,6 @@ public class GradleModuleResourceConfiguration {
   public int computeModuleConfigurationHash() {
     int result = id.hashCode();
     result = 31 * result + (parentId != null ? parentId.hashCode() : 0);
-    result = 31 * result + directory.hashCode();
     result = 31 * result + (outputDirectory != null ? outputDirectory.hashCode() : 0);
     result = 31 * result + (overwrite ? 1 : 0);
     return result;
index af0fd08fb6cfb12785d27e3e2ac15861ee14a8f5..bbf28f77dc64e610db5bc663f30f44ef3a69355f 100644 (file)
@@ -41,6 +41,16 @@ public class JpsGradleDependenciesEnumerationHandler extends JpsJavaDependencies
     return JpsGradleExtensionService.getInstance().isProductionOnTestDependency(element);
   }
 
+  @Override
+  public boolean shouldIncludeTestsFromDependentModulesToTestClasspath() {
+    return false;
+  }
+
+  @Override
+  public boolean shouldProcessDependenciesRecursively() {
+    return false;
+  }
+
   public static class GradleFactory extends Factory {
     @Nullable
     @Override
index 2a5f1a245638e38044a7c1660b2018a64c64b651..9a116de6fb81cfe72f16513cba6877fd98c6b879 100644 (file)
 
     <externalSystemConfigLocator implementation="org.jetbrains.plugins.gradle.service.settings.GradleConfigLocator"/>
     <externalSystemManager implementation="org.jetbrains.plugins.gradle.GradleManager"/>
+    <externalProjectDataService implementation="org.jetbrains.plugins.gradle.service.project.data.GradleSourceSetDataService"/>
     <externalProjectDataService implementation="org.jetbrains.plugins.gradle.service.project.data.BuildClasspathModuleGradleDataService"/>
     <externalProjectDataService implementation="org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService"/>
+    <externalSystemViewContributor implementation="org.jetbrains.plugins.gradle.service.project.view.GradleViewContributor"/>
+    <externalProjectStructureCustomizer implementation="org.jetbrains.plugins.gradle.service.project.GradleProjectStructureCustomizer"/>
     <externalSystemNotificationExtension implementation="org.jetbrains.plugins.gradle.service.notification.GradleNotificationExtension" order="last"/>
     <externalSystemTaskNotificationListener implementation="org.jetbrains.plugins.gradle.service.project.GradleProjectImportNotificationListener"/>
 
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.project.GradleNotification"/>
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager"/>
     <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.task.ExecuteGradleTaskHistoryService"/>
+    <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataCache"/>
 
     <stepsBeforeRunProvider implementation="org.jetbrains.plugins.gradle.execution.GradleBeforeRunTaskProvider" />
     <configurationProducer implementation="org.jetbrains.plugins.gradle.service.execution.GradleRuntimeConfigurationProducer"/>
index 84840daa87dbeb9cc6cc846846de5d6bf3bd46e6..c4b78ed757215b26dd9f03ee00580cc498917fa8 100644 (file)
@@ -140,8 +140,9 @@ public class GradleManager
 
       @Override
       public GradleExecutionSettings fun(Pair<Project, String> pair) {
-        GradleSettings settings = GradleSettings.getInstance(pair.first);
-        File gradleHome = myInstallationManager.getGradleHome(pair.first, pair.second);
+        final Project project = pair.first;
+        GradleSettings settings = GradleSettings.getInstance(project);
+        File gradleHome = myInstallationManager.getGradleHome(project, pair.second);
         String localGradlePath = null;
         if (gradleHome != null) {
           try {
@@ -174,12 +175,13 @@ public class GradleManager
           result.addResolverExtensionClass(ClassHolder.from(extension.getClass()));
         }
 
-        final Sdk gradleJdk = myInstallationManager.getGradleJdk(pair.first, pair.second);
+        final Sdk gradleJdk = myInstallationManager.getGradleJdk(project, pair.second);
         final String javaHome = gradleJdk != null ? gradleJdk.getHomePath() : null;
         if (!StringUtil.isEmpty(javaHome)) {
           LOG.info("Instructing gradle to use java from " + javaHome);
         }
         result.setJavaHome(javaHome);
+        result.setIdeProjectPath(project.getBasePath());
         return result;
       }
     };
index 053ca2dfbd4cc397dddbe15dbc874307b46ccc52..2cb1dcabd128670435eb44193d745b1137d32a88 100644 (file)
@@ -21,14 +21,8 @@ import com.intellij.facet.Facet;
 import com.intellij.facet.FacetManager;
 import com.intellij.openapi.compiler.CompileContext;
 import com.intellij.openapi.compiler.CompilerMessageCategory;
-import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
-import org.jetbrains.plugins.gradle.model.ExternalFilter;
-import org.jetbrains.plugins.gradle.model.ExternalProject;
-import org.jetbrains.plugins.gradle.model.ExternalSourceDirectorySet;
-import org.jetbrains.plugins.gradle.model.ExternalSourceSet;
 import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
-import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.module.Module;
@@ -51,7 +45,11 @@ import org.jdom.Element;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.gradle.model.impl.*;
-import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService;
+import org.jetbrains.plugins.gradle.model.ExternalFilter;
+import org.jetbrains.plugins.gradle.model.ExternalProject;
+import org.jetbrains.plugins.gradle.model.ExternalSourceDirectorySet;
+import org.jetbrains.plugins.gradle.model.ExternalSourceSet;
+import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataCache;
 import org.jetbrains.plugins.gradle.util.GradleConstants;
 
 import java.io.File;
@@ -73,14 +71,13 @@ public class GradleResourceCompilerConfigurationGenerator {
   private final Project myProject;
   @NotNull
   private final Map<String, Integer> myModulesConfigurationHash;
-  private final ExternalProjectDataService myExternalProjectDataService;
+  private final ExternalProjectDataCache externalProjectDataCache;
 
   public GradleResourceCompilerConfigurationGenerator(@NotNull final Project project) {
     myProject = project;
     myModulesConfigurationHash = ContainerUtil.newConcurrentMap();
-    myExternalProjectDataService =
-      (ExternalProjectDataService)ServiceManager.getService(ProjectDataManager.class).getDataService(ExternalProjectDataService.KEY);
-    assert myExternalProjectDataService != null;
+    externalProjectDataCache = ExternalProjectDataCache.getInstance(project);
+    assert externalProjectDataCache != null;
 
     project.getMessageBus().connect(project).subscribe(ProjectTopics.MODULES, new ModuleAdapter() {
       public void moduleRemoved(@NotNull Project project, @NotNull Module module) {
@@ -186,12 +183,14 @@ public class GradleResourceCompilerConfigurationGenerator {
       @Nullable
       @Override
       protected ExternalProject create(String gradleProjectPath) {
-        return myExternalProjectDataService.getRootExternalProject(GradleConstants.SYSTEM_ID, new File(gradleProjectPath));
+        return externalProjectDataCache.getRootExternalProject(GradleConstants.SYSTEM_ID, new File(gradleProjectPath));
       }
     };
 
     for (Module module : context.getCompileScope().getAffectedModules()) {
       if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) continue;
+      if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) continue;
+      if (!GradleConstants.GRADLE_SOURCE_SET_MODULE_TYPE_KEY.equals(ExternalSystemApiUtil.getExternalModuleType(module))) continue;
 
       if (shouldBeBuiltByExternalSystem(module)) continue;
 
@@ -206,21 +205,22 @@ public class GradleResourceCompilerConfigurationGenerator {
         continue;
       }
 
-      ExternalProject externalProject = myExternalProjectDataService.findExternalProject(externalRootProject, module);
-      if (externalProject == null) {
-        LOG.warn("Unable to find config for module: " + module.getName());
+      Map<String, ExternalSourceSet> externalSourceSets = externalProjectDataCache.findExternalProject(externalRootProject, module);
+      if (externalSourceSets.isEmpty()) {
+        LOG.debug("Unable to find source sets config for module: " + module.getName());
         continue;
       }
 
       GradleModuleResourceConfiguration resourceConfig = new GradleModuleResourceConfiguration();
-      resourceConfig.id = new ModuleVersion(externalProject.getGroup(), externalProject.getName(), externalProject.getVersion());
-      resourceConfig.directory = FileUtil.toSystemIndependentName(externalProject.getProjectDir().getPath());
-
-      final ExternalSourceSet mainSourcesSet = externalProject.getSourceSets().get("main");
-      addResources(resourceConfig.resources, mainSourcesSet, ExternalSystemSourceType.RESOURCE);
-
-      final ExternalSourceSet testSourcesSet = externalProject.getSourceSets().get("test");
-      addResources(resourceConfig.testResources, testSourcesSet, ExternalSystemSourceType.TEST_RESOURCE);
+      resourceConfig.id = new ModuleVersion(
+        ExternalSystemApiUtil.getExternalProjectGroup(module),
+        ExternalSystemApiUtil.getExternalProjectId(module),
+        ExternalSystemApiUtil.getExternalProjectVersion(module));
+
+      for (ExternalSourceSet sourceSet : externalSourceSets.values()) {
+        addResources(resourceConfig.resources, sourceSet.getSources().get(ExternalSystemSourceType.RESOURCE));
+        addResources(resourceConfig.testResources, sourceSet.getSources().get(ExternalSystemSourceType.TEST_RESOURCE));
+      }
 
       final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module);
       if (compilerModuleExtension != null && compilerModuleExtension.isCompilerOutputPathInherited()) {
@@ -264,10 +264,7 @@ public class GradleResourceCompilerConfigurationGenerator {
   }
 
   private static void addResources(@NotNull List<ResourceRootConfiguration> container,
-                                   @Nullable ExternalSourceSet externalSourceSet,
-                                   @NotNull ExternalSystemSourceType sourceType) {
-    if (externalSourceSet == null) return;
-    final ExternalSourceDirectorySet directorySet = externalSourceSet.getSources().get(sourceType);
+                                   @Nullable final ExternalSourceDirectorySet directorySet) {
     if (directorySet == null) return;
 
     for (File file : directorySet.getSrcDirs()) {
index 6363e73b3693fd6612c324ef9d5e1d77639e2e0e..22bc590392f88eadba24b17bb1ab106cd0891235 100644 (file)
@@ -17,13 +17,8 @@ package org.jetbrains.plugins.gradle.execution;
 
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ex.ApplicationEx;
-import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
-import org.jetbrains.plugins.gradle.model.ExternalProject;
-import org.jetbrains.plugins.gradle.model.ExternalSourceDirectorySet;
-import org.jetbrains.plugins.gradle.model.ExternalSourceSet;
 import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
-import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.module.Module;
@@ -39,11 +34,15 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService;
+import org.jetbrains.plugins.gradle.model.ExternalProject;
+import org.jetbrains.plugins.gradle.model.ExternalSourceDirectorySet;
+import org.jetbrains.plugins.gradle.model.ExternalSourceSet;
+import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataCache;
 import org.jetbrains.plugins.gradle.util.GradleConstants;
 
 import java.io.File;
 import java.util.Collection;
+import java.util.Map;
 
 public class GradleOrderEnumeratorHandler extends OrderEnumerationHandler {
   private static final Logger LOG = Logger.getInstance(GradleOrderEnumeratorHandler.class);
@@ -69,6 +68,21 @@ public class GradleOrderEnumeratorHandler extends OrderEnumerationHandler {
 
   private static final GradleOrderEnumeratorHandler INSTANCE = new GradleOrderEnumeratorHandler();
 
+  @Override
+  public boolean shouldAddRuntimeDependenciesToTestCompilationClasspath() {
+    return true;
+  }
+
+  @Override
+  public boolean shouldIncludeTestsFromDependentModulesToTestClasspath() {
+    return false;
+  }
+
+  @Override
+  public boolean shouldProcessDependenciesRecursively() {
+    return false;
+  }
+
   @Override
   public boolean addCustomModuleRoots(@NotNull OrderRootType type,
                                       @NotNull ModuleRootModel rootModel,
@@ -84,35 +98,33 @@ public class GradleOrderEnumeratorHandler extends OrderEnumerationHandler {
       return false;
     }
 
-    final ExternalProjectDataService externalProjectDataService =
-      (ExternalProjectDataService)ServiceManager.getService(ProjectDataManager.class).getDataService(ExternalProjectDataService.KEY);
-
-    assert externalProjectDataService != null;
+    final ExternalProjectDataCache externalProjectDataCache = ExternalProjectDataCache.getInstance(rootModel.getModule().getProject());
+    assert externalProjectDataCache != null;
     final ExternalProject externalRootProject =
-      externalProjectDataService.getRootExternalProject(GradleConstants.SYSTEM_ID, new File(gradleProjectPath));
+      externalProjectDataCache.getRootExternalProject(GradleConstants.SYSTEM_ID, new File(gradleProjectPath));
     if (externalRootProject == null) {
       LOG.debug("Root external project was not yep imported for the project path: " + gradleProjectPath);
       return false;
     }
 
-    ExternalProject externalProject = externalProjectDataService.findExternalProject(externalRootProject, rootModel.getModule());
-    if (externalProject == null) return false;
+    Map<String, ExternalSourceSet> externalSourceSets =
+      externalProjectDataCache.findExternalProject(externalRootProject, rootModel.getModule());
+    if (externalSourceSets.isEmpty()) return false;
 
-    if (includeTests) {
-      addOutputModuleRoots(externalProject.getSourceSets().get("test"), ExternalSystemSourceType.TEST_RESOURCE, result);
-    }
-    if (includeProduction) {
-      addOutputModuleRoots(externalProject.getSourceSets().get("main"), ExternalSystemSourceType.RESOURCE, result);
+    for (ExternalSourceSet sourceSet : externalSourceSets.values()) {
+      if (includeTests) {
+        addOutputModuleRoots(sourceSet.getSources().get(ExternalSystemSourceType.TEST_RESOURCE), result);
+      }
+      if (includeProduction) {
+        addOutputModuleRoots(sourceSet.getSources().get(ExternalSystemSourceType.RESOURCE), result);
+      }
     }
 
     return true;
   }
 
-  private static void addOutputModuleRoots(@Nullable ExternalSourceSet externalSourceSet,
-                                           @NotNull ExternalSystemSourceType sourceType,
+  private static void addOutputModuleRoots(@Nullable ExternalSourceDirectorySet directorySet,
                                            @NotNull Collection<String> result) {
-    if (externalSourceSet == null) return;
-    final ExternalSourceDirectorySet directorySet = externalSourceSet.getSources().get(sourceType);
     if (directorySet == null) return;
 
     if (directorySet.isCompilerOutputPathInherited()) return;
index f6dbe149c6871376bd489a828cb9055346192489..8b481884763e2adfdf247c27577bdd8de1e0735b 100644 (file)
@@ -18,16 +18,15 @@ package org.jetbrains.plugins.gradle.integrations.javaee;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.project.ModuleData;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
 import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.util.BooleanFunction;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
 import org.gradle.tooling.model.idea.IdeaModule;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.plugins.gradle.model.data.War;
-import org.jetbrains.plugins.gradle.model.data.WarDirectory;
-import org.jetbrains.plugins.gradle.model.data.WebConfigurationModelData;
-import org.jetbrains.plugins.gradle.model.data.WebResource;
+import org.jetbrains.plugins.gradle.model.data.*;
 import org.jetbrains.plugins.gradle.model.web.WebConfiguration;
 import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension;
 import org.jetbrains.plugins.gradle.util.GradleConstants;
@@ -47,7 +46,7 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve
   private static final Logger LOG = Logger.getInstance(JavaEEGradleProjectResolverExtension.class);
 
   @Override
-  public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode<ModuleData> ideModule) {
+  public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull final DataNode<ModuleData> ideModule) {
     final List<War> warModels;
     final WebConfiguration webConfiguration = resolverCtx.getExtraProject(gradleModule, WebConfiguration.class);
     if (webConfiguration != null) {
@@ -62,7 +61,19 @@ public class JavaEEGradleProjectResolverExtension extends AbstractProjectResolve
             return war;
           }
         });
-      ideModule.createChild(WebConfigurationModelData.KEY, new WebConfigurationModelData(GradleConstants.SYSTEM_ID, warModels));
+
+      final String mainSourceSetModuleId = ideModule.getData().getId() + ":main";
+      DataNode<? extends ModuleData> targetModuleNode =
+        ExternalSystemApiUtil.find(ideModule, GradleSourceSetData.KEY, new BooleanFunction<DataNode<GradleSourceSetData>>() {
+          @Override
+          public boolean fun(DataNode<GradleSourceSetData> node) {
+            return mainSourceSetModuleId.equals(node.getData().getId());
+          }
+        });
+      if(targetModuleNode == null) {
+        targetModuleNode = ideModule;
+      }
+      targetModuleNode.createChild(WebConfigurationModelData.KEY, new WebConfigurationModelData(GradleConstants.SYSTEM_ID, warModels));
     }
     nextResolver.populateModuleExtraModels(gradleModule, ideModule);
   }
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/GradleSourceSetData.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/GradleSourceSetData.java
new file mode 100644 (file)
index 0000000..ea7eba9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 org.jetbrains.plugins.gradle.model.data;
+
+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.module.StdModuleTypes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.util.GradleConstants;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 8/5/2015
+ */
+public class GradleSourceSetData extends ModuleData {
+
+  @NotNull
+  public static final Key<GradleSourceSetData> KEY = Key.create(GradleSourceSetData.class, ProjectKeys.MODULE.getProcessingWeight() + 1);
+
+  public GradleSourceSetData(@NotNull String id,
+                             @NotNull String externalName,
+                             @NotNull String internalName,
+                             @NotNull String moduleFileDirectoryPath,
+                             @NotNull String externalConfigPath) {
+    super(id, GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(),
+          externalName, internalName,
+          moduleFileDirectoryPath, externalConfigPath);
+  }
+}
index 04c1023df8e81e45a1b60f83ffc38b906d2b95df..c0fedfe9110f3b82169f63f75e6378d37bca1d58 100644 (file)
@@ -88,8 +88,8 @@ public abstract class AbstractProjectResolverExtension implements GradleProjectR
 
   @NotNull
   @Override
-  public ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData) {
-    return nextResolver.createModule(gradleModule, projectData);
+  public DataNode<ModuleData> createModule(@NotNull IdeaModule gradleModule, @NotNull DataNode<ProjectData> projectDataNode) {
+    return nextResolver.createModule(gradleModule, projectDataNode);
   }
 
   @Override
index 904e48efe04dbe0f12da02f8c0011d8401f4392a..90bf6d4efb2caf9a1be36cf98b0075614d9c3663 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.jetbrains.plugins.gradle.service.project;
 
+import com.google.common.collect.Multimap;
 import com.google.gson.GsonBuilder;
 import com.intellij.execution.ExecutionException;
 import com.intellij.execution.configurations.SimpleJavaParameters;
@@ -35,6 +36,7 @@ import com.intellij.openapi.module.ModuleType;
 import com.intellij.openapi.module.StdModuleTypes;
 import com.intellij.openapi.roots.DependencyScope;
 import com.intellij.openapi.util.KeyValue;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.io.FileFilters;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.io.FileUtilRt;
@@ -55,12 +57,13 @@ import org.gradle.tooling.model.UnsupportedMethodException;
 import org.gradle.tooling.model.gradle.BasicGradleProject;
 import org.gradle.tooling.model.gradle.GradleBuild;
 import org.gradle.tooling.model.idea.*;
-import org.gradle.util.GradleVersion;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.ExternalDependencyId;
 import org.jetbrains.plugins.gradle.model.*;
 import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData;
+import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
 import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService;
 import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl;
 import org.jetbrains.plugins.gradle.tooling.internal.init.Init;
@@ -79,6 +82,8 @@ import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.*;
+
 /**
  * {@link BaseGradleProjectResolverExtension} provides base implementation of Gradle project resolver.
  *
@@ -90,8 +95,6 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
   private static final Logger LOG = Logger.getInstance("#" + BaseGradleProjectResolverExtension.class.getName());
 
   @NotNull @NonNls private static final String UNRESOLVED_DEPENDENCY_PREFIX = "unresolved dependency - ";
-  private static final String MAIN_SOURCE_SET = "main";
-  private static final String TEST_SOURCE_SET = "test";
 
   @NotNull private ProjectResolverContext resolverCtx;
   @NotNull private final BaseProjectImportErrorHandler myErrorHandler = new BaseProjectImportErrorHandler();
@@ -144,37 +147,83 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
 
   @NotNull
   @Override
-  public ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData) {
+  public DataNode<ModuleData> createModule(@NotNull IdeaModule gradleModule, @NotNull DataNode<ProjectData> projectDataNode) {
     final String moduleName = gradleModule.getName();
     if (moduleName == null) {
       throw new IllegalStateException("Module with undefined name detected: " + gradleModule);
     }
 
-    final String moduleConfigPath = getModuleConfigPath(gradleModule, projectData.getLinkedExternalProjectPath());
-
+    final ProjectData projectData = projectDataNode.getData();
+    final String mainModuleConfigPath = getModuleConfigPath(gradleModule, projectData.getLinkedExternalProjectPath());
+    final String ideProjectPath = resolverCtx.getIdeProjectPath();
+    final String relativePath;
+    if (FileUtil.isAncestor(projectData.getLinkedExternalProjectPath(), mainModuleConfigPath, false)) {
+      relativePath = FileUtil.getRelativePath(projectData.getLinkedExternalProjectPath(), mainModuleConfigPath, '/');
+    }
+    else {
+      relativePath = String.valueOf(FileUtil.pathHashCode(mainModuleConfigPath));
+    }
+    final String mainModuleFileDirectoryPath =
+      ideProjectPath == null ? mainModuleConfigPath : ideProjectPath + "/.idea/modules/" +
+                                                      (relativePath == null || relativePath.equals(".") ? "" : relativePath);
     if (ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
       LOG.info(String.format(
-        "Creating module data ('%s') with the external config path: '%s'", gradleModule.getGradleProject().getPath(), moduleConfigPath
+        "Creating module data ('%s') with the external config path: '%s'", gradleModule.getGradleProject().getPath(), mainModuleConfigPath
       ));
     }
 
     String gradlePath = gradleModule.getGradleProject().getPath();
-    String moduleId = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? moduleName : gradlePath;
-    ModuleData moduleData =
-      new ModuleData(moduleId, GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName, moduleConfigPath, moduleConfigPath);
-
-    final ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class);
-    if (moduleExtendedModel != null) {
-      moduleData.setGroup(moduleExtendedModel.getGroup());
-      moduleData.setVersion(moduleExtendedModel.getVersion());
-      moduleData.setArtifacts(moduleExtendedModel.getArtifacts());
-    }
+    final boolean isRootModule = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath);
+    String mainModuleId = isRootModule ? moduleName : gradlePath;
+    final ModuleData moduleData =
+      new ModuleData(mainModuleId, GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName,
+                     mainModuleFileDirectoryPath, mainModuleConfigPath);
+    final String[] moduleGroup = null;
+    moduleData.setIdeModuleGroup(moduleGroup);
+    DataNode<ModuleData> mainModuleNode = projectDataNode.createChild(ProjectKeys.MODULE, moduleData);
 
     ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
     if (externalProject != null) {
+      moduleData.setGroup(externalProject.getGroup());
+      moduleData.setVersion(externalProject.getVersion());
       moduleData.setDescription(externalProject.getDescription());
+      moduleData.setArtifacts(externalProject.getArtifacts());
+
+      for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) {
+        final String moduleId = getModuleId(externalProject, sourceSet);
+        final String moduleExternalName = gradleModule.getName() + ":" + sourceSet.getName();
+        final String moduleInternalName = gradleModule.getName() + "_" + sourceSet.getName();
+
+        GradleSourceSetData sourceSetData = new GradleSourceSetData(
+          moduleId, moduleExternalName, moduleInternalName, mainModuleFileDirectoryPath, mainModuleConfigPath);
+
+        sourceSetData.setGroup(externalProject.getGroup());
+        sourceSetData.setVersion(externalProject.getVersion());
+        sourceSetData.setIdeModuleGroup(moduleGroup);
+
+        if ("main".equals(sourceSet.getName())) {
+          final List<File> artifacts = ContainerUtil.newArrayList();
+          final Set<File> defaultArtifacts = externalProject.getArtifactsByConfiguration().get("default");
+          if (defaultArtifacts != null) {
+            artifacts.addAll(defaultArtifacts);
+          }
+          sourceSetData.setArtifacts(artifacts);
+        }
+
+        DataNode<GradleSourceSetData> sourceSetDataNode = mainModuleNode.createChild(GradleSourceSetData.KEY, sourceSetData);
+        final Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap =
+          projectDataNode.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS);
+        assert sourceSetMap != null;
+        sourceSetMap.put(moduleId, Pair.create(sourceSetDataNode, sourceSet));
+      }
     }
-    return moduleData;
+
+    if (StringUtil.equals(moduleData.getLinkedExternalProjectPath(), projectData.getLinkedExternalProjectPath())) {
+      projectData.setGroup(moduleData.getGroup());
+      projectData.setVersion(moduleData.getVersion());
+    }
+
+    return mainModuleNode;
   }
 
   @Override
@@ -200,15 +249,26 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
   @Override
   public void populateModuleContentRoots(@NotNull IdeaModule gradleModule,
                                          @NotNull DataNode<ModuleData> ideModule) {
-    DomainObjectSet<? extends IdeaContentRoot> contentRoots;
-    ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class);
-    if (moduleExtendedModel != null) {
-      contentRoots = moduleExtendedModel.getContentRoots();
-    }
-    else {
-      contentRoots = gradleModule.getContentRoots();
+    ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
+    if (externalProject != null) {
+      processSourceSets(externalProject, ideModule, new SourceSetsProcessor() {
+        @Override
+        public void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet) {
+          for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> directorySetEntry : sourceSet.getSources().entrySet()) {
+            ExternalSystemSourceType sourceType = ExternalSystemSourceType.from(directorySetEntry.getKey());
+            ExternalSourceDirectorySet sourceDirectorySet = directorySetEntry.getValue();
+
+            for (File file : sourceDirectorySet.getSrcDirs()) {
+              ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, file.getAbsolutePath());
+              ideContentRoot.storePath(sourceType, file.getAbsolutePath());
+              dataNode.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot);
+            }
+          }
+        }
+      });
     }
 
+    DomainObjectSet<? extends IdeaContentRoot> contentRoots = gradleModule.getContentRoots();
     if (contentRoots == null) {
       return;
     }
@@ -219,14 +279,15 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
       if (rootDirectory == null) continue;
 
       ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, rootDirectory.getAbsolutePath());
-      ideModule.getData().setModuleFileDirectoryPath(ideContentRoot.getRootPath());
-      populateContentRoot(ideContentRoot, ExternalSystemSourceType.SOURCE, gradleContentRoot.getSourceDirectories());
-      populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST, gradleContentRoot.getTestDirectories());
-
-      if (gradleContentRoot instanceof ExtIdeaContentRoot) {
-        ExtIdeaContentRoot extIdeaContentRoot = (ExtIdeaContentRoot)gradleContentRoot;
-        populateContentRoot(ideContentRoot, ExternalSystemSourceType.RESOURCE, extIdeaContentRoot.getResourceDirectories());
-        populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST_RESOURCE, extIdeaContentRoot.getTestResourceDirectories());
+      if (externalProject == null) {
+        populateContentRoot(ideContentRoot, ExternalSystemSourceType.SOURCE, gradleContentRoot.getSourceDirectories());
+        populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST, gradleContentRoot.getTestDirectories());
+
+        if (gradleContentRoot instanceof ExtIdeaContentRoot) {
+          ExtIdeaContentRoot extIdeaContentRoot = (ExtIdeaContentRoot)gradleContentRoot;
+          populateContentRoot(ideContentRoot, ExternalSystemSourceType.RESOURCE, extIdeaContentRoot.getResourceDirectories());
+          populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST_RESOURCE, extIdeaContentRoot.getTestResourceDirectories());
+        }
       }
 
       Set<File> excluded = gradleContentRoot.getExcludeDirectories();
@@ -239,10 +300,47 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
     }
   }
 
+  private static void processSourceSets(@NotNull ExternalProject externalProject,
+                                        @NotNull DataNode<ModuleData> ideModule,
+                                        @NotNull SourceSetsProcessor processor) {
+    Map<String, DataNode<GradleSourceSetData>> sourceSetsMap = ContainerUtil.newHashMap();
+    for (DataNode<GradleSourceSetData> dataNode : ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)) {
+      sourceSetsMap.put(dataNode.getData().getId(), dataNode);
+    }
+
+    for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) {
+      if (sourceSet == null || sourceSet.getSources().isEmpty()) continue;
+
+      final String moduleId = getModuleId(externalProject, sourceSet);
+      final DataNode<GradleSourceSetData> sourceSetDataNode = sourceSetsMap.get(moduleId);
+      if (sourceSetDataNode == null) continue;
+
+      processor.process(sourceSetDataNode, sourceSet);
+    }
+  }
+
 
   @Override
   public void populateModuleCompileOutputSettings(@NotNull IdeaModule gradleModule,
                                                   @NotNull DataNode<ModuleData> ideModule) {
+    ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
+    if (externalProject != null) {
+      processSourceSets(externalProject, ideModule, new SourceSetsProcessor() {
+        @Override
+        public void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet) {
+          for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> directorySetEntry : sourceSet.getSources().entrySet()) {
+            ExternalSystemSourceType sourceType = ExternalSystemSourceType.from(directorySetEntry.getKey());
+            ExternalSourceDirectorySet sourceDirectorySet = directorySetEntry.getValue();
+            final GradleSourceSetData sourceSetData = dataNode.getData();
+            sourceSetData.setCompileOutputPath(sourceType, sourceDirectorySet.getOutputDir().getAbsolutePath());
+            sourceSetData.setInheritProjectCompileOutputPath(sourceDirectorySet.isCompilerOutputPathInherited());
+          }
+        }
+      });
+
+      return;
+    }
+
     IdeaCompilerOutput moduleCompilerOutput = gradleModule.getCompilerOutput();
 
     File buildDir = null;
@@ -268,29 +366,6 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
       inheritOutputDirs = moduleCompilerOutput.getInheritOutputDirs();
     }
 
-    ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
-    if (externalProject != null) {
-      externalProject = new DefaultExternalProject(externalProject);
-      buildDir = buildDir == null ? externalProject.getBuildDir() : buildDir;
-
-      if (!inheritOutputDirs) {
-        final File sourceCompileOutputPath = compileOutputPaths.get(ExternalSystemSourceType.SOURCE);
-        if (sourceCompileOutputPath == null) {
-          addCompileOutputPath(compileOutputPaths, externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.SOURCE);
-          addCompileOutputPath(compileOutputPaths, externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.RESOURCE);
-        }
-
-        final File testCompileOutputPath = compileOutputPaths.get(ExternalSystemSourceType.TEST);
-        if (testCompileOutputPath == null) {
-          addCompileOutputPath(compileOutputPaths, externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST);
-          addCompileOutputPath(compileOutputPaths, externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST_RESOURCE);
-        }
-      }
-    }
-    else {
-      LOG.warn(String.format("Unable to get ExternalProject model for '%s'", gradleModule.getName()));
-    }
-
     for (Map.Entry<ExternalSystemSourceType, File> sourceTypeFileEntry : compileOutputPaths.entrySet()) {
       final File outputPath = ObjectUtils.chooseNotNull(sourceTypeFileEntry.getValue(), buildDir);
       if (outputPath != null) {
@@ -304,7 +379,41 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
   @Override
   public void populateModuleDependencies(@NotNull IdeaModule gradleModule,
                                          @NotNull DataNode<ModuleData> ideModule,
-                                         @NotNull DataNode<ProjectData> ideProject) {
+                                         @NotNull final DataNode<ProjectData> ideProject) {
+
+    ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
+    if (externalProject != null) {
+      final Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap =
+        ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS);
+      assert sourceSetMap != null;
+
+      processSourceSets(externalProject, ideModule, new SourceSetsProcessor() {
+        @Override
+        public void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet) {
+          Map<ExternalDependencyId, ExternalDependency> dependencyMap = ContainerUtil.newLinkedHashMap();
+          for (ExternalDependency dependency : sourceSet.getDependencies()) {
+            ExternalDependency d = dependencyMap.get(dependency.getId());
+            if (d != null) {
+              DependencyScope prevScope = d.getScope() == null ? DependencyScope.COMPILE : DependencyScope.valueOf(d.getScope());
+              DependencyScope currentScope =
+                dependency.getScope() == null ? DependencyScope.COMPILE : DependencyScope.valueOf(dependency.getScope());
+
+              if (prevScope.isForProductionCompile()) continue;
+              if (prevScope.isForProductionRuntime() && currentScope.isForProductionRuntime()) continue;
+            }
+
+            dependencyMap.put(dependency.getId(), dependency);
+          }
+
+          for (ExternalDependency dependency : dependencyMap.values()) {
+            buildSourceSetDependency(sourceSetMap, dataNode, dependency, ideProject);
+          }
+        }
+      });
+
+      return;
+    }
+
     final List<? extends IdeaDependency> dependencies = gradleModule.getDependencies().getAll();
 
     if (dependencies == null) return;
@@ -393,10 +502,11 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
   @NotNull
   @Override
   public Set<Class> getExtraProjectModelClasses() {
-    Set<Class> result = ContainerUtil.<Class>set(GradleBuild.class, ExternalProject.class, ModuleExtendedModel.class);
+    Set<Class> result = ContainerUtil.<Class>set(GradleBuild.class, ModuleExtendedModel.class);
     if (!ExternalSystemApiUtil.isInProcessMode(GradleConstants.SYSTEM_ID) || !resolverCtx.isPreviewMode()) {
       result.add(BuildScriptClasspathModel.class);
     }
+    result.add(resolverCtx.isPreviewMode() ? ExternalProjectPreview.class : ExternalProject.class);
     return result;
   }
 
@@ -410,6 +520,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
       ProjectImportAction.class,
       // gradle-tooling-extension-impl jar
       ModelBuildScriptClasspathBuilderImpl.class,
+      Multimap.class,
       GsonBuilder.class,
       ShortTypeHandling.class
     );
@@ -728,7 +839,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
         libraryName = FileUtil.getNameWithoutExtension(binaryPath);
       }
       else {
-        libraryName = FileUtil.sanitizeFileName(binaryPath.getPath());
+        libraryName = "";
       }
 
       if (unresolved) {
@@ -773,7 +884,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
     }
 
     // add packaging type to distinguish different artifact dependencies with same groupId:artifactId:version
-    if(!FileUtilRt.extensionEquals(binaryPath.getPath(), "jar")) {
+    if (StringUtil.isNotEmpty(libraryName) && !FileUtilRt.extensionEquals(binaryPath.getPath(), "jar")) {
       libraryName += (":" + FileUtilRt.getExtension(binaryPath.getPath()));
     }
 
@@ -788,7 +899,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
     }
 
     if (!unresolved && sourcePath == null) {
-      attachGradleSdkSources(gradleModule, libraryName, binaryPath, library);
+      attachGradleSdkSources(gradleModule, binaryPath, library, resolverCtx);
     }
 
     File javadocPath = dependency.getJavadoc();
@@ -797,73 +908,150 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver
     }
 
     if(level == LibraryLevel.PROJECT) {
-      DataNode<LibraryData> libraryData =
-        ExternalSystemApiUtil.find(ideProject, ProjectKeys.LIBRARY, new BooleanFunction<DataNode<LibraryData>>() {
-          @Override
-          public boolean fun(DataNode<LibraryData> node) {
-            return library.equals(node.getData());
-          }
-        });
-      if (libraryData == null) {
-        ideProject.createChild(ProjectKeys.LIBRARY, library);
-      }
+      linkProjectLibrary(ideProject, library);
     }
 
     return new LibraryDependencyData(ownerModule.getData(), library, level);
   }
 
-  private void attachGradleSdkSources(@NotNull IdeaModule gradleModule,
-                                      @NotNull final String libName,
-                                      @Nullable final File libFile,
-                                      LibraryData library) {
-    if (libFile == null || !libName.startsWith("gradle-")) return;
-
-    final BuildScriptClasspathModel buildScriptClasspathModel =
-      resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class);
-    if (buildScriptClasspathModel == null) return;
-    final File gradleHomeDir = buildScriptClasspathModel.getGradleHomeDir();
-    if (gradleHomeDir == null) return;
+  private static void buildSourceSetDependency(@NotNull Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap,
+                                               @NotNull DataNode<? extends ModuleData> ownerModule,
+                                               @NotNull ExternalDependency dependency,
+                                               @NotNull DataNode<ProjectData> ideProject)
+    throws IllegalStateException {
 
-    if (!FileUtil.isAncestor(gradleHomeDir, libFile, true)) return;
+    DependencyScope dependencyScope = getDependencyScope(dependency.getScope());
 
-    File libOrPluginsFile = libFile.getParentFile();
-    if (libOrPluginsFile != null && ("plugins".equals(libOrPluginsFile.getName()))) {
-      libOrPluginsFile = libOrPluginsFile.getParentFile();
+    if (dependency instanceof ExternalProjectDependency) {
+      String moduleId = getModuleId((ExternalProjectDependency)dependency);
+      Pair<DataNode<GradleSourceSetData>, ExternalSourceSet> projectPair = sourceSetMap.get(moduleId);
+      ModuleDependencyData moduleDependencyData = new ModuleDependencyData(ownerModule.getData(), projectPair.first.getData());
+      moduleDependencyData.setScope(dependencyScope);
+      if ("test".equals(projectPair.second.getName())) {
+        moduleDependencyData.setProductionOnTestDependency(true);
+      }
+      moduleDependencyData.setOrder(dependency.getClasspathOrder());
+      moduleDependencyData.setExported(dependency.getExported());
+      DataNode<ModuleDependencyData> ideModuleDependencyNode = ownerModule.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData);
+      for (ExternalDependency externalDependency : dependency.getDependencies()) {
+        buildTransitiveDependency(sourceSetMap, ideModuleDependencyNode, externalDependency, ideProject);
+      }
     }
+    if (dependency instanceof ExternalLibraryDependency) {
+      final LibraryLevel level = LibraryLevel.PROJECT;
+      String libraryName = dependency.getId().getPresentableName();
+      final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName);
+      LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule.getData(), library, level);
+      libraryDependencyData.setScope(dependencyScope);
+      libraryDependencyData.setOrder(dependency.getClasspathOrder());
+      libraryDependencyData.setExported(dependency.getExported());
+
+      library.addPath(LibraryPathType.BINARY, ((ExternalLibraryDependency)dependency).getFile().getAbsolutePath());
+      File sourcePath = ((ExternalLibraryDependency)dependency).getSource();
+
+      if (sourcePath != null) {
+        library.addPath(LibraryPathType.SOURCE, sourcePath.getAbsolutePath());
+      }
+      DataNode<LibraryDependencyData> libraryDependencyDataNode =
+        ownerModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData);
 
-    if (libOrPluginsFile != null && "lib".equals(libOrPluginsFile.getName()) && libOrPluginsFile.getParentFile() != null) {
-      File srcDir = new File(libOrPluginsFile.getParentFile(), "src");
+      linkProjectLibrary(ideProject, library);
 
-      GradleVersion current = GradleVersion.version(buildScriptClasspathModel.getGradleVersion());
-      if (current.compareTo(GradleVersion.version("1.9")) >= 0) {
-        int endIndex = libName.indexOf(current.getVersion());
-        if (endIndex != -1) {
-          String srcDirChild = libName.substring("gradle-".length(), endIndex - 1);
-          srcDir = new File(srcDir, srcDirChild);
-        }
+      for (ExternalDependency externalDependency : dependency.getDependencies()) {
+        buildTransitiveDependency(sourceSetMap, libraryDependencyDataNode, externalDependency, ideProject);
+      }
+    }
+    if (dependency instanceof FileCollectionDependency) {
+      final LibraryLevel level = LibraryLevel.MODULE;
+      String libraryName = "";
+      final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName);
+      LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule.getData(), library, level);
+      libraryDependencyData.setScope(dependencyScope);
+      libraryDependencyData.setOrder(dependency.getClasspathOrder());
+      libraryDependencyData.setExported(dependency.getExported());
+
+      for (File file : ((FileCollectionDependency)dependency).getFiles()) {
+        library.addPath(LibraryPathType.BINARY, file.getAbsolutePath());
       }
 
-      if (srcDir.isDirectory()) {
-        library.addPath(LibraryPathType.SOURCE, srcDir.getAbsolutePath());
+      ownerModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData);
+    }
+    if (dependency instanceof UnresolvedExternalDependency) {
+      final LibraryLevel level = LibraryLevel.PROJECT;
+      String libraryName = dependency.getId().getPresentableName();
+      final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName, true);
+      LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule.getData(), library, level);
+      libraryDependencyData.setScope(dependencyScope);
+      final String failureMessage = ((UnresolvedExternalDependency)dependency).getFailureMessage();
+      if (failureMessage != null) {
+        library.addPath(LibraryPathType.BINARY, failureMessage);
       }
+      ownerModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData);
+      linkProjectLibrary(ideProject, library);
     }
   }
 
-  private static boolean isIdeaTask(final String taskName, @Nullable String group) {
-    if ((group == null || "ide".equalsIgnoreCase(group)) && StringUtil.containsIgnoreCase(taskName, "idea")) return true;
-    return "other".equalsIgnoreCase(group) && StringUtil.containsIgnoreCase(taskName, "idea");
-  }
+  private static void buildTransitiveDependency(@NotNull Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap,
+                                                @NotNull DataNode<? extends DependencyData> ownerModule,
+                                                @NotNull ExternalDependency dependency,
+                                                @NotNull DataNode<ProjectData> ideProject)
+    throws IllegalStateException {
 
-  private static void addCompileOutputPath(@NotNull Map<ExternalSystemSourceType, File> compileOutputPaths,
-                                           @NotNull ExternalProject externalProject,
-                                           @NotNull String sourceSetName,
-                                           @NotNull ExternalSystemSourceType sourceType) {
-    final ExternalSourceSet sourceSet = externalProject.getSourceSets().get(sourceSetName);
-    if (sourceSet == null) return;
+    DependencyScope dependencyScope = getDependencyScope(dependency.getScope());
+    if (dependency instanceof ExternalProjectDependency) {
+      String moduleId = getModuleId((ExternalProjectDependency)dependency);
+      Pair<DataNode<GradleSourceSetData>, ExternalSourceSet> projectPair = sourceSetMap.get(moduleId);
+      ModuleDependencyData moduleDependencyData =
+        new ModuleDependencyData(ownerModule.getData().getOwnerModule(), projectPair.first.getData());
+      moduleDependencyData.setScope(dependencyScope);
+      if ("test".equals(projectPair.second.getName())) {
+        moduleDependencyData.setProductionOnTestDependency(true);
+      }
+      moduleDependencyData.setOrder(dependency.getClasspathOrder());
+      moduleDependencyData.setExported(dependency.getExported());
+      DataNode<ModuleDependencyData> ideModuleDependencyNode = ownerModule.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData);
+      for (ExternalDependency externalDependency : dependency.getDependencies()) {
+        buildTransitiveDependency(sourceSetMap, ideModuleDependencyNode, externalDependency, ideProject);
+      }
+    }
+    if (dependency instanceof ExternalLibraryDependency) {
+      final LibraryLevel level = LibraryLevel.PROJECT;
+      String libraryName = dependency.getId().getPresentableName();
+      final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName);
+      LibraryDependencyData libraryDependencyData = new LibraryDependencyData(ownerModule.getData().getOwnerModule(), library, level);
+
+      libraryDependencyData.setScope(dependencyScope);
+      libraryDependencyData.setOrder(dependency.getClasspathOrder());
+      libraryDependencyData.setExported(dependency.getExported());
+      library.addPath(LibraryPathType.BINARY, ((ExternalLibraryDependency)dependency).getFile().getAbsolutePath());
+      File sourcePath = ((ExternalLibraryDependency)dependency).getSource();
+      if (sourcePath != null) {
+        library.addPath(LibraryPathType.SOURCE, sourcePath.getAbsolutePath());
+      }
+      DataNode<LibraryDependencyData> libraryDependencyDataNode =
+        ownerModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, libraryDependencyData);
 
-    final ExternalSourceDirectorySet directorySet = sourceSet.getSources().get(sourceType);
-    if (directorySet != null) {
-      compileOutputPaths.put(sourceType, directorySet.getOutputDir());
+      linkProjectLibrary(ideProject, library);
+      for (ExternalDependency externalDependency : dependency.getDependencies()) {
+        buildTransitiveDependency(sourceSetMap, libraryDependencyDataNode, externalDependency, ideProject);
+      }
     }
   }
+
+  private static void linkProjectLibrary(@NotNull DataNode<ProjectData> ideProject, @NotNull final LibraryData library) {
+    DataNode<LibraryData> libraryData =
+      ExternalSystemApiUtil.find(ideProject, ProjectKeys.LIBRARY, new BooleanFunction<DataNode<LibraryData>>() {
+        @Override
+        public boolean fun(DataNode<LibraryData> node) {
+          return library.equals(node.getData());
+        }
+      });
+    if (libraryData == null) {
+      ideProject.createChild(ProjectKeys.LIBRARY, library);
+    }
+  }
+
+  interface SourceSetsProcessor {
+    void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet);
+  }
 }
index 75dea5c23f124f62c31e0a08e04292f8892d4a20..666fddeee20c44280e582f1768a16995eaa118e8 100644 (file)
@@ -190,6 +190,12 @@ public class GradleExecutionHelper {
         listener.onStatusChange(new ExternalSystemTaskNotificationEvent(id, event.getDescription()));
       }
     });
+    operation.addProgressListener(new org.gradle.tooling.events.ProgressListener() {
+      @Override
+      public void statusChanged(org.gradle.tooling.events.ProgressEvent event) {
+        listener.onStatusChange(new ExternalSystemTaskNotificationEvent(id, event.getDisplayName()));
+      }
+    });
     operation.setStandardOutput(standardOutput);
     operation.setStandardError(standardError);
   }
index d9f7500e28037121bd6cbc56576cc0886d7c20fd..c12133a671e19f448289c9d71976483060000442 100644 (file)
@@ -21,19 +21,19 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.externalSystem.model.DataNode;
 import com.intellij.openapi.externalSystem.model.ExternalSystemException;
 import com.intellij.openapi.externalSystem.model.ProjectKeys;
-import com.intellij.openapi.externalSystem.model.project.LibraryData;
-import com.intellij.openapi.externalSystem.model.project.ModuleData;
-import com.intellij.openapi.externalSystem.model.project.ProjectData;
+import com.intellij.openapi.externalSystem.model.project.*;
 import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
 import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
-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.Key;
 import com.intellij.openapi.util.KeyValue;
 import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ArrayUtil;
 import com.intellij.util.BooleanFunction;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
@@ -48,7 +48,8 @@ import org.gradle.tooling.model.idea.IdeaProject;
 import org.gradle.util.GradleVersion;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.jetbrains.plugins.gradle.model.ProjectImportAction;
+import org.jetbrains.plugins.gradle.model.*;
+import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
 import org.jetbrains.plugins.gradle.remote.impl.GradleLibraryNamesMixer;
 import org.jetbrains.plugins.gradle.service.execution.UnsupportedCancellationToken;
 import org.jetbrains.plugins.gradle.settings.ClassHolder;
@@ -58,6 +59,9 @@ import org.jetbrains.plugins.gradle.util.GradleConstants;
 import java.io.File;
 import java.util.*;
 
+import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.attachGradleSdkSources;
+import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.getModuleId;
+
 /**
  * @author Denis Zhdanov, Vladislav Soroka
  * @since 8/8/11 11:09 AM
@@ -70,6 +74,12 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
   private final GradleLibraryNamesMixer myLibraryNamesMixer = new GradleLibraryNamesMixer();
 
   private final MultiMap<ExternalSystemTaskId, CancellationTokenSource> myCancellationMap = MultiMap.create();
+  public static final Key<Map<String/* module id */, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>>> RESOLVED_SOURCE_SETS =
+    Key.create("resolvedSourceSets");
+  public static final Key<Map<String/* output path */, Pair<String /* module id*/, ExternalSystemSourceType>>> MODULES_OUTPUTS =
+    Key.create("moduleOutputsMap");
+  public static final Key<Map<String/* artifact path */, String /* module id*/>> CONFIGURATION_ARTIFACTS =
+    Key.create("gradleArtifactsMap");
 
   // This constructor is called by external system API, see AbstractExternalSystemFacadeImpl class constructor.
   @SuppressWarnings("UnusedDeclaration")
@@ -175,6 +185,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
       resolverCtx.getSettings(), resolverCtx.getListener(),
       parametersList.getParameters(), commandLineArgs, resolverCtx.getConnection());
 
+    resolverCtx.checkCancelled();
+
     ProjectImportAction.AllModels allModels;
     final CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource();
     try {
@@ -191,6 +203,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
       }
     }
     catch (UnsupportedVersionException unsupportedVersionException) {
+      resolverCtx.checkCancelled();
+
       // Old gradle distribution version used (before ver. 1.8)
       // fallback to use ModelBuilder gradle tooling API
       Class<? extends IdeaProject> aClass = resolverCtx.isPreviewMode() ? BasicIdeaProject.class : IdeaProject.class;
@@ -211,7 +225,10 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
       }
     }
 
+    resolverCtx.checkCancelled();
+
     allModels.setBuildEnvironment(buildEnvironment);
+    extractExternalProjectModels(allModels, resolverCtx.isPreviewMode());
     resolverCtx.setModels(allModels);
 
     // import project data
@@ -230,7 +247,17 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
     if (gradleModules == null || gradleModules.isEmpty()) {
       throw new IllegalStateException("No modules found for the target project: " + ideaProject);
     }
-    final Map<String, Pair<DataNode<ModuleData>, IdeaModule>> moduleMap = ContainerUtilRt.newHashMap();
+
+    final Map<String /* module id */, Pair<DataNode<ModuleData>, IdeaModule>> moduleMap = ContainerUtilRt.newHashMap();
+    final Map<String /* module id */, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetsMap = ContainerUtil.newHashMap();
+    projectDataNode.putUserData(RESOLVED_SOURCE_SETS, sourceSetsMap);
+
+    final Map<String/* output path */, Pair<String /* module id*/, ExternalSystemSourceType>> moduleOutputsMap =
+      ContainerUtil.newTroveMap(FileUtil.PATH_HASHING_STRATEGY);
+    projectDataNode.putUserData(MODULES_OUTPUTS, moduleOutputsMap);
+    final Map<String/* artifact path */, String /* module id*/> artifactsMap =
+      ContainerUtil.newTroveMap(FileUtil.PATH_HASHING_STRATEGY);
+    projectDataNode.putUserData(CONFIGURATION_ARTIFACTS, artifactsMap);
 
     // import modules data
     for (IdeaModule gradleModule : gradleModules) {
@@ -238,46 +265,77 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
         continue;
       }
 
+      resolverCtx.checkCancelled();
+
       if (ExternalSystemDebugEnvironment.DEBUG_ORPHAN_MODULES_PROCESSING) {
         LOG.info(String.format("Importing module data: %s", gradleModule));
       }
-
       final String moduleName = gradleModule.getName();
       if (moduleName == null) {
         throw new IllegalStateException("Module with undefined name detected: " + gradleModule);
       }
 
-      ModuleData moduleData = projectResolverChain.createModule(gradleModule, projectData);
-
-      Pair<DataNode<ModuleData>, IdeaModule> previouslyParsedModule = moduleMap.get(moduleName);
-      if (previouslyParsedModule != null) {
-        throw new IllegalStateException(
-          String.format("Modules with duplicate name (%s) detected: '%s' and '%s'", moduleName, moduleData, previouslyParsedModule)
-        );
-      }
-      DataNode<ModuleData> moduleDataNode = projectDataNode.createChild(ProjectKeys.MODULE, moduleData);
-      moduleMap.put(moduleName, Pair.create(moduleDataNode, gradleModule));
-      if(StringUtil.equals(moduleData.getLinkedExternalProjectPath(), projectData.getLinkedExternalProjectPath())) {
-        projectData.setGroup(moduleData.getGroup());
-        projectData.setVersion(moduleData.getVersion());
-      }
+      DataNode<ModuleData> moduleDataNode = projectResolverChain.createModule(gradleModule, projectDataNode);
+      String mainModuleId = getModuleId(gradleModule);
+      moduleMap.put(mainModuleId, Pair.create(moduleDataNode, gradleModule));
     }
 
+    File gradleHomeDir = null;
     // populate modules nodes
-    final List<TaskData> allTasks = ContainerUtil.newArrayList();
     for (final Pair<DataNode<ModuleData>, IdeaModule> pair : moduleMap.values()) {
       final DataNode<ModuleData> moduleDataNode = pair.first;
       final IdeaModule ideaModule = pair.second;
+
+      if (gradleHomeDir == null) {
+        final BuildScriptClasspathModel buildScriptClasspathModel =
+          resolverCtx.getExtraProject(ideaModule, BuildScriptClasspathModel.class);
+        if (buildScriptClasspathModel != null) {
+          gradleHomeDir = buildScriptClasspathModel.getGradleHomeDir();
+        }
+      }
+
       projectResolverChain.populateModuleExtraModels(ideaModule, moduleDataNode);
       projectResolverChain.populateModuleContentRoots(ideaModule, moduleDataNode);
       projectResolverChain.populateModuleCompileOutputSettings(ideaModule, moduleDataNode);
-      projectResolverChain.populateModuleDependencies(ideaModule, moduleDataNode, projectDataNode);
       if (!isBuildSrcProject) {
-        final Collection<TaskData> moduleTasks = projectResolverChain.populateModuleTasks(ideaModule, moduleDataNode, projectDataNode);
-        allTasks.addAll(moduleTasks);
+        projectResolverChain.populateModuleTasks(ideaModule, moduleDataNode, projectDataNode);
+      }
+
+      final List<DataNode<? extends ModuleData>> modules = ContainerUtil.newSmartList();
+      modules.add(moduleDataNode);
+      modules.addAll(ExternalSystemApiUtil.findAll(moduleDataNode, GradleSourceSetData.KEY));
+
+      final ExternalSystemSourceType[] sourceTypes = new ExternalSystemSourceType[]{
+        ExternalSystemSourceType.SOURCE,
+        ExternalSystemSourceType.RESOURCE,
+        ExternalSystemSourceType.TEST,
+        ExternalSystemSourceType.TEST_RESOURCE
+      };
+      for (DataNode<? extends ModuleData> module : modules) {
+        final ModuleData moduleData = module.getData();
+        for (ExternalSystemSourceType sourceType : sourceTypes) {
+          final String path = moduleData.getCompileOutputPath(sourceType);
+          if (path != null) {
+            moduleOutputsMap.put(path, Pair.create(moduleData.getId(), sourceType));
+          }
+        }
+
+        if (moduleData instanceof GradleSourceSetData) {
+          for (File artifactFile : moduleData.getArtifacts()) {
+            artifactsMap.put(ExternalSystemApiUtil.toCanonicalPath(artifactFile.getAbsolutePath()), moduleData.getId());
+          }
+        }
       }
     }
 
+    for (final Pair<DataNode<ModuleData>, IdeaModule> pair : moduleMap.values()) {
+      final DataNode<ModuleData> moduleDataNode = pair.first;
+      final IdeaModule ideaModule = pair.second;
+      projectResolverChain.populateModuleDependencies(ideaModule, moduleDataNode, projectDataNode);
+    }
+
+    mergeLibraryAndModuleDependencyData(projectDataNode, gradleHomeDir, gradleVersion);
+
     // ensure unique library names
     Collection<DataNode<LibraryData>> libraries = ExternalSystemApiUtil.getChildren(projectDataNode, ProjectKeys.LIBRARY);
     myLibraryNamesMixer.mixNames(libraries);
@@ -285,6 +343,193 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
     return projectDataNode;
   }
 
+  private static void mergeLibraryAndModuleDependencyData(DataNode<ProjectData> projectDataNode,
+                                                          @Nullable File gradleHomeDir,
+                                                          @Nullable GradleVersion gradleVersion) {
+    final Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap =
+      projectDataNode.getUserData(RESOLVED_SOURCE_SETS);
+    assert sourceSetMap != null;
+
+    final Map<String, Pair<String, ExternalSystemSourceType>> moduleOutputsMap =
+      projectDataNode.getUserData(MODULES_OUTPUTS);
+    assert moduleOutputsMap != null;
+
+    final Map<String, String> artifactsMap = projectDataNode.getUserData(CONFIGURATION_ARTIFACTS);
+    assert artifactsMap != null;
+
+    final Collection<DataNode<LibraryDependencyData>> libraryDependencies =
+      ExternalSystemApiUtil.findAllRecursively(projectDataNode, ProjectKeys.LIBRARY_DEPENDENCY);
+
+    for (DataNode<LibraryDependencyData> libraryDependencyDataNode : libraryDependencies) {
+      if (!libraryDependencyDataNode.getChildren().isEmpty()) continue;
+
+      final DataNode<?> libraryNodeParent = libraryDependencyDataNode.getParent();
+      if (libraryNodeParent == null) continue;
+
+      final LibraryDependencyData libraryDependencyData = libraryDependencyDataNode.getData();
+      final LibraryData libraryData = libraryDependencyData.getTarget();
+      final Set<String> libraryPaths = libraryData.getPaths(LibraryPathType.BINARY);
+      if (libraryPaths.isEmpty()) continue;
+
+      final LinkedList<String> unprocessedPaths = ContainerUtil.newLinkedList(libraryPaths);
+      while (!unprocessedPaths.isEmpty()) {
+        final String path = unprocessedPaths.remove();
+
+        Set<String> targetModuleOutputPaths = null;
+
+        final String moduleId;
+        final Pair<String, ExternalSystemSourceType> sourceTypePair = moduleOutputsMap.get(path);
+        if (sourceTypePair == null) {
+          moduleId = artifactsMap.get(path);
+          if (moduleId != null) {
+            targetModuleOutputPaths = ContainerUtil.set(path);
+          }
+        }
+        else {
+          moduleId = sourceTypePair.first;
+        }
+        if (moduleId == null) continue;
+
+        final Pair<DataNode<GradleSourceSetData>, ExternalSourceSet> pair = sourceSetMap.get(moduleId);
+        if (pair == null) {
+          continue;
+        }
+
+        final ModuleData moduleData = pair.first.getData();
+        if (targetModuleOutputPaths == null) {
+          final Set<String> compileSet = ContainerUtil.newHashSet();
+          ContainerUtil.addAllNotNull(compileSet,
+                                      moduleData.getCompileOutputPath(ExternalSystemSourceType.SOURCE),
+                                      moduleData.getCompileOutputPath(ExternalSystemSourceType.RESOURCE));
+          if (!compileSet.isEmpty() && libraryPaths.containsAll(compileSet)) {
+            targetModuleOutputPaths = compileSet;
+          }
+          else {
+            final Set<String> testSet = ContainerUtil.newHashSet();
+            ContainerUtil.addAllNotNull(testSet,
+                                        moduleData.getCompileOutputPath(ExternalSystemSourceType.TEST),
+                                        moduleData.getCompileOutputPath(ExternalSystemSourceType.TEST_RESOURCE));
+            if (compileSet.isEmpty() && libraryPaths.containsAll(testSet)) {
+              targetModuleOutputPaths = testSet;
+            }
+          }
+        }
+
+        final ModuleData ownerModule = libraryDependencyData.getOwnerModule();
+        final ModuleDependencyData moduleDependencyData = new ModuleDependencyData(ownerModule, moduleData);
+        moduleDependencyData.setScope(libraryDependencyData.getScope());
+        if ("test".equals(pair.second.getName())) {
+          moduleDependencyData.setProductionOnTestDependency(true);
+        }
+        final DataNode<ModuleDependencyData> found = ExternalSystemApiUtil.find(
+          libraryNodeParent, ProjectKeys.MODULE_DEPENDENCY, new BooleanFunction<DataNode<ModuleDependencyData>>() {
+            @Override
+            public boolean fun(DataNode<ModuleDependencyData> node) {
+              return moduleDependencyData.equals(node.getData());
+            }
+          });
+
+        if (targetModuleOutputPaths != null) {
+          if (found == null) {
+            libraryNodeParent.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData);
+          }
+          libraryPaths.removeAll(targetModuleOutputPaths);
+          unprocessedPaths.removeAll(targetModuleOutputPaths);
+          if (libraryPaths.isEmpty()) {
+            libraryDependencyDataNode.clear(true);
+            break;
+          }
+          continue;
+        }
+        else {
+          // do not add the path as library dependency if another module dependency is already contain the path as one of its output paths
+          if (found != null) {
+            libraryPaths.remove(path);
+            if (libraryPaths.isEmpty()) {
+              libraryDependencyDataNode.clear(true);
+              break;
+            }
+            continue;
+          }
+        }
+
+        final ExternalSourceDirectorySet directorySet = pair.second.getSources().get(sourceTypePair.second);
+        if (directorySet != null) {
+          for (File file : directorySet.getSrcDirs()) {
+            libraryData.addPath(LibraryPathType.SOURCE, file.getAbsolutePath());
+          }
+        }
+      }
+
+      if (libraryDependencyDataNode.getParent() != null) {
+        if (libraryPaths.size() > 1) {
+          List<String> toRemove = ContainerUtil.newSmartList();
+          for (String path : libraryPaths) {
+            final File binaryPath = new File(path);
+            if (binaryPath.isFile()) {
+              final LibraryData extractedLibrary = new LibraryData(libraryDependencyData.getOwner(), "");
+              extractedLibrary.addPath(LibraryPathType.BINARY, path);
+              if (gradleHomeDir != null && gradleVersion != null) {
+                attachGradleSdkSources(binaryPath, extractedLibrary, gradleHomeDir, gradleVersion);
+              }
+              LibraryDependencyData extractedDependencyData = new LibraryDependencyData(
+                libraryDependencyData.getOwnerModule(), extractedLibrary, LibraryLevel.MODULE);
+              libraryDependencyDataNode.getParent().createChild(ProjectKeys.LIBRARY_DEPENDENCY, extractedDependencyData);
+
+              toRemove.add(path);
+            }
+          }
+          libraryPaths.removeAll(toRemove);
+          if (libraryPaths.isEmpty()) {
+            libraryDependencyDataNode.clear(true);
+          }
+        }
+      }
+    }
+  }
+
+  private static Map<String, ExternalProject> extractExternalProjectModels(ProjectImportAction.AllModels models, boolean isPreview) {
+    final ExternalProject externalRootProject =
+      isPreview ? models.getExtraProject(null, ExternalProjectPreview.class) : models.getExtraProject(null, ExternalProject.class);
+    if (externalRootProject == null) return Collections.emptyMap();
+
+    final DefaultExternalProject wrappedExternalRootProject = new DefaultExternalProject(externalRootProject);
+    models.addExtraProject(wrappedExternalRootProject, ExternalProject.class);
+    final Map<String, ExternalProject> externalProjectsMap = createExternalProjectsMap(wrappedExternalRootProject);
+
+    DomainObjectSet<? extends IdeaModule> gradleModules = models.getIdeaProject().getModules();
+    if (gradleModules != null && !gradleModules.isEmpty()) {
+      for (IdeaModule ideaModule : gradleModules) {
+        final ExternalProject externalProject = externalProjectsMap.get(getModuleId(ideaModule));
+        if (externalProject != null) {
+          models.addExtraProject(externalProject, ExternalProject.class, ideaModule);
+        }
+      }
+    }
+
+    return externalProjectsMap;
+  }
+
+  private static Map<String, ExternalProject> createExternalProjectsMap(@Nullable final ExternalProject rootExternalProject) {
+    final Map<String, ExternalProject> externalProjectMap = ContainerUtilRt.newHashMap();
+
+    if (rootExternalProject == null) return externalProjectMap;
+
+    Queue<ExternalProject> queue = new LinkedList<ExternalProject>();
+    queue.add(rootExternalProject);
+
+    while (!queue.isEmpty()) {
+      ExternalProject externalProject = queue.remove();
+      queue.addAll(externalProject.getChildProjects().values());
+      final String moduleName = externalProject.getName();
+      final String qName = externalProject.getQName();
+      String moduleId = StringUtil.isEmpty(qName) || ":".equals(qName) ? moduleName : qName;
+      externalProjectMap.put(moduleId, externalProject);
+    }
+
+    return externalProjectMap;
+  }
+
   private void handleBuildSrcProject(@NotNull final DataNode<ProjectData> resultProjectDataNode,
                                      @NotNull final ProjectConnectionDataNodeFunction projectConnectionDataNodeFunction) {
 
@@ -315,22 +560,18 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
       projectConnectionDataNodeFunction.myProjectPath, projectConnectionDataNodeFunction.mySettings, projectConnectionDataNodeFunction);
 
     if (buildSrcProjectDataDataNode != null) {
-      final DataNode<ModuleData> moduleDataNode = ExternalSystemApiUtil.find(
-        buildSrcProjectDataDataNode, ProjectKeys.MODULE, new BooleanFunction<DataNode<ModuleData>>() {
-          @Override
-          public boolean fun(DataNode<ModuleData> node) {
-            return projectConnectionDataNodeFunction.myProjectPath.equals(node.getData().getLinkedExternalProjectPath());
-          }
-        });
-      if (moduleDataNode != null) {
-        for (DataNode<LibraryData> libraryDataNode : ExternalSystemApiUtil.findAll(buildSrcProjectDataDataNode, ProjectKeys.LIBRARY)) {
-          resultProjectDataNode.createChild(libraryDataNode.getKey(), libraryDataNode.getData());
-        }
+      for (DataNode<ModuleData> moduleNode : ExternalSystemApiUtil.getChildren(buildSrcProjectDataDataNode, ProjectKeys.MODULE)) {
+        resultProjectDataNode.addChild(moduleNode);
+
+        // adjust ide module group
+        final ModuleData moduleData = moduleNode.getData();
+        if (moduleData.getIdeModuleGroup() != null) {
+          String[] moduleGroup = ArrayUtil.prepend(resultProjectDataNode.getData().getInternalName(), moduleData.getIdeModuleGroup());
+          moduleData.setIdeModuleGroup(moduleGroup);
 
-        final DataNode<ModuleData> newModuleDataNode = resultProjectDataNode.createChild(ProjectKeys.MODULE, moduleDataNode.getData());
-        for (DataNode node : moduleDataNode.getChildren()) {
-          if(!ProjectKeys.MODULE.equals(node.getKey()) && !ProjectKeys.MODULE_DEPENDENCY.equals(node.getKey()))
-          newModuleDataNode.createChild(node.getKey(), node.getData());
+          for (DataNode<GradleSourceSetData> sourceSetNode : ExternalSystemApiUtil.getChildren(moduleNode, GradleSourceSetData.KEY)) {
+            sourceSetNode.getData().setIdeModuleGroup(moduleGroup);
+          }
         }
       }
     }
@@ -375,7 +616,6 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad
     }
   }
 
-
   @NotNull
   public static GradleProjectResolverExtension createProjectResolverChain(@Nullable final GradleExecutionSettings settings) {
     GradleProjectResolverExtension projectResolverChain;
index 308da948053c0bbadb73d6b4a8b09e73ed7c06b6..e4f5214406cc79df51dcf2341b425fa656080070 100644 (file)
@@ -66,7 +66,7 @@ public interface GradleProjectResolverExtension extends ParametersEnhancer {
   void populateProjectExtraModels(@NotNull IdeaProject gradleProject, @NotNull DataNode<ProjectData> ideProject);
 
   @NotNull
-  ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData);
+  DataNode<ModuleData> createModule(@NotNull IdeaModule gradleModule, @NotNull DataNode<ProjectData> projectDataNode);
 
   /**
    * Populates extra models of the given ide module on the basis of the information provided by {@link org.jetbrains.plugins.gradle.tooling.ModelBuilderService}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java
new file mode 100644 (file)
index 0000000..d80071e
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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 org.jetbrains.plugins.gradle.service.project;
+
+import com.intellij.openapi.externalSystem.model.project.LibraryData;
+import com.intellij.openapi.externalSystem.model.project.LibraryPathType;
+import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import org.gradle.tooling.model.GradleProject;
+import org.gradle.tooling.model.idea.IdeaModule;
+import org.gradle.util.GradleVersion;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel;
+import org.jetbrains.plugins.gradle.model.ExternalProject;
+import org.jetbrains.plugins.gradle.model.ExternalProjectDependency;
+import org.jetbrains.plugins.gradle.model.ExternalSourceSet;
+
+import java.io.File;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/6/2015
+ */
+public class GradleProjectResolverUtil {
+
+  @NotNull
+  public static String getModuleId(@NotNull IdeaModule gradleModule) {
+    GradleProject gradleProject = gradleModule.getGradleProject();
+    return getModuleId(gradleProject.getPath(), gradleProject.getName());
+  }
+
+  @NotNull
+  public static String getModuleId(@NotNull ExternalProject externalProject) {
+    return getModuleId(externalProject.getQName(), externalProject.getName());
+  }
+
+  @NotNull
+  public static String getModuleId(String gradlePath, String moduleName) {
+    return StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath) ? moduleName : gradlePath;
+  }
+
+  @NotNull
+  public static String getModuleId(@NotNull ExternalProject externalProject, @NotNull ExternalSourceSet sourceSet) {
+    String mainModuleId = getModuleId(externalProject);
+    return mainModuleId + ":" + sourceSet.getName();
+  }
+
+  @NotNull
+  public static String getModuleId(@NotNull ExternalProjectDependency projectDependency) {
+    DependencyScope dependencyScope = getDependencyScope(projectDependency.getScope());
+    String projectPath = projectDependency.getProjectPath();
+    String moduleId = StringUtil.isEmpty(projectPath) || ":".equals(projectPath) ? projectDependency.getName() : projectPath;
+    if (dependencyScope == DependencyScope.TEST) {
+      moduleId += ":test";
+    }
+    else {
+      moduleId += ":main";
+    }
+    return moduleId;
+  }
+
+  @NotNull
+  public static DependencyScope getDependencyScope(@Nullable String scope) {
+    return scope != null ? DependencyScope.valueOf(scope) : DependencyScope.COMPILE;
+  }
+
+  public static void attachGradleSdkSources(@NotNull final IdeaModule gradleModule,
+                                            @Nullable final File libFile,
+                                            @NotNull final LibraryData library,
+                                            @NotNull final ProjectResolverContext resolverCtx) {
+    final BuildScriptClasspathModel buildScriptClasspathModel =
+      resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class);
+    if (buildScriptClasspathModel == null) return;
+    final File gradleHomeDir = buildScriptClasspathModel.getGradleHomeDir();
+    if (gradleHomeDir == null) return;
+    final GradleVersion gradleVersion = GradleVersion.version(buildScriptClasspathModel.getGradleVersion());
+    attachGradleSdkSources(libFile, library, gradleHomeDir, gradleVersion);
+  }
+
+  public static void attachGradleSdkSources(@Nullable final File libFile,
+                                            @NotNull final LibraryData library,
+                                            @NotNull final File gradleHomeDir,
+                                            @NotNull final GradleVersion gradleVersion) {
+    if (libFile == null || !libFile.getName().startsWith("gradle-")) return;
+    if (!FileUtil.isAncestor(gradleHomeDir, libFile, true)) return;
+
+    File libOrPluginsFile = libFile.getParentFile();
+    if (libOrPluginsFile != null && ("plugins".equals(libOrPluginsFile.getName()))) {
+      libOrPluginsFile = libOrPluginsFile.getParentFile();
+    }
+
+    if (libOrPluginsFile != null && "lib".equals(libOrPluginsFile.getName()) && libOrPluginsFile.getParentFile() != null) {
+      File srcDir = new File(libOrPluginsFile.getParentFile(), "src");
+
+      if (gradleVersion.compareTo(GradleVersion.version("1.9")) >= 0) {
+        int endIndex = libFile.getName().indexOf(gradleVersion.getVersion());
+        if (endIndex != -1) {
+          String srcDirChild = libFile.getName().substring("gradle-".length(), endIndex - 1);
+          srcDir = new File(srcDir, srcDirChild);
+        }
+      }
+
+      if (srcDir.isDirectory()) {
+        library.addPath(LibraryPathType.SOURCE, srcDir.getAbsolutePath());
+      }
+    }
+  }
+
+  public static boolean isIdeaTask(final String taskName, @Nullable String group) {
+    if ((group == null || "ide".equalsIgnoreCase(group)) && StringUtil.containsIgnoreCase(taskName, "idea")) return true;
+    return "other".equalsIgnoreCase(group) && StringUtil.containsIgnoreCase(taskName, "idea");
+  }
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectStructureCustomizer.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectStructureCustomizer.java
new file mode 100644 (file)
index 0000000..31f6dcc
--- /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 org.jetbrains.plugins.gradle.service.project;
+
+import com.intellij.openapi.externalSystem.ExternalSystemUiAware;
+import com.intellij.openapi.externalSystem.importing.ExternalProjectStructureCustomizer;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.project.Identifiable;
+import com.intellij.openapi.util.Couple;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 5/13/2015
+ */
+public class GradleProjectStructureCustomizer extends ExternalProjectStructureCustomizer {
+
+  private final Set<Key<GradleSourceSetData>> myKeys = Collections.singleton(GradleSourceSetData.KEY);
+
+  @NotNull
+  @Override
+  public Set<? extends Key<?>> getIgnorableDataKeys() {
+    return myKeys;
+  }
+
+  @NotNull
+  @Override
+  public Set<? extends Key<?>> getPublicDataKeys() {
+    return myKeys;
+  }
+
+  @NotNull
+  @Override
+  public Set<? extends Key<? extends Identifiable>> getDependencyAwareDataKeys() {
+    return myKeys;
+  }
+
+  @Nullable
+  @Override
+  public Icon suggestIcon(@NotNull DataNode node, @NotNull ExternalSystemUiAware uiAware) {
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public Couple<String> getRepresentationName(@NotNull DataNode node) {
+    if (node.getKey().equals(GradleSourceSetData.KEY)) {
+      final GradleSourceSetData data = (GradleSourceSetData)node.getData();
+      return Couple.of("Source Set", StringUtil.substringAfter(data.getExternalName(), ":"));
+    }
+    return super.getRepresentationName(node);
+  }
+}
index 3aa0027185bfc39581400b5855ea1cc2cb86f933..a42352251703d26d0b60edb560dec63d19f0fcc8 100644 (file)
@@ -17,9 +17,10 @@ package org.jetbrains.plugins.gradle.service.project;
 
 import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
 import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import org.gradle.tooling.CancellationTokenSource;
 import org.gradle.tooling.ProjectConnection;
 import org.gradle.tooling.model.idea.IdeaModule;
-import org.gradle.tooling.model.idea.IdeaProject;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.plugins.gradle.model.ProjectImportAction;
@@ -36,6 +37,7 @@ public class ProjectResolverContext {
   @NotNull private final String myProjectPath;
   @Nullable private final GradleExecutionSettings mySettings;
   @NotNull private final ProjectConnection myConnection;
+  @Nullable private CancellationTokenSource myCancellationTokenSource;
   @NotNull private final ExternalSystemTaskNotificationListener myListener;
   private final boolean myIsPreviewMode;
   @NotNull
@@ -60,6 +62,11 @@ public class ProjectResolverContext {
     return myExternalSystemTaskId;
   }
 
+  @Nullable
+  public String getIdeProjectPath() {
+    return mySettings != null ? mySettings.getIdeProjectPath() : null;
+  }
+
   @NotNull
   public String getProjectPath() {
     return myProjectPath;
@@ -75,6 +82,15 @@ public class ProjectResolverContext {
     return myConnection;
   }
 
+  @Nullable
+  public CancellationTokenSource getCancellationTokenSource() {
+    return myCancellationTokenSource;
+  }
+
+  public void setCancellationTokenSource(@Nullable CancellationTokenSource cancellationTokenSource) {
+    myCancellationTokenSource = cancellationTokenSource;
+  }
+
   @NotNull
   public ExternalSystemTaskNotificationListener getListener() {
     return myListener;
@@ -107,4 +123,10 @@ public class ProjectResolverContext {
   public Collection<String> findModulesWithModel(@NotNull Class modelClazz) {
     return myModels.findModulesWithModel(modelClazz);
   }
+
+  public void checkCancelled() {
+    if (myCancellationTokenSource != null && myCancellationTokenSource.token().isCancellationRequested()) {
+      throw new ProcessCanceledException();
+    }
+  }
 }
index 21bffe80b9674c3a6a362821c23b239d38576b8f..b5bb4781d6badc64bef33f5160c8ef893f8fc542 100644 (file)
@@ -33,9 +33,12 @@ 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.util.NotNullLazyValue;
 import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.FactoryMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData;
@@ -65,13 +68,10 @@ public class BuildClasspathModuleGradleDataService extends AbstractProjectDataSe
 
   @Override
   public void importData(@NotNull final Collection<DataNode<BuildScriptClasspathData>> toImport,
-                         @Nullable ProjectData projectData,
+                         @Nullable final ProjectData projectData,
                          @NotNull final Project project,
-                         @NotNull IdeModifiableModelsProvider modelsProvider) {
-    if (toImport.isEmpty()) {
-      return;
-    }
-    if (!project.isInitialized()) {
+                         @NotNull final IdeModifiableModelsProvider modelsProvider) {
+    if (projectData == null || toImport.isEmpty()) {
       return;
     }
 
@@ -81,20 +81,19 @@ public class BuildClasspathModuleGradleDataService extends AbstractProjectDataSe
     assert manager != null;
     AbstractExternalSystemLocalSettings localSettings = manager.getLocalSettingsProvider().fun(project);
 
-    //noinspection MismatchedQueryAndUpdateOfCollection
-    Map<String/* externalProjectPath */, Set<String>> externalProjectGradleSdkLibs = new FactoryMap<String, Set<String>>() {
-      @Nullable
+    final String linkedExternalProjectPath = projectData.getLinkedExternalProjectPath();
+    final NotNullLazyValue<Set<String>> externalProjectGradleSdkLibs = new NotNullLazyValue<Set<String>>() {
+      @NotNull
       @Override
-      protected Set<String> create(String externalProjectPath) {
-        GradleProjectSettings settings = GradleSettings.getInstance(project).getLinkedProjectSettings(externalProjectPath);
+      protected Set<String> compute() {
+        GradleProjectSettings settings = GradleSettings.getInstance(project).getLinkedProjectSettings(linkedExternalProjectPath);
         if (settings == null || settings.getDistributionType() == null) return Collections.emptySet();
 
         final Set<String> gradleSdkLibraries = ContainerUtil.newLinkedHashSet();
         File gradleHome =
-          gradleInstallationManager.getGradleHome(settings.getDistributionType(), externalProjectPath, settings.getGradleHome());
+          gradleInstallationManager.getGradleHome(settings.getDistributionType(), linkedExternalProjectPath, settings.getGradleHome());
         if (gradleHome != null && gradleHome.isDirectory()) {
-
-          final Collection<File> libraries = gradleInstallationManager.getClassRoots(project, externalProjectPath);
+          final Collection<File> libraries = gradleInstallationManager.getClassRoots(project, linkedExternalProjectPath);
           if (libraries != null) {
             for (File library : libraries) {
               gradleSdkLibraries.add(FileUtil.toCanonicalPath(library.getPath()));
@@ -105,14 +104,33 @@ public class BuildClasspathModuleGradleDataService extends AbstractProjectDataSe
       }
     };
 
+    final NotNullLazyValue<Set<String>> buildSrcProjectsRoots = new NotNullLazyValue<Set<String>>() {
+      @NotNull
+      @Override
+      protected Set<String> compute() {
+        Set<String> result = new LinkedHashSet<String>();
+        //// add main java root of buildSrc project
+        result.add(linkedExternalProjectPath + "/buildSrc/src/main/java");
+        //// add main groovy root of buildSrc project
+        result.add(linkedExternalProjectPath + "/buildSrc/src/main/groovy");
+        for (Module module : modelsProvider.getModules(projectData)) {
+          final String projectPath = ExternalSystemApiUtil.getExternalProjectPath(module);
+          if(projectPath != null && StringUtil.startsWith(projectPath, linkedExternalProjectPath + "/buildSrc")) {
+            final List<String> sourceRoots = ContainerUtil.map(modelsProvider.getSourceRoots(module, false), new Function<VirtualFile, String>() {
+              @Override
+              public String fun(VirtualFile file) {
+                return file.getPath();
+              }
+            });
+            result.addAll(sourceRoots);
+          }
+        }
+        return result;
+      }
+    };
+
     for (final DataNode<BuildScriptClasspathData> node : toImport) {
       if (GradleConstants.SYSTEM_ID.equals(node.getData().getOwner())) {
-
-
-        DataNode<ProjectData> projectDataNode = ExternalSystemApiUtil.findParent(node, ProjectKeys.PROJECT);
-        assert projectDataNode != null;
-
-        String linkedExternalProjectPath = projectDataNode.getData().getLinkedExternalProjectPath();
         DataNode<ModuleData> moduleDataNode = ExternalSystemApiUtil.findParent(node, ProjectKeys.MODULE);
         if (moduleDataNode == null) continue;
 
@@ -144,11 +162,8 @@ public class BuildClasspathModuleGradleDataService extends AbstractProjectDataSe
           localSettings.getProjectBuildClasspath().put(linkedExternalProjectPath, projectBuildClasspathPojo);
         }
 
-        List<String> projectBuildClasspath = ContainerUtil.newArrayList(externalProjectGradleSdkLibs.get(linkedExternalProjectPath));
-        // add main java root of buildSrc project
-        projectBuildClasspath.add(linkedExternalProjectPath + "/buildSrc/src/main/java");
-        // add main groovy root of buildSrc project
-        projectBuildClasspath.add(linkedExternalProjectPath + "/buildSrc/src/main/groovy");
+        List<String> projectBuildClasspath = ContainerUtil.newArrayList(externalProjectGradleSdkLibs.getValue());
+        projectBuildClasspath.addAll(buildSrcProjectsRoots.getValue());
 
         projectBuildClasspathPojo.setProjectBuildClasspath(projectBuildClasspath);
         projectBuildClasspathPojo.getModulesBuildClasspath().put(
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataCache.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataCache.java
new file mode 100644 (file)
index 0000000..17987e4
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 org.jetbrains.plugins.gradle.service.project.data;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ConcurrentFactoryMap;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.model.DefaultExternalProject;
+import org.jetbrains.plugins.gradle.model.ExternalProject;
+import org.jetbrains.plugins.gradle.model.ExternalSourceSet;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Queue;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 9/29/2015
+ */
+public class ExternalProjectDataCache {
+  private static final Logger LOG = Logger.getInstance(ExternalProjectDataCache.class);
+
+  public static ExternalProjectDataCache getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, ExternalProjectDataCache.class);
+  }
+
+  @NotNull private final Project myProject;
+
+  @NotNull private final Map<Pair<ProjectSystemId, File>, ExternalProject> myExternalRootProjects;
+
+  public ExternalProjectDataCache(@NotNull Project project) {
+    myProject = project;
+    myExternalRootProjects = new ConcurrentFactoryMap<Pair<ProjectSystemId, File>, ExternalProject>() {
+      @Override
+      protected Map<Pair<ProjectSystemId, File>, ExternalProject> createMap() {
+        return ContainerUtil.newConcurrentMap(ExternalSystemUtil.HASHING_STRATEGY);
+      }
+
+      @Nullable
+      @Override
+      protected ExternalProject create(Pair<ProjectSystemId, File> key) {
+        return new ExternalProjectSerializer().load(key.first, key.second);
+      }
+
+      @Override
+      public ExternalProject put(Pair<ProjectSystemId, File> key, ExternalProject value) {
+        new ExternalProjectSerializer().save(value);
+        return super.put(key, value);
+      }
+    };
+  }
+
+
+  @Nullable
+  public ExternalProject getRootExternalProject(@NotNull ProjectSystemId systemId, @NotNull File projectRootDir) {
+    ExternalProject externalProject = myExternalRootProjects.get(Pair.create(systemId, projectRootDir));
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Can not find data for project at: " + projectRootDir);
+      LOG.debug("Existing imported projects paths: " + ContainerUtil.map(
+        myExternalRootProjects.entrySet(),
+        new Function<Map.Entry<Pair<ProjectSystemId, File>, ExternalProject>, Object>() {
+          @Override
+          public Object fun(Map.Entry<Pair<ProjectSystemId, File>, ExternalProject> entry) {
+            //noinspection ConstantConditions
+            if (!(entry.getValue() instanceof ExternalProject)) return null;
+            return Pair.create(entry.getKey(), entry.getValue().getProjectDir());
+          }
+        }));
+    }
+    return externalProject;
+  }
+
+  public void saveExternalProject(@NotNull ExternalProject externalProject) {
+    myExternalRootProjects.put(
+      Pair.create(new ProjectSystemId(externalProject.getExternalSystemId()), externalProject.getProjectDir()),
+      new DefaultExternalProject(externalProject)
+    );
+  }
+
+  @NotNull
+  public Map<String, ExternalSourceSet> findExternalProject(@NotNull ExternalProject parentProject, @NotNull Module module) {
+    String externalProjectId = ExternalSystemApiUtil.getExternalProjectId(module);
+    return externalProjectId != null ? findExternalProject(parentProject, externalProjectId)
+                                     : Collections.<String, ExternalSourceSet>emptyMap();
+  }
+
+  @NotNull
+  private static Map<String, ExternalSourceSet> findExternalProject(@NotNull ExternalProject parentProject,
+                                                                    @NotNull String externalProjectId) {
+    Queue<ExternalProject> queue = ContainerUtil.newLinkedList();
+    queue.add(parentProject);
+
+    while (!queue.isEmpty()) {
+      final ExternalProject externalProject = queue.remove();
+      final String projectId = externalProject.getQName();
+      final Map<String, ExternalSourceSet> result = ContainerUtil.newHashMap();
+      for (Map.Entry<String, ExternalSourceSet> sourceSetEntry : externalProject.getSourceSets().entrySet()) {
+        final String sourceSetName = sourceSetEntry.getKey();
+        final String sourceSetId = projectId + ":" + sourceSetName;
+        if (externalProjectId.equals(sourceSetId)) {
+          result.put(sourceSetName, sourceSetEntry.getValue());
+        }
+      }
+      if(!result.isEmpty() || projectId.equals(externalProjectId)) return result;
+
+      queue.addAll(externalProject.getChildProjects().values());
+    }
+
+    return Collections.emptyMap();
+  }
+
+
+}
index f072265db44f6e7830e019dee2c8d6ca9ac2f668..6aaaf86ca3743127f312e217db9391878ff00f52 100644 (file)
@@ -19,28 +19,16 @@ 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.ProjectData;
 import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
 import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService;
-import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
-import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
 import com.intellij.openapi.externalSystem.util.Order;
-import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Pair;
-import com.intellij.util.Function;
-import com.intellij.util.containers.ConcurrentFactoryMap;
-import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
-import org.jetbrains.plugins.gradle.model.DefaultExternalProject;
 import org.jetbrains.plugins.gradle.model.ExternalProject;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @author Vladislav.Soroka
@@ -52,30 +40,6 @@ public class ExternalProjectDataService extends AbstractProjectDataService<Exter
 
   @NotNull public static final Key<ExternalProject> KEY = Key.create(ExternalProject.class, ProjectKeys.TASK.getProcessingWeight() + 1);
 
-  @NotNull private final Map<Pair<ProjectSystemId, File>, ExternalProject> myExternalRootProjects;
-
-  public ExternalProjectDataService() {
-    myExternalRootProjects = new ConcurrentFactoryMap<Pair<ProjectSystemId, File>, ExternalProject>() {
-
-      @Override
-      protected Map<Pair<ProjectSystemId, File>, ExternalProject> createMap() {
-        return ContainerUtil.newConcurrentMap(ExternalSystemUtil.HASHING_STRATEGY);
-      }
-
-      @Nullable
-      @Override
-      protected ExternalProject create(Pair<ProjectSystemId, File> key) {
-        return new ExternalProjectSerializer().load(key.first, key.second);
-      }
-
-      @Override
-      public ExternalProject put(Pair<ProjectSystemId, File> key, ExternalProject value) {
-        new ExternalProjectSerializer().save(value);
-        return super.put(key, value);
-      }
-    };
-  }
-
   @NotNull
   @Override
   public Key<ExternalProject> getTargetDataKey() {
@@ -91,51 +55,6 @@ public class ExternalProjectDataService extends AbstractProjectDataService<Exter
       throw new IllegalArgumentException(
         String.format("Expected to get a single external project but got %d: %s", toImport.size(), toImport));
     }
-    saveExternalProject(toImport.iterator().next().getData());
-  }
-
-  @Nullable
-  public ExternalProject getRootExternalProject(@NotNull ProjectSystemId systemId, @NotNull File projectRootDir) {
-    ExternalProject externalProject = myExternalRootProjects.get(Pair.create(systemId, projectRootDir));
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Can not find data for project at: " + projectRootDir);
-      LOG.debug("Existing imported projects paths: " + ContainerUtil.map(
-        myExternalRootProjects.entrySet(),
-        new Function<Map.Entry<Pair<ProjectSystemId, File>, ExternalProject>, Object>() {
-          @Override
-          public Object fun(Map.Entry<Pair<ProjectSystemId, File>, ExternalProject> entry) {
-            //noinspection ConstantConditions
-            if (!(entry.getValue() instanceof ExternalProject)) return null;
-            return Pair.create(entry.getKey(), entry.getValue().getProjectDir());
-          }
-        }));
-    }
-    return externalProject;
-  }
-
-  public void saveExternalProject(@NotNull ExternalProject externalProject) {
-    myExternalRootProjects.put(
-      Pair.create(new ProjectSystemId(externalProject.getExternalSystemId()), externalProject.getProjectDir()),
-      new DefaultExternalProject(externalProject)
-    );
-  }
-
-  @Nullable
-  public ExternalProject findExternalProject(@NotNull ExternalProject parentProject, @NotNull Module module) {
-    String externalProjectId = ExternalSystemApiUtil.getExternalProjectId(module);
-    return externalProjectId != null ? findExternalProject(parentProject, externalProjectId) : null;
-  }
-
-  @Nullable
-  private static ExternalProject findExternalProject(@NotNull ExternalProject parentProject, @NotNull String externalProjectId) {
-    if (parentProject.getQName().equals(externalProjectId)) return parentProject;
-    if (parentProject.getChildProjects().containsKey(externalProjectId)) {
-      return parentProject.getChildProjects().get(externalProjectId);
-    }
-    for (ExternalProject externalProject : parentProject.getChildProjects().values()) {
-      final ExternalProject project = findExternalProject(externalProject, externalProjectId);
-      if (project != null) return project;
-    }
-    return null;
+    ExternalProjectDataCache.getInstance(project).saveExternalProject(toImport.iterator().next().getData());
   }
 }
index a92ee45be08ed945141b1e076284958db26b3e98..4a797a22f7fc05776bffcc6da01278c27f03c5d7 100644 (file)
@@ -35,6 +35,7 @@ import gnu.trove.THashMap;
 import gnu.trove.THashSet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.DefaultExternalDependencyId;
 import org.jetbrains.plugins.gradle.model.*;
 import org.objenesis.strategy.StdInstantiatorStrategy;
 
@@ -137,6 +138,56 @@ public class ExternalProjectSerializer {
 
     myKryo.register(ExternalSystemSourceType.class, new DefaultSerializers.EnumSerializer(ExternalSystemSourceType.class));
 
+    myKryo.register(
+      DefaultExternalProjectDependency.class,
+      new FieldSerializer<DefaultExternalProjectDependency>(myKryo, DefaultExternalProjectDependency.class) {
+        @Override
+        protected DefaultExternalProjectDependency create(Kryo kryo, Input input, Class<DefaultExternalProjectDependency> type) {
+          return new DefaultExternalProjectDependency();
+        }
+      }
+    );
+
+    myKryo.register(
+      DefaultFileCollectionDependency.class,
+      new FieldSerializer<DefaultFileCollectionDependency>(myKryo, DefaultFileCollectionDependency.class) {
+        @Override
+        protected DefaultFileCollectionDependency create(Kryo kryo, Input input, Class<DefaultFileCollectionDependency> type) {
+          return new DefaultFileCollectionDependency();
+        }
+      }
+    );
+
+    myKryo.register(
+      DefaultExternalLibraryDependency.class,
+      new FieldSerializer<DefaultExternalLibraryDependency>(myKryo, DefaultExternalLibraryDependency.class) {
+        @Override
+        protected DefaultExternalLibraryDependency create(Kryo kryo, Input input, Class<DefaultExternalLibraryDependency> type) {
+          return new DefaultExternalLibraryDependency();
+        }
+      }
+    );
+
+    myKryo.register(
+      DefaultUnresolvedExternalDependency.class,
+      new FieldSerializer<DefaultUnresolvedExternalDependency>(myKryo, DefaultUnresolvedExternalDependency.class) {
+        @Override
+        protected DefaultUnresolvedExternalDependency create(Kryo kryo, Input input, Class<DefaultUnresolvedExternalDependency> type) {
+          return new DefaultUnresolvedExternalDependency();
+        }
+      }
+    );
+
+    myKryo.register(
+      DefaultExternalDependencyId.class,
+      new FieldSerializer<DefaultExternalDependencyId>(myKryo, DefaultExternalDependencyId.class) {
+        @Override
+        protected DefaultExternalDependencyId create(Kryo kryo, Input input, Class<DefaultExternalDependencyId> type) {
+          return new DefaultExternalDependencyId();
+        }
+      }
+    );
+
     myKryo.register(LinkedHashSet.class, new CollectionSerializer() {
       @Override
       protected Collection create(Kryo kryo, Input input, Class<Collection> type) {
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/GradleSourceSetDataService.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/GradleSourceSetDataService.java
new file mode 100644 (file)
index 0000000..9da3435
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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 org.jetbrains.plugins.gradle.service.project.data;
+
+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.IdeModifiableModelsProvider;
+import com.intellij.openapi.externalSystem.service.project.manage.AbstractModuleDataService;
+import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
+import com.intellij.openapi.externalSystem.util.ExternalSystemConstants;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
+import org.jetbrains.plugins.gradle.util.GradleConstants;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 8/5/2015
+ */
+@Order(ExternalSystemConstants.BUILTIN_MODULE_DATA_SERVICE_ORDER + 1)
+public class GradleSourceSetDataService extends AbstractModuleDataService<GradleSourceSetData> {
+
+  @NotNull
+  @Override
+  public Key<GradleSourceSetData> getTargetDataKey() {
+    return GradleSourceSetData.KEY;
+  }
+
+  @NotNull
+  @Override
+  public Computable<Collection<Module>> computeOrphanData(@NotNull final Collection<DataNode<GradleSourceSetData>> toImport,
+                                                          @NotNull final ProjectData projectData,
+                                                          @NotNull final Project project,
+                                                          @NotNull final IdeModifiableModelsProvider modelsProvider) {
+    return new Computable<Collection<Module>>() {
+      @Override
+      public Collection<Module> compute() {
+        List<Module> orphanIdeModules = ContainerUtil.newSmartList();
+
+        for (Module module : modelsProvider.getModules()) {
+          if (module.isDisposed()) continue;
+          if (!ExternalSystemApiUtil.isExternalSystemAwareModule(projectData.getOwner(), module)) continue;
+          if (!GradleConstants.GRADLE_SOURCE_SET_MODULE_TYPE_KEY.equals(ExternalSystemApiUtil.getExternalModuleType(module))) continue;
+
+          final String rootProjectPath = ExternalSystemApiUtil.getExternalRootProjectPath(module);
+          if (projectData.getLinkedExternalProjectPath().equals(rootProjectPath)) {
+            final String projectId = ExternalSystemApiUtil.getExternalProjectId(module);
+
+            final DataNode<GradleSourceSetData> found = ContainerUtil.find(toImport, new Condition<DataNode<GradleSourceSetData>>() {
+              @Override
+              public boolean value(DataNode<GradleSourceSetData> node) {
+                return node.getData().getId().equals(projectId);
+              }
+            });
+
+            if (found == null) {
+              orphanIdeModules.add(module);
+            }
+          }
+        }
+
+        return orphanIdeModules;
+      }
+    };
+  }
+
+  @Override
+  protected void setModuleOptions(Module module, DataNode<GradleSourceSetData> moduleDataNode) {
+    super.setModuleOptions(module, moduleDataNode);
+    module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_MODULE_TYPE_KEY, GradleConstants.GRADLE_SOURCE_SET_MODULE_TYPE_KEY);
+  }
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/view/GradleViewContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/view/GradleViewContributor.java
new file mode 100644 (file)
index 0000000..26c7afa
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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 org.jetbrains.plugins.gradle.service.project.view;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.externalSystem.model.DataNode;
+import com.intellij.openapi.externalSystem.model.Key;
+import com.intellij.openapi.externalSystem.model.ProjectSystemId;
+import com.intellij.openapi.externalSystem.util.Order;
+import com.intellij.openapi.externalSystem.view.ExternalProjectsView;
+import com.intellij.openapi.externalSystem.view.ExternalSystemNode;
+import com.intellij.openapi.externalSystem.view.ExternalSystemViewContributor;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.MultiMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
+import org.jetbrains.plugins.gradle.util.GradleConstants;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 8/5/2015
+ */
+public class GradleViewContributor extends ExternalSystemViewContributor {
+  private static final Key<?>[] KEYS = new Key[]{
+    GradleSourceSetData.KEY,
+  };
+
+
+  @NotNull
+  @Override
+  public ProjectSystemId getSystemId() {
+    return GradleConstants.SYSTEM_ID;
+  }
+
+  @NotNull
+  @Override
+  public List<Key<?>> getKeys() {
+    return Arrays.asList(KEYS);
+  }
+
+  @NotNull
+  @Override
+  public List<ExternalSystemNode<?>> createNodes(ExternalProjectsView externalProjectsView, MultiMap<Key<?>, DataNode<?>> dataNodes) {
+    final List<ExternalSystemNode<?>> result = new SmartList<ExternalSystemNode<?>>();
+    addCustomSourceSetsNodes(externalProjectsView, dataNodes, result);
+    return result;
+  }
+
+  private static void addCustomSourceSetsNodes(@NotNull ExternalProjectsView externalProjectsView,
+                                               @NotNull MultiMap<Key<?>, DataNode<?>> dataNodes,
+                                               @NotNull List<ExternalSystemNode<?>> result) {
+    final Collection<DataNode<?>> sourceSetsDataNodes = dataNodes.get(GradleSourceSetData.KEY);
+    if (!sourceSetsDataNodes.isEmpty()) {
+      final ExternalSystemNode sourceSetsNode = new SourceSetsNode(externalProjectsView);
+      for (DataNode<?> dataNode : sourceSetsDataNodes) {
+        //noinspection unchecked
+        sourceSetsNode.add(new SourceSetNode(externalProjectsView, (DataNode<GradleSourceSetData>)dataNode));
+      }
+      result.add(sourceSetsNode);
+    }
+  }
+
+  @Order(0)
+  private static class SourceSetsNode extends ExternalSystemNode {
+    public SourceSetsNode(ExternalProjectsView externalProjectsView) {
+      //noinspection unchecked
+      super(externalProjectsView, null, null);
+    }
+
+    @Override
+    protected void update(PresentationData presentation) {
+      super.update(presentation);
+      presentation.setIcon(AllIcons.Nodes.ModuleGroup);
+    }
+
+    @Override
+    public String getName() {
+      return "Source Sets";
+    }
+  }
+
+  private static class SourceSetNode extends ExternalSystemNode<GradleSourceSetData> {
+
+    public SourceSetNode(ExternalProjectsView externalProjectsView, DataNode<GradleSourceSetData> dataNode) {
+      super(externalProjectsView, null, dataNode);
+    }
+
+    @Override
+    protected void update(PresentationData presentation) {
+      super.update(presentation);
+      presentation.setIcon(AllIcons.Modules.SourceFolder);
+
+      final GradleSourceSetData data = getData();
+      if (data != null) {
+        setNameAndTooltip(getName(), null);
+      }
+    }
+
+    @Override
+    public String getName() {
+      final GradleSourceSetData data = getData();
+      return data != null ? StringUtil.substringAfter(data.getExternalName(), ":") : "";
+    }
+  }
+}
index 4da922089684807d494ef78e0a7994e84540d4ee..bc88258c2f5d412ec9b7589ffba223e8d75d875c 100644 (file)
@@ -17,6 +17,7 @@ package org.jetbrains.plugins.gradle.service.project.wizard;
 
 import com.intellij.ide.fileTemplates.FileTemplate;
 import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.ide.highlighter.ModuleFileType;
 import com.intellij.ide.projectWizard.ProjectSettingsStep;
 import com.intellij.ide.util.EditorHelper;
 import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
@@ -37,10 +38,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
 import com.intellij.openapi.externalSystem.util.ExternalSystemUtil;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
-import com.intellij.openapi.module.JavaModuleType;
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleType;
-import com.intellij.openapi.module.StdModuleTypes;
+import com.intellij.openapi.module.*;
 import com.intellij.openapi.options.ConfigurationException;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ex.ProjectManagerEx;
@@ -49,6 +47,7 @@ import com.intellij.openapi.projectRoots.SdkTypeId;
 import com.intellij.openapi.roots.ModifiableRootModel;
 import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
 import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.InvalidDataException;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.io.FileUtilRt;
@@ -62,6 +61,7 @@ import com.intellij.psi.PsiManager;
 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
 import com.intellij.util.containers.ContainerUtil;
 import org.gradle.util.GradleVersion;
+import org.jdom.JDOMException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder;
@@ -111,6 +111,23 @@ public class GradleModuleBuilder extends AbstractExternalModuleBuilder<GradlePro
     super(GradleConstants.SYSTEM_ID, new GradleProjectSettings());
   }
 
+  @NotNull
+  @Override
+  public Module createModule(@NotNull ModifiableModuleModel moduleModel)
+    throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
+    LOG.assertTrue(getName() != null);
+    final String originModuleFilePath = getModuleFilePath();
+    LOG.assertTrue(originModuleFilePath != null);
+
+    String moduleName = myProjectId == null ? getName() : myProjectId.getArtifactId();
+    String moduleFilePath = myWizardContext.getProjectFileDirectory() + "/.idea/modules/" + moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION;
+    deleteModuleFile(moduleFilePath);
+    final ModuleType moduleType = getModuleType();
+    final Module module = moduleModel.newModule(moduleFilePath, moduleType.getId());
+    setupModule(module);
+    return module;
+  }
+
   @Override
   public void setupRootModel(final ModifiableRootModel modifiableRootModel) throws ConfigurationException {
     String contentEntryPath = getContentEntryPath();
index 3f08b970d5685f18876dbedc2d3d075ef274c020..da6209608f70263bd2e611d5306af845ca611665 100644 (file)
@@ -46,6 +46,8 @@ public class GradleExecutionSettings extends ExternalSystemExecutionSettings {
   @Nullable private String wrapperPropertyFile;
 
   @Nullable private String myJavaHome;
+  @Nullable
+  private String myIdeProjectPath;
 
   public GradleExecutionSettings(@Nullable String gradleHome,
                                  @Nullable String serviceDirectory,
@@ -61,6 +63,15 @@ public class GradleExecutionSettings extends ExternalSystemExecutionSettings {
     setVerboseProcessing(USE_VERBOSE_GRADLE_API_BY_DEFAULT);
   }
 
+  public void setIdeProjectPath(@Nullable String ideProjectPath) {
+    myIdeProjectPath = ideProjectPath;
+  }
+
+  @Nullable
+  public String getIdeProjectPath() {
+    return myIdeProjectPath;
+  }
+
   @Nullable
   public String getGradleHome() {
     return myGradleHome;
index df51e51eee8a7e2a6e367abfe36d00f78cca94f2..0fb43a7bb69ad95aba366b80e406c0e1e0498662 100644 (file)
@@ -4,8 +4,6 @@ import com.intellij.openapi.externalSystem.model.ProjectSystemId;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.concurrent.TimeUnit;
-
 /**
  * Holds object representation of icons used at the <code>Gradle</code> plugin.
  * 
@@ -29,6 +27,8 @@ public class GradleConstants {
   @NotNull @NonNls public static final String OFFLINE_MODE_CMD_OPTION = "--offline";
   @NotNull @NonNls public static final String INIT_SCRIPT_CMD_OPTION = "--init-script";
 
+  @NotNull @NonNls public static final String GRADLE_SOURCE_SET_MODULE_TYPE_KEY = "sourceSet";
+
   private GradleConstants() {
   }
 }
index df9c6a879ac7a48c5acb58c03d1932293b10526f..94c59d7be40b7efd2efd2e62010538d2bb46b44e 100644 (file)
@@ -43,8 +43,8 @@ public class GradleResourceFilteringTest extends GradleCompilingTestCase {
       "  filter(HeadFilter, lines:3, skip:2)\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main");
 
     assertCopied("build/resources/main/dir/file.txt", "3 another text\n" +
                                                       "4\n" +
@@ -70,8 +70,8 @@ public class GradleResourceFilteringTest extends GradleCompilingTestCase {
       "  filter(ReplaceTokens, tokens:[token1:'<11111>', token2:'<2222>'], beginToken: '#', endToken: '#')\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main");
 
     assertCopied("build/resources/main/dir/file.txt", "1 Header\n" +
                                                       "2\n" +
@@ -94,8 +94,8 @@ public class GradleResourceFilteringTest extends GradleCompilingTestCase {
       "  rename 'file.txt', 'file001.txt'\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main");
 
     assertCopied("build/resources/main/dir/file001.txt");
   }
@@ -121,8 +121,8 @@ public class GradleResourceFilteringTest extends GradleCompilingTestCase {
       "  rename 'file.txt', 'file001.txt'\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main");
 
     assertCopied("build/resources/main/dir/file001.txt", "3 another text<11111>\n" +
                                                          "4\n" +
index 873234425e3038d32f2be1f4136ee039f6d715a3..aea32f8947ae9cf35cdcef6ff72a475ef166ce00 100644 (file)
@@ -33,8 +33,8 @@ public class GradleResourceProcessingTest extends GradleCompilingTestCase {
     importProject(
       "apply plugin: 'java'"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main", "project_test");
 
     assertCopied("build/resources/main/dir/file.properties");
     assertCopied("build/resources/test/dir/file-test.properties");
@@ -55,8 +55,8 @@ public class GradleResourceProcessingTest extends GradleCompilingTestCase {
       "  }\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main", "project_test");
 
     assertCopied("muchBetterOutputDir/dir/file.properties");
     assertCopied("muchBetterTestOutputDir/dir/file-test.properties");
@@ -88,8 +88,8 @@ public class GradleResourceProcessingTest extends GradleCompilingTestCase {
       "  }\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main", "project_test");
 
     assertCopiedResources();
   }
@@ -110,8 +110,8 @@ public class GradleResourceProcessingTest extends GradleCompilingTestCase {
       "  }\n" +
       "}"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main", "project_test");
 
     assertCopiedResources();
   }
@@ -138,8 +138,8 @@ public class GradleResourceProcessingTest extends GradleCompilingTestCase {
       "  exclude '*.xxx'\n" +
       "}\n"
     );
-    assertModules("project");
-    compileModules("project");
+    assertModules("project", "project_main", "project_test");
+    compileModules("project_main", "project_test");
 
     assertCopiedResources();
   }
index 2fc5a625848fb47f04ceedb5592c2ce2bcb65b44..2ce927ad487751923a8fab947455d7c70a7ec43d 100644 (file)
@@ -57,8 +57,10 @@ public class GradleClassFinderTest extends GradleImportingTestCase {
     importProject("subprojects {\n" +
                   "    apply plugin: 'groovy'\n" +
                   "}");
-    assertModules("multiproject", "app", "buildSrc");
-    Module buildSrcModule = getModule("buildSrc");
+    assertModules("multiproject",
+                  "app", "app_main", "app_test",
+                  "buildSrc", "buildSrc_main", "buildSrc_test");
+    Module buildSrcModule = getModule("buildSrc_main");
     assertNotNull(buildSrcModule);
     final AccessToken accessToken = ReadAction.start();
     try {
index 922b3ea48eb4c69576249b6dd85f25382c811e0a..8adadf48661f6e255a2db6ab0004f6fc2223ac3d 100644 (file)
 package org.jetbrains.plugins.gradle.importing;
 
 import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderRootType;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PathUtil;
 import org.gradle.util.GradleVersion;
 import org.junit.Test;
 
+import java.util.List;
+
 /**
  * @author Vladislav.Soroka
  * @since 6/30/2014
@@ -50,16 +55,61 @@ public class GradleDependenciesImportingTest extends GradleImportingTestCase {
       "}"
     );
 
-    assertModules("project", "api", "impl");
-    assertModuleModuleDepScope("project", "api", DependencyScope.COMPILE);
+    assertModules("project", "project_main", "project_test", "api", "api_main", "api_test", "impl", "impl_main", "impl_test");
+    assertModuleModuleDepScope("project_test", "project_main", DependencyScope.COMPILE);
+    assertModuleModuleDepScope("api_test", "api_main", DependencyScope.COMPILE);
+    assertModuleModuleDepScope("impl_test", "impl_main", DependencyScope.COMPILE);
+
+    assertModuleModuleDepScope("project_main", "api_main", DependencyScope.COMPILE);
 
-    // scope merge works incorrectly in Gradle lower than 1.12 version
-    if (GradleVersion.version(gradleVersion).compareTo(GradleVersion.version("1.12")) >= 0) {
-      assertModuleModuleDepScope("project", "impl", DependencyScope.RUNTIME, DependencyScope.TEST);
-    }
+    assertModuleModuleDepScope("project_main", "impl_main", DependencyScope.RUNTIME);
+    assertModuleModuleDepScope("project_test", "impl_main", DependencyScope.COMPILE);
 
-    assertModuleLibDepScope("project", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.TEST);
-    assertModuleLibDepScope("project", "Gradle: junit:junit:4.11", DependencyScope.TEST);
+    assertModuleLibDepScope("project_test", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.COMPILE);
+    assertModuleLibDepScope("project_test", "Gradle: junit:junit:4.11", DependencyScope.COMPILE);
+  }
+
+  @Test
+  public void testCustomSourceSetsDependencies() throws Exception {
+    createSettingsFile("include 'api', 'impl' ");
+
+    importProject(
+      "allprojects {\n" +
+      "  apply plugin: 'java'\n" +
+      "\n" +
+      "  sourceCompatibility = 1.5\n" +
+      "  version = '1.0'\n" +
+      "\n" +
+      "  repositories {\n" +
+      "    mavenCentral()\n" +
+      "  }\n" +
+      "}\n" +
+      "\n" +
+      "project(\"impl\") {\n" +
+      "  sourceSets {\n" +
+      "    myCustomSourceSet\n" +
+      "    myAnotherSourceSet\n" +
+      "  }\n" +
+      "  \n" +
+      "  dependencies {\n" +
+      "    myCustomSourceSetCompile sourceSets.main.output\n" +
+      "    myCustomSourceSetCompile project(\":api\")\n" +
+      "    myCustomSourceSetRuntime 'junit:junit:4.11'\n" +
+      "  }\n" +
+      "}\n"
+    );
+
+    assertModules("project", "project_main", "project_test", "api", "api_main", "api_test", "impl", "impl_main", "impl_test",
+                  "impl_myCustomSourceSet", "impl_myAnotherSourceSet");
+
+    assertModuleModuleDepScope("project_test", "project_main", DependencyScope.COMPILE);
+    assertModuleModuleDepScope("api_test", "api_main", DependencyScope.COMPILE);
+    assertModuleModuleDepScope("impl_test", "impl_main", DependencyScope.COMPILE);
+
+    assertModuleModuleDepScope("impl_myCustomSourceSet", "impl_main", DependencyScope.COMPILE);
+    assertModuleModuleDepScope("impl_myCustomSourceSet", "api_main", DependencyScope.COMPILE);
+    assertModuleLibDepScope("impl_myCustomSourceSet", "Gradle: org.hamcrest:hamcrest-core:1.3", DependencyScope.RUNTIME);
+    assertModuleLibDepScope("impl_myCustomSourceSet", "Gradle: junit:junit:4.11", DependencyScope.RUNTIME);
   }
 
   @Test
@@ -75,7 +125,6 @@ public class GradleDependenciesImportingTest extends GradleImportingTestCase {
       "  version = '1.0'\n" +
       "\n" +
       "  repositories {\n" +
-      "    mavenCentral()\n" +
       "    maven{ url file('lib') }\n" +
       "  }\n" +
       "}\n" +
@@ -87,15 +136,74 @@ public class GradleDependenciesImportingTest extends GradleImportingTestCase {
       "}"
     );
 
-    assertModules("project");
+    assertModules("project", "project_main", "project_test");
+
+    assertModuleModuleDepScope("project_test", "project_main", DependencyScope.COMPILE);
 
-    assertModuleLibDep("project", "Gradle: dep:dep:1.0", depJar.getUrl());
-    assertModuleLibDepScope("project", "Gradle: dep:dep:1.0", DependencyScope.COMPILE);
+    final String depName = "Gradle: dep:dep:1.0";
+    assertModuleLibDep("project_main", depName, depJar.getUrl());
+    assertModuleLibDepScope("project_main", depName, DependencyScope.COMPILE);
+    assertModuleLibDep("project_test", depName, depJar.getUrl());
+    assertModuleLibDepScope("project_test", depName, DependencyScope.COMPILE);
+
+    final boolean isArtifactResolutionQuerySupported = GradleVersion.version(gradleVersion).compareTo(GradleVersion.version("2.0")) >= 0;
+    final String depTestsName = isArtifactResolutionQuerySupported ? "Gradle: dep:dep:tests:1.0" : PathUtil.toPresentableUrl(depTestsJar.getUrl());
+    assertModuleLibDep("project_test", depTestsName, depTestsJar.getUrl());
+    assertModuleLibDepScope("project_test", depTestsName, DependencyScope.COMPILE);
+
+    final String depNonJarName = isArtifactResolutionQuerySupported ? "Gradle: dep:dep:someExt:1.0" : PathUtil.toPresentableUrl(depNonJar.getUrl());
+    assertModuleLibDep("project_main", depNonJarName, depNonJar.getUrl());
+    assertModuleLibDepScope("project_main", depNonJarName, DependencyScope.RUNTIME);
+    assertModuleLibDep("project_test", depNonJarName, depNonJar.getUrl());
+    assertModuleLibDepScope("project_test", depNonJarName, DependencyScope.RUNTIME);
+  }
 
-    assertModuleLibDep("project", "Gradle: dep:dep:1.0:tests", depTestsJar.getUrl());
-    assertModuleLibDepScope("project", "Gradle: dep:dep:1.0:tests", DependencyScope.TEST);
+  @Test
+  public void testProjectWithUnresolvedDependency() throws Exception {
+    final VirtualFile depJar = createProjectJarSubFile("lib/dep/dep/1.0/dep-1.0.jar");
+    importProject(
+      "apply plugin: 'java'\n" +
+      "\n" +
+      "repositories {\n" +
+      "  maven { url file('lib') }\n" +
+      "}\n" +
+      "dependencies {\n" +
+      "  compile 'dep:dep:1.0'\n" +
+      "  compile 'some:unresolvable-lib:0.1'\n" +
+      "}\n"
+    );
+
+    assertModules("project", "project_main", "project_test");
+
+    final String depName = "Gradle: dep:dep:1.0";
+    assertModuleLibDep("project_main", depName, depJar.getUrl());
+    assertModuleLibDepScope("project_main", depName, DependencyScope.COMPILE);
+    assertModuleLibDepScope("project_main", "Gradle: some:unresolvable-lib:0.1", DependencyScope.COMPILE);
+
+    final List<LibraryOrderEntry> unresolvableDep = getModuleLibDeps("project_main", "Gradle: some:unresolvable-lib:0.1");
+    assertEquals(1, unresolvableDep.size());
+    final LibraryOrderEntry unresolvableEntry = unresolvableDep.iterator().next();
+    assertFalse(unresolvableEntry.isModuleLevel());
+    assertEquals(DependencyScope.COMPILE, unresolvableEntry.getScope());
+    final String[] unresolvableEntryUrls = unresolvableEntry.getUrls(OrderRootType.CLASSES);
+    assertEquals(1, unresolvableEntryUrls.length);
+    assertTrue(unresolvableEntryUrls[0].contains("Could not find some:unresolvable-lib:0.1"));
+
+    assertModuleLibDep("project_test", depName, depJar.getUrl());
+    assertModuleLibDepScope("project_test", depName, DependencyScope.COMPILE);
+  }
+
+  @Test
+  public void testSourceSetOutputDirsAsRuntimeDependencies() throws Exception {
+    importProject(
+      "apply plugin: 'java'\n" +
+      "sourceSets.main.output.dir file(\"$buildDir/generated-resources/main\")"
+    );
 
-    assertModuleLibDep("project", "Gradle: dep:dep:1.0:someExt", depNonJar.getUrl());
-    assertModuleLibDepScope("project", "Gradle: dep:dep:1.0:someExt", DependencyScope.RUNTIME);
+    assertModules("project", "project_main", "project_test");
+    final String path = pathFromBasedir("build/generated-resources/main");
+    final String depName = PathUtil.toPresentableUrl(path);
+    assertModuleLibDep("project_main", depName, "file://" + path);
+    assertModuleLibDepScope("project_main", depName, DependencyScope.RUNTIME);
   }
 }
index bcba2f73cf36bd3f6468a51a0255b61d3a7fcc93..a8b4f0519741f429179b3139a516d053040757c5 100644 (file)
@@ -33,14 +33,13 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
       "apply plugin: 'java'"
     );
 
-    assertModules("project");
+    assertModules("project", "project_main", "project_test");
     assertContentRoots("project", getProjectPath());
 
     assertDefaultGradleJavaProjectFolders("project");
 
-    assertModuleOutput("project",
-                       getProjectPath() + "/build/classes/main",
-                       getProjectPath() + "/build/classes/test");
+    assertModuleOutput("project_main", getProjectPath() + "/build/classes/main", "");
+    assertModuleOutput("project_test", "", getProjectPath() + "/build/classes/test");
   }
 
   @Test
@@ -56,14 +55,13 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
       "}"
     );
 
-    assertModules("project");
+    assertModules("project", "project_main", "project_test");
     assertContentRoots("project", getProjectPath());
 
     assertDefaultGradleJavaProjectFolders("project");
 
-    assertModuleOutput("project",
-                       getProjectPath() + "/build",
-                       getProjectPath() + "/build/classes/test");
+    assertModuleOutput("project_main", getProjectPath() + "/build", "");
+    assertModuleOutput("project_test", "", getProjectPath() + "/build/classes/test");
   }
 
   @Test
@@ -81,12 +79,12 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
       "}"
     );
 
-    assertModules("project");
+    assertModules("project", "project_main", "project_test");
     assertContentRoots("project", getProjectPath());
 
     assertDefaultGradleJavaProjectFolders("project");
-    assertGeneratedSources("project", "src/main/java");
-    assertGeneratedTestSources("project", "src/test/java");
+    assertGeneratedSources("project_main", "src/main/java");
+    assertGeneratedTestSources("project_test", "src/test/java");
   }
 
   @Test
@@ -102,19 +100,23 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase {
       "}"
     );
 
-    assertModules("project");
+    assertModules("project", "project_main", "project_test");
     assertContentRoots("project", getProjectPath());
 
     assertDefaultGradleJavaProjectFolders("project");
 
     assertModuleInheritedOutput("project");
+    assertModuleInheritedOutput("project_main");
+    assertModuleInheritedOutput("project_test");
   }
 
-  protected void assertDefaultGradleJavaProjectFolders(@NotNull String moduleName) {
-    assertSources(moduleName, "src/main/java");
-    assertResources(moduleName, "src/main/resources");
-    assertTestSources(moduleName, "src/test/java");
-    assertTestResources(moduleName, "src/test/resources");
-    assertExcludes(moduleName, ".gradle", "build");
+  protected void assertDefaultGradleJavaProjectFolders(@NotNull String mainModuleName) {
+    assertExcludes(mainModuleName, ".gradle", "build");
+    final String mainSourceSetModuleName = mainModuleName + "_main";
+    assertSources(mainSourceSetModuleName, "src/main/java");
+    assertResources(mainSourceSetModuleName, "src/main/resources");
+    final String testSourceSetModuleName = mainModuleName + "_test";
+    assertTestSources(testSourceSetModuleName, "src/test/java");
+    assertTestResources(testSourceSetModuleName, "src/test/resources");
   }
 }
index dcc3ab9ea297f14ace640a0bb9cf5b3e8d516d77..83b43e6a7c7f946dedcd6a0b00340bf5402f1ef6 100644 (file)
@@ -190,6 +190,11 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes
     super.importProject();
   }
 
+  @Override
+  protected void importProject(@NonNls @Language("Groovy") String config) throws IOException {
+    super.importProject(config);
+  }
+
   @Override
   protected ExternalProjectSettings getCurrentExternalProjectSettings() {
     return myProjectSettings;
diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/DefaultExternalDependencyId.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/DefaultExternalDependencyId.java
new file mode 100644 (file)
index 0000000..8708820
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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 org.jetbrains.plugins.gradle;
+
+import com.google.common.base.Objects;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.Serializable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 1/19/2015
+ */
+public class DefaultExternalDependencyId implements ExternalDependencyId, Serializable {
+
+  private static final long serialVersionUID = 1L;
+
+  private String group;
+  private String name;
+  private String version;
+  @NotNull
+  private String packaging = "jar";
+  @Nullable
+  private String classifier;
+
+  public DefaultExternalDependencyId() {
+  }
+
+  public DefaultExternalDependencyId(String group, String name, String version) {
+    this.group = group;
+    this.name = name;
+    this.version = version;
+  }
+
+  public DefaultExternalDependencyId(ExternalDependencyId dependencyId) {
+    this(dependencyId.getGroup(), dependencyId.getName(), dependencyId.getVersion());
+    packaging = dependencyId.getPackaging();
+    classifier = dependencyId.getClassifier();
+  }
+
+  @Override
+  public String getGroup() {
+    return group;
+  }
+
+  public void setGroup(String group) {
+    this.group = group;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  @NotNull
+  @Override
+  public String getPackaging() {
+    return packaging;
+  }
+
+  public void setPackaging(@NotNull String packaging) {
+    this.packaging = packaging;
+  }
+
+  @Nullable
+  @Override
+  public String getClassifier() {
+    return classifier;
+  }
+
+  public void setClassifier(@Nullable String classifier) {
+    this.classifier = classifier;
+  }
+
+  @Override
+  @NotNull
+  public String getPresentableName() {
+    final StringBuilder buf = new StringBuilder();
+    if (group != null) {
+      buf.append(group).append(':');
+    }
+    buf.append(name);
+    if (!"jar".equals(packaging)) {
+      buf.append(':').append(packaging);
+    }
+    if (classifier != null) {
+      buf.append(':').append(classifier);
+    }
+    if (version != null) {
+      buf.append(':').append(version);
+    }
+    return buf.toString();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof DefaultExternalDependencyId)) return false;
+    DefaultExternalDependencyId that = (DefaultExternalDependencyId)o;
+    return Objects.equal(group, that.group) &&
+           Objects.equal(name, that.name) &&
+           Objects.equal(packaging, that.packaging) &&
+           Objects.equal(classifier, that.classifier) &&
+           Objects.equal(version, that.version);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(group, name, packaging, classifier, version);
+  }
+
+  @Override
+  public String toString() {
+    return getPresentableName();
+  }
+}
diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/ExternalDependencyId.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/ExternalDependencyId.java
new file mode 100644 (file)
index 0000000..3759940
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 org.jetbrains.plugins.gradle;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 1/19/2015
+ */
+public interface ExternalDependencyId {
+  String getGroup();
+  String getName();
+  String getVersion();
+  @NotNull
+  String getPackaging();
+  @Nullable
+  String getClassifier();
+  @NotNull
+  String getPresentableName();
+
+}
diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalDependency.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalDependency.java
new file mode 100644 (file)
index 0000000..7a831b9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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 org.jetbrains.plugins.gradle.model;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.gradle.ExternalDependencyId;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 7/14/2014
+ */
+public interface ExternalDependency extends Serializable {
+  /**
+   * <p>The group of the dependency.
+   *
+   * @return dependency group
+   */
+  String getGroup();
+
+  /**
+   * <p>The name of the dependency.
+   *
+   * @return dependency name
+   */
+  String getName();
+
+  /**
+   * <p>The version of the dependency
+   *
+   * @return dependency version
+   */
+  String getVersion();
+
+  /**
+   * Returns the {@link ExternalDependencyId} containing the group, the name and the version of this dependency.
+   * Contains the same information as {@link #getGroup()}, {@link #getName()} and {@link #getVersion()}
+   *
+   * @return the dependency identifier
+   */
+  @NotNull
+  ExternalDependencyId getId();
+
+
+  /**
+   * <p>The scope of the dependency
+   *
+   * @return dependency scope
+   */
+  String getScope();
+
+  /**
+   * <p>The packaging type
+   *
+   * @return packaging type
+   */
+  @Nullable
+  String getPackaging();
+
+  /**
+   * <p>The classifier
+   *
+   * @return classifier
+   */
+  @Nullable
+  String getClassifier();
+
+  /**
+   * <p>Returns the reason why this particular dependency was selected in the result.
+   * Useful if multiple candidates were found during dependency resolution.
+   *
+   * @return the reason for selecting the dependency
+   */
+  @Nullable
+  String getSelectionReason();
+
+  /**
+   * <p>The order of the dependency in it's classpath.
+   *
+   * @return classpath order
+   */
+  int getClasspathOrder();
+
+  /**
+   * <p>Returns transitive dependencies
+   *
+   * @return transitive dependencies
+   */
+  @NotNull
+  Collection<ExternalDependency> getDependencies();
+
+  /**
+   * Allows to check if current dependency is transitive, i.e. is visible to the module which depends on module that has current dependency.
+   * @return <code>true</code> if current dependency is transitive; <code>false</code> otherwise
+   */
+  boolean getExported();
+}
diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalLibraryDependency.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ExternalLibraryDependency.java
new file mode 100644 (file)
index 0000000..8e7d20e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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