Merge pull request #19 from xfournet/IDEA-91145-trunk
[idea/community.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / project / MavenProjectsTree.java
1 /*
2  * Copyright 2000-2009 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.idea.maven.project;
17
18 import com.intellij.openapi.application.AccessToken;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.util.Comparing;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.util.io.FileUtil;
25 import com.intellij.openapi.vfs.LocalFileSystem;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.util.Function;
28 import com.intellij.util.SmartList;
29 import com.intellij.util.containers.ArrayListSet;
30 import com.intellij.util.containers.ContainerUtil;
31 import com.intellij.util.containers.Stack;
32 import gnu.trove.THashMap;
33 import gnu.trove.THashSet;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36 import org.jetbrains.annotations.TestOnly;
37 import org.jetbrains.idea.maven.model.*;
38 import org.jetbrains.idea.maven.server.MavenEmbedderWrapper;
39 import org.jetbrains.idea.maven.server.NativeMavenProjectHolder;
40 import org.jetbrains.idea.maven.utils.*;
41
42 import java.io.*;
43 import java.util.*;
44 import java.util.concurrent.locks.Lock;
45 import java.util.concurrent.locks.ReentrantReadWriteLock;
46 import java.util.regex.Pattern;
47
48 public class MavenProjectsTree {
49   private static final String STORAGE_VERSION = MavenProjectsTree.class.getSimpleName() + ".6";
50
51   private final Object myStateLock = new Object();
52   private final ReentrantReadWriteLock myStructureLock = new ReentrantReadWriteLock();
53   private final Lock myStructureReadLock = myStructureLock.readLock();
54   private final Lock myStructureWriteLock = myStructureLock.writeLock();
55
56   // TODO replace with sets
57   private volatile List<String> myManagedFilesPaths = new ArrayList<String>();
58   private volatile List<String> myIgnoredFilesPaths = new ArrayList<String>();
59   private volatile List<String> myIgnoredFilesPatterns = new ArrayList<String>();
60   private volatile Pattern myIgnoredFilesPatternsCache;
61
62   private volatile Set<String> myExplicitProfiles = new THashSet<String>();
63   private final Set<String> myTemporarilyRemovedExplicitProfiles = new THashSet<String>();
64
65   private final List<MavenProject> myRootProjects = new ArrayList<MavenProject>();
66
67   private final Map<MavenProject, MavenProjectTimestamp> myTimestamps = new THashMap<MavenProject, MavenProjectTimestamp>();
68   private final MavenWorkspaceMap myWorkspaceMap = new MavenWorkspaceMap();
69   private final Map<MavenId, MavenProject> myMavenIdToProjectMapping = new THashMap<MavenId, MavenProject>();
70   private final Map<VirtualFile, MavenProject> myVirtualFileToProjectMapping = new THashMap<VirtualFile, MavenProject>();
71   private final Map<MavenProject, List<MavenProject>> myAggregatorToModuleMapping = new THashMap<MavenProject, List<MavenProject>>();
72   private final Map<MavenProject, MavenProject> myModuleToAggregatorMapping = new THashMap<MavenProject, MavenProject>();
73
74   private final List<Listener> myListeners = ContainerUtil.createEmptyCOWList();
75
76   private final MavenProjectReaderProjectLocator myProjectLocator = new MavenProjectReaderProjectLocator() {
77     public VirtualFile findProjectFile(MavenId coordinates) {
78       MavenProject project = findProject(coordinates);
79       return project == null ? null : project.getFile();
80     }
81   };
82
83   @Nullable
84   public static MavenProjectsTree read(File file) throws IOException {
85     MavenProjectsTree result = new MavenProjectsTree();
86
87     DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
88     try {
89       try {
90         if (!STORAGE_VERSION.equals(in.readUTF())) return null;
91         result.myManagedFilesPaths = readList(in);
92         result.myIgnoredFilesPaths = readList(in);
93         result.myIgnoredFilesPatterns = readList(in);
94         result.myExplicitProfiles = readCollection(in, new Function<Integer, Set<String>>() {
95           public Set<String> fun(Integer integer) {
96             return new THashSet<String>();
97           }
98         });
99         result.myRootProjects.addAll(readProjectsRecursively(in, result));
100       }
101       catch (Throwable e) {
102         throw new IOException(e);
103       }
104     }
105     finally {
106       in.close();
107     }
108     return result;
109   }
110
111   private static List<String> readList(DataInputStream in) throws IOException {
112     return readCollection(in, new Function<Integer, List<String>>() {
113       public List<String> fun(Integer integer) {
114         return new ArrayList<String>(integer);
115       }
116     });
117   }
118
119   private static <T extends Collection<String>> T readCollection(DataInputStream in, Function<Integer, T> factory) throws IOException {
120     int count = in.readInt();
121     T result = factory.fun(count);
122     while (count-- > 0) {
123       result.add(in.readUTF());
124     }
125     return result;
126   }
127
128   private static List<MavenProject> readProjectsRecursively(DataInputStream in,
129                                                             MavenProjectsTree tree) throws IOException {
130     int count = in.readInt();
131     List<MavenProject> result = new ArrayList<MavenProject>(count);
132     while (count-- > 0) {
133       MavenProject project = MavenProject.read(in);
134       MavenProjectTimestamp timestamp = MavenProjectTimestamp.read(in);
135       List<MavenProject> modules = readProjectsRecursively(in, tree);
136       if (project != null) {
137         result.add(project);
138         tree.myTimestamps.put(project, timestamp);
139         tree.myVirtualFileToProjectMapping.put(project.getFile(), project);
140         tree.fillIDMaps(project);
141         tree.myAggregatorToModuleMapping.put(project, modules);
142         for (MavenProject eachModule : modules) {
143           tree.myModuleToAggregatorMapping.put(eachModule, project);
144         }
145       }
146     }
147     return result;
148   }
149
150   public void save(File file) throws IOException {
151     synchronized (myStateLock) {
152       readLock();
153       try {
154         file.getParentFile().mkdirs();
155         DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
156         try {
157           out.writeUTF(STORAGE_VERSION);
158           writeCollection(out, myManagedFilesPaths);
159           writeCollection(out, myIgnoredFilesPaths);
160           writeCollection(out, myIgnoredFilesPatterns);
161           writeCollection(out, myExplicitProfiles);
162           writeProjectsRecursively(out, myRootProjects);
163         }
164         finally {
165           out.close();
166         }
167       }
168       finally {
169         readUnlock();
170       }
171     }
172   }
173
174   private static void writeCollection(DataOutputStream out, Collection<String> list) throws IOException {
175     out.writeInt(list.size());
176     for (String each : list) {
177       out.writeUTF(each);
178     }
179   }
180
181   private void writeProjectsRecursively(DataOutputStream out, List<MavenProject> list) throws IOException {
182     out.writeInt(list.size());
183     for (MavenProject each : list) {
184       each.write(out);
185       myTimestamps.get(each).write(out);
186       writeProjectsRecursively(out, getModules(each));
187     }
188   }
189
190   public List<String> getManagedFilesPaths() {
191     synchronized (myStateLock) {
192       return new ArrayList<String>(myManagedFilesPaths);
193     }
194   }
195
196   public void resetManagedFilesPathsAndProfiles(List<String> paths, Collection<String> profiles) {
197     synchronized (myStateLock) {
198       myManagedFilesPaths = new ArrayList<String>(paths);
199     }
200     setExplicitProfiles(profiles);
201   }
202
203   @TestOnly
204   public void resetManagedFilesAndProfiles(List<VirtualFile> files, Collection<String> profiles) {
205     resetManagedFilesPathsAndProfiles(MavenUtil.collectPaths(files), profiles);
206   }
207
208   public void addManagedFilesWithProfiles(List<VirtualFile> files, Collection<String> profiles) {
209     List<String> newFiles;
210     Set<String> newProfiles;
211     synchronized (myStateLock) {
212       newFiles = new ArrayList<String>(myManagedFilesPaths);
213       newFiles.addAll(MavenUtil.collectPaths(files));
214
215       newProfiles = new THashSet<String>(myExplicitProfiles);
216       newProfiles.addAll(profiles);
217     }
218
219     resetManagedFilesPathsAndProfiles(newFiles, newProfiles);
220   }
221
222   public void removeManagedFiles(List<VirtualFile> files) {
223     synchronized (myStateLock) {
224       myManagedFilesPaths.removeAll(MavenUtil.collectPaths(files));
225     }
226   }
227
228   public List<VirtualFile> getExistingManagedFiles() {
229     List<VirtualFile> result = new ArrayList<VirtualFile>();
230     for (String path : getManagedFilesPaths()) {
231       VirtualFile f = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
232       if (f != null) result.add(f);
233     }
234     return result;
235   }
236
237   public List<String> getIgnoredFilesPaths() {
238     synchronized (myStateLock) {
239       return new ArrayList<String>(myIgnoredFilesPaths);
240     }
241   }
242
243   public void setIgnoredFilesPaths(final List<String> paths) {
244     doChangeIgnoreStatus(new Runnable() {
245       public void run() {
246         myIgnoredFilesPaths = new ArrayList<String>(paths);
247       }
248     });
249   }
250
251   public boolean getIgnoredState(MavenProject project) {
252     synchronized (myStateLock) {
253       return myIgnoredFilesPaths.contains(project.getPath());
254     }
255   }
256
257   public void setIgnoredState(List<MavenProject> projects, boolean ignored) {
258     setIgnoredState(projects, ignored, false);
259   }
260
261   public void setIgnoredState(List<MavenProject> projects, boolean ignored, boolean fromImport) {
262     doSetIgnoredState(projects, ignored, fromImport);
263   }
264
265   private void doSetIgnoredState(List<MavenProject> projects, final boolean ignored, boolean fromImport) {
266     final List<String> paths = MavenUtil.collectPaths(MavenUtil.collectFiles(projects));
267     doChangeIgnoreStatus(new Runnable() {
268         public void run() {
269           if (ignored) {
270             myIgnoredFilesPaths.addAll(paths);
271           }
272           else {
273             myIgnoredFilesPaths.removeAll(paths);
274           }
275         }
276       }, fromImport);
277   }
278
279   public List<String> getIgnoredFilesPatterns() {
280     synchronized (myStateLock) {
281       return new ArrayList<String>(myIgnoredFilesPatterns);
282     }
283   }
284
285   public void setIgnoredFilesPatterns(final List<String> patterns) {
286     doChangeIgnoreStatus(new Runnable() {
287       public void run() {
288         myIgnoredFilesPatternsCache = null;
289         myIgnoredFilesPatterns = new ArrayList<String>(patterns);
290       }
291     });
292   }
293
294   private void doChangeIgnoreStatus(Runnable runnable) {
295     doChangeIgnoreStatus(runnable, false);
296   }
297
298   private void doChangeIgnoreStatus(Runnable runnable, boolean fromImport) {
299     List<MavenProject> ignoredBefore;
300     List<MavenProject> ignoredAfter;
301
302     synchronized (myStateLock) {
303       ignoredBefore = getIgnoredProjects();
304       runnable.run();
305       ignoredAfter = getIgnoredProjects();
306     }
307
308     List<MavenProject> ignored = new ArrayList<MavenProject>(ignoredAfter);
309     ignored.removeAll(ignoredBefore);
310
311     List<MavenProject> unignored = new ArrayList<MavenProject>(ignoredBefore);
312     unignored.removeAll(ignoredAfter);
313
314     if (ignored.isEmpty() && unignored.isEmpty()) return;
315
316     fireProjectsIgnoredStateChanged(ignored, unignored, fromImport);
317   }
318
319   private List<MavenProject> getIgnoredProjects() {
320     List<MavenProject> result = new ArrayList<MavenProject>();
321     for (MavenProject each : getProjects()) {
322       if (isIgnored(each)) result.add(each);
323     }
324     return result;
325   }
326
327   public boolean isIgnored(MavenProject project) {
328     String path = project.getPath();
329     synchronized (myStateLock) {
330       return myIgnoredFilesPaths.contains(path) || matchesIgnoredFilesPatterns(path);
331     }
332   }
333
334   private boolean matchesIgnoredFilesPatterns(String path) {
335     synchronized (myStateLock) {
336       if (myIgnoredFilesPatternsCache == null) {
337         myIgnoredFilesPatternsCache = Pattern.compile(Strings.translateMasks(myIgnoredFilesPatterns));
338       }
339       return myIgnoredFilesPatternsCache.matcher(path).matches();
340     }
341   }
342
343   public Collection<String> getExplicitProfiles() {
344     synchronized (myStateLock) {
345       return new THashSet<String>(myExplicitProfiles);
346     }
347   }
348
349   public void setExplicitProfiles(Collection<String> explicitProfiles) {
350     synchronized (myStateLock) {
351       myExplicitProfiles = new THashSet<String>(explicitProfiles);
352     }
353     fireProfilesChanged();
354   }
355
356   private void updateExplicitProfiles() {
357     Collection<String> available = getAvailableProfiles();
358
359     synchronized (myStateLock) {
360       Collection<String> removedProfiles = new THashSet<String>(myExplicitProfiles);
361       removedProfiles.removeAll(available);
362       myTemporarilyRemovedExplicitProfiles.addAll(removedProfiles);
363
364       Collection<String> restoredProfiles = new THashSet<String>(myTemporarilyRemovedExplicitProfiles);
365       restoredProfiles.retainAll(available);
366       myTemporarilyRemovedExplicitProfiles.removeAll(restoredProfiles);
367
368       myExplicitProfiles.removeAll(removedProfiles);
369       myExplicitProfiles.addAll(restoredProfiles);
370     }
371   }
372
373   public Collection<String> getAvailableProfiles() {
374     return getAvailableAndActiveProfiles(true, false).first;
375   }
376
377   private Pair<Collection<String>, Collection<String>> getAvailableAndActiveProfiles(boolean includeAvailable, boolean includeActive) {
378     Collection<String> available = includeAvailable ? new THashSet<String>() : null;
379     Collection<String> active = includeActive ? new THashSet<String>() : null;
380     for (MavenProject each : getProjects()) {
381       if (available != null) available.addAll(each.getProfilesIds());
382       if (active != null) active.addAll(each.getActivatedProfilesIds());
383     }
384     return Pair.create(available, active);
385   }
386
387   public Collection<Pair<String, MavenProfileKind>> getProfilesWithStates() {
388     Collection<Pair<String, MavenProfileKind>> result = new ArrayListSet<Pair<String, MavenProfileKind>>();
389
390     Pair<Collection<String>, Collection<String>> profiles = getAvailableAndActiveProfiles(true, true);
391     Collection<String> available = profiles.first;
392     Collection<String> active = profiles.second;
393     Collection<String> explicitProfiles = getExplicitProfiles();
394
395     for (String each : available) {
396       MavenProfileKind state = MavenProfileKind.NONE;
397       if (explicitProfiles.contains(each)) {
398         state = MavenProfileKind.EXPLICIT;
399       }
400       else if (active.contains(each)) state = MavenProfileKind.IMPLICIT;
401       result.add(Pair.create(each, state));
402     }
403     return result;
404   }
405
406   public void updateAll(boolean force, MavenGeneralSettings generalSettings, MavenProgressIndicator process) {
407     List<VirtualFile> managedFiles = getExistingManagedFiles();
408     Collection<String> explicitProfiles = getExplicitProfiles();
409
410     MavenProjectReader projectReader = new MavenProjectReader();
411     update(managedFiles, true, force, explicitProfiles, projectReader, generalSettings, process);
412
413     List<VirtualFile> obsoleteFiles = getRootProjectsFiles();
414     obsoleteFiles.removeAll(managedFiles);
415     delete(projectReader, obsoleteFiles, explicitProfiles, generalSettings, process);
416   }
417
418   public void update(Collection<VirtualFile> files,
419                      boolean force,
420                      MavenGeneralSettings generalSettings,
421                      MavenProgressIndicator process) {
422     update(files, false, force, getExplicitProfiles(), new MavenProjectReader(), generalSettings, process);
423   }
424
425   private void update(Collection<VirtualFile> files,
426                       boolean recursive,
427                       boolean force,
428                       Collection<String> explicitProfiles,
429                       MavenProjectReader projectReader,
430                       MavenGeneralSettings generalSettings,
431                       MavenProgressIndicator process) {
432     if (files.isEmpty()) return;
433
434     UpdateContext updateContext = new UpdateContext();
435     Stack<MavenProject> updateStack = new Stack<MavenProject>();
436
437     for (VirtualFile each : files) {
438       MavenProject mavenProject = findProject(each);
439       if (mavenProject == null) {
440         doAdd(each, recursive, explicitProfiles, updateContext, updateStack, projectReader, generalSettings, process);
441       }
442       else {
443         doUpdate(mavenProject,
444                  findAggregator(mavenProject),
445                  false,
446                  recursive,
447                  force,
448                  explicitProfiles,
449                  updateContext,
450                  updateStack,
451                  projectReader,
452                  generalSettings,
453                  process);
454       }
455     }
456
457     updateExplicitProfiles();
458     updateContext.fireUpdatedIfNecessary();
459   }
460
461   private void doAdd(final VirtualFile f,
462                      boolean recursuve,
463                      Collection<String> explicitProfiles,
464                      UpdateContext updateContext,
465                      Stack<MavenProject> updateStack,
466                      MavenProjectReader reader,
467                      MavenGeneralSettings generalSettings,
468                      MavenProgressIndicator process) {
469     MavenProject newMavenProject = new MavenProject(f);
470
471     MavenProject intendedAggregator = null;
472     for (MavenProject each : getProjects()) {
473       if (each.getExistingModuleFiles().contains(f)) {
474         intendedAggregator = each;
475         break;
476       }
477     }
478
479     doUpdate(newMavenProject,
480              intendedAggregator,
481              true,
482              recursuve,
483              false,
484              explicitProfiles,
485              updateContext,
486              updateStack,
487              reader,
488              generalSettings,
489              process);
490   }
491
492   private void doUpdate(MavenProject mavenProject,
493                         MavenProject aggregator,
494                         boolean isNew,
495                         boolean recursive,
496                         boolean force,
497                         Collection<String> explicitProfiles,
498                         UpdateContext updateContext,
499                         Stack<MavenProject> updateStack,
500                         MavenProjectReader reader,
501                         MavenGeneralSettings generalSettings,
502                         MavenProgressIndicator process) {
503     if (updateStack.contains(mavenProject)) {
504       MavenLog.LOG.info("Recursion detected in " + mavenProject.getFile());
505       return;
506     }
507     updateStack.push(mavenProject);
508
509     process.setText(ProjectBundle.message("maven.reading.pom", mavenProject.getPath()));
510     process.setText2("");
511
512     List<MavenProject> prevModules = getModules(mavenProject);
513     Set<MavenProject> prevInheritors = isNew
514                                        ? new THashSet<MavenProject>()
515                                        : findInheritors(mavenProject);
516
517     MavenProjectTimestamp timestamp = calculateTimestamp(mavenProject, explicitProfiles, generalSettings);
518     boolean isChanged = force || !timestamp.equals(myTimestamps.get(mavenProject));
519
520     MavenProjectChanges changes = force ? MavenProjectChanges.ALL : MavenProjectChanges.NONE;
521     if (isChanged) {
522       writeLock();
523       try {
524         if (!isNew) {
525           clearIDMaps(mavenProject);
526         }
527       }
528       finally {
529         writeUnlock();
530       }
531       MavenId oldParentId = mavenProject.getParentId();
532       changes = changes.mergedWith(mavenProject.read(generalSettings, explicitProfiles, reader, myProjectLocator));
533
534       writeLock();
535       try {
536         myVirtualFileToProjectMapping.put(mavenProject.getFile(), mavenProject);
537         fillIDMaps(mavenProject);
538       }
539       finally {
540         writeUnlock();
541       }
542
543       if (!Comparing.equal(oldParentId, mavenProject.getParentId())) {
544         // ensure timestamp reflects actual parent's timestamp
545         timestamp = calculateTimestamp(mavenProject, explicitProfiles, generalSettings);
546       }
547       myTimestamps.put(mavenProject, timestamp);
548     }
549
550     boolean reconnected = isNew;
551     if (isNew) {
552       connect(aggregator, mavenProject);
553     }
554     else {
555       reconnected = reconnect(aggregator, mavenProject);
556     }
557
558     if (isChanged || reconnected) {
559       updateContext.update(mavenProject, changes);
560     }
561
562     List<VirtualFile> existingModuleFiles = mavenProject.getExistingModuleFiles();
563     List<MavenProject> modulesToRemove = new ArrayList<MavenProject>();
564     List<MavenProject> modulesToBecomeRoots = new ArrayList<MavenProject>();
565
566     for (MavenProject each : prevModules) {
567       VirtualFile moduleFile = each.getFile();
568       if (!existingModuleFiles.contains(moduleFile)) {
569         if (isManagedFile(moduleFile)) {
570           modulesToBecomeRoots.add(each);
571         }
572         else {
573           modulesToRemove.add(each);
574         }
575       }
576     }
577     for (MavenProject each : modulesToRemove) {
578       removeModule(mavenProject, each);
579       doDelete(mavenProject, each, updateContext);
580       prevInheritors.removeAll(updateContext.deletedProjects);
581     }
582
583     for (MavenProject each : modulesToBecomeRoots) {
584       if (reconnect(null, each)) updateContext.update(each, MavenProjectChanges.NONE);
585     }
586
587     for (VirtualFile each : existingModuleFiles) {
588       MavenProject module = findProject(each);
589       boolean isNewModule = module == null;
590       if (isNewModule) {
591         module = new MavenProject(each);
592       }
593       else {
594         MavenProject currentAggregator = findAggregator(module);
595         if (currentAggregator != null && currentAggregator != mavenProject) {
596           MavenLog.LOG.info("Module " + each + " is already included into " + mavenProject.getFile());
597           continue;
598         }
599       }
600
601       if (isChanged || isNewModule || recursive) {
602         doUpdate(module,
603                  mavenProject,
604                  isNewModule,
605                  recursive,
606                  recursive ? force : false, // do not force update modules if only this project was requested to be updated
607                  explicitProfiles,
608                  updateContext,
609                  updateStack,
610                  reader,
611                  generalSettings,
612                  process);
613       }
614       else {
615         if (reconnect(mavenProject, module)) {
616           updateContext.update(module, MavenProjectChanges.NONE);
617         }
618       }
619     }
620
621     Set<MavenProject> allInheritors = findInheritors(mavenProject);
622     allInheritors.addAll(prevInheritors);
623     for (MavenProject each : allInheritors) {
624       doUpdate(each,
625                findAggregator(each),
626                false,
627                false, // no need to go recursively in case of inheritance, only when updating modules
628                false,
629                explicitProfiles,
630                updateContext,
631                updateStack,
632                reader,
633                generalSettings,
634                process);
635     }
636
637     updateStack.pop();
638   }
639
640   private MavenProjectTimestamp calculateTimestamp(final MavenProject mavenProject,
641                                                    final Collection<String> explicitProfiles,
642                                                    final MavenGeneralSettings generalSettings) {
643     AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
644     try {
645       long pomTimestamp = getFileTimestamp(mavenProject.getFile());
646       MavenProject parent = findParent(mavenProject);
647       long parentLastReadStamp = parent == null ? -1 : parent.getLastReadStamp();
648       VirtualFile profilesXmlFile = mavenProject.getProfilesXmlFile();
649       long profilesTimestamp = getFileTimestamp(profilesXmlFile);
650
651       long userSettingsTimestamp = getFileTimestamp(generalSettings.getEffectiveUserSettingsFile());
652       long globalSettingsTimestamp = getFileTimestamp(generalSettings.getEffectiveGlobalSettingsFile());
653
654       int profilesHashCode = explicitProfiles.hashCode();
655
656       return new MavenProjectTimestamp(pomTimestamp,
657                                        parentLastReadStamp,
658                                        profilesTimestamp,
659                                        userSettingsTimestamp,
660                                        globalSettingsTimestamp,
661                                        profilesHashCode);
662     }
663     finally {
664       accessToken.finish();
665     }
666   }
667
668   private static long getFileTimestamp(VirtualFile file) {
669     if (file == null || !file.isValid()) return -1;
670     return file.getTimeStamp();
671   }
672
673   public boolean isManagedFile(VirtualFile moduleFile) {
674     return isManagedFile(moduleFile.getPath());
675   }
676
677   public boolean isManagedFile(String path) {
678     for (String each : getManagedFilesPaths()) {
679       if (FileUtil.pathsEqual(each, path)) return true;
680     }
681     return false;
682   }
683
684   public boolean isPotentialProject(String path) {
685     if (isManagedFile(path)) return true;
686
687     for (MavenProject each : getProjects()) {
688       if (FileUtil.pathsEqual(path, each.getPath())) return true;
689       if (each.getModulePaths().contains(path)) return true;
690     }
691     return false;
692   }
693
694   public void delete(List<VirtualFile> files,
695                      MavenGeneralSettings generalSettings,
696                      MavenProgressIndicator process) {
697     delete(new MavenProjectReader(), files, getExplicitProfiles(), generalSettings, process);
698   }
699
700   private void delete(MavenProjectReader projectReader,
701                       List<VirtualFile> files,
702                       Collection<String> explicitProfiles,
703                       MavenGeneralSettings generalSettings,
704                       MavenProgressIndicator process) {
705     if (files.isEmpty()) return;
706
707     UpdateContext updateContext = new UpdateContext();
708     Stack<MavenProject> updateStack = new Stack<MavenProject>();
709
710     Set<MavenProject> inheritorsToUpdate = new THashSet<MavenProject>();
711     for (VirtualFile each : files) {
712       MavenProject mavenProject = findProject(each);
713       if (mavenProject == null) return;
714
715       inheritorsToUpdate.addAll(findInheritors(mavenProject));
716       doDelete(findAggregator(mavenProject), mavenProject, updateContext);
717     }
718     inheritorsToUpdate.removeAll(updateContext.deletedProjects);
719
720     for (MavenProject each : inheritorsToUpdate) {
721       doUpdate(each, null, false, false, false, explicitProfiles, updateContext, updateStack, projectReader, generalSettings, process);
722     }
723
724     updateExplicitProfiles();
725     updateContext.fireUpdatedIfNecessary();
726   }
727
728   private void doDelete(MavenProject aggregator, MavenProject project, UpdateContext updateContext) {
729     for (MavenProject each : getModules(project)) {
730       if (isManagedFile(each.getPath())) {
731         if (reconnect(null, each)) {
732           updateContext.update(each, MavenProjectChanges.NONE);
733         }
734       }
735       else {
736         doDelete(project, each, updateContext);
737       }
738     }
739
740     writeLock();
741     try {
742       if (aggregator != null) {
743         removeModule(aggregator, project);
744       }
745       else {
746         myRootProjects.remove(project);
747       }
748       myTimestamps.remove(project);
749       myVirtualFileToProjectMapping.remove(project.getFile());
750       clearIDMaps(project);
751       myAggregatorToModuleMapping.remove(project);
752       myModuleToAggregatorMapping.remove(project);
753     }
754     finally {
755       writeUnlock();
756     }
757
758     updateContext.deleted(project);
759   }
760
761   private void fillIDMaps(MavenProject mavenProject) {
762     MavenId id = mavenProject.getMavenId();
763     myWorkspaceMap.register(id, new File(mavenProject.getFile().getPath()));
764     myMavenIdToProjectMapping.put(id, mavenProject);
765   }
766
767   private void clearIDMaps(MavenProject mavenProject) {
768     MavenId id = mavenProject.getMavenId();
769     myWorkspaceMap.unregister(id);
770     myMavenIdToProjectMapping.remove(id);
771   }
772
773   private void connect(MavenProject newAggregator, MavenProject project) {
774     writeLock();
775     try {
776       if (newAggregator != null) {
777         addModule(newAggregator, project);
778       }
779       else {
780         myRootProjects.add(project);
781       }
782     }
783     finally {
784       writeUnlock();
785     }
786   }
787
788   private boolean reconnect(MavenProject newAggregator, MavenProject project) {
789     MavenProject prevAggregator = findAggregator(project);
790
791     if (prevAggregator == newAggregator) return false;
792
793     writeLock();
794     try {
795       if (prevAggregator != null) {
796         removeModule(prevAggregator, project);
797       }
798       else {
799         myRootProjects.remove(project);
800       }
801
802       if (newAggregator != null) {
803         addModule(newAggregator, project);
804       }
805       else {
806         myRootProjects.add(project);
807       }
808     }
809     finally {
810       writeUnlock();
811     }
812
813     return true;
814   }
815
816   public boolean hasProjects() {
817     readLock();
818     try {
819       return !myRootProjects.isEmpty();
820     }
821     finally {
822       readUnlock();
823     }
824   }
825
826   public List<MavenProject> getRootProjects() {
827     readLock();
828     try {
829       return new ArrayList<MavenProject>(myRootProjects);
830     }
831     finally {
832       readUnlock();
833     }
834   }
835
836   public List<VirtualFile> getRootProjectsFiles() {
837     return MavenUtil.collectFiles(getRootProjects());
838   }
839
840   public List<MavenProject> getProjects() {
841     readLock();
842     try {
843       return new ArrayList<MavenProject>(myVirtualFileToProjectMapping.values());
844     }
845     finally {
846       readUnlock();
847     }
848   }
849
850   public List<MavenProject> getNonIgnoredProjects() {
851     readLock();
852     try {
853       List<MavenProject> result = new ArrayList<MavenProject>();
854       for (MavenProject each : myVirtualFileToProjectMapping.values()) {
855         if (!isIgnored(each)) result.add(each);
856       }
857       return result;
858     }
859     finally {
860       readUnlock();
861     }
862   }
863
864   public List<VirtualFile> getProjectsFiles() {
865     readLock();
866     try {
867       return new ArrayList<VirtualFile>(myVirtualFileToProjectMapping.keySet());
868     }
869     finally {
870       readUnlock();
871     }
872   }
873
874   @Nullable
875   public MavenProject findProject(VirtualFile f) {
876     readLock();
877     try {
878       return myVirtualFileToProjectMapping.get(f);
879     }
880     finally {
881       readUnlock();
882     }
883   }
884
885   @Nullable
886   public MavenProject findProject(MavenId id) {
887     readLock();
888     try {
889       return myMavenIdToProjectMapping.get(id);
890     }
891     finally {
892       readUnlock();
893     }
894   }
895
896   @Nullable
897   public MavenProject findProject(MavenArtifact artifact) {
898     return findProject(artifact.getMavenId());
899   }
900
901   private MavenWorkspaceMap getWorkspaceMap() {
902     readLock();
903     try {
904       return myWorkspaceMap.copy();
905     }
906     finally {
907       readUnlock();
908     }
909   }
910
911   public MavenProject findAggregator(MavenProject project) {
912     readLock();
913     try {
914       return myModuleToAggregatorMapping.get(project);
915     }
916     finally {
917       readUnlock();
918     }
919   }
920
921   public MavenProject findRootProject(@NotNull MavenProject project) {
922     readLock();
923     try {
924       MavenProject rootProject = project;
925       while (true) {
926         MavenProject aggregator = myModuleToAggregatorMapping.get(project);
927         if (aggregator == null) {
928           return rootProject;
929         }
930         rootProject = aggregator;
931       }
932     }
933     finally {
934       readUnlock();
935     }
936   }
937
938   public boolean isRootProject(@NotNull MavenProject project) {
939     readLock();
940     try {
941       return myModuleToAggregatorMapping.get(project) == null;
942     }
943     finally {
944       readUnlock();
945     }
946   }
947
948   public List<MavenProject> getModules(MavenProject aggregator) {
949     readLock();
950     try {
951       List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
952       return modules == null
953              ? Collections.<MavenProject>emptyList()
954              : new ArrayList<MavenProject>(modules);
955     }
956     finally {
957       readUnlock();
958     }
959   }
960
961   private void addModule(MavenProject aggregator, MavenProject module) {
962     writeLock();
963     try {
964       List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
965       if (modules == null) {
966         modules = new ArrayList<MavenProject>();
967         myAggregatorToModuleMapping.put(aggregator, modules);
968       }
969       modules.add(module);
970
971       myModuleToAggregatorMapping.put(module, aggregator);
972     }
973     finally {
974       writeUnlock();
975     }
976   }
977
978   private void removeModule(MavenProject aggregator, MavenProject module) {
979     writeLock();
980     try {
981       List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
982       if (modules == null) return;
983       modules.remove(module);
984       myModuleToAggregatorMapping.remove(module);
985     }
986     finally {
987       writeUnlock();
988     }
989   }
990
991   private MavenProject findParent(MavenProject project) {
992     return findProject(project.getParentId());
993   }
994
995   public Set<MavenProject> findInheritors(MavenProject project) {
996     Set<MavenProject> result = new THashSet<MavenProject>();
997     MavenId id = project.getMavenId();
998
999     for (MavenProject each : getProjects()) {
1000       if (each == project) continue;
1001       if (id.equals(each.getParentId())) result.add(each);
1002     }
1003     return result;
1004   }
1005
1006   public List<MavenProject> getDependentProjects(MavenProject project) {
1007     List<MavenProject> result = new SmartList<MavenProject>();
1008     for (MavenProject eachProject : getProjects()) {
1009       if (eachProject == project) continue;
1010       if (!eachProject.findDependencies(project).isEmpty()) {
1011         result.add(eachProject);
1012       }
1013     }
1014     return result;
1015   }
1016
1017   public void resolve(@NotNull Project project,
1018                       @NotNull MavenProject mavenProject,
1019                       @NotNull MavenGeneralSettings generalSettings,
1020                       @NotNull MavenEmbeddersManager embeddersManager,
1021                       @NotNull MavenConsole console,
1022                       @NotNull MavenProgressIndicator process) throws MavenProcessCanceledException {
1023     MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.FOR_DEPENDENCIES_RESOLVE);
1024     embedder.customizeForResolve(getWorkspaceMap(), console, process);
1025
1026     try {
1027       process.checkCanceled();
1028       process.setText(ProjectBundle.message("maven.resolving.pom", mavenProject.getDisplayName()));
1029       process.setText2("");
1030       Pair<MavenProjectChanges, NativeMavenProjectHolder> resolveResult =
1031         mavenProject.resolve(project, generalSettings, embedder, new MavenProjectReader(), myProjectLocator);
1032
1033       fireProjectResolved(Pair.create(mavenProject, resolveResult.first), resolveResult.second);
1034     }
1035     finally {
1036       embeddersManager.release(embedder);
1037     }
1038   }
1039
1040   public void resolvePlugins(@NotNull MavenProject mavenProject,
1041                              @NotNull NativeMavenProjectHolder nativeMavenProject,
1042                              @NotNull MavenEmbeddersManager embeddersManager,
1043                              @NotNull MavenConsole console,
1044                              @NotNull MavenProgressIndicator process) throws MavenProcessCanceledException {
1045     MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.FOR_PLUGINS_RESOLVE);
1046     embedder.customizeForResolve(console, process);
1047     embedder.clearCachesFor(mavenProject.getMavenId());
1048
1049     Set<File> filesToRefresh = new HashSet<File>();
1050
1051     try {
1052       process.setText(ProjectBundle.message("maven.downloading.pom.plugins", mavenProject.getDisplayName()));
1053
1054       for (MavenPlugin each : mavenProject.getDeclaredPlugins()) {
1055         process.checkCanceled();
1056
1057         Collection<MavenArtifact> artifacts = embedder.resolvePlugin(each, mavenProject.getRemoteRepositories(), nativeMavenProject, false);
1058
1059         for (MavenArtifact artifact : artifacts) {
1060           File pluginJar = artifact.getFile();
1061           File pluginDir = pluginJar.getParentFile();
1062           if (pluginDir != null) {
1063             filesToRefresh.add(pluginDir); // Refresh both *.pom and *.jar files.
1064           }
1065         }
1066       }
1067
1068       mavenProject.resetCache();
1069       firePluginsResolved(mavenProject);
1070     }
1071     finally {
1072       if (filesToRefresh.size() > 0) {
1073         LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh);
1074       }
1075
1076       embeddersManager.release(embedder);
1077     }
1078   }
1079
1080   public void resolveFolders(@NotNull final MavenProject mavenProject,
1081                              @NotNull final MavenImportingSettings importingSettings,
1082                              @NotNull final MavenEmbeddersManager embeddersManager,
1083                              @NotNull final MavenConsole console,
1084                              @NotNull final MavenProgressIndicator process) throws MavenProcessCanceledException {
1085     executeWithEmbedder(mavenProject,
1086                         embeddersManager,
1087                         MavenEmbeddersManager.FOR_FOLDERS_RESOLVE,
1088                         console,
1089                         process,
1090                         new EmbedderTask() {
1091                           public void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException {
1092                             process.checkCanceled();
1093                             process.setText(ProjectBundle.message("maven.updating.folders.pom", mavenProject.getDisplayName()));
1094                             process.setText2("");
1095
1096                             Pair<Boolean, MavenProjectChanges> resolveResult = mavenProject.resolveFolders(embedder,
1097                                                                                                            importingSettings,
1098                                                                                                            new MavenProjectReader(),
1099                                                                                                            console);
1100                             if (resolveResult.first) {
1101                               fireFoldersResolved(Pair.create(mavenProject, resolveResult.second));
1102                             }
1103                           }
1104                         });
1105   }
1106
1107   public MavenArtifactDownloader.DownloadResult downloadSourcesAndJavadocs(@NotNull Collection<MavenProject> projects,
1108                                                                            @Nullable Collection<MavenArtifact> artifacts,
1109                                                                            boolean downloadSources,
1110                                                                            boolean downloadDocs,
1111                                                                            @NotNull MavenEmbeddersManager embeddersManager,
1112                                                                            @NotNull MavenConsole console,
1113                                                                            @NotNull MavenProgressIndicator process)
1114     throws MavenProcessCanceledException {
1115     MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.FOR_DOWNLOAD);
1116     embedder.customizeForResolve(console, process);
1117
1118     try {
1119       MavenArtifactDownloader.DownloadResult result =
1120         MavenArtifactDownloader.download(this, projects, artifacts, downloadSources, downloadDocs, embedder, process);
1121
1122       for (MavenProject each : projects) {
1123         fireArtifactsDownloaded(each);
1124       }
1125       return result;
1126     }
1127     finally {
1128       embeddersManager.release(embedder);
1129     }
1130   }
1131
1132   public void executeWithEmbedder(@NotNull MavenProject mavenProject,
1133                                   @NotNull MavenEmbeddersManager embeddersManager,
1134                                   @NotNull Key embedderKind,
1135                                   @NotNull MavenConsole console,
1136                                   @NotNull MavenProgressIndicator process,
1137                                   @NotNull EmbedderTask task) throws MavenProcessCanceledException {
1138     MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(embedderKind);
1139     embedder.customizeForResolve(getWorkspaceMap(), console, process);
1140     embedder.clearCachesFor(mavenProject.getMavenId());
1141     try {
1142       task.run(embedder);
1143     }
1144     finally {
1145       embeddersManager.release(embedder);
1146     }
1147   }
1148
1149   public <Result> Result visit(Visitor<Result> visitor) {
1150     for (MavenProject each : getRootProjects()) {
1151       if (visitor.isDone()) break;
1152       doVisit(each, visitor);
1153     }
1154     return visitor.getResult();
1155   }
1156
1157   private <Result> void doVisit(MavenProject project, Visitor<Result> visitor) {
1158     if (!visitor.isDone() && visitor.shouldVisit(project)) {
1159       visitor.visit(project);
1160       for (MavenProject each : getModules(project)) {
1161         if (visitor.isDone()) break;
1162         doVisit(each, visitor);
1163       }
1164       visitor.leave(project);
1165     }
1166   }
1167
1168   private void writeLock() {
1169     myStructureWriteLock.lock();
1170   }
1171
1172   private void writeUnlock() {
1173     myStructureWriteLock.unlock();
1174   }
1175
1176   private void readLock() {
1177     myStructureReadLock.lock();
1178   }
1179
1180   private void readUnlock() {
1181     myStructureReadLock.unlock();
1182   }
1183
1184   public void addListener(Listener l) {
1185     myListeners.add(l);
1186   }
1187
1188   private void fireProfilesChanged() {
1189     for (Listener each : myListeners) {
1190       each.profilesChanged();
1191     }
1192   }
1193
1194   private void fireProjectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, boolean fromImport) {
1195     for (Listener each : myListeners) {
1196       each.projectsIgnoredStateChanged(ignored, unignored, fromImport);
1197     }
1198   }
1199
1200   private void fireProjectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted) {
1201     for (Listener each : myListeners) {
1202       each.projectsUpdated(updated, deleted);
1203     }
1204   }
1205
1206   private void fireProjectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1207                                    NativeMavenProjectHolder nativeMavenProject) {
1208     for (Listener each : myListeners) {
1209       each.projectResolved(projectWithChanges, nativeMavenProject);
1210     }
1211   }
1212
1213   private void firePluginsResolved(MavenProject project) {
1214     for (Listener each : myListeners) {
1215       each.pluginsResolved(project);
1216     }
1217   }
1218
1219   private void fireFoldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges) {
1220     for (Listener each : myListeners) {
1221       each.foldersResolved(projectWithChanges);
1222     }
1223   }
1224
1225   private void fireArtifactsDownloaded(MavenProject project) {
1226     for (Listener each : myListeners) {
1227       each.artifactsDownloaded(project);
1228     }
1229   }
1230
1231   private class UpdateContext {
1232     public final Map<MavenProject, MavenProjectChanges> updatedProjectsWithChanges = new LinkedHashMap<MavenProject, MavenProjectChanges>();
1233     public final Set<MavenProject> deletedProjects = new LinkedHashSet<MavenProject>();
1234
1235     public void update(MavenProject project, MavenProjectChanges changes) {
1236       deletedProjects.remove(project);
1237       updatedProjectsWithChanges.put(project, changes.mergedWith(updatedProjectsWithChanges.get(project)));
1238     }
1239
1240     public void deleted(MavenProject project) {
1241       updatedProjectsWithChanges.remove(project);
1242       deletedProjects.add(project);
1243     }
1244
1245     public void deleted(Collection<MavenProject> projects) {
1246       for (MavenProject each : projects) {
1247         deleted(each);
1248       }
1249     }
1250
1251     public void fireUpdatedIfNecessary() {
1252       if (updatedProjectsWithChanges.isEmpty() && deletedProjects.isEmpty()) return;
1253       List<MavenProject> mavenProjects = deletedProjects.isEmpty()
1254                                          ? Collections.<MavenProject>emptyList()
1255                                          : new ArrayList<MavenProject>(deletedProjects);
1256       List<Pair<MavenProject, MavenProjectChanges>> updated = updatedProjectsWithChanges.isEmpty()
1257                                                               ? Collections.<Pair<MavenProject, MavenProjectChanges>>emptyList()
1258                                                               : MavenUtil.mapToList(updatedProjectsWithChanges);
1259       fireProjectsUpdated(updated, mavenProjects);
1260     }
1261   }
1262
1263   public interface EmbedderTask {
1264     void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException;
1265   }
1266
1267   public abstract static class Visitor<Result> {
1268     private Result result;
1269
1270     public boolean shouldVisit(MavenProject project) {
1271       return true;
1272     }
1273
1274     public abstract void visit(MavenProject project);
1275
1276     public void leave(MavenProject node) {
1277     }
1278
1279     public void setResult(Result result) {
1280       this.result = result;
1281     }
1282
1283     public Result getResult() {
1284       return result;
1285     }
1286
1287     public boolean isDone() {
1288       return result != null;
1289     }
1290   }
1291
1292   public abstract static class SimpleVisitor extends Visitor<Object> {
1293   }
1294
1295   private static class MavenProjectTimestamp {
1296     private final long myPomTimestamp;
1297     private final long myParentLastReadStamp;
1298     private final long myProfilesTimestamp;
1299     private final long myUserSettingsTimestamp;
1300     private final long myGlobalSettingsTimestamp;
1301     private final long myExplicitProfilesHashCode;
1302
1303     private MavenProjectTimestamp(long pomTimestamp,
1304                                   long parentLastReadStamp,
1305                                   long profilesTimestamp,
1306                                   long userSettingsTimestamp,
1307                                   long globalSettingsTimestamp,
1308                                   long explicitProfilesHashCode) {
1309       myPomTimestamp = pomTimestamp;
1310       myParentLastReadStamp = parentLastReadStamp;
1311       myProfilesTimestamp = profilesTimestamp;
1312       myUserSettingsTimestamp = userSettingsTimestamp;
1313       myGlobalSettingsTimestamp = globalSettingsTimestamp;
1314       myExplicitProfilesHashCode = explicitProfilesHashCode;
1315     }
1316
1317     public static MavenProjectTimestamp read(DataInputStream in) throws IOException {
1318       return new MavenProjectTimestamp(in.readLong(),
1319                                        in.readLong(),
1320                                        in.readLong(),
1321                                        in.readLong(),
1322                                        in.readLong(),
1323                                        in.readLong());
1324     }
1325
1326     public void write(DataOutputStream out) throws IOException {
1327       out.writeLong(myPomTimestamp);
1328       out.writeLong(myParentLastReadStamp);
1329       out.writeLong(myProfilesTimestamp);
1330       out.writeLong(myUserSettingsTimestamp);
1331       out.writeLong(myGlobalSettingsTimestamp);
1332       out.writeLong(myExplicitProfilesHashCode);
1333     }
1334
1335     @Override
1336     public String toString() {
1337       return "(" + myPomTimestamp
1338              + ":" + myParentLastReadStamp
1339              + ":" + myProfilesTimestamp
1340              + ":" + myUserSettingsTimestamp
1341              + ":" + myGlobalSettingsTimestamp
1342              + ":" + myExplicitProfilesHashCode + ")";
1343     }
1344
1345     @Override
1346     public boolean equals(Object o) {
1347       if (this == o) return true;
1348       if (o == null || getClass() != o.getClass()) return false;
1349
1350       MavenProjectTimestamp timestamp = (MavenProjectTimestamp)o;
1351
1352       if (myPomTimestamp != timestamp.myPomTimestamp) return false;
1353       if (myParentLastReadStamp != timestamp.myParentLastReadStamp) return false;
1354       if (myProfilesTimestamp != timestamp.myProfilesTimestamp) return false;
1355       if (myUserSettingsTimestamp != timestamp.myUserSettingsTimestamp) return false;
1356       if (myGlobalSettingsTimestamp != timestamp.myGlobalSettingsTimestamp) return false;
1357       if (myExplicitProfilesHashCode != timestamp.myExplicitProfilesHashCode) return false;
1358
1359       return true;
1360     }
1361
1362     @Override
1363     public int hashCode() {
1364       int result = 0;
1365       result = 31 * result + (int)(myPomTimestamp ^ (myPomTimestamp >>> 32));
1366       result = 31 * result + (int)(myParentLastReadStamp ^ (myParentLastReadStamp >>> 32));
1367       result = 31 * result + (int)(myProfilesTimestamp ^ (myProfilesTimestamp >>> 32));
1368       result = 31 * result + (int)(myUserSettingsTimestamp ^ (myUserSettingsTimestamp >>> 32));
1369       result = 31 * result + (int)(myGlobalSettingsTimestamp ^ (myGlobalSettingsTimestamp >>> 32));
1370       result = 31 * result + (int)(myExplicitProfilesHashCode ^ (myExplicitProfilesHashCode >>> 32));
1371       return result;
1372     }
1373   }
1374
1375   public interface Listener extends EventListener {
1376     void profilesChanged();
1377
1378     void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, boolean fromImport);
1379
1380     void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted);
1381
1382     void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1383                          @Nullable NativeMavenProjectHolder nativeMavenProject);
1384
1385     void pluginsResolved(MavenProject project);
1386
1387     void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges);
1388
1389     void artifactsDownloaded(MavenProject project);
1390   }
1391
1392   public static class ListenerAdapter implements Listener {
1393     public void profilesChanged() {
1394     }
1395
1396     public void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, boolean fromImport) {
1397     }
1398
1399     public void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted) {
1400     }
1401
1402     public void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1403                                 @Nullable NativeMavenProjectHolder nativeMavenProject) {
1404     }
1405
1406     public void pluginsResolved(MavenProject project) {
1407     }
1408
1409     public void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges) {
1410     }
1411
1412     public void artifactsDownloaded(MavenProject project) {
1413     }
1414   }
1415 }