[maven] IDEA-91662 doesn't take into account mirror repositories as Indexed Maven...
[idea/community.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / project / MavenProjectReader.java
1 // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.idea.maven.project;
3
4 import com.intellij.openapi.project.Project;
5 import com.intellij.openapi.util.Pair;
6 import com.intellij.openapi.util.text.StringUtil;
7 import com.intellij.openapi.vfs.VfsUtilCore;
8 import com.intellij.openapi.vfs.VirtualFile;
9 import com.intellij.util.containers.CollectionFactory;
10 import com.intellij.util.containers.ContainerUtil;
11 import org.jdom.Element;
12 import org.jetbrains.annotations.NotNull;
13 import org.jetbrains.annotations.Nullable;
14 import org.jetbrains.idea.maven.dom.converters.MavenConsumerPomUtil;
15 import org.jetbrains.idea.maven.model.*;
16 import org.jetbrains.idea.maven.server.MavenEmbedderWrapper;
17 import org.jetbrains.idea.maven.server.MavenServerExecutionResult;
18 import org.jetbrains.idea.maven.server.MavenServerManager;
19 import org.jetbrains.idea.maven.server.ProfileApplicationResult;
20 import org.jetbrains.idea.maven.utils.MavenJDOMUtil;
21 import org.jetbrains.idea.maven.utils.MavenLog;
22 import org.jetbrains.idea.maven.utils.MavenProcessCanceledException;
23 import org.jetbrains.idea.maven.utils.MavenUtil;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.util.*;
28 import java.util.function.Function;
29
30 import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
31 import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces;
32 import static com.intellij.util.containers.ContainerUtil.getFirstItem;
33 import static java.util.stream.Collectors.toMap;
34 import static org.jetbrains.idea.maven.utils.MavenUtil.getBaseDir;
35
36 public final class MavenProjectReader {
37   private static final String UNKNOWN = MavenId.UNKNOWN_VALUE;
38
39   private final Map<VirtualFile, RawModelReadResult> myRawModelsCache = new HashMap<>();
40   private final Project myProject;
41   private SettingsProfilesCache mySettingsProfilesCache;
42
43   public MavenProjectReader(@NotNull Project project) {
44     myProject = project;
45   }
46
47   public MavenProjectReaderResult readProject(MavenGeneralSettings generalSettings,
48                                               VirtualFile file,
49                                               MavenExplicitProfiles explicitProfiles,
50                                               MavenProjectReaderProjectLocator locator) {
51     File basedir = getBaseDir(file).toFile();
52
53     Pair<RawModelReadResult, MavenExplicitProfiles> readResult =
54       doReadProjectModel(generalSettings, basedir, file, explicitProfiles, new HashSet<>(), locator);
55
56     MavenModel model = MavenServerManager.getInstance().getConnector(myProject, basedir.getPath())
57       .interpolateAndAlignModel(readResult.first.model, basedir);
58
59     Map<String, String> modelMap = new HashMap<>();
60     modelMap.put("groupId", model.getMavenId().getGroupId());
61     modelMap.put("artifactId", model.getMavenId().getArtifactId());
62     modelMap.put("version", model.getMavenId().getVersion());
63     modelMap.put("build.outputDirectory", model.getBuild().getOutputDirectory());
64     modelMap.put("build.testOutputDirectory", model.getBuild().getTestOutputDirectory());
65     modelMap.put("build.finalName", model.getBuild().getFinalName());
66     modelMap.put("build.directory", model.getBuild().getDirectory());
67
68     return new MavenProjectReaderResult(model,
69                                         modelMap,
70                                         readResult.second,
71                                         null,
72                                         readResult.first.problems,
73                                         new HashSet<>());
74   }
75
76   private Pair<RawModelReadResult, MavenExplicitProfiles> doReadProjectModel(MavenGeneralSettings generalSettings,
77                                                                              File projectPomDir,
78                                                                              VirtualFile file,
79                                                                              MavenExplicitProfiles explicitProfiles,
80                                                                              Set<VirtualFile> recursionGuard,
81                                                                              MavenProjectReaderProjectLocator locator) {
82     RawModelReadResult cachedModel = myRawModelsCache.get(file);
83     if (cachedModel == null) {
84       cachedModel = doReadProjectModel(file, false);
85       myRawModelsCache.put(file, cachedModel);
86     }
87
88     // todo modifying cached model and problems here??????
89     MavenModel model = cachedModel.model;
90     Set<String> alwaysOnProfiles = cachedModel.alwaysOnProfiles;
91     Collection<MavenProjectProblem> problems = cachedModel.problems;
92
93     model = resolveInheritance(generalSettings, model, projectPomDir, file, explicitProfiles, recursionGuard, locator, problems);
94     addSettingsProfiles(generalSettings, model, alwaysOnProfiles, problems);
95
96     ProfileApplicationResult applied = applyProfiles(model, projectPomDir, getBaseDir(file).toFile(), explicitProfiles, alwaysOnProfiles);
97     model = applied.getModel();
98
99     repairModelBody(model);
100
101     return Pair.create(new RawModelReadResult(model, problems, alwaysOnProfiles),
102                        applied.getActivatedProfiles());
103   }
104
105   private RawModelReadResult doReadProjectModel(VirtualFile file, boolean headerOnly) {
106     MavenModel result = null;
107     Collection<MavenProjectProblem> problems = MavenProjectProblem.createProblemsList();
108     Set<String> alwaysOnProfiles = new HashSet<>();
109
110     String fileExtension = file.getExtension();
111     if (!"pom".equalsIgnoreCase(fileExtension) && !"xml".equalsIgnoreCase(fileExtension)) {
112       String basedir = getBaseDir(file).toString();
113       MavenEmbeddersManager manager = MavenProjectsManager.getInstance(myProject).getEmbeddersManager();
114       MavenEmbedderWrapper embedder = manager.getEmbedder(MavenEmbeddersManager.FOR_MODEL_READ, basedir, basedir);
115       try {
116         result = embedder.readModel(VfsUtilCore.virtualToIoFile(file));
117       }
118       catch (MavenProcessCanceledException ignore) {
119       }
120       finally {
121         manager.release(embedder);
122       }
123
124       if (result == null) {
125         result = new MavenModel();
126         result.setPackaging(MavenConstants.TYPE_JAR);
127       }
128       return new RawModelReadResult(result, problems, alwaysOnProfiles);
129     }
130
131     result = new MavenModel();
132     Element xmlProject = readXml(file, problems, MavenProjectProblem.ProblemType.SYNTAX);
133     if (xmlProject == null || !"project".equals(xmlProject.getName())) {
134       result.setPackaging(MavenConstants.TYPE_JAR);
135       return new RawModelReadResult(result, problems, alwaysOnProfiles);
136     }
137
138     MavenParent parent;
139     if (MavenJDOMUtil.hasChildByPath(xmlProject, "parent")) {
140       parent = new MavenParent(new MavenId(MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.groupId", UNKNOWN),
141                                            MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.artifactId", UNKNOWN),
142                                            calculateParentVersion(xmlProject, problems, file)),
143                                MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.relativePath", "../pom.xml"));
144       result.setParent(parent);
145     }
146     else {
147       parent = new MavenParent(new MavenId(UNKNOWN, UNKNOWN, UNKNOWN), "../pom.xml");
148     }
149
150     result.setMavenId(new MavenId(MavenJDOMUtil.findChildValueByPath(xmlProject, "groupId", parent.getMavenId().getGroupId()),
151                                   MavenJDOMUtil.findChildValueByPath(xmlProject, "artifactId", UNKNOWN),
152                                   MavenJDOMUtil.findChildValueByPath(xmlProject, "version", parent.getMavenId().getVersion())));
153
154     if (headerOnly) return new RawModelReadResult(result, problems, alwaysOnProfiles);
155
156     result.setPackaging(MavenJDOMUtil.findChildValueByPath(xmlProject, "packaging", MavenConstants.TYPE_JAR));
157     result.setName(MavenJDOMUtil.findChildValueByPath(xmlProject, "name"));
158
159     readModelBody(result, result.getBuild(), xmlProject);
160
161     result.setProfiles(collectProfiles(file, xmlProject, problems, alwaysOnProfiles));
162     return new RawModelReadResult(result, problems, alwaysOnProfiles);
163   }
164
165   @NotNull
166   private String calculateParentVersion(Element xmlProject,
167                                         Collection<MavenProjectProblem> problems,
168                                         VirtualFile file) {
169     String version = MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.version");
170     if (version != null || !MavenConsumerPomUtil.isConsumerPomResolutionApplicable(myProject)) {
171       return StringUtil.notNullize(version, UNKNOWN);
172     }
173     String parentGroupId = MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.groupId");
174     String parentArtifactId = MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.artifactId");
175     if (parentGroupId == null || parentArtifactId == null) {
176       problems.add(new MavenProjectProblem(file.getPath(), MavenProjectBundle.message("consumer.pom.cannot.determine.parent.version"),
177                                            MavenProjectProblem.ProblemType.STRUCTURE,
178                                            false));
179       return UNKNOWN;
180     }
181     VirtualFile parentFile = file.findFileByRelativePath("../../pom.xml");
182     if (parentFile == null) {
183       problems.add(new MavenProjectProblem(file.getPath(), MavenProjectBundle.message("consumer.pom.cannot.determine.parent.version"),
184                                            MavenProjectProblem.ProblemType.STRUCTURE,
185                                            false));
186       return UNKNOWN;
187     }
188
189     Element parentXmlProject = readXml(parentFile, problems, MavenProjectProblem.ProblemType.SYNTAX);
190     version = MavenJDOMUtil.findChildValueByPath(parentXmlProject, "version");
191     if (version != null) {
192       return version;
193     }
194     return calculateParentVersion(parentXmlProject, problems, parentFile);
195   }
196
197   private static void readModelBody(MavenModelBase mavenModelBase, MavenBuildBase mavenBuildBase, Element xmlModel) {
198     mavenModelBase.setModules(MavenJDOMUtil.findChildrenValuesByPath(xmlModel, "modules", "module"));
199     collectProperties(MavenJDOMUtil.findChildByPath(xmlModel, "properties"), mavenModelBase);
200
201     Element xmlBuild = MavenJDOMUtil.findChildByPath(xmlModel, "build");
202
203     mavenBuildBase.setFinalName(MavenJDOMUtil.findChildValueByPath(xmlBuild, "finalName"));
204     mavenBuildBase.setDefaultGoal(MavenJDOMUtil.findChildValueByPath(xmlBuild, "defaultGoal"));
205     mavenBuildBase.setDirectory(MavenJDOMUtil.findChildValueByPath(xmlBuild, "directory"));
206     mavenBuildBase.setResources(collectResources(MavenJDOMUtil.findChildrenByPath(xmlBuild, "resources", "resource")));
207     mavenBuildBase.setTestResources(collectResources(MavenJDOMUtil.findChildrenByPath(xmlBuild, "testResources", "testResource")));
208     mavenBuildBase.setFilters(MavenJDOMUtil.findChildrenValuesByPath(xmlBuild, "filters", "filter"));
209
210     if (mavenBuildBase instanceof MavenBuild) {
211       MavenBuild mavenBuild = (MavenBuild)mavenBuildBase;
212
213       String source = MavenJDOMUtil.findChildValueByPath(xmlBuild, "sourceDirectory");
214       if (!isEmptyOrSpaces(source)) mavenBuild.addSource(source);
215       String testSource = MavenJDOMUtil.findChildValueByPath(xmlBuild, "testSourceDirectory");
216       if (!isEmptyOrSpaces(testSource)) mavenBuild.addTestSource(testSource);
217
218       mavenBuild.setOutputDirectory(MavenJDOMUtil.findChildValueByPath(xmlBuild, "outputDirectory"));
219       mavenBuild.setTestOutputDirectory(MavenJDOMUtil.findChildValueByPath(xmlBuild, "testOutputDirectory"));
220     }
221   }
222
223   private static List<MavenResource> collectResources(List<Element> xmlResources) {
224     List<MavenResource> result = new ArrayList<>();
225     for (Element each : xmlResources) {
226       result.add(new MavenResource(MavenJDOMUtil.findChildValueByPath(each, "directory"),
227                                    "true".equals(MavenJDOMUtil.findChildValueByPath(each, "filtering")),
228                                    MavenJDOMUtil.findChildValueByPath(each, "targetPath"),
229                                    MavenJDOMUtil.findChildrenValuesByPath(each, "includes", "include"),
230                                    MavenJDOMUtil.findChildrenValuesByPath(each, "excludes", "exclude")));
231     }
232     return result;
233   }
234
235   private void repairModelBody(MavenModel model) {
236     MavenBuild build = model.getBuild();
237
238     if (isEmptyOrSpaces(build.getFinalName())) {
239       build.setFinalName("${project.artifactId}-${project.version}");
240     }
241
242     if (build.getSources().isEmpty()) build.addSource("src/main/java");
243     if (build.getTestSources().isEmpty()) build.addTestSource("src/test/java");
244
245     build.setResources(repairResources(build.getResources(), "src/main/resources"));
246     build.setTestResources(repairResources(build.getTestResources(), "src/test/resources"));
247
248     build.setDirectory(isEmptyOrSpaces(build.getDirectory()) ? "target" : build.getDirectory());
249     build.setOutputDirectory(isEmptyOrSpaces(build.getOutputDirectory())
250                              ? "${project.build.directory}/classes" : build.getOutputDirectory());
251     build.setTestOutputDirectory(isEmptyOrSpaces(build.getTestOutputDirectory())
252                                  ? "${project.build.directory}/test-classes" : build.getTestOutputDirectory());
253   }
254
255   private List<MavenResource> repairResources(List<MavenResource> resources, String defaultDir) {
256     List<MavenResource> result = new ArrayList<>();
257     if (resources.isEmpty()) {
258       result.add(createResource(defaultDir));
259       return result;
260     }
261
262     for (MavenResource each : resources) {
263       if (isEmptyOrSpaces(each.getDirectory())) continue;
264       result.add(each);
265     }
266     return result;
267   }
268
269   private MavenResource createResource(String directory) {
270     return new MavenResource(directory, false, null, Collections.emptyList(), Collections.emptyList());
271   }
272
273   private List<MavenProfile> collectProfiles(VirtualFile projectFile,
274                                              Element xmlProject,
275                                              Collection<MavenProjectProblem> problems,
276                                              Set<String> alwaysOnProfiles) {
277     List<MavenProfile> result = new ArrayList<>();
278     collectProfiles(MavenJDOMUtil.findChildrenByPath(xmlProject, "profiles", "profile"), result, MavenConstants.PROFILE_FROM_POM);
279
280     VirtualFile profilesFile = MavenUtil.findProfilesXmlFile(projectFile);
281     if (profilesFile != null) {
282       collectProfilesFromSettingsXmlOrProfilesXml(profilesFile,
283                                                   "profilesXml",
284                                                   true,
285                                                   MavenConstants.PROFILE_FROM_PROFILES_XML,
286                                                   result,
287                                                   alwaysOnProfiles,
288                                                   problems);
289     }
290
291     return result;
292   }
293
294   private void addSettingsProfiles(MavenGeneralSettings generalSettings,
295                                    MavenModel model,
296                                    Set<String> alwaysOnProfiles,
297                                    Collection<MavenProjectProblem> problems) {
298     if (mySettingsProfilesCache == null) {
299
300       List<MavenProfile> settingsProfiles = new ArrayList<>();
301       Collection<MavenProjectProblem> settingsProblems = MavenProjectProblem.createProblemsList();
302       Set<String> settingsAlwaysOnProfiles = new HashSet<>();
303
304       for (VirtualFile each : generalSettings.getEffectiveSettingsFiles()) {
305         collectProfilesFromSettingsXmlOrProfilesXml(each,
306                                                     "settings",
307                                                     false,
308                                                     MavenConstants.PROFILE_FROM_SETTINGS_XML,
309                                                     settingsProfiles,
310                                                     settingsAlwaysOnProfiles,
311                                                     settingsProblems);
312       }
313       mySettingsProfilesCache = new SettingsProfilesCache(settingsProfiles, settingsAlwaysOnProfiles, settingsProblems);
314     }
315
316     List<MavenProfile> modelProfiles = new ArrayList<>(model.getProfiles());
317     for (MavenProfile each : mySettingsProfilesCache.profiles) {
318       addProfileIfDoesNotExist(each, modelProfiles);
319     }
320     model.setProfiles(modelProfiles);
321
322     problems.addAll(mySettingsProfilesCache.problems);
323     alwaysOnProfiles.addAll(mySettingsProfilesCache.alwaysOnProfiles);
324   }
325
326   private void collectProfilesFromSettingsXmlOrProfilesXml(VirtualFile profilesFile,
327                                                            String rootElementName,
328                                                            boolean wrapRootIfNecessary,
329                                                            String profilesSource,
330                                                            List<MavenProfile> result,
331                                                            Set<String> alwaysOnProfiles,
332                                                            Collection<MavenProjectProblem> problems) {
333     Element rootElement = readXml(profilesFile, problems, MavenProjectProblem.ProblemType.SETTINGS_OR_PROFILES);
334     if (rootElement == null) return;
335
336     if (wrapRootIfNecessary && !rootElementName.equals(rootElement.getName())) {
337       Element wrapper = new Element(rootElementName);
338       wrapper.addContent(rootElement);
339       rootElement = wrapper;
340     }
341
342     List<Element> xmlProfiles = MavenJDOMUtil.findChildrenByPath(rootElement, "profiles", "profile");
343     collectProfiles(xmlProfiles, result, profilesSource);
344
345     alwaysOnProfiles.addAll(MavenJDOMUtil.findChildrenValuesByPath(rootElement, "activeProfiles", "activeProfile"));
346   }
347
348   private void collectProfiles(List<Element> xmlProfiles, List<MavenProfile> result, String source) {
349     for (Element each : xmlProfiles) {
350       String id = MavenJDOMUtil.findChildValueByPath(each, "id");
351       if (isEmptyOrSpaces(id)) continue;
352
353       MavenProfile profile = new MavenProfile(id, source);
354       if (!addProfileIfDoesNotExist(profile, result)) continue;
355
356       Element xmlActivation = MavenJDOMUtil.findChildByPath(each, "activation");
357       if (xmlActivation != null) {
358         MavenProfileActivation activation = new MavenProfileActivation();
359         activation.setActiveByDefault("true".equals(MavenJDOMUtil.findChildValueByPath(xmlActivation, "activeByDefault")));
360
361         Element xmlOS = MavenJDOMUtil.findChildByPath(xmlActivation, "os");
362         if (xmlOS != null) {
363           activation.setOs(new MavenProfileActivationOS(
364             MavenJDOMUtil.findChildValueByPath(xmlOS, "name"),
365             MavenJDOMUtil.findChildValueByPath(xmlOS, "family"),
366             MavenJDOMUtil.findChildValueByPath(xmlOS, "arch"),
367             MavenJDOMUtil.findChildValueByPath(xmlOS, "version")));
368         }
369
370         activation.setJdk(MavenJDOMUtil.findChildValueByPath(xmlActivation, "jdk"));
371
372         Element xmlProperty = MavenJDOMUtil.findChildByPath(xmlActivation, "property");
373         if (xmlProperty != null) {
374           activation.setProperty(new MavenProfileActivationProperty(
375             MavenJDOMUtil.findChildValueByPath(xmlProperty, "name"),
376             MavenJDOMUtil.findChildValueByPath(xmlProperty, "value")));
377         }
378
379         Element xmlFile = MavenJDOMUtil.findChildByPath(xmlActivation, "file");
380         if (xmlFile != null) {
381           activation.setFile(new MavenProfileActivationFile(
382             MavenJDOMUtil.findChildValueByPath(xmlFile, "exists"),
383             MavenJDOMUtil.findChildValueByPath(xmlFile, "missing")));
384         }
385
386         profile.setActivation(activation);
387       }
388
389       readModelBody(profile, profile.getBuild(), each);
390     }
391   }
392
393   private boolean addProfileIfDoesNotExist(MavenProfile profile, List<MavenProfile> result) {
394     for (MavenProfile each : result) {
395       if (Objects.equals(each.getId(), profile.getId())) return false;
396     }
397     result.add(profile);
398     return true;
399   }
400
401   private static void collectProperties(Element xmlProperties, MavenModelBase mavenModelBase) {
402     if (xmlProperties == null) return;
403
404     Properties props = mavenModelBase.getProperties();
405
406     for (Element each : xmlProperties.getChildren()) {
407       String name = each.getName();
408       String value = each.getTextTrim();
409       if (!props.containsKey(name) && !isEmptyOrSpaces(name)) {
410         props.setProperty(name, value);
411       }
412     }
413   }
414
415   private ProfileApplicationResult applyProfiles(MavenModel model,
416                                                  File projectPomDir,
417                                                  File basedir,
418                                                  MavenExplicitProfiles explicitProfiles,
419                                                  Collection<String> alwaysOnProfiles) {
420     return MavenServerManager.getInstance().getConnector(myProject, projectPomDir.getAbsolutePath())
421       .applyProfiles(model, basedir, explicitProfiles, alwaysOnProfiles);
422   }
423
424   private MavenModel resolveInheritance(final MavenGeneralSettings generalSettings,
425                                         MavenModel model,
426                                         final File projectPomDir,
427                                         final VirtualFile file,
428                                         final MavenExplicitProfiles explicitProfiles,
429                                         final Set<VirtualFile> recursionGuard,
430                                         final MavenProjectReaderProjectLocator locator,
431                                         Collection<MavenProjectProblem> problems) {
432     if (recursionGuard.contains(file)) {
433       problems
434         .add(MavenProjectProblem.createProblem(file.getPath(), MavenProjectBundle.message("maven.project.problem.recursiveInheritance"),
435                                                MavenProjectProblem.ProblemType.PARENT, false));
436       return model;
437     }
438     recursionGuard.add(file);
439
440     try {
441       final MavenParentDesc[] parentDesc = new MavenParentDesc[1];
442       MavenParent parent = model.getParent();
443       if (parent != null) {
444         if (model.getMavenId().equals(parent.getMavenId())) {
445           problems
446             .add(MavenProjectProblem.createProblem(file.getPath(), MavenProjectBundle.message("maven.project.problem.selfInheritance"),
447                                                    MavenProjectProblem.ProblemType.PARENT, false));
448           return model;
449         }
450         parentDesc[0] = new MavenParentDesc(parent.getMavenId(), parent.getRelativePath());
451       }
452
453       Pair<VirtualFile, RawModelReadResult> parentModelWithProblems =
454         new MavenParentProjectFileProcessor<Pair<VirtualFile, RawModelReadResult>>() {
455           @Override
456           @Nullable
457           protected VirtualFile findManagedFile(@NotNull MavenId id) {
458             return locator.findProjectFile(id);
459           }
460
461           @Override
462           @Nullable
463           protected Pair<VirtualFile, RawModelReadResult> processRelativeParent(VirtualFile parentFile) {
464             MavenModel parentModel = doReadProjectModel(parentFile, true).model;
465             MavenId parentId = parentDesc[0].getParentId();
466             if (!parentId.equals(parentModel.getMavenId())) return null;
467
468             return super.processRelativeParent(parentFile);
469           }
470
471           @Override
472           protected Pair<VirtualFile, RawModelReadResult> processSuperParent(VirtualFile parentFile) {
473             return null; // do not process superPom
474           }
475
476           @Override
477           protected Pair<VirtualFile, RawModelReadResult> doProcessParent(VirtualFile parentFile) {
478             RawModelReadResult result =
479               doReadProjectModel(generalSettings, projectPomDir, parentFile, explicitProfiles, recursionGuard, locator).first;
480             return Pair.create(parentFile, result);
481           }
482         }.process(generalSettings, file, parentDesc[0]);
483
484       if (parentModelWithProblems == null) return model; // no parent or parent not found;
485
486       MavenModel parentModel = parentModelWithProblems.second.model;
487       if (!parentModelWithProblems.second.problems.isEmpty()) {
488         problems.add(MavenProjectProblem.createProblem(parentModelWithProblems.first.getPath(),
489                                                        MavenProjectBundle.message("maven.project.problem.parentHasProblems",
490                                                                                   parentModel.getMavenId()),
491                                                        MavenProjectProblem.ProblemType.PARENT, false));
492       }
493
494       model = MavenServerManager.getInstance().getConnector(myProject, projectPomDir.getAbsolutePath())
495         .assembleInheritance(model, parentModel);
496
497       // todo: it is a quick-hack here - we add inherited dummy profiles to correctly collect activated profiles in 'applyProfiles'.
498       List<MavenProfile> profiles = model.getProfiles();
499       for (MavenProfile each : parentModel.getProfiles()) {
500         MavenProfile copyProfile = new MavenProfile(each.getId(), each.getSource());
501         if (each.getActivation() != null) {
502           copyProfile.setActivation(each.getActivation().clone());
503         }
504
505         addProfileIfDoesNotExist(copyProfile, profiles);
506       }
507       return model;
508     }
509     finally {
510       recursionGuard.remove(file);
511     }
512   }
513
514   public Collection<MavenProjectReaderResult> resolveProject(final MavenGeneralSettings generalSettings,
515                                                              MavenEmbedderWrapper embedder,
516                                                              Collection<VirtualFile> files,
517                                                              final MavenExplicitProfiles explicitProfiles,
518                                                              final MavenProjectReaderProjectLocator locator)
519     throws MavenProcessCanceledException {
520     try {
521       Collection<MavenServerExecutionResult> executionResults = embedder
522         .resolveProject(files, explicitProfiles.getEnabledProfiles(), explicitProfiles.getDisabledProfiles());
523       Map<String, VirtualFile> filesMap = CollectionFactory.createFilePathMap();
524       filesMap.putAll(files.stream().collect(toMap(VirtualFile::getPath, Function.identity())));
525
526       Collection<MavenProjectReaderResult> readerResults = new ArrayList<>();
527       for (MavenServerExecutionResult result : executionResults) {
528         MavenServerExecutionResult.ProjectData projectData = result.projectData;
529         if (projectData == null) {
530           VirtualFile file = detectPomFile(filesMap, result);
531           if (file != null) {
532             MavenProjectReaderResult temp = readProject(generalSettings, file, explicitProfiles, locator);
533             temp.readingProblems.addAll(result.problems);
534             temp.unresolvedArtifactIds.addAll(result.unresolvedArtifacts);
535             readerResults.add(temp);
536           }
537         }
538         else {
539           readerResults.add(new MavenProjectReaderResult(
540             projectData.mavenModel,
541             projectData.mavenModelMap,
542             new MavenExplicitProfiles(projectData.activatedProfiles, explicitProfiles.getDisabledProfiles()),
543             projectData.nativeMavenProject,
544             result.problems,
545             result.unresolvedArtifacts));
546         }
547       }
548
549       return readerResults;
550     }
551     catch (MavenProcessCanceledException e) {
552       throw e;
553     }
554     catch (final Throwable e) {
555       MavenLog.LOG.info(e);
556       MavenLog.printInTests(e); // print exception since we need to know if something wrong with our logic
557
558       return ContainerUtil.mapNotNull(files, file -> {
559         MavenProjectReaderResult result = readProject(generalSettings, file, explicitProfiles, locator);
560         String message = e.getMessage();
561         if (message != null) {
562           result.readingProblems.add(MavenProjectProblem.createStructureProblem(file.getPath(), message));
563         }
564         else {
565           result.readingProblems.add(MavenProjectProblem.createSyntaxProblem(file.getPath(), MavenProjectProblem.ProblemType.SYNTAX));
566         }
567         return result;
568       });
569     }
570   }
571
572   @Nullable
573   private static VirtualFile detectPomFile(Map<String, VirtualFile> filesMap, MavenServerExecutionResult result) {
574     if (filesMap.size() == 1) {
575       return getFirstItem(filesMap.values());
576     }
577     if (!result.problems.isEmpty()) {
578       String path = getFirstItem(result.problems).getPath();
579       if (path != null) {
580         return filesMap.get(toSystemIndependentName(path));
581       }
582     }
583     return null;
584   }
585
586   @Nullable
587   public static MavenProjectReaderResult generateSources(MavenEmbedderWrapper embedder,
588                                                          MavenImportingSettings importingSettings,
589                                                          VirtualFile file,
590                                                          MavenExplicitProfiles profiles,
591                                                          MavenConsole console) throws MavenProcessCanceledException {
592     try {
593       List<String> goals = Collections.singletonList(importingSettings.getUpdateFoldersOnImportPhase());
594       MavenServerExecutionResult result = embedder.execute(file, profiles.getEnabledProfiles(), profiles.getDisabledProfiles(), goals);
595       MavenServerExecutionResult.ProjectData projectData = result.projectData;
596       if (projectData == null) return null;
597
598       return new MavenProjectReaderResult(projectData.mavenModel,
599                                           projectData.mavenModelMap,
600                                           new MavenExplicitProfiles(projectData.activatedProfiles, profiles.getDisabledProfiles()),
601                                           projectData.nativeMavenProject,
602                                           result.problems,
603                                           result.unresolvedArtifacts);
604     }
605     catch (Throwable e) {
606       console.printException(e);
607       MavenLog.LOG.warn(e);
608       return null;
609     }
610   }
611
612   private static Element readXml(final VirtualFile file,
613                                  final Collection<MavenProjectProblem> problems,
614                                  final MavenProjectProblem.ProblemType type) {
615     return MavenJDOMUtil.read(file, new MavenJDOMUtil.ErrorHandler() {
616       @Override
617       public void onReadError(IOException e) {
618         MavenLog.LOG.warn("Cannot read the pom file: " + e);
619         problems.add(MavenProjectProblem.createProblem(file.getPath(), e.getMessage(), type, false));
620       }
621
622       @Override
623       public void onSyntaxError() {
624         problems.add(MavenProjectProblem.createSyntaxProblem(file.getPath(), type));
625       }
626     });
627   }
628
629   private static final class SettingsProfilesCache {
630     final List<MavenProfile> profiles;
631     final Set<String> alwaysOnProfiles;
632     final Collection<MavenProjectProblem> problems;
633
634     private SettingsProfilesCache(List<MavenProfile> profiles, Set<String> alwaysOnProfiles, Collection<MavenProjectProblem> problems) {
635       this.profiles = profiles;
636       this.alwaysOnProfiles = alwaysOnProfiles;
637       this.problems = problems;
638     }
639   }
640
641   private static final class RawModelReadResult {
642     public MavenModel model;
643     public Collection<MavenProjectProblem> problems;
644     public Set<String> alwaysOnProfiles;
645
646     private RawModelReadResult(MavenModel model, Collection<MavenProjectProblem> problems, Set<String> alwaysOnProfiles) {
647       this.model = model;
648       this.problems = problems;
649       this.alwaysOnProfiles = alwaysOnProfiles;
650     }
651   }
652 }