maven dependency management gutter support (override icons)
[fedora-idea.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / project / MavenProjectsTree.java
blob50f9d6a4258b5087b595d87caa011b8362dfa30b
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.jetbrains.idea.maven.project;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.util.Comparing;
20 import com.intellij.openapi.util.Computable;
21 import com.intellij.openapi.util.Pair;
22 import com.intellij.openapi.util.io.FileUtil;
23 import com.intellij.openapi.vfs.LocalFileSystem;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.util.Function;
26 import com.intellij.util.containers.ArrayListSet;
27 import com.intellij.util.containers.ContainerUtil;
28 import com.intellij.util.containers.Stack;
29 import gnu.trove.THashMap;
30 import gnu.trove.THashSet;
31 import org.apache.maven.artifact.Artifact;
32 import org.jetbrains.annotations.TestOnly;
33 import org.jetbrains.idea.maven.embedder.MavenConsole;
34 import org.jetbrains.idea.maven.embedder.MavenEmbedderWrapper;
35 import org.jetbrains.idea.maven.utils.*;
37 import java.io.*;
38 import java.util.*;
39 import java.util.concurrent.locks.Lock;
40 import java.util.concurrent.locks.ReentrantReadWriteLock;
41 import java.util.regex.Pattern;
43 public class MavenProjectsTree {
44 private static final String STORAGE_VERSION = MavenProjectsTree.class.getSimpleName() + ".5";
46 private final Object myStateLock = new Object();
47 private final ReentrantReadWriteLock myStructureLock = new ReentrantReadWriteLock();
48 private final Lock myStructureReadLock = myStructureLock.readLock();
49 private final Lock myStructureWriteLock = myStructureLock.writeLock();
51 // TODO replace with sets
52 private volatile List<String> myManagedFilesPaths = new ArrayList<String>();
53 private volatile List<String> myIgnoredFilesPaths = new ArrayList<String>();
54 private volatile List<String> myIgnoredFilesPatterns = new ArrayList<String>();
55 private volatile Pattern myIgnoredFilesPatternsCache;
57 private volatile Set<String> myExplicitProfiles = new THashSet<String>();
58 private volatile Set<String> myTemporarilyRemovedExplicitProfiles = new THashSet<String>();
60 private final List<MavenProject> myRootProjects = new ArrayList<MavenProject>();
62 private final Map<MavenProject, MavenProjectTimestamp> myTimestamps = new THashMap<MavenProject, MavenProjectTimestamp>();
63 private final Map<VirtualFile, MavenProject> myVirtualFileToProjectMapping = new THashMap<VirtualFile, MavenProject>();
64 private final Map<MavenId, MavenProject> myMavenIdToProjectMapping = new THashMap<MavenId, MavenProject>();
65 private final Map<MavenId, VirtualFile> myMavenIdToFileMapping = new THashMap<MavenId, VirtualFile>();
66 private final Map<MavenProject, List<MavenProject>> myAggregatorToModuleMapping = new THashMap<MavenProject, List<MavenProject>>();
67 private final Map<MavenProject, MavenProject> myModuleToAggregatorMapping = new THashMap<MavenProject, MavenProject>();
69 private final List<Listener> myListeners = ContainerUtil.createEmptyCOWList();
71 private final MavenProjectReaderProjectLocator myProjectLocator = new MavenProjectReaderProjectLocator() {
72 public VirtualFile findProjectFile(MavenId coordinates) {
73 MavenProject project = findProject(coordinates);
74 return project == null ? null : project.getFile();
78 public static MavenProjectsTree read(File file) throws IOException {
79 FileInputStream fs = new FileInputStream(file);
80 DataInputStream in = new DataInputStream(fs);
82 MavenProjectsTree result = new MavenProjectsTree();
83 try {
84 try {
85 if (!STORAGE_VERSION.equals(in.readUTF())) return null;
86 result.myManagedFilesPaths = readList(in);
87 result.myIgnoredFilesPaths = readList(in);
88 result.myIgnoredFilesPatterns = readList(in);
89 result.myExplicitProfiles = readCollection(in, new Function<Integer, Set<String>>() {
90 public Set<String> fun(Integer integer) {
91 return new THashSet<String>(integer);
93 });
94 result.myRootProjects.addAll(readProjectsRecursively(in, result));
96 catch (Throwable e) {
97 IOException ioException = new IOException();
98 ioException.initCause(e);
99 throw ioException;
102 finally {
103 in.close();
104 fs.close();
106 return result;
109 private static List<String> readList(DataInputStream in) throws IOException {
110 return readCollection(in, new Function<Integer, List<String>>() {
111 public List<String> fun(Integer integer) {
112 return new ArrayList<String>(integer);
117 private static <T extends Collection<String>> T readCollection(DataInputStream in, Function<Integer, T> factory) throws IOException {
118 int count = in.readInt();
119 T result = factory.fun(count);
120 while (count-- > 0) {
121 result.add(in.readUTF());
123 return result;
126 private static List<MavenProject> readProjectsRecursively(DataInputStream in,
127 MavenProjectsTree tree) throws IOException {
128 int count = in.readInt();
129 List<MavenProject> result = new ArrayList<MavenProject>(count);
130 while (count-- > 0) {
131 MavenProject project = MavenProject.read(in);
132 MavenProjectTimestamp timestamp = MavenProjectTimestamp.read(in);
133 List<MavenProject> modules = readProjectsRecursively(in, tree);
134 if (project != null) {
135 result.add(project);
136 tree.myTimestamps.put(project, timestamp);
137 tree.myVirtualFileToProjectMapping.put(project.getFile(), project);
138 tree.myMavenIdToProjectMapping.put(project.getMavenId(), project);
139 tree.myMavenIdToFileMapping.put(project.getMavenId(), project.getFile());
140 tree.myAggregatorToModuleMapping.put(project, modules);
141 for (MavenProject eachModule : modules) {
142 tree.myModuleToAggregatorMapping.put(eachModule, project);
146 return result;
149 public void save(File file) throws IOException {
150 synchronized (myStateLock) {
151 readLock();
152 try {
153 file.getParentFile().mkdirs();
154 FileOutputStream fs = new FileOutputStream(file);
155 DataOutputStream out = new DataOutputStream(fs);
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);
164 finally {
165 out.close();
166 fs.close();
169 finally {
170 readUnlock();
175 private static void writeCollection(DataOutputStream out, Collection<String> list) throws IOException {
176 out.writeInt(list.size());
177 for (String each : list) {
178 out.writeUTF(each);
182 private void writeProjectsRecursively(DataOutputStream out, List<MavenProject> list) throws IOException {
183 out.writeInt(list.size());
184 for (MavenProject each : list) {
185 each.write(out);
186 myTimestamps.get(each).write(out);
187 writeProjectsRecursively(out, getModules(each));
191 public List<String> getManagedFilesPaths() {
192 synchronized (myStateLock) {
193 return new ArrayList<String>(myManagedFilesPaths);
197 public void resetManagedFilesPathsAndProfiles(List<String> paths, Collection<String> profiles) {
198 synchronized (myStateLock) {
199 myManagedFilesPaths = new ArrayList<String>(paths);
201 setExplicitProfiles(profiles);
204 @TestOnly
205 public void resetManagedFilesAndProfiles(List<VirtualFile> files, Collection<String> profiles) {
206 resetManagedFilesPathsAndProfiles(MavenUtil.collectPaths(files), profiles);
209 public void addManagedFilesWithProfiles(List<VirtualFile> files, Collection<String> profiles) {
210 List<String> newFiles;
211 Set<String> newProfiles;
212 synchronized (myStateLock) {
213 newFiles = new ArrayList<String>(myManagedFilesPaths);
214 newFiles.addAll(MavenUtil.collectPaths(files));
216 newProfiles = new THashSet<String>(myExplicitProfiles);
217 newProfiles.addAll(profiles);
220 resetManagedFilesPathsAndProfiles(newFiles, newProfiles);
223 public void removeManagedFiles(List<VirtualFile> files) {
224 synchronized (myStateLock) {
225 myManagedFilesPaths.removeAll(MavenUtil.collectPaths(files));
229 public List<VirtualFile> getExistingManagedFiles() {
230 List<VirtualFile> result = new ArrayList<VirtualFile>();
231 for (String path : getManagedFilesPaths()) {
232 VirtualFile f = LocalFileSystem.getInstance().findFileByPath(path);
233 if (f != null) result.add(f);
235 return result;
238 public List<String> getIgnoredFilesPaths() {
239 synchronized (myStateLock) {
240 return new ArrayList<String>(myIgnoredFilesPaths);
244 public void setIgnoredFilesPaths(final List<String> paths) {
245 doChangeIgnoreStatus(new Runnable() {
246 public void run() {
247 myIgnoredFilesPaths = new ArrayList<String>(paths);
252 public boolean getIgnoredState(MavenProject project) {
253 synchronized (myStateLock) {
254 return myIgnoredFilesPaths.contains(project.getPath());
258 public void setIgnoredState(List<MavenProject> projects, boolean ignored) {
259 setIgnoredState(projects, ignored, null);
262 public void setIgnoredState(List<MavenProject> projects, boolean ignored, Object message) {
263 doSetIgnoredState(projects, ignored, message);
266 private void doSetIgnoredState(List<MavenProject> projects, final boolean ignored, Object message) {
267 final List<String> paths = MavenUtil.collectPaths(MavenUtil.collectFiles(projects));
268 doChangeIgnoreStatus(new Runnable() {
269 public void run() {
270 if (ignored) {
271 myIgnoredFilesPaths.addAll(paths);
273 else {
274 myIgnoredFilesPaths.removeAll(paths);
277 }, message);
280 public List<String> getIgnoredFilesPatterns() {
281 synchronized (myStateLock) {
282 return new ArrayList<String>(myIgnoredFilesPatterns);
286 public void setIgnoredFilesPatterns(final List<String> patterns) {
287 doChangeIgnoreStatus(new Runnable() {
288 public void run() {
289 myIgnoredFilesPatternsCache = null;
290 myIgnoredFilesPatterns = new ArrayList<String>(patterns);
295 private void doChangeIgnoreStatus(Runnable runnable) {
296 doChangeIgnoreStatus(runnable, null);
299 private void doChangeIgnoreStatus(Runnable runnable, Object message) {
300 List<MavenProject> ignoredBefore;
301 List<MavenProject> ignoredAfter;
303 synchronized (myStateLock) {
304 ignoredBefore = getIgnoredProjects();
305 runnable.run();
306 ignoredAfter = getIgnoredProjects();
309 List<MavenProject> ignored = new ArrayList<MavenProject>(ignoredAfter);
310 ignored.removeAll(ignoredBefore);
312 List<MavenProject> unignored = new ArrayList<MavenProject>(ignoredBefore);
313 unignored.removeAll(ignoredAfter);
315 if (ignored.isEmpty() && unignored.isEmpty()) return;
317 fireProjectsIgnoredStateChanged(ignored, unignored, message);
320 private List<MavenProject> getIgnoredProjects() {
321 List<MavenProject> result = new ArrayList<MavenProject>();
322 for (MavenProject each : getProjects()) {
323 if (isIgnored(each)) result.add(each);
325 return result;
328 public boolean isIgnored(MavenProject project) {
329 String path = project.getPath();
330 synchronized (myStateLock) {
331 return myIgnoredFilesPaths.contains(path) || matchesIgnoredFilesPatterns(path);
335 private boolean matchesIgnoredFilesPatterns(String path) {
336 synchronized (myStateLock) {
337 if (myIgnoredFilesPatternsCache == null) {
338 myIgnoredFilesPatternsCache = Pattern.compile(Strings.translateMasks(myIgnoredFilesPatterns));
340 return myIgnoredFilesPatternsCache.matcher(path).matches();
344 public Collection<String> getExplicitProfiles() {
345 synchronized (myStateLock) {
346 return new THashSet<String>(myExplicitProfiles);
350 public void setExplicitProfiles(Collection<String> explicitProfiles) {
351 synchronized (myStateLock) {
352 myExplicitProfiles = new THashSet<String>(explicitProfiles);
354 fireProfilesChanged();
357 private void updateExplicitProfiles() {
358 Collection<String> available = getAvailableProfiles();
360 synchronized (myStateLock) {
361 Collection<String> removedProfiles = new THashSet<String>(myExplicitProfiles);
362 removedProfiles.removeAll(available);
363 myTemporarilyRemovedExplicitProfiles.addAll(removedProfiles);
365 Collection<String> restoredProfiles = new THashSet<String>(myTemporarilyRemovedExplicitProfiles);
366 restoredProfiles.retainAll(available);
367 myTemporarilyRemovedExplicitProfiles.removeAll(restoredProfiles);
369 myExplicitProfiles.removeAll(removedProfiles);
370 myExplicitProfiles.addAll(restoredProfiles);
374 public Collection<String> getAvailableProfiles() {
375 return getAvailableAndActiveProfiles(true, false).first;
378 private Pair<Collection<String>, Collection<String>> getAvailableAndActiveProfiles(boolean includeAvailable, boolean includeActive) {
379 Collection<String> available = includeAvailable ? new THashSet<String>() : null;
380 Collection<String> active = includeActive ? new THashSet<String>() : null;
381 for (MavenProject each : getProjects()) {
382 if (available != null) available.addAll(each.getProfilesIds());
383 if (active != null) active.addAll(each.getActiveProfilesIds());
385 return Pair.create(available, active);
388 public Collection<Pair<String, MavenProfileState>> getProfilesWithStates() {
389 Collection<Pair<String, MavenProfileState>> result = new ArrayListSet<Pair<String, MavenProfileState>>();
391 Pair<Collection<String>, Collection<String>> profiles = getAvailableAndActiveProfiles(true, true);
392 Collection<String> available = profiles.first;
393 Collection<String> active = profiles.second;
394 Collection<String> explicitProfiles = getExplicitProfiles();
396 for (String each : available) {
397 MavenProfileState state = MavenProfileState.NONE;
398 if (explicitProfiles.contains(each)) {
399 state = MavenProfileState.EXPLICIT;
401 else if (active.contains(each)) state = MavenProfileState.IMPLICIT;
402 result.add(Pair.create(each, state));
404 return result;
407 public void updateAll(boolean force, MavenGeneralSettings generalSettings, MavenProgressIndicator process, Object message) {
408 List<VirtualFile> managedFiles = getExistingManagedFiles();
409 Collection<String> explicitProfiles = getExplicitProfiles();
411 MavenProjectReader projectReader = new MavenProjectReader();
412 update(managedFiles, true, force, explicitProfiles, projectReader, generalSettings, process, message);
414 List<VirtualFile> obsoleteFiles = getRootProjectsFiles();
415 obsoleteFiles.removeAll(managedFiles);
416 delete(projectReader, obsoleteFiles, explicitProfiles, generalSettings, process, message);
419 public void update(Collection<VirtualFile> files,
420 boolean force,
421 MavenGeneralSettings generalSettings,
422 MavenProgressIndicator process,
423 Object message) {
424 update(files, false, force, getExplicitProfiles(), new MavenProjectReader(), generalSettings, process, message);
427 private void update(Collection<VirtualFile> files,
428 boolean recursive,
429 boolean force,
430 Collection<String> explicitProfiles,
431 MavenProjectReader projectReader,
432 MavenGeneralSettings generalSettings,
433 MavenProgressIndicator process,
434 Object message) {
435 if (files.isEmpty()) return;
437 UpdateContext updateContext = new UpdateContext();
438 Stack<MavenProject> updateStack = new Stack<MavenProject>();
440 for (VirtualFile each : files) {
441 MavenProject mavenProject = findProject(each);
442 if (mavenProject == null) {
443 doAdd(each, recursive, explicitProfiles, updateContext, updateStack, projectReader, generalSettings, process);
445 else {
446 doUpdate(mavenProject,
447 findAggregator(mavenProject),
448 false,
449 recursive,
450 force,
451 explicitProfiles,
452 updateContext,
453 updateStack,
454 projectReader,
455 generalSettings,
456 process);
460 updateExplicitProfiles();
461 updateContext.fireUpdatedIfNecessary(message);
464 private void doAdd(final VirtualFile f,
465 boolean recursuve,
466 Collection<String> explicitProfiles,
467 UpdateContext updateContext,
468 Stack<MavenProject> updateStack,
469 MavenProjectReader reader,
470 MavenGeneralSettings generalSettings,
471 MavenProgressIndicator process) {
472 MavenProject newMavenProject = new MavenProject(f);
474 MavenProject intendedAggregator = null;
475 for (MavenProject each : getProjects()) {
476 if (each.getExistingModuleFiles().contains(f)) {
477 intendedAggregator = each;
478 break;
482 doUpdate(newMavenProject,
483 intendedAggregator,
484 true,
485 recursuve,
486 false,
487 explicitProfiles,
488 updateContext,
489 updateStack,
490 reader,
491 generalSettings,
492 process);
495 private void doUpdate(MavenProject mavenProject,
496 MavenProject aggregator,
497 boolean isNew,
498 boolean recursive,
499 boolean force,
500 Collection<String> explicitProfiles,
501 UpdateContext updateContext,
502 Stack<MavenProject> updateStack,
503 MavenProjectReader reader,
504 MavenGeneralSettings generalSettings,
505 MavenProgressIndicator process) {
506 if (updateStack.contains(mavenProject)) {
507 MavenLog.LOG.info("Recursion detected in " + mavenProject.getFile());
508 return;
510 updateStack.push(mavenProject);
512 process.setText(ProjectBundle.message("maven.reading.pom", mavenProject.getPath()));
513 process.setText2("");
515 List<MavenProject> prevModules = getModules(mavenProject);
516 Set<MavenProject> prevInheritors = isNew
517 ? new THashSet<MavenProject>()
518 : findInheritors(mavenProject);
520 MavenProjectTimestamp timestamp = calculateTimestamp(mavenProject, explicitProfiles, generalSettings);
521 boolean isChanged = force || !timestamp.equals(myTimestamps.get(mavenProject));
523 MavenProjectChanges changes = force ? MavenProjectChanges.ALL : MavenProjectChanges.NONE;
524 if (isChanged) {
525 writeLock();
526 try {
527 if (!isNew) {
528 myMavenIdToProjectMapping.remove(mavenProject.getMavenId());
529 myMavenIdToFileMapping.remove(mavenProject.getMavenId());
532 finally {
533 writeUnlock();
535 MavenId oldParentId = mavenProject.getParentId();
536 changes = changes.mergedWith(mavenProject.read(generalSettings, explicitProfiles, reader, myProjectLocator));
538 writeLock();
539 try {
540 myVirtualFileToProjectMapping.put(mavenProject.getFile(), mavenProject);
541 myMavenIdToProjectMapping.put(mavenProject.getMavenId(), mavenProject);
542 myMavenIdToFileMapping.put(mavenProject.getMavenId(), mavenProject.getFile());
544 finally {
545 writeUnlock();
548 if (!Comparing.equal(oldParentId, mavenProject.getParentId())) {
549 // ensure timestamp reflects actual parent's timestamp
550 timestamp = calculateTimestamp(mavenProject, explicitProfiles, generalSettings);
552 myTimestamps.put(mavenProject, timestamp);
555 boolean reconnected = isNew;
556 if (isNew) {
557 connect(aggregator, mavenProject);
559 else {
560 reconnected = reconnect(aggregator, mavenProject);
563 if (isChanged || reconnected) {
564 updateContext.update(mavenProject, changes);
567 List<VirtualFile> existingModuleFiles = mavenProject.getExistingModuleFiles();
568 List<MavenProject> modulesToRemove = new ArrayList<MavenProject>();
569 List<MavenProject> modulesToBecomeRoots = new ArrayList<MavenProject>();
571 for (MavenProject each : prevModules) {
572 VirtualFile moduleFile = each.getFile();
573 if (!existingModuleFiles.contains(moduleFile)) {
574 if (isManagedFile(moduleFile)) {
575 modulesToBecomeRoots.add(each);
577 else {
578 modulesToRemove.add(each);
582 for (MavenProject each : modulesToRemove) {
583 removeModule(mavenProject, each);
584 doDelete(mavenProject, each, updateContext);
585 prevInheritors.removeAll(updateContext.deletedProjects);
588 for (MavenProject each : modulesToBecomeRoots) {
589 if (reconnect(null, each)) updateContext.update(each, MavenProjectChanges.NONE);
592 for (VirtualFile each : existingModuleFiles) {
593 MavenProject module = findProject(each);
594 boolean isNewModule = module == null;
595 if (isNewModule) {
596 module = new MavenProject(each);
598 else {
599 MavenProject currentAggregator = findAggregator(module);
600 if (currentAggregator != null && currentAggregator != mavenProject) {
601 MavenLog.LOG.info("Module " + each + " is already included into " + mavenProject.getFile());
602 continue;
606 if (isChanged || isNewModule || recursive) {
607 doUpdate(module,
608 mavenProject,
609 isNewModule,
610 recursive,
611 recursive ? force : false, // do not force update modules if only this project was requested to be updated
612 explicitProfiles,
613 updateContext,
614 updateStack,
615 reader,
616 generalSettings,
617 process);
619 else {
620 if (reconnect(mavenProject, module)) {
621 updateContext.update(module, MavenProjectChanges.NONE);
626 Set<MavenProject> allInheritors = findInheritors(mavenProject);
627 allInheritors.addAll(prevInheritors);
628 for (MavenProject each : allInheritors) {
629 doUpdate(each,
630 findAggregator(each),
631 false,
632 false, // no need to go recursively in case of inheritance, only when updating modules
633 false,
634 explicitProfiles,
635 updateContext,
636 updateStack,
637 reader,
638 generalSettings,
639 process);
642 updateStack.pop();
645 private MavenProjectTimestamp calculateTimestamp(final MavenProject mavenProject,
646 final Collection<String> explicitProfiles,
647 final MavenGeneralSettings generalSettings) {
648 return ApplicationManager.getApplication().runReadAction(new Computable<MavenProjectTimestamp>() {
649 public MavenProjectTimestamp compute() {
650 long pomTimestamp = getFileTimestamp(mavenProject.getFile());
651 MavenProject parent = findParent(mavenProject);
652 long parentLastReadStamp = parent == null ? -1 : parent.getLastReadStamp();
653 VirtualFile profilesXmlFile = mavenProject.getProfilesXmlFile();
654 long profilesTimestamp = getFileTimestamp(profilesXmlFile);
656 long userSettingsTimestamp = getFileTimestamp(generalSettings.getEffectiveUserSettingsFile());
657 long globalSettingsTimestamp = getFileTimestamp(generalSettings.getEffectiveGlobalSettingsFile());
659 int profilesHashCode = explicitProfiles.hashCode();
661 return new MavenProjectTimestamp(pomTimestamp,
662 parentLastReadStamp,
663 profilesTimestamp,
664 userSettingsTimestamp,
665 globalSettingsTimestamp,
666 profilesHashCode);
671 private long getFileTimestamp(VirtualFile file) {
672 if (file == null) return -1;
673 return file.getTimeStamp();
676 public boolean isManagedFile(VirtualFile moduleFile) {
677 return isManagedFile(moduleFile.getPath());
680 public boolean isManagedFile(String path) {
681 for (String each : getManagedFilesPaths()) {
682 if (FileUtil.pathsEqual(each, path)) return true;
684 return false;
687 public boolean isPotentialProject(String path) {
688 if (isManagedFile(path)) return true;
690 for (MavenProject each : getProjects()) {
691 if (FileUtil.pathsEqual(path, each.getPath())) return true;
692 if (each.getModulePaths().contains(path)) return true;
694 return false;
697 public void delete(List<VirtualFile> files,
698 MavenGeneralSettings generalSettings,
699 MavenProgressIndicator process,
700 Object message) {
701 delete(new MavenProjectReader(), files, getExplicitProfiles(), generalSettings, process, message);
704 private void delete(MavenProjectReader projectReader,
705 List<VirtualFile> files,
706 Collection<String> explicitProfiles,
707 MavenGeneralSettings generalSettings,
708 MavenProgressIndicator process,
709 Object message) {
710 if (files.isEmpty()) return;
712 UpdateContext updateContext = new UpdateContext();
713 Stack<MavenProject> updateStack = new Stack<MavenProject>();
715 Set<MavenProject> inheritorsToUpdate = new THashSet<MavenProject>();
716 for (VirtualFile each : files) {
717 MavenProject mavenProject = findProject(each);
718 if (mavenProject == null) return;
720 inheritorsToUpdate.addAll(findInheritors(mavenProject));
721 doDelete(findAggregator(mavenProject), mavenProject, updateContext);
723 inheritorsToUpdate.removeAll(updateContext.deletedProjects);
725 for (MavenProject each : inheritorsToUpdate) {
726 doUpdate(each, null, false, false, false, explicitProfiles, updateContext, updateStack, projectReader, generalSettings, process);
729 updateExplicitProfiles();
730 updateContext.fireUpdatedIfNecessary(message);
733 private void doDelete(MavenProject aggregator, MavenProject project, UpdateContext updateContext) {
734 for (MavenProject each : getModules(project)) {
735 if (isManagedFile(each.getPath())) {
736 if (reconnect(null, each)) {
737 updateContext.update(each, MavenProjectChanges.NONE);
740 else {
741 doDelete(project, each, updateContext);
745 writeLock();
746 try {
747 if (aggregator != null) {
748 removeModule(aggregator, project);
750 else {
751 myRootProjects.remove(project);
753 myTimestamps.remove(project);
754 myVirtualFileToProjectMapping.remove(project.getFile());
755 myMavenIdToProjectMapping.remove(project.getMavenId());
756 myMavenIdToFileMapping.remove(project.getMavenId());
757 myAggregatorToModuleMapping.remove(project);
758 myModuleToAggregatorMapping.remove(project);
760 finally {
761 writeUnlock();
764 updateContext.deleted(project);
767 private void connect(MavenProject newAggregator, MavenProject project) {
768 writeLock();
769 try {
770 if (newAggregator != null) {
771 addModule(newAggregator, project);
773 else {
774 myRootProjects.add(project);
777 finally {
778 writeUnlock();
782 private boolean reconnect(MavenProject newAggregator, MavenProject project) {
783 MavenProject prevAggregator = findAggregator(project);
785 if (prevAggregator == newAggregator) return false;
787 writeLock();
788 try {
789 if (prevAggregator != null) {
790 removeModule(prevAggregator, project);
792 else {
793 myRootProjects.remove(project);
796 if (newAggregator != null) {
797 addModule(newAggregator, project);
799 else {
800 myRootProjects.add(project);
803 finally {
804 writeUnlock();
807 return true;
810 public boolean hasProjects() {
811 readLock();
812 try {
813 return !myRootProjects.isEmpty();
815 finally {
816 readUnlock();
820 public List<MavenProject> getRootProjects() {
821 readLock();
822 try {
823 return new ArrayList<MavenProject>(myRootProjects);
825 finally {
826 readUnlock();
830 public List<VirtualFile> getRootProjectsFiles() {
831 return MavenUtil.collectFiles(getRootProjects());
834 public List<MavenProject> getProjects() {
835 readLock();
836 try {
837 return new ArrayList<MavenProject>(myVirtualFileToProjectMapping.values());
839 finally {
840 readUnlock();
844 public List<MavenProject> getNonIgnoredProjects() {
845 readLock();
846 try {
847 List<MavenProject> result = new ArrayList<MavenProject>();
848 for (MavenProject each : myVirtualFileToProjectMapping.values()) {
849 if (!isIgnored(each)) result.add(each);
851 return result;
853 finally {
854 readUnlock();
858 public List<VirtualFile> getProjectsFiles() {
859 readLock();
860 try {
861 return new ArrayList<VirtualFile>(myVirtualFileToProjectMapping.keySet());
863 finally {
864 readUnlock();
868 public MavenProject findProject(VirtualFile f) {
869 readLock();
870 try {
871 return myVirtualFileToProjectMapping.get(f);
873 finally {
874 readUnlock();
878 public MavenProject findProject(MavenId id) {
879 readLock();
880 try {
881 return myMavenIdToProjectMapping.get(id);
883 finally {
884 readUnlock();
888 public MavenProject findProject(MavenArtifact artifact) {
889 return findProject(artifact.getMavenId());
892 public MavenProject findProject(Artifact artifact) {
893 return findProject(new MavenId(artifact));
896 private Map<MavenId, VirtualFile> getProjectIdToFileMapping() {
897 readLock();
898 try {
899 return new THashMap<MavenId, VirtualFile>(myMavenIdToFileMapping);
901 finally {
902 readUnlock();
906 public MavenProject findAggregator(MavenProject project) {
907 readLock();
908 try {
909 return myModuleToAggregatorMapping.get(project);
911 finally {
912 readUnlock();
916 public List<MavenProject> getModules(MavenProject aggregator) {
917 readLock();
918 try {
919 List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
920 return modules == null
921 ? Collections.<MavenProject>emptyList()
922 : new ArrayList<MavenProject>(modules);
924 finally {
925 readUnlock();
929 private void addModule(MavenProject aggregator, MavenProject module) {
930 writeLock();
931 try {
932 List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
933 if (modules == null) {
934 modules = new ArrayList<MavenProject>();
935 myAggregatorToModuleMapping.put(aggregator, modules);
937 modules.add(module);
939 myModuleToAggregatorMapping.put(module, aggregator);
941 finally {
942 writeUnlock();
946 private void removeModule(MavenProject aggregator, MavenProject module) {
947 writeLock();
948 try {
949 List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
950 if (modules == null) return;
951 modules.remove(module);
952 myModuleToAggregatorMapping.remove(module);
954 finally {
955 writeUnlock();
959 private MavenProject findParent(MavenProject project) {
960 return findProject(project.getParentId());
963 public Set<MavenProject> findInheritors(MavenProject project) {
964 Set<MavenProject> result = new THashSet<MavenProject>();
965 MavenId id = project.getMavenId();
967 for (MavenProject each : getProjects()) {
968 if (each == project) continue;
969 if (id.equals(each.getParentId())) result.add(each);
971 return result;
974 public List<MavenProject> getDependentProjects(MavenProject project) {
975 MavenId projectId = project.getMavenId();
976 List<MavenProject> result = new ArrayList<MavenProject>();
977 for (MavenProject eachProject : getProjects()) {
978 if (eachProject == project) continue;
979 for (MavenArtifact eachDependency : eachProject.getDependencies()) {
980 if (eachDependency.getMavenId().equals(projectId)) {
981 result.add(eachProject);
982 break;
986 return result;
989 public void resolve(MavenProject mavenProject,
990 MavenGeneralSettings generalSettings,
991 MavenEmbeddersManager embeddersManager,
992 MavenConsole console,
993 MavenProgressIndicator process,
994 Object message) throws MavenProcessCanceledException {
995 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_DEPENDENCIES_RESOLVE);
996 embedder.customizeForResolve(getProjectIdToFileMapping(), console, process);
998 try {
999 process.checkCanceled();
1000 process.setText(ProjectBundle.message("maven.resolving.pom", mavenProject.getDisplayName()));
1001 process.setText2("");
1002 Pair<MavenProjectChanges, org.apache.maven.project.MavenProject> resolveResult = mavenProject.resolve(generalSettings,
1003 embedder,
1004 new MavenProjectReader(),
1005 myProjectLocator);
1007 fireProjectResolved(Pair.create(mavenProject, resolveResult.first), resolveResult.second, message);
1009 finally {
1010 embeddersManager.release(embedder);
1014 public void resolvePlugins(MavenProject mavenProject,
1015 org.apache.maven.project.MavenProject nativeMavenProject,
1016 MavenEmbeddersManager embeddersManager,
1017 MavenConsole console,
1018 MavenProgressIndicator process) throws MavenProcessCanceledException {
1019 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_PLUGINS_RESOLVE);
1020 embedder.customizeForResolve(console, process);
1021 embedder.clearCachesFor(mavenProject);
1023 try {
1024 for (MavenPlugin each : mavenProject.getDeclaredPlugins()) {
1025 process.checkCanceled();
1026 process.setText(ProjectBundle.message("maven.downloading.pom.plugins", mavenProject.getDisplayName()));
1027 embedder.resolvePlugin(each, nativeMavenProject, false);
1029 firePluginsResolved(mavenProject);
1031 finally {
1032 embeddersManager.release(embedder);
1036 public void resolveFolders(final MavenProject mavenProject,
1037 final MavenGeneralSettings generalSettings,
1038 final MavenImportingSettings importingSettings,
1039 final MavenEmbeddersManager embeddersManager,
1040 final MavenConsole console,
1041 final MavenProgressIndicator process,
1042 final Object message) throws MavenProcessCanceledException {
1043 executeWithEmbedder(mavenProject,
1044 embeddersManager,
1045 MavenEmbeddersManager.EmbedderKind.FOR_FOLDERS_RESOLVE,
1046 console,
1047 process,
1048 new EmbedderTask() {
1049 public void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException {
1050 process.checkCanceled();
1051 process.setText(ProjectBundle.message("maven.updating.folders.pom", mavenProject.getDisplayName()));
1052 process.setText2("");
1054 Pair<Boolean, MavenProjectChanges> resolveResult = mavenProject.resolveFolders(embedder,
1055 generalSettings,
1056 importingSettings,
1057 new MavenProjectReader(),
1058 console);
1059 if (resolveResult.first) {
1060 fireFoldersResolved(Pair.create(mavenProject, resolveResult.second), message);
1066 public void downloadArtifacts(MavenProject mavenProject,
1067 boolean downloadSources,
1068 boolean downloadDocs,
1069 MavenEmbeddersManager embeddersManager,
1070 MavenConsole console,
1071 MavenProgressIndicator process) throws MavenProcessCanceledException {
1072 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_DOWNLOAD);
1073 embedder.customizeForResolve(console, process);
1075 try {
1076 MavenArtifactDownloader.download(this, Collections.singletonList(mavenProject), downloadSources, downloadDocs, embedder, process);
1077 fireArtifactsDownloaded(mavenProject);
1079 finally {
1080 embeddersManager.release(embedder);
1084 public MavenArtifact downloadArtifact(MavenProject mavenProject,
1085 MavenId id,
1086 MavenEmbeddersManager embeddersManager,
1087 MavenConsole console,
1088 MavenProgressIndicator process) throws MavenProcessCanceledException {
1089 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_DOWNLOAD);
1090 embedder.customizeForResolve(console, process);
1092 try {
1093 Artifact artifact = embedder.resolve(id, MavenConstants.TYPE_JAR, null, mavenProject.getRemoteRepositories());
1094 artifact.setScope(Artifact.SCOPE_COMPILE);
1095 return new MavenArtifact(artifact, mavenProject.getLocalRepository());
1097 finally {
1098 embeddersManager.release(embedder);
1102 public void executeWithEmbedder(MavenProject mavenProject,
1103 MavenEmbeddersManager embeddersManager,
1104 MavenEmbeddersManager.EmbedderKind embedderKind,
1105 MavenConsole console,
1106 MavenProgressIndicator process,
1107 EmbedderTask task) throws MavenProcessCanceledException {
1108 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(embedderKind);
1109 embedder.customizeForStrictResolve(getProjectIdToFileMapping(), console, process);
1110 embedder.clearCachesFor(mavenProject);
1111 try {
1112 task.run(embedder);
1114 finally {
1115 embeddersManager.release(embedder);
1119 public <Result> Result visit(Visitor<Result> visitor) {
1120 for (MavenProject each : getRootProjects()) {
1121 if (visitor.isDone()) break;
1122 doVisit(each, visitor);
1124 return visitor.getResult();
1127 private <Result> void doVisit(MavenProject project, Visitor<Result> visitor) {
1128 if (!visitor.isDone() && visitor.shouldVisit(project)) {
1129 visitor.visit(project);
1130 for (MavenProject each : getModules(project)) {
1131 if (visitor.isDone()) break;
1132 doVisit(each, visitor);
1134 visitor.leave(project);
1138 private void writeLock() {
1139 myStructureWriteLock.lock();
1142 private void writeUnlock() {
1143 myStructureWriteLock.unlock();
1146 private void readLock() {
1147 myStructureReadLock.lock();
1150 private void readUnlock() {
1151 myStructureReadLock.unlock();
1154 public void addListener(Listener l) {
1155 myListeners.add(l);
1158 private void fireProfilesChanged() {
1159 for (Listener each : myListeners) {
1160 each.profilesChanged();
1164 private void fireProjectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, Object fromImport) {
1165 for (Listener each : myListeners) {
1166 each.projectsIgnoredStateChanged(ignored, unignored, fromImport);
1170 private void fireProjectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted, Object message) {
1171 for (Listener each : myListeners) {
1172 each.projectsUpdated(updated, deleted, message);
1176 private void fireProjectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1177 org.apache.maven.project.MavenProject nativeMavenProject,
1178 Object message) {
1179 for (Listener each : myListeners) {
1180 each.projectResolved(projectWithChanges, nativeMavenProject, message);
1184 private void firePluginsResolved(MavenProject project) {
1185 for (Listener each : myListeners) {
1186 each.pluginsResolved(project);
1190 private void fireFoldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges, Object message) {
1191 for (Listener each : myListeners) {
1192 each.foldersResolved(projectWithChanges, message);
1196 private void fireArtifactsDownloaded(MavenProject project) {
1197 for (Listener each : myListeners) {
1198 each.artifactsDownloaded(project);
1202 private class UpdateContext {
1203 public final Map<MavenProject, MavenProjectChanges> updatedProjectsWithChanges = new LinkedHashMap<MavenProject, MavenProjectChanges>();
1204 public final Set<MavenProject> deletedProjects = new LinkedHashSet<MavenProject>();
1206 public void update(MavenProject project, MavenProjectChanges changes) {
1207 deletedProjects.remove(project);
1208 updatedProjectsWithChanges.put(project, changes.mergedWith(updatedProjectsWithChanges.get(project)));
1211 public void deleted(MavenProject project) {
1212 updatedProjectsWithChanges.remove(project);
1213 deletedProjects.add(project);
1216 public void deleted(Collection<MavenProject> projects) {
1217 for (MavenProject each : projects) {
1218 deleted(each);
1222 public void fireUpdatedIfNecessary(Object message) {
1223 if (updatedProjectsWithChanges.isEmpty() && deletedProjects.isEmpty()) return;
1224 fireProjectsUpdated(updatedProjectsWithChanges.isEmpty()
1225 ? Collections.EMPTY_LIST
1226 : MavenUtil.mapToList(updatedProjectsWithChanges),
1227 deletedProjects.isEmpty()
1228 ? Collections.EMPTY_LIST
1229 : new ArrayList<MavenProject>(deletedProjects),
1230 message);
1234 public interface EmbedderTask {
1235 void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException;
1238 public static abstract class Visitor<Result> {
1239 private Result result;
1241 public boolean shouldVisit(MavenProject project) {
1242 return true;
1245 public abstract void visit(MavenProject project);
1247 public void leave(MavenProject node) {
1250 public void setResult(Result result) {
1251 this.result = result;
1254 public Result getResult() {
1255 return result;
1258 public boolean isDone() {
1259 return result != null;
1263 public static abstract class SimpleVisitor extends Visitor<Object> {
1266 private static class MavenProjectTimestamp {
1267 private final long myPomTimestamp;
1268 private final long myParentLastReadStamp;
1269 private final long myProfilesTimestamp;
1270 private final long myUserSettingsTimestamp;
1271 private final long myGlobalSettingsTimestamp;
1272 private final long myExplicitProfilesHashCode;
1274 private MavenProjectTimestamp(long pomTimestamp,
1275 long parentLastReadStamp,
1276 long profilesTimestamp,
1277 long userSettingsTimestamp,
1278 long globalSettingsTimestamp,
1279 long explicitProfilesHashCode) {
1280 myPomTimestamp = pomTimestamp;
1281 myParentLastReadStamp = parentLastReadStamp;
1282 myProfilesTimestamp = profilesTimestamp;
1283 myUserSettingsTimestamp = userSettingsTimestamp;
1284 myGlobalSettingsTimestamp = globalSettingsTimestamp;
1285 myExplicitProfilesHashCode = explicitProfilesHashCode;
1288 public static MavenProjectTimestamp read(DataInputStream in) throws IOException {
1289 return new MavenProjectTimestamp(in.readLong(),
1290 in.readLong(),
1291 in.readLong(),
1292 in.readLong(),
1293 in.readLong(),
1294 in.readLong());
1297 public void write(DataOutputStream out) throws IOException {
1298 out.writeLong(myPomTimestamp);
1299 out.writeLong(myParentLastReadStamp);
1300 out.writeLong(myProfilesTimestamp);
1301 out.writeLong(myUserSettingsTimestamp);
1302 out.writeLong(myGlobalSettingsTimestamp);
1303 out.writeLong(myExplicitProfilesHashCode);
1306 @Override
1307 public String toString() {
1308 return "(" + myPomTimestamp
1309 + ":" + myParentLastReadStamp
1310 + ":" + myProfilesTimestamp
1311 + ":" + myUserSettingsTimestamp
1312 + ":" + myGlobalSettingsTimestamp
1313 + ":" + myExplicitProfilesHashCode + ")";
1316 @Override
1317 public boolean equals(Object o) {
1318 if (this == o) return true;
1319 if (o == null || getClass() != o.getClass()) return false;
1321 MavenProjectTimestamp timestamp = (MavenProjectTimestamp)o;
1323 if (myPomTimestamp != timestamp.myPomTimestamp) return false;
1324 if (myParentLastReadStamp != timestamp.myParentLastReadStamp) return false;
1325 if (myProfilesTimestamp != timestamp.myProfilesTimestamp) return false;
1326 if (myUserSettingsTimestamp != timestamp.myUserSettingsTimestamp) return false;
1327 if (myGlobalSettingsTimestamp != timestamp.myGlobalSettingsTimestamp) return false;
1328 if (myExplicitProfilesHashCode != timestamp.myExplicitProfilesHashCode) return false;
1330 return true;
1333 @Override
1334 public int hashCode() {
1335 int result = 0;
1336 result = 31 * result + (int)(myPomTimestamp ^ (myPomTimestamp >>> 32));
1337 result = 31 * result + (int)(myParentLastReadStamp ^ (myParentLastReadStamp >>> 32));
1338 result = 31 * result + (int)(myProfilesTimestamp ^ (myProfilesTimestamp >>> 32));
1339 result = 31 * result + (int)(myUserSettingsTimestamp ^ (myUserSettingsTimestamp >>> 32));
1340 result = 31 * result + (int)(myGlobalSettingsTimestamp ^ (myGlobalSettingsTimestamp >>> 32));
1341 result = 31 * result + (int)(myExplicitProfilesHashCode ^ (myExplicitProfilesHashCode >>> 32));
1342 return result;
1346 public interface Listener extends EventListener {
1347 void profilesChanged();
1349 void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, Object message);
1351 void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted, Object message);
1353 void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1354 org.apache.maven.project.MavenProject nativeMavenProject,
1355 Object message);
1357 void pluginsResolved(MavenProject project);
1359 void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges, Object message);
1361 void artifactsDownloaded(MavenProject project);
1364 public static class ListenerAdapter implements Listener {
1365 public void profilesChanged() {
1368 public void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, Object message) {
1371 public void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted, Object message) {
1374 public void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1375 org.apache.maven.project.MavenProject nativeMavenProject,
1376 Object message) {
1379 public void pluginsResolved(MavenProject project) {
1382 public void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges, Object message) {
1385 public void artifactsDownloaded(MavenProject project) {