09ce935ed1018c7d6cab69448b09b69755a80027
[idea/community.git] / plugins / gradle / src / org / jetbrains / plugins / gradle / service / project / BaseGradleProjectResolverExtension.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.gradle.service.project;
17
18 import com.google.common.collect.Multimap;
19 import com.google.gson.GsonBuilder;
20 import com.intellij.execution.ExecutionException;
21 import com.intellij.execution.configurations.SimpleJavaParameters;
22 import com.intellij.externalSystem.JavaProjectData;
23 import com.intellij.openapi.application.PathManager;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.externalSystem.model.DataNode;
26 import com.intellij.openapi.externalSystem.model.ExternalSystemException;
27 import com.intellij.openapi.externalSystem.model.ProjectKeys;
28 import com.intellij.openapi.externalSystem.model.project.*;
29 import com.intellij.openapi.externalSystem.model.task.TaskData;
30 import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
31 import com.intellij.openapi.externalSystem.util.Order;
32 import com.intellij.openapi.module.EmptyModuleType;
33 import com.intellij.openapi.module.JavaModuleType;
34 import com.intellij.openapi.module.ModuleType;
35 import com.intellij.openapi.module.StdModuleTypes;
36 import com.intellij.openapi.roots.DependencyScope;
37 import com.intellij.openapi.util.KeyValue;
38 import com.intellij.openapi.util.Pair;
39 import com.intellij.openapi.util.io.FileFilters;
40 import com.intellij.openapi.util.io.FileUtil;
41 import com.intellij.openapi.util.io.FileUtilRt;
42 import com.intellij.openapi.util.text.StringUtil;
43 import com.intellij.pom.java.LanguageLevel;
44 import com.intellij.util.*;
45 import com.intellij.util.containers.ContainerUtil;
46 import com.intellij.util.containers.ContainerUtilRt;
47 import com.intellij.util.net.HttpConfigurable;
48 import com.intellij.util.text.CharArrayUtil;
49 import groovy.lang.GroovyObject;
50 import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
51 import org.gradle.tooling.ProjectConnection;
52 import org.gradle.tooling.model.DomainObjectSet;
53 import org.gradle.tooling.model.GradleModuleVersion;
54 import org.gradle.tooling.model.GradleTask;
55 import org.gradle.tooling.model.UnsupportedMethodException;
56 import org.gradle.tooling.model.gradle.GradleBuild;
57 import org.gradle.tooling.model.idea.*;
58 import org.jetbrains.annotations.NonNls;
59 import org.jetbrains.annotations.NotNull;
60 import org.jetbrains.annotations.Nullable;
61 import org.jetbrains.plugins.gradle.model.*;
62 import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData;
63 import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData;
64 import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService;
65 import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl;
66 import org.jetbrains.plugins.gradle.tooling.internal.init.Init;
67 import org.jetbrains.plugins.gradle.util.GradleBundle;
68 import org.jetbrains.plugins.gradle.util.GradleConstants;
69 import org.slf4j.impl.Log4jLoggerFactory;
70
71 import java.io.File;
72 import java.lang.reflect.Field;
73 import java.lang.reflect.Method;
74 import java.lang.reflect.Proxy;
75 import java.net.URL;
76 import java.util.*;
77 import java.util.regex.Matcher;
78 import java.util.regex.Pattern;
79
80 import static org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil.*;
81
82 /**
83  * {@link BaseGradleProjectResolverExtension} provides base implementation of Gradle project resolver.
84  *
85  * @author Vladislav.Soroka
86  * @since 10/14/13
87  */
88 @Order(Integer.MAX_VALUE)
89 public class BaseGradleProjectResolverExtension implements GradleProjectResolverExtension {
90   private static final Logger LOG = Logger.getInstance(BaseGradleProjectResolverExtension.class);
91
92   @NotNull @NonNls private static final String UNRESOLVED_DEPENDENCY_PREFIX = "unresolved dependency - ";
93
94   @NotNull private ProjectResolverContext resolverCtx;
95   @NotNull private final BaseProjectImportErrorHandler myErrorHandler = new BaseProjectImportErrorHandler();
96
97   @Override
98   public void setProjectResolverContext(@NotNull ProjectResolverContext projectResolverContext) {
99     resolverCtx = projectResolverContext;
100   }
101
102   @Override
103   public void setNext(@NotNull GradleProjectResolverExtension next) {
104     // should be the last extension in the chain
105   }
106
107   @Nullable
108   @Override
109   public GradleProjectResolverExtension getNext() {
110     return null;
111   }
112
113   @NotNull
114   @Override
115   public ProjectData createProject() {
116     final String projectDirPath = resolverCtx.getProjectPath();
117     final IdeaProject ideaProject = resolverCtx.getModels().getIdeaProject();
118     return new ProjectData(GradleConstants.SYSTEM_ID, ideaProject.getName(), projectDirPath, projectDirPath);
119   }
120
121   @NotNull
122   @Override
123   public JavaProjectData createJavaProjectData() {
124     final String projectDirPath = resolverCtx.getProjectPath();
125     final IdeaProject ideaProject = resolverCtx.getModels().getIdeaProject();
126
127     // Gradle API doesn't expose gradleProject compile output path yet.
128     JavaProjectData javaProjectData = new JavaProjectData(GradleConstants.SYSTEM_ID, projectDirPath + "/build/classes");
129     javaProjectData.setJdkVersion(ideaProject.getJdkName());
130     LanguageLevel resolvedLanguageLevel = null;
131     // org.gradle.tooling.model.idea.IdeaLanguageLevel.getLevel() returns something like JDK_1_6
132     final String languageLevel = ideaProject.getLanguageLevel().getLevel();
133     for (LanguageLevel level : LanguageLevel.values()) {
134       if (level.name().equals(languageLevel)) {
135         resolvedLanguageLevel = level;
136         break;
137       }
138     }
139     if (resolvedLanguageLevel != null) {
140       javaProjectData.setLanguageLevel(resolvedLanguageLevel);
141     }
142     else {
143       javaProjectData.setLanguageLevel(languageLevel);
144     }
145     return javaProjectData;
146   }
147
148   @Override
149   public void populateProjectExtraModels(@NotNull IdeaProject gradleProject, @NotNull DataNode<ProjectData> ideProject) {
150     final ExternalProject externalProject = resolverCtx.getExtraProject(ExternalProject.class);
151     if (externalProject != null) {
152       ideProject.createChild(ExternalProjectDataService.KEY, externalProject);
153       ideProject.getData().setDescription(externalProject.getDescription());
154     }
155   }
156
157   @NotNull
158   @Override
159   public DataNode<ModuleData> createModule(@NotNull IdeaModule gradleModule, @NotNull DataNode<ProjectData> projectDataNode) {
160     DataNode<ModuleData> mainModuleNode = createMainModule(resolverCtx, gradleModule, projectDataNode);
161     final ModuleData mainModuleData = mainModuleNode.getData();
162     final String mainModuleConfigPath = mainModuleData.getLinkedExternalProjectPath();
163     final String mainModuleFileDirectoryPath = mainModuleData.getModuleFileDirectoryPath();
164
165     ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
166     if (externalProject != null) {
167       String gradlePath = gradleModule.getGradleProject().getPath();
168       final boolean isRootModule = StringUtil.isEmpty(gradlePath) || ":".equals(gradlePath);
169       final String[] moduleGroup;
170       if (isRootModule) {
171         moduleGroup = new String[]{mainModuleData.getInternalName()};
172       }
173       else {
174         moduleGroup = ArrayUtil.remove(gradlePath.split(":"), 0);
175       }
176       mainModuleData.setIdeModuleGroup(isRootModule ? null : moduleGroup);
177
178       for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) {
179         final String moduleId = getModuleId(externalProject, sourceSet);
180         final String moduleExternalName = gradleModule.getName() + ":" + sourceSet.getName();
181         final String moduleInternalName = gradleModule.getName() + "_" + sourceSet.getName();
182
183         GradleSourceSetData sourceSetData = new GradleSourceSetData(
184           moduleId, moduleExternalName, moduleInternalName, mainModuleFileDirectoryPath, mainModuleConfigPath);
185
186         sourceSetData.setGroup(externalProject.getGroup());
187         sourceSetData.setVersion(externalProject.getVersion());
188         sourceSetData.setIdeModuleGroup(moduleGroup);
189
190         sourceSetData.setSourceCompatibility(sourceSet.getSourceCompatibility());
191         sourceSetData.setTargetCompatibility(sourceSet.getTargetCompatibility());
192
193         final Set<File> artifacts = ContainerUtil.newTroveSet(FileUtil.FILE_HASHING_STRATEGY);
194         if ("main".equals(sourceSet.getName())) {
195           final Set<File> defaultArtifacts = externalProject.getArtifactsByConfiguration().get("default");
196           if (defaultArtifacts != null) {
197             artifacts.addAll(defaultArtifacts);
198           }
199           if (externalProject.getArtifactsByConfiguration().get("archives") != null) {
200             final Set<File> archivesArtifacts = ContainerUtil.newHashSet(externalProject.getArtifactsByConfiguration().get("archives"));
201             final Set<File> testsArtifacts = externalProject.getArtifactsByConfiguration().get("tests");
202             if (testsArtifacts != null) {
203               archivesArtifacts.removeAll(testsArtifacts);
204             }
205             artifacts.addAll(archivesArtifacts);
206           }
207         } else if("test".equals(sourceSet.getName())) {
208           final Set<File> testsArtifacts = externalProject.getArtifactsByConfiguration().get("tests");
209           if (testsArtifacts != null) {
210             artifacts.addAll(testsArtifacts);
211           }
212         }
213         sourceSetData.setArtifacts(ContainerUtil.newArrayList(artifacts));
214
215         DataNode<GradleSourceSetData> sourceSetDataNode = mainModuleNode.createChild(GradleSourceSetData.KEY, sourceSetData);
216         final Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap =
217           projectDataNode.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS);
218         assert sourceSetMap != null;
219         sourceSetMap.put(moduleId, Pair.create(sourceSetDataNode, sourceSet));
220       }
221     }
222
223     final ProjectData projectData = projectDataNode.getData();
224     if (StringUtil.equals(mainModuleData.getLinkedExternalProjectPath(), projectData.getLinkedExternalProjectPath())) {
225       projectData.setGroup(mainModuleData.getGroup());
226       projectData.setVersion(mainModuleData.getVersion());
227     }
228
229     return mainModuleNode;
230   }
231
232   @Override
233   public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode<ModuleData> ideModule) {
234     final BuildScriptClasspathModel buildScriptClasspathModel = resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class);
235     final List<BuildScriptClasspathData.ClasspathEntry> classpathEntries;
236     if (buildScriptClasspathModel != null) {
237       classpathEntries = ContainerUtil
238         .map(buildScriptClasspathModel.getClasspath(), new Function<ClasspathEntryModel, BuildScriptClasspathData.ClasspathEntry>() {
239           @Override
240           public BuildScriptClasspathData.ClasspathEntry fun(ClasspathEntryModel model) {
241             return new BuildScriptClasspathData.ClasspathEntry(model.getClasses(), model.getSources(), model.getJavadoc());
242           }
243         });
244     }
245     else {
246       classpathEntries = ContainerUtil.emptyList();
247     }
248     BuildScriptClasspathData buildScriptClasspathData = new BuildScriptClasspathData(GradleConstants.SYSTEM_ID, classpathEntries);
249     ideModule.createChild(BuildScriptClasspathData.KEY, buildScriptClasspathData);
250   }
251
252   @Override
253   public void populateModuleContentRoots(@NotNull IdeaModule gradleModule,
254                                          @NotNull DataNode<ModuleData> ideModule) {
255     ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
256     if (externalProject != null) {
257       processSourceSets(externalProject, ideModule, new SourceSetsProcessor() {
258         @Override
259         public void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet) {
260           for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> directorySetEntry : sourceSet.getSources().entrySet()) {
261             ExternalSystemSourceType sourceType = ExternalSystemSourceType.from(directorySetEntry.getKey());
262             ExternalSourceDirectorySet sourceDirectorySet = directorySetEntry.getValue();
263
264             for (File file : sourceDirectorySet.getSrcDirs()) {
265               ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, file.getAbsolutePath());
266               ideContentRoot.storePath(sourceType, file.getAbsolutePath());
267               dataNode.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot);
268             }
269           }
270         }
271       });
272     }
273
274     DomainObjectSet<? extends IdeaContentRoot> contentRoots = gradleModule.getContentRoots();
275     if (contentRoots == null) {
276       return;
277     }
278     for (IdeaContentRoot gradleContentRoot : contentRoots) {
279       if (gradleContentRoot == null) continue;
280
281       File rootDirectory = gradleContentRoot.getRootDirectory();
282       if (rootDirectory == null) continue;
283
284       ContentRootData ideContentRoot = new ContentRootData(GradleConstants.SYSTEM_ID, rootDirectory.getAbsolutePath());
285       if (externalProject == null) {
286         populateContentRoot(ideContentRoot, ExternalSystemSourceType.SOURCE, gradleContentRoot.getSourceDirectories());
287         populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST, gradleContentRoot.getTestDirectories());
288
289         if (gradleContentRoot instanceof ExtIdeaContentRoot) {
290           ExtIdeaContentRoot extIdeaContentRoot = (ExtIdeaContentRoot)gradleContentRoot;
291           populateContentRoot(ideContentRoot, ExternalSystemSourceType.RESOURCE, extIdeaContentRoot.getResourceDirectories());
292           populateContentRoot(ideContentRoot, ExternalSystemSourceType.TEST_RESOURCE, extIdeaContentRoot.getTestResourceDirectories());
293         }
294       }
295
296       Set<File> excluded = gradleContentRoot.getExcludeDirectories();
297       if (excluded != null) {
298         for (File file : excluded) {
299           ideContentRoot.storePath(ExternalSystemSourceType.EXCLUDED, file.getAbsolutePath());
300         }
301       }
302       ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot);
303     }
304   }
305
306   private static void processSourceSets(@NotNull ExternalProject externalProject,
307                                         @NotNull DataNode<ModuleData> ideModule,
308                                         @NotNull SourceSetsProcessor processor) {
309     Map<String, DataNode<GradleSourceSetData>> sourceSetsMap = ContainerUtil.newHashMap();
310     for (DataNode<GradleSourceSetData> dataNode : ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)) {
311       sourceSetsMap.put(dataNode.getData().getId(), dataNode);
312     }
313
314     for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) {
315       if (sourceSet == null || sourceSet.getSources().isEmpty()) continue;
316
317       final String moduleId = getModuleId(externalProject, sourceSet);
318       final DataNode<GradleSourceSetData> sourceSetDataNode = sourceSetsMap.get(moduleId);
319       if (sourceSetDataNode == null) continue;
320
321       processor.process(sourceSetDataNode, sourceSet);
322     }
323   }
324
325
326   @Override
327   public void populateModuleCompileOutputSettings(@NotNull IdeaModule gradleModule,
328                                                   @NotNull DataNode<ModuleData> ideModule) {
329     ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
330     if (externalProject != null) {
331       processSourceSets(externalProject, ideModule, new SourceSetsProcessor() {
332         @Override
333         public void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet) {
334           for (Map.Entry<IExternalSystemSourceType, ExternalSourceDirectorySet> directorySetEntry : sourceSet.getSources().entrySet()) {
335             ExternalSystemSourceType sourceType = ExternalSystemSourceType.from(directorySetEntry.getKey());
336             ExternalSourceDirectorySet sourceDirectorySet = directorySetEntry.getValue();
337             final GradleSourceSetData sourceSetData = dataNode.getData();
338             sourceSetData.setCompileOutputPath(sourceType, sourceDirectorySet.getOutputDir().getAbsolutePath());
339             sourceSetData.setInheritProjectCompileOutputPath(sourceDirectorySet.isCompilerOutputPathInherited());
340           }
341         }
342       });
343
344       return;
345     }
346
347     IdeaCompilerOutput moduleCompilerOutput = gradleModule.getCompilerOutput();
348
349     File buildDir = null;
350     try {
351       buildDir = gradleModule.getGradleProject().getBuildDirectory();
352     }
353     catch (UnsupportedMethodException ignore) {
354       // see org.gradle.tooling.model.GradleProject.getBuildDirectory method supported only since Gradle 2.0
355       // will use com.intellij.openapi.externalSystem.model.ExternalProject.getBuildDir() instead
356     }
357
358     Map<ExternalSystemSourceType, File> compileOutputPaths = ContainerUtil.newHashMap();
359
360     boolean inheritOutputDirs = false;
361
362     ModuleData moduleData = ideModule.getData();
363     if (moduleCompilerOutput != null) {
364       compileOutputPaths.put(ExternalSystemSourceType.SOURCE, moduleCompilerOutput.getOutputDir());
365       compileOutputPaths.put(ExternalSystemSourceType.RESOURCE, moduleCompilerOutput.getOutputDir());
366       compileOutputPaths.put(ExternalSystemSourceType.TEST, moduleCompilerOutput.getTestOutputDir());
367       compileOutputPaths.put(ExternalSystemSourceType.TEST_RESOURCE, moduleCompilerOutput.getTestOutputDir());
368
369       inheritOutputDirs = moduleCompilerOutput.getInheritOutputDirs();
370     }
371
372     for (Map.Entry<ExternalSystemSourceType, File> sourceTypeFileEntry : compileOutputPaths.entrySet()) {
373       final File outputPath = ObjectUtils.chooseNotNull(sourceTypeFileEntry.getValue(), buildDir);
374       if (outputPath != null) {
375         moduleData.setCompileOutputPath(sourceTypeFileEntry.getKey(), outputPath.getAbsolutePath());
376       }
377     }
378
379     moduleData.setInheritProjectCompileOutputPath(inheritOutputDirs);
380   }
381
382   @Override
383   public void populateModuleDependencies(@NotNull IdeaModule gradleModule,
384                                          @NotNull DataNode<ModuleData> ideModule,
385                                          @NotNull final DataNode<ProjectData> ideProject) {
386
387     ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
388     if (externalProject != null) {
389       final Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>> sourceSetMap =
390         ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS);
391       assert sourceSetMap != null;
392
393       processSourceSets(externalProject, ideModule, new SourceSetsProcessor() {
394         @Override
395         public void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet) {
396           buildDependencies(sourceSetMap, dataNode, sourceSet.getDependencies(), ideProject);
397         }
398       });
399
400       return;
401     }
402
403     final List<? extends IdeaDependency> dependencies = gradleModule.getDependencies().getAll();
404
405     if (dependencies == null) return;
406
407     for (IdeaDependency dependency : dependencies) {
408       if (dependency == null) {
409         continue;
410       }
411       DependencyScope scope = parseScope(dependency.getScope());
412
413       if (dependency instanceof IdeaModuleDependency) {
414         ModuleDependencyData d = buildDependency(ideModule, (IdeaModuleDependency)dependency, ideProject);
415         d.setExported(dependency.getExported());
416         if (scope != null) {
417           d.setScope(scope);
418         }
419         ideModule.createChild(ProjectKeys.MODULE_DEPENDENCY, d);
420       }
421       else if (dependency instanceof IdeaSingleEntryLibraryDependency) {
422         LibraryDependencyData d = buildDependency(gradleModule, ideModule, (IdeaSingleEntryLibraryDependency)dependency, ideProject);
423         d.setExported(dependency.getExported());
424         if (scope != null) {
425           d.setScope(scope);
426         }
427         ideModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, d);
428       }
429     }
430   }
431
432   @NotNull
433   @Override
434   public Collection<TaskData> populateModuleTasks(@NotNull IdeaModule gradleModule,
435                                                   @NotNull DataNode<ModuleData> ideModule,
436                                                   @NotNull DataNode<ProjectData> ideProject)
437     throws IllegalArgumentException, IllegalStateException {
438
439     final Collection<TaskData> tasks = ContainerUtil.newArrayList();
440     final String moduleConfigPath = ideModule.getData().getLinkedExternalProjectPath();
441
442     ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class);
443     final String rootProjectPath = ideProject.getData().getLinkedExternalProjectPath();
444     final boolean isFlatProject = !FileUtil.isAncestor(rootProjectPath, moduleConfigPath, false);
445     if (externalProject != null) {
446       for (ExternalTask task : externalProject.getTasks().values()) {
447         String taskName = isFlatProject ? task.getQName() : task.getName();
448         String taskGroup = task.getGroup();
449         if (taskName.trim().isEmpty() || isIdeaTask(taskName, taskGroup)) {
450           continue;
451         }
452         final String taskPath = isFlatProject ? rootProjectPath : moduleConfigPath;
453         TaskData taskData = new TaskData(GradleConstants.SYSTEM_ID, taskName, taskPath, task.getDescription());
454         taskData.setGroup(taskGroup);
455         taskData.setType(task.getType());
456         ideModule.createChild(ProjectKeys.TASK, taskData);
457         taskData.setInherited(StringUtil.equals(task.getName(), task.getQName()));
458         tasks.add(taskData);
459       }
460
461       return tasks;
462     }
463
464     for (GradleTask task : gradleModule.getGradleProject().getTasks()) {
465       String taskName = task.getName();
466       String taskGroup = getTaskGroup(task);
467       if (taskName == null || taskName.trim().isEmpty() || isIdeaTask(taskName, taskGroup)) {
468         continue;
469       }
470       TaskData taskData = new TaskData(GradleConstants.SYSTEM_ID, taskName, moduleConfigPath, task.getDescription());
471       taskData.setGroup(taskGroup);
472       ideModule.createChild(ProjectKeys.TASK, taskData);
473       tasks.add(taskData);
474     }
475
476     return tasks;
477   }
478
479   @Nullable
480   private static String getTaskGroup(GradleTask task) {
481     String taskGroup;
482     try {
483       taskGroup = task.getGroup();
484     }
485     catch (UnsupportedMethodException e) {
486       taskGroup = null;
487     }
488     return taskGroup;
489   }
490
491   @NotNull
492   @Override
493   public Set<Class> getExtraProjectModelClasses() {
494     Set<Class> result = ContainerUtil.<Class>set(GradleBuild.class, ModuleExtendedModel.class);
495     result.add(BuildScriptClasspathModel.class);
496     result.add(ExternalProject.class);
497     return result;
498   }
499
500   @NotNull
501   @Override
502   public Set<Class> getToolingExtensionsClasses() {
503     return ContainerUtil.<Class>set(
504       // external-system-rt.jar
505       ExternalSystemSourceType.class,
506       // gradle-tooling-extension-api jar
507       ProjectImportAction.class,
508       // gradle-tooling-extension-impl jar
509       ModelBuildScriptClasspathBuilderImpl.class,
510       Multimap.class,
511       GsonBuilder.class,
512       ShortTypeHandling.class
513     );
514   }
515
516   @NotNull
517   @Override
518   public List<KeyValue<String, String>> getExtraJvmArgs() {
519     if (ExternalSystemApiUtil.isInProcessMode(GradleConstants.SYSTEM_ID)) {
520       final List<KeyValue<String, String>> extraJvmArgs = ContainerUtil.newArrayList();
521       final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
522       if (!StringUtil.isEmpty(httpConfigurable.PROXY_EXCEPTIONS)) {
523         List<String> hosts = StringUtil.split(httpConfigurable.PROXY_EXCEPTIONS, ",");
524         if (!hosts.isEmpty()) {
525           final String nonProxyHosts = StringUtil.join(hosts, StringUtil.TRIMMER, "|");
526           extraJvmArgs.add(KeyValue.create("http.nonProxyHosts", nonProxyHosts));
527           extraJvmArgs.add(KeyValue.create("https.nonProxyHosts", nonProxyHosts));
528         }
529       }
530       if (httpConfigurable.USE_HTTP_PROXY && StringUtil.isNotEmpty(httpConfigurable.PROXY_LOGIN)) {
531         extraJvmArgs.add(KeyValue.create("http.proxyUser", httpConfigurable.PROXY_LOGIN));
532         extraJvmArgs.add(KeyValue.create("https.proxyUser", httpConfigurable.PROXY_LOGIN));
533         final String plainProxyPassword = httpConfigurable.getPlainProxyPassword();
534         extraJvmArgs.add(KeyValue.create("http.proxyPassword", plainProxyPassword));
535         extraJvmArgs.add(KeyValue.create("https.proxyPassword", plainProxyPassword));
536       }
537       extraJvmArgs.addAll(HttpConfigurable.getJvmPropertiesList(false, null));
538
539       return extraJvmArgs;
540     }
541     return Collections.emptyList();
542   }
543
544   @NotNull
545   @Override
546   public List<String> getExtraCommandLineArgs() {
547     return Collections.emptyList();
548   }
549
550   @NotNull
551   @Override
552   public ExternalSystemException getUserFriendlyError(@NotNull Throwable error,
553                                                       @NotNull String projectPath,
554                                                       @Nullable String buildFilePath) {
555     return myErrorHandler.getUserFriendlyError(error, projectPath, buildFilePath);
556   }
557
558   @Override
559   public void preImportCheck() {
560   }
561
562   @Override
563   public void enhanceTaskProcessing(@NotNull List<String> taskNames,
564                                     @Nullable String debuggerSetup,
565                                     @NotNull Consumer<String> initScriptConsumer) {
566     if (!StringUtil.isEmpty(debuggerSetup)) {
567       final String[] lines = {
568         "gradle.taskGraph.beforeTask { Task task ->",
569         "    if (task instanceof JavaForkOptions) {",
570         "        def jvmArgs = task.jvmArgs.findAll{!it?.startsWith('-agentlib') && !it?.startsWith('-Xrunjdwp')}",
571         "        jvmArgs << '" + debuggerSetup.trim() + '\'',
572         "        task.jvmArgs jvmArgs",
573         "    }" +
574         "}",
575       };
576       final String script = StringUtil.join(lines, SystemProperties.getLineSeparator());
577       initScriptConsumer.consume(script);
578     }
579   }
580
581   @Override
582   public void enhanceRemoteProcessing(@NotNull SimpleJavaParameters parameters) throws ExecutionException {
583     PathsList classPath = parameters.getClassPath();
584
585     // Gradle i18n bundle.
586     ExternalSystemApiUtil.addBundle(classPath, GradleBundle.PATH_TO_BUNDLE, GradleBundle.class);
587
588     // Gradle tool jars.
589     String toolingApiPath = PathManager.getJarPathForClass(ProjectConnection.class);
590     if (toolingApiPath == null) {
591       LOG.warn(GradleBundle.message("gradle.generic.text.error.jar.not.found"));
592       throw new ExecutionException("Can't find gradle libraries");
593     }
594     File gradleJarsDir = new File(toolingApiPath).getParentFile();
595     File[] gradleJars = gradleJarsDir.listFiles(FileFilters.filesWithExtension("jar"));
596     if (gradleJars == null) {
597       LOG.warn(GradleBundle.message("gradle.generic.text.error.jar.not.found"));
598       throw new ExecutionException("Can't find gradle libraries at " + gradleJarsDir.getAbsolutePath());
599     }
600     for (File jar : gradleJars) {
601       classPath.add(jar.getAbsolutePath());
602     }
603
604     List<String> additionalEntries = ContainerUtilRt.newArrayList();
605     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(GroovyObject.class));
606     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(GsonBuilder.class));
607     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ExternalProject.class));
608     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(JavaProjectData.class));
609     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(LanguageLevel.class));
610     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(StdModuleTypes.class));
611     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(JavaModuleType.class));
612     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ModuleType.class));
613     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(EmptyModuleType.class));
614     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ProjectImportAction.class));
615     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(Init.class));
616     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(org.slf4j.Logger.class));
617     ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(Log4jLoggerFactory.class));
618     for (String entry : additionalEntries) {
619       classPath.add(entry);
620     }
621   }
622
623   @Override
624   public void enhanceLocalProcessing(@NotNull List<URL> urls) {
625   }
626
627   /**
628    * Stores information about given directories at the given content root
629    *
630    * @param contentRoot target paths info holder
631    * @param type        type of data located at the given directories
632    * @param dirs        directories which paths should be stored at the given content root
633    * @throws IllegalArgumentException if specified by {@link ContentRootData#storePath(ExternalSystemSourceType, String)}
634    */
635   private static void populateContentRoot(@NotNull final ContentRootData contentRoot,
636                                           @NotNull final ExternalSystemSourceType type,
637                                           @Nullable final Iterable<? extends IdeaSourceDirectory> dirs)
638     throws IllegalArgumentException {
639     if (dirs == null) {
640       return;
641     }
642     for (IdeaSourceDirectory dir : dirs) {
643       ExternalSystemSourceType dirSourceType = type;
644       try {
645         if (dir.isGenerated() && !dirSourceType.isGenerated()) {
646           final ExternalSystemSourceType generatedType = ExternalSystemSourceType.from(
647             dirSourceType.isTest(), dir.isGenerated(), dirSourceType.isResource(), dirSourceType.isExcluded()
648           );
649           dirSourceType = generatedType != null ? generatedType : dirSourceType;
650         }
651       }
652       catch (UnsupportedMethodException e) {
653         // org.gradle.tooling.model.idea.IdeaSourceDirectory.isGenerated method supported only since Gradle 2.2
654         LOG.warn(e.getMessage());
655         printToolingProxyDiagnosticInfo(dir);
656       }
657       catch (Throwable e) {
658         LOG.debug(e);
659         printToolingProxyDiagnosticInfo(dir);
660       }
661       contentRoot.storePath(dirSourceType, dir.getDirectory().getAbsolutePath());
662     }
663   }
664
665   private static void printToolingProxyDiagnosticInfo(@Nullable Object obj) {
666     if (!LOG.isDebugEnabled() || obj == null) return;
667
668     LOG.debug(String.format("obj: %s", obj));
669     final Class<?> aClass = obj.getClass();
670     LOG.debug(String.format("obj class: %s", aClass));
671     LOG.debug(String.format("classloader: %s", aClass.getClassLoader()));
672     for (Method m : aClass.getDeclaredMethods()) {
673       LOG.debug(String.format("obj m: %s", m));
674     }
675
676     if (obj instanceof Proxy) {
677       try {
678         final Field hField = ReflectionUtil.findField(obj.getClass(), null, "h");
679         hField.setAccessible(true);
680         final Object h = hField.get(obj);
681         final Field delegateField = ReflectionUtil.findField(h.getClass(), null, "delegate");
682         delegateField.setAccessible(true);
683         final Object delegate = delegateField.get(h);
684         LOG.debug(String.format("delegate: %s", delegate));
685         LOG.debug(String.format("delegate class: %s", delegate.getClass()));
686         LOG.debug(String.format("delegate classloader: %s", delegate.getClass().getClassLoader()));
687         for (Method m : delegate.getClass().getDeclaredMethods()) {
688           LOG.debug(String.format("delegate m: %s", m));
689         }
690       }
691       catch (NoSuchFieldException e) {
692         LOG.debug(e);
693       }
694       catch (IllegalAccessException e) {
695         LOG.debug(e);
696       }
697     }
698   }
699
700   @Nullable
701   private static DependencyScope parseScope(@Nullable IdeaDependencyScope scope) {
702     if (scope == null) {
703       return null;
704     }
705     String scopeAsString = scope.getScope();
706     if (scopeAsString == null) {
707       return null;
708     }
709     for (DependencyScope dependencyScope : DependencyScope.values()) {
710       if (scopeAsString.equalsIgnoreCase(dependencyScope.toString())) {
711         return dependencyScope;
712       }
713     }
714     return null;
715   }
716
717   @NotNull
718   private static ModuleDependencyData buildDependency(@NotNull DataNode<ModuleData> ownerModule,
719                                                       @NotNull IdeaModuleDependency dependency,
720                                                       @NotNull DataNode<ProjectData> ideProject)
721     throws IllegalStateException {
722     IdeaModule module = dependency.getDependencyModule();
723     if (module == null) {
724       throw new IllegalStateException(
725         String.format("Can't parse gradle module dependency '%s'. Reason: referenced module is null", dependency)
726       );
727     }
728
729     String moduleName = module.getName();
730     if (moduleName == null) {
731       throw new IllegalStateException(String.format(
732         "Can't parse gradle module dependency '%s'. Reason: referenced module name is undefined (module: '%s') ", dependency, module
733       ));
734     }
735
736     Set<String> registeredModuleNames = ContainerUtilRt.newHashSet();
737     Collection<DataNode<ModuleData>> modulesDataNode = ExternalSystemApiUtil.getChildren(ideProject, ProjectKeys.MODULE);
738     for (DataNode<ModuleData> moduleDataNode : modulesDataNode) {
739       String name = moduleDataNode.getData().getExternalName();
740       registeredModuleNames.add(name);
741       if (name.equals(moduleName)) {
742         return new ModuleDependencyData(ownerModule.getData(), moduleDataNode.getData());
743       }
744     }
745     throw new IllegalStateException(String.format(
746       "Can't parse gradle module dependency '%s'. Reason: no module with such name (%s) is found. Registered modules: %s",
747       dependency, moduleName, registeredModuleNames
748     ));
749   }
750
751   @NotNull
752   private LibraryDependencyData buildDependency(@NotNull IdeaModule gradleModule,
753                                                 @NotNull DataNode<ModuleData> ownerModule,
754                                                 @NotNull IdeaSingleEntryLibraryDependency dependency,
755                                                 @NotNull DataNode<ProjectData> ideProject)
756     throws IllegalStateException {
757     File binaryPath = dependency.getFile();
758     if (binaryPath == null) {
759       throw new IllegalStateException(String.format(
760         "Can't parse external library dependency '%s'. Reason: it doesn't specify path to the binaries", dependency
761       ));
762     }
763
764     String libraryName;
765     final GradleModuleVersion moduleVersion = dependency.getGradleModuleVersion();
766     final LibraryLevel level;
767
768     // Gradle API doesn't explicitly provide information about unresolved libraries (http://issues.gradle.org/browse/GRADLE-1995).
769     // That's why we use this dirty hack here.
770     boolean unresolved = binaryPath.getPath().startsWith(UNRESOLVED_DEPENDENCY_PREFIX);
771
772     if (moduleVersion == null) {
773       // use module library level if the dependency does not originate from a remote repository.
774       level = LibraryLevel.MODULE;
775
776       if (binaryPath.isFile()) {
777         libraryName = FileUtil.getNameWithoutExtension(binaryPath);
778       }
779       else {
780         libraryName = "";
781       }
782
783       if (unresolved) {
784         // Gradle uses names like 'unresolved dependency - commons-collections commons-collections 3.2' for unresolved dependencies.
785         libraryName = binaryPath.getPath().substring(UNRESOLVED_DEPENDENCY_PREFIX.length());
786         int i = libraryName.indexOf(' ');
787         if (i >= 0) {
788           i = CharArrayUtil.shiftForward(libraryName, i + 1, " ");
789         }
790
791         if (i >= 0 && i < libraryName.length()) {
792           int dependencyNameIndex = i;
793           i = libraryName.indexOf(' ', dependencyNameIndex);
794           if (i > 0) {
795             libraryName = String.format("%s-%s", libraryName.substring(dependencyNameIndex, i), libraryName.substring(i + 1));
796           }
797         }
798       }
799     }
800     else {
801       level = LibraryLevel.PROJECT;
802       libraryName = String.format("%s:%s:%s", moduleVersion.getGroup(), moduleVersion.getName(), moduleVersion.getVersion());
803       if (binaryPath.isFile()) {
804         String libraryFileName = FileUtil.getNameWithoutExtension(binaryPath);
805         final String mavenLibraryFileName = String.format("%s-%s", moduleVersion.getName(), moduleVersion.getVersion());
806         if (!mavenLibraryFileName.equals(libraryFileName)) {
807           Pattern pattern = Pattern.compile(moduleVersion.getName() + "-" + moduleVersion.getVersion() + "-(.*)");
808           Matcher matcher = pattern.matcher(libraryFileName);
809           if (matcher.matches()) {
810             final String classifier = matcher.group(1);
811             libraryName += (":" + classifier);
812           }
813           else {
814             final String artifactId = StringUtil.trimEnd(StringUtil.trimEnd(libraryFileName, moduleVersion.getVersion()), "-");
815             libraryName = String.format("%s:%s:%s",
816                                         moduleVersion.getGroup(),
817                                         artifactId,
818                                         moduleVersion.getVersion());
819           }
820         }
821       }
822     }
823
824     // add packaging type to distinguish different artifact dependencies with same groupId:artifactId:version
825     if (StringUtil.isNotEmpty(libraryName) && !FileUtilRt.extensionEquals(binaryPath.getPath(), "jar")) {
826       libraryName += (":" + FileUtilRt.getExtension(binaryPath.getPath()));
827     }
828
829     final LibraryData library = new LibraryData(GradleConstants.SYSTEM_ID, libraryName, unresolved);
830     if (!unresolved) {
831       library.addPath(LibraryPathType.BINARY, binaryPath.getAbsolutePath());
832     }
833
834     File sourcePath = dependency.getSource();
835     if (!unresolved && sourcePath != null) {
836       library.addPath(LibraryPathType.SOURCE, sourcePath.getAbsolutePath());
837     }
838
839     if (!unresolved && sourcePath == null) {
840       attachGradleSdkSources(gradleModule, binaryPath, library, resolverCtx);
841     }
842
843     File javadocPath = dependency.getJavadoc();
844     if (!unresolved && javadocPath != null) {
845       library.addPath(LibraryPathType.DOC, javadocPath.getAbsolutePath());
846     }
847
848     if (level == LibraryLevel.PROJECT) {
849       linkProjectLibrary(ideProject, library);
850     }
851
852     return new LibraryDependencyData(ownerModule.getData(), library, level);
853   }
854
855   private interface SourceSetsProcessor {
856     void process(@NotNull DataNode<GradleSourceSetData> dataNode, @NotNull ExternalSourceSet sourceSet);
857   }
858 }