update copyright
[fedora-idea.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / project / MavenProjectsTree.java
bloba74e686826bd42ac1c57f3c6a07fa52518b6ca5d
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.util.Comparing;
19 import com.intellij.openapi.util.Pair;
20 import com.intellij.openapi.util.io.FileUtil;
21 import com.intellij.openapi.vfs.LocalFileSystem;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.util.containers.ContainerUtil;
24 import com.intellij.util.containers.Stack;
25 import gnu.trove.THashMap;
26 import gnu.trove.THashSet;
27 import org.apache.maven.artifact.Artifact;
28 import org.jetbrains.annotations.TestOnly;
29 import org.jetbrains.idea.maven.embedder.MavenConsole;
30 import org.jetbrains.idea.maven.embedder.MavenEmbedderWrapper;
31 import org.jetbrains.idea.maven.utils.*;
33 import java.io.*;
34 import java.util.*;
35 import java.util.concurrent.locks.Lock;
36 import java.util.concurrent.locks.ReentrantReadWriteLock;
37 import java.util.regex.Pattern;
39 public class MavenProjectsTree {
40 private static final String STORAGE_VERSION = MavenProjectsTree.class.getSimpleName() + ".5";
42 private final Object myStateLock = new Object();
43 private final ReentrantReadWriteLock myStructureLock = new ReentrantReadWriteLock();
44 private final Lock myStructureReadLock = myStructureLock.readLock();
45 private final Lock myStructureWriteLock = myStructureLock.writeLock();
47 // TODO replace with sets
48 private volatile List<String> myManagedFilesPaths = new ArrayList<String>();
49 private volatile List<String> myIgnoredFilesPaths = new ArrayList<String>();
50 private volatile List<String> myIgnoredFilesPatterns = new ArrayList<String>();
51 private volatile Pattern myIgnoredFilesPatternsCache;
53 private volatile List<String> myActiveProfiles = new ArrayList<String>();
54 private volatile List<String> myTemporarilyRemovedProfiles = new ArrayList<String>();
56 private final List<MavenProject> myRootProjects = new ArrayList<MavenProject>();
58 private final Map<MavenProject, MavenProjectTimestamp> myTimestamps = new THashMap<MavenProject, MavenProjectTimestamp>();
59 private final Map<VirtualFile, MavenProject> myVirtualFileToProjectMapping = new THashMap<VirtualFile, MavenProject>();
60 private final Map<MavenId, MavenProject> myMavenIdToProjectMapping = new THashMap<MavenId, MavenProject>();
61 private final Map<MavenId, VirtualFile> myMavenIdToFileMapping = new THashMap<MavenId, VirtualFile>();
62 private final Map<MavenProject, List<MavenProject>> myAggregatorToModuleMapping = new THashMap<MavenProject, List<MavenProject>>();
63 private final Map<MavenProject, MavenProject> myModuleToAggregatorMapping = new THashMap<MavenProject, MavenProject>();
65 private final List<Listener> myListeners = ContainerUtil.createEmptyCOWList();
67 private final MavenProjectReaderProjectLocator myProjectLocator = new MavenProjectReaderProjectLocator() {
68 public VirtualFile findProjectFile(MavenId coordinates) {
69 MavenProject project = findProject(coordinates);
70 return project == null ? null : project.getFile();
74 public static MavenProjectsTree read(File file) throws IOException {
75 FileInputStream fs = new FileInputStream(file);
76 DataInputStream in = new DataInputStream(fs);
78 MavenProjectsTree result = new MavenProjectsTree();
79 try {
80 try {
81 if (!STORAGE_VERSION.equals(in.readUTF())) return null;
82 result.myManagedFilesPaths = readList(in);
83 result.myIgnoredFilesPaths = readList(in);
84 result.myIgnoredFilesPatterns = readList(in);
85 result.myActiveProfiles = readList(in);
86 result.myRootProjects.addAll(readProjectsRecursively(in, result));
88 catch (Throwable e) {
89 IOException ioException = new IOException();
90 ioException.initCause(e);
91 throw ioException;
94 finally {
95 in.close();
96 fs.close();
98 return result;
101 private static List<String> readList(DataInputStream in) throws IOException {
102 int count = in.readInt();
103 List<String> result = new ArrayList<String>(count);
104 while (count-- > 0) {
105 result.add(in.readUTF());
107 return result;
110 private static List<MavenProject> readProjectsRecursively(DataInputStream in,
111 MavenProjectsTree tree) throws IOException {
112 int count = in.readInt();
113 List<MavenProject> result = new ArrayList<MavenProject>(count);
114 while (count-- > 0) {
115 MavenProject project = MavenProject.read(in);
116 MavenProjectTimestamp timestamp = MavenProjectTimestamp.read(in);
117 List<MavenProject> modules = readProjectsRecursively(in, tree);
118 if (project != null) {
119 result.add(project);
120 tree.myTimestamps.put(project, timestamp);
121 tree.myVirtualFileToProjectMapping.put(project.getFile(), project);
122 tree.myMavenIdToProjectMapping.put(project.getMavenId(), project);
123 tree.myMavenIdToFileMapping.put(project.getMavenId(), project.getFile());
124 tree.myAggregatorToModuleMapping.put(project, modules);
125 for (MavenProject eachModule : modules) {
126 tree.myModuleToAggregatorMapping.put(eachModule, project);
130 return result;
133 public void save(File file) throws IOException {
134 synchronized (myStateLock) {
135 readLock();
136 try {
137 file.getParentFile().mkdirs();
138 FileOutputStream fs = new FileOutputStream(file);
139 DataOutputStream out = new DataOutputStream(fs);
140 try {
141 out.writeUTF(STORAGE_VERSION);
142 writeList(out, myManagedFilesPaths);
143 writeList(out, myIgnoredFilesPaths);
144 writeList(out, myIgnoredFilesPatterns);
145 writeList(out, myActiveProfiles);
146 writeProjectsRecursively(out, myRootProjects);
148 finally {
149 out.close();
150 fs.close();
153 finally {
154 readUnlock();
159 private static void writeList(DataOutputStream out, List<String> list) throws IOException {
160 out.writeInt(list.size());
161 for (String each : list) {
162 out.writeUTF(each);
166 private void writeProjectsRecursively(DataOutputStream out, List<MavenProject> list) throws IOException {
167 out.writeInt(list.size());
168 for (MavenProject each : list) {
169 each.write(out);
170 myTimestamps.get(each).write(out);
171 writeProjectsRecursively(out, getModules(each));
175 public List<String> getManagedFilesPaths() {
176 synchronized (myStateLock) {
177 return new ArrayList<String>(myManagedFilesPaths);
181 public void resetManagedFilesPathsAndProfiles(List<String> paths, List<String> profiles) {
182 synchronized (myStateLock) {
183 myManagedFilesPaths = new ArrayList<String>(paths);
185 setActiveProfiles(profiles);
188 @TestOnly
189 public void resetManagedFilesAndProfiles(List<VirtualFile> files, List<String> profiles) {
190 resetManagedFilesPathsAndProfiles(MavenUtil.collectPaths(files), profiles);
193 public void addManagedFilesWithProfiles(List<VirtualFile> files, List<String> profiles) {
194 List<String> newFiles;
195 List<String> newProfiles;
196 synchronized (myStateLock) {
197 newFiles = ContainerUtil.concat(myManagedFilesPaths, MavenUtil.collectPaths(files));
198 newProfiles = ContainerUtil.concat(myActiveProfiles, profiles);
201 resetManagedFilesPathsAndProfiles(newFiles, newProfiles);
204 public void removeManagedFiles(List<VirtualFile> files) {
205 synchronized (myStateLock) {
206 myManagedFilesPaths.removeAll(MavenUtil.collectPaths(files));
210 public List<VirtualFile> getExistingManagedFiles() {
211 List<VirtualFile> result = new ArrayList<VirtualFile>();
212 for (String path : getManagedFilesPaths()) {
213 VirtualFile f = LocalFileSystem.getInstance().findFileByPath(path);
214 if (f != null) result.add(f);
216 return result;
219 public List<String> getIgnoredFilesPaths() {
220 synchronized (myStateLock) {
221 return new ArrayList<String>(myIgnoredFilesPaths);
225 public void setIgnoredFilesPaths(final List<String> paths) {
226 doChangeIgnoreStatus(new Runnable() {
227 public void run() {
228 myIgnoredFilesPaths = new ArrayList<String>(paths);
233 public boolean getIgnoredState(MavenProject project) {
234 synchronized (myStateLock) {
235 return myIgnoredFilesPaths.contains(project.getPath());
239 public void setIgnoredState(List<MavenProject> projects, boolean ignored) {
240 setIgnoredState(projects, ignored, null);
243 public void setIgnoredState(List<MavenProject> projects, boolean ignored, Object message) {
244 doSetIgnoredState(projects, ignored, message);
247 private void doSetIgnoredState(List<MavenProject> projects, final boolean ignored, Object message) {
248 final List<String> paths = MavenUtil.collectPaths(MavenUtil.collectFiles(projects));
249 doChangeIgnoreStatus(new Runnable() {
250 public void run() {
251 if (ignored) {
252 myIgnoredFilesPaths.addAll(paths);
254 else {
255 myIgnoredFilesPaths.removeAll(paths);
258 }, message);
261 public List<String> getIgnoredFilesPatterns() {
262 synchronized (myStateLock) {
263 return new ArrayList<String>(myIgnoredFilesPatterns);
267 public void setIgnoredFilesPatterns(final List<String> patterns) {
268 doChangeIgnoreStatus(new Runnable() {
269 public void run() {
270 myIgnoredFilesPatternsCache = null;
271 myIgnoredFilesPatterns = new ArrayList<String>(patterns);
276 private void doChangeIgnoreStatus(Runnable runnable) {
277 doChangeIgnoreStatus(runnable, null);
280 private void doChangeIgnoreStatus(Runnable runnable, Object message) {
281 List<MavenProject> ignoredBefore;
282 List<MavenProject> ignoredAfter;
284 synchronized (myStateLock) {
285 ignoredBefore = getIgnoredProjects();
286 runnable.run();
287 ignoredAfter = getIgnoredProjects();
290 List<MavenProject> ignored = new ArrayList<MavenProject>(ignoredAfter);
291 ignored.removeAll(ignoredBefore);
293 List<MavenProject> unignored = new ArrayList<MavenProject>(ignoredBefore);
294 unignored.removeAll(ignoredAfter);
296 if (ignored.isEmpty() && unignored.isEmpty()) return;
298 fireProjectsIgnoredStateChanged(ignored, unignored, message);
301 private List<MavenProject> getIgnoredProjects() {
302 List<MavenProject> result = new ArrayList<MavenProject>();
303 for (MavenProject each : getProjects()) {
304 if (isIgnored(each)) result.add(each);
306 return result;
309 public boolean isIgnored(MavenProject project) {
310 String path = project.getPath();
311 synchronized (myStateLock) {
312 return myIgnoredFilesPaths.contains(path) || matchesIgnoredFilesPatterns(path);
316 private boolean matchesIgnoredFilesPatterns(String path) {
317 synchronized (myStateLock) {
318 if (myIgnoredFilesPatternsCache == null) {
319 myIgnoredFilesPatternsCache = Pattern.compile(Strings.translateMasks(myIgnoredFilesPatterns));
321 return myIgnoredFilesPatternsCache.matcher(path).matches();
325 public List<String> getActiveProfiles() {
326 synchronized (myStateLock) {
327 return new ArrayList<String>(myActiveProfiles);
331 public void setActiveProfiles(List<String> activeProfiles) {
332 synchronized (myStateLock) {
333 myActiveProfiles = new ArrayList<String>(activeProfiles);
335 fireProfilesChanged(activeProfiles);
338 private void updateActiveProfiles() {
339 List<String> availableProfiles = getAvailableProfiles();
341 synchronized (myStateLock) {
342 List<String> removedProfiles = new ArrayList<String>(myActiveProfiles);
343 removedProfiles.removeAll(availableProfiles);
344 myTemporarilyRemovedProfiles.addAll(removedProfiles);
346 List<String> restoredProfiles = new ArrayList<String>(myTemporarilyRemovedProfiles);
347 restoredProfiles.retainAll(availableProfiles);
348 myTemporarilyRemovedProfiles.removeAll(restoredProfiles);
350 myActiveProfiles.removeAll(removedProfiles);
351 myActiveProfiles.addAll(restoredProfiles);
355 public void updateAll(boolean force, MavenGeneralSettings generalSettings, MavenProgressIndicator process, Object message) {
356 List<VirtualFile> managedFiles = getExistingManagedFiles();
357 List<String> activeProfiles = getActiveProfiles();
359 MavenProjectReader projectReader = new MavenProjectReader();
360 update(managedFiles, true, force, activeProfiles, projectReader, generalSettings, process, message);
362 List<VirtualFile> obsoleteFiles = getRootProjectsFiles();
363 obsoleteFiles.removeAll(managedFiles);
364 delete(projectReader, obsoleteFiles, activeProfiles, generalSettings, process, message);
367 public void update(Collection<VirtualFile> files,
368 boolean force,
369 MavenGeneralSettings generalSettings,
370 MavenProgressIndicator process,
371 Object message) {
372 update(files, false, force, getActiveProfiles(), new MavenProjectReader(), generalSettings, process, message);
375 private void update(Collection<VirtualFile> files,
376 boolean recursive,
377 boolean force,
378 List<String> activeProfiles,
379 MavenProjectReader projectReader,
380 MavenGeneralSettings generalSettings,
381 MavenProgressIndicator process,
382 Object message) {
383 if (files.isEmpty()) return;
385 UpdateContext updateContext = new UpdateContext();
386 Stack<MavenProject> updateStack = new Stack<MavenProject>();
388 for (VirtualFile each : files) {
389 MavenProject mavenProject = findProject(each);
390 if (mavenProject == null) {
391 doAdd(each, recursive, activeProfiles, updateContext, updateStack, projectReader, generalSettings, process);
393 else {
394 doUpdate(mavenProject,
395 findAggregator(mavenProject),
396 false,
397 recursive,
398 force,
399 activeProfiles,
400 updateContext,
401 updateStack,
402 projectReader,
403 generalSettings,
404 process);
408 updateActiveProfiles();
409 updateContext.fireUpdatedIfNecessary(message);
412 private void doAdd(final VirtualFile f,
413 boolean recursuve,
414 List<String> activeProfiles,
415 UpdateContext updateContext,
416 Stack<MavenProject> updateStack,
417 MavenProjectReader reader,
418 MavenGeneralSettings generalSettings,
419 MavenProgressIndicator process) {
420 MavenProject newMavenProject = new MavenProject(f);
422 MavenProject intendedAggregator = null;
423 for (MavenProject each : getProjects()) {
424 if (each.getExistingModuleFiles().contains(f)) {
425 intendedAggregator = each;
426 break;
430 doUpdate(newMavenProject,
431 intendedAggregator,
432 true,
433 recursuve,
434 false,
435 activeProfiles,
436 updateContext,
437 updateStack,
438 reader,
439 generalSettings,
440 process);
443 private void doUpdate(MavenProject mavenProject,
444 MavenProject aggregator,
445 boolean isNew,
446 boolean recursive,
447 boolean force,
448 List<String> activeProfiles,
449 UpdateContext updateContext,
450 Stack<MavenProject> updateStack,
451 MavenProjectReader reader,
452 MavenGeneralSettings generalSettings,
453 MavenProgressIndicator process) {
454 if (updateStack.contains(mavenProject)) {
455 MavenLog.LOG.info("Recursion detected in " + mavenProject.getFile());
456 return;
458 updateStack.push(mavenProject);
460 process.setText(ProjectBundle.message("maven.reading.pom", mavenProject.getPath()));
461 process.setText2("");
463 List<MavenProject> prevModules = getModules(mavenProject);
464 Set<MavenProject> prevInheritors = isNew
465 ? new THashSet<MavenProject>()
466 : findInheritors(mavenProject);
468 MavenProjectTimestamp timestamp = calculateTimestamp(mavenProject, activeProfiles, generalSettings);
469 boolean isChanged = force || !timestamp.equals(myTimestamps.get(mavenProject));
471 MavenProjectChanges changes = force ? MavenProjectChanges.ALL : MavenProjectChanges.NONE;
472 if (isChanged) {
473 writeLock();
474 try {
475 if (!isNew) {
476 myMavenIdToProjectMapping.remove(mavenProject.getMavenId());
477 myMavenIdToFileMapping.remove(mavenProject.getMavenId());
480 finally {
481 writeUnlock();
483 MavenId oldParentId = mavenProject.getParentId();
484 changes = changes.mergedWith(mavenProject.read(generalSettings, activeProfiles, reader, myProjectLocator));
486 writeLock();
487 try {
488 myVirtualFileToProjectMapping.put(mavenProject.getFile(), mavenProject);
489 myMavenIdToProjectMapping.put(mavenProject.getMavenId(), mavenProject);
490 myMavenIdToFileMapping.put(mavenProject.getMavenId(), mavenProject.getFile());
492 finally {
493 writeUnlock();
496 if (!Comparing.equal(oldParentId, mavenProject.getParentId())) {
497 // ensure timestamp reflects actual parent's timestamp
498 timestamp = calculateTimestamp(mavenProject, activeProfiles, generalSettings);
500 myTimestamps.put(mavenProject, timestamp);
503 boolean reconnected = isNew;
504 if (isNew) {
505 connect(aggregator, mavenProject);
507 else {
508 reconnected = reconnect(aggregator, mavenProject);
511 if (isChanged || reconnected) {
512 updateContext.update(mavenProject, changes);
515 List<VirtualFile> existingModuleFiles = mavenProject.getExistingModuleFiles();
516 List<MavenProject> modulesToRemove = new ArrayList<MavenProject>();
517 List<MavenProject> modulesToBecomeRoots = new ArrayList<MavenProject>();
519 for (MavenProject each : prevModules) {
520 VirtualFile moduleFile = each.getFile();
521 if (!existingModuleFiles.contains(moduleFile)) {
522 if (isManagedFile(moduleFile)) {
523 modulesToBecomeRoots.add(each);
525 else {
526 modulesToRemove.add(each);
530 for (MavenProject each : modulesToRemove) {
531 removeModule(mavenProject, each);
532 doDelete(mavenProject, each, updateContext);
533 prevInheritors.removeAll(updateContext.deletedProjects);
536 for (MavenProject each : modulesToBecomeRoots) {
537 if (reconnect(null, each)) updateContext.update(each, MavenProjectChanges.NONE);
540 for (VirtualFile each : existingModuleFiles) {
541 MavenProject module = findProject(each);
542 boolean isNewModule = module == null;
543 if (isNewModule) {
544 module = new MavenProject(each);
546 else {
547 MavenProject currentAggregator = findAggregator(module);
548 if (currentAggregator != null && currentAggregator != mavenProject) {
549 MavenLog.LOG.info("Module " + each + " is already included into " + mavenProject.getFile());
550 continue;
554 if (isChanged || isNewModule || recursive) {
555 doUpdate(module,
556 mavenProject,
557 isNewModule,
558 recursive,
559 recursive ? force : false, // do not force update modules if only this project was requested to be updated
560 activeProfiles,
561 updateContext,
562 updateStack,
563 reader,
564 generalSettings,
565 process);
567 else {
568 if (reconnect(mavenProject, module)) {
569 updateContext.update(module, MavenProjectChanges.NONE);
574 Set<MavenProject> allInheritors = findInheritors(mavenProject);
575 allInheritors.addAll(prevInheritors);
576 for (MavenProject each : allInheritors) {
577 doUpdate(each,
578 findAggregator(each),
579 false,
580 false, // no need to go recursively in case of inheritance, only when updating modules
581 false,
582 activeProfiles,
583 updateContext,
584 updateStack,
585 reader,
586 generalSettings,
587 process);
590 updateStack.pop();
593 private MavenProjectTimestamp calculateTimestamp(final MavenProject mavenProject,
594 final List<String> activeProfiles,
595 final MavenGeneralSettings generalSettings) {
596 long pomTimestamp = getFileTimestamp(mavenProject.getFile());
597 MavenProject parent = findParent(mavenProject);
598 long parentLastReadStamp = parent == null ? -1 : parent.getLastReadStamp();
599 VirtualFile profilesXmlFile = mavenProject.getProfilesXmlFile();
600 long profilesTimestamp = getFileTimestamp(profilesXmlFile);
602 long userSettingsTimestamp = getFileTimestamp(generalSettings.getEffectiveUserSettingsFile());
603 long globalSettingsTimestamp = getFileTimestamp(generalSettings.getEffectiveGlobalSettingsFile());
605 int profilesHashCode = new THashSet<String>(activeProfiles).hashCode();
607 return new MavenProjectTimestamp(pomTimestamp,
608 parentLastReadStamp,
609 profilesTimestamp,
610 userSettingsTimestamp,
611 globalSettingsTimestamp,
612 profilesHashCode);
615 private long getFileTimestamp(VirtualFile file) {
616 if (file == null) return -1;
617 return file.getTimeStamp();
620 public boolean isManagedFile(VirtualFile moduleFile) {
621 return isManagedFile(moduleFile.getPath());
624 public boolean isManagedFile(String path) {
625 for (String each : getManagedFilesPaths()) {
626 if (FileUtil.pathsEqual(each, path)) return true;
628 return false;
631 public boolean isPotentialProject(String path) {
632 if (isManagedFile(path)) return true;
634 for (MavenProject each : getProjects()) {
635 if (FileUtil.pathsEqual(path, each.getPath())) return true;
636 if (each.getModulePaths().contains(path)) return true;
638 return false;
641 public void delete(List<VirtualFile> files,
642 MavenGeneralSettings generalSettings,
643 MavenProgressIndicator process,
644 Object message) {
645 delete(new MavenProjectReader(), files, getActiveProfiles(), generalSettings, process, message);
648 private void delete(MavenProjectReader projectReader,
649 List<VirtualFile> files,
650 List<String> activeProfiles,
651 MavenGeneralSettings generalSettings,
652 MavenProgressIndicator process,
653 Object message) {
654 if (files.isEmpty()) return;
656 UpdateContext updateContext = new UpdateContext();
657 Stack<MavenProject> updateStack = new Stack<MavenProject>();
659 Set<MavenProject> inheritorsToUpdate = new THashSet<MavenProject>();
660 for (VirtualFile each : files) {
661 MavenProject mavenProject = findProject(each);
662 if (mavenProject == null) return;
664 inheritorsToUpdate.addAll(findInheritors(mavenProject));
665 doDelete(findAggregator(mavenProject), mavenProject, updateContext);
667 inheritorsToUpdate.removeAll(updateContext.deletedProjects);
669 for (MavenProject each : inheritorsToUpdate) {
670 doUpdate(each, null, false, false, false, activeProfiles, updateContext, updateStack, projectReader, generalSettings, process);
673 updateActiveProfiles();
674 updateContext.fireUpdatedIfNecessary(message);
677 private void doDelete(MavenProject aggregator, MavenProject project, UpdateContext updateContext) {
678 for (MavenProject each : getModules(project)) {
679 if (isManagedFile(each.getPath())) {
680 if (reconnect(null, each)) {
681 updateContext.update(each, MavenProjectChanges.NONE);
684 else {
685 doDelete(project, each, updateContext);
689 writeLock();
690 try {
691 if (aggregator != null) {
692 removeModule(aggregator, project);
694 else {
695 myRootProjects.remove(project);
697 myTimestamps.remove(project);
698 myVirtualFileToProjectMapping.remove(project.getFile());
699 myMavenIdToProjectMapping.remove(project.getMavenId());
700 myMavenIdToFileMapping.remove(project.getMavenId());
701 myAggregatorToModuleMapping.remove(project);
702 myModuleToAggregatorMapping.remove(project);
704 finally {
705 writeUnlock();
708 updateContext.deleted(project);
711 private void connect(MavenProject newAggregator, MavenProject project) {
712 writeLock();
713 try {
714 if (newAggregator != null) {
715 addModule(newAggregator, project);
717 else {
718 myRootProjects.add(project);
721 finally {
722 writeUnlock();
726 private boolean reconnect(MavenProject newAggregator, MavenProject project) {
727 MavenProject prevAggregator = findAggregator(project);
729 if (prevAggregator == newAggregator) return false;
731 writeLock();
732 try {
733 if (prevAggregator != null) {
734 removeModule(prevAggregator, project);
736 else {
737 myRootProjects.remove(project);
740 if (newAggregator != null) {
741 addModule(newAggregator, project);
743 else {
744 myRootProjects.add(project);
747 finally {
748 writeUnlock();
751 return true;
754 public List<String> getAvailableProfiles() {
755 Set<String> result = new THashSet<String>();
756 for (MavenProject each : getProjects()) {
757 result.addAll(each.getProfilesIds());
759 return new ArrayList<String>(result);
762 public boolean hasProjects() {
763 readLock();
764 try {
765 return !myRootProjects.isEmpty();
767 finally {
768 readUnlock();
772 public List<MavenProject> getRootProjects() {
773 readLock();
774 try {
775 return new ArrayList<MavenProject>(myRootProjects);
777 finally {
778 readUnlock();
782 public List<VirtualFile> getRootProjectsFiles() {
783 return MavenUtil.collectFiles(getRootProjects());
786 public List<MavenProject> getProjects() {
787 readLock();
788 try {
789 return new ArrayList<MavenProject>(myVirtualFileToProjectMapping.values());
791 finally {
792 readUnlock();
796 public List<MavenProject> getNonIgnoredProjects() {
797 readLock();
798 try {
799 List<MavenProject> result = new ArrayList<MavenProject>();
800 for (MavenProject each : myVirtualFileToProjectMapping.values()) {
801 if (!isIgnored(each)) result.add(each);
803 return result;
805 finally {
806 readUnlock();
810 public List<VirtualFile> getProjectsFiles() {
811 readLock();
812 try {
813 return new ArrayList<VirtualFile>(myVirtualFileToProjectMapping.keySet());
815 finally {
816 readUnlock();
820 public MavenProject findProject(VirtualFile f) {
821 readLock();
822 try {
823 return myVirtualFileToProjectMapping.get(f);
825 finally {
826 readUnlock();
830 public MavenProject findProject(MavenId id) {
831 readLock();
832 try {
833 return myMavenIdToProjectMapping.get(id);
835 finally {
836 readUnlock();
840 public MavenProject findProject(MavenArtifact artifact) {
841 return findProject(artifact.getMavenId());
844 public MavenProject findProject(Artifact artifact) {
845 return findProject(new MavenId(artifact));
848 private Map<MavenId, VirtualFile> getProjectIdToFileMapping() {
849 readLock();
850 try {
851 return new THashMap<MavenId, VirtualFile>(myMavenIdToFileMapping);
853 finally {
854 readUnlock();
858 public MavenProject findAggregator(MavenProject project) {
859 readLock();
860 try {
861 return myModuleToAggregatorMapping.get(project);
863 finally {
864 readUnlock();
868 public List<MavenProject> getModules(MavenProject aggregator) {
869 readLock();
870 try {
871 List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
872 return modules == null
873 ? Collections.<MavenProject>emptyList()
874 : new ArrayList<MavenProject>(modules);
876 finally {
877 readUnlock();
881 private void addModule(MavenProject aggregator, MavenProject module) {
882 writeLock();
883 try {
884 List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
885 if (modules == null) {
886 modules = new ArrayList<MavenProject>();
887 myAggregatorToModuleMapping.put(aggregator, modules);
889 modules.add(module);
891 myModuleToAggregatorMapping.put(module, aggregator);
893 finally {
894 writeUnlock();
898 private void removeModule(MavenProject aggregator, MavenProject module) {
899 writeLock();
900 try {
901 List<MavenProject> modules = myAggregatorToModuleMapping.get(aggregator);
902 if (modules == null) return;
903 modules.remove(module);
904 myModuleToAggregatorMapping.remove(module);
906 finally {
907 writeUnlock();
911 private MavenProject findParent(MavenProject project) {
912 return findProject(project.getParentId());
915 private Set<MavenProject> findInheritors(MavenProject project) {
916 Set<MavenProject> result = new THashSet<MavenProject>();
917 MavenId id = project.getMavenId();
919 for (MavenProject each : getProjects()) {
920 if (each == project) continue;
921 if (id.equals(each.getParentId())) result.add(each);
923 return result;
926 public List<MavenProject> getDependentProjects(MavenProject project) {
927 MavenId projectId = project.getMavenId();
928 List<MavenProject> result = new ArrayList<MavenProject>();
929 for (MavenProject eachProject : getProjects()) {
930 if (eachProject == project) continue;
931 for (MavenArtifact eachDependency : eachProject.getDependencies()) {
932 if (eachDependency.getMavenId().equals(projectId)) {
933 result.add(eachProject);
934 break;
938 return result;
941 public void resolve(MavenProject mavenProject,
942 MavenGeneralSettings generalSettings,
943 MavenEmbeddersManager embeddersManager,
944 MavenConsole console,
945 MavenProgressIndicator process,
946 Object message) throws MavenProcessCanceledException {
947 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_DEPENDENCIES_RESOLVE);
948 embedder.customizeForResolve(getProjectIdToFileMapping(), console, process);
950 try {
951 process.checkCanceled();
952 process.setText(ProjectBundle.message("maven.resolving.pom", mavenProject.getDisplayName()));
953 process.setText2("");
954 Pair<MavenProjectChanges, org.apache.maven.project.MavenProject> resolveResult = mavenProject.resolve(generalSettings,
955 embedder,
956 new MavenProjectReader(),
957 myProjectLocator);
959 fireProjectResolved(Pair.create(mavenProject, resolveResult.first), resolveResult.second, message);
961 finally {
962 embeddersManager.release(embedder);
966 public void resolvePlugins(MavenProject mavenProject,
967 org.apache.maven.project.MavenProject nativeMavenProject,
968 MavenEmbeddersManager embeddersManager,
969 MavenConsole console,
970 MavenProgressIndicator process) throws MavenProcessCanceledException {
971 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_PLUGINS_RESOLVE);
972 embedder.customizeForResolve(console, process);
973 embedder.clearCachesFor(mavenProject);
975 try {
976 for (MavenPlugin each : mavenProject.getPlugins()) {
977 process.checkCanceled();
978 process.setText(ProjectBundle.message("maven.downloading.pom.plugins", mavenProject.getDisplayName()));
979 embedder.resolvePlugin(each, nativeMavenProject);
981 firePluginsResolved(mavenProject);
983 finally {
984 embeddersManager.release(embedder);
988 public void resolveFolders(final MavenProject mavenProject,
989 final MavenImportingSettings importingSettings,
990 final MavenEmbeddersManager embeddersManager,
991 final MavenConsole console,
992 final MavenProgressIndicator process,
993 final Object message) throws MavenProcessCanceledException {
994 doWithEmbedder(mavenProject,
995 embeddersManager,
996 MavenEmbeddersManager.EmbedderKind.FOR_FOLDERS_RESOLVE,
997 console,
998 process,
999 new EmbedderTask() {
1000 public void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException {
1001 process.checkCanceled();
1002 process.setText(ProjectBundle.message("maven.updating.folders.pom", mavenProject.getDisplayName()));
1003 process.setText2("");
1005 Pair<Boolean, MavenProjectChanges> resolveResult = mavenProject.resolveFolders(embedder,
1006 importingSettings,
1007 new MavenProjectReader(),
1008 console);
1009 if (resolveResult.first) {
1010 fireFoldersResolved(Pair.create(mavenProject, resolveResult.second), message);
1016 public void downloadArtifacts(MavenProject mavenProject,
1017 MavenEmbeddersManager embeddersManager,
1018 MavenConsole console,
1019 MavenProgressIndicator process) throws MavenProcessCanceledException {
1020 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_DOWNLOAD);
1021 embedder.customizeForResolve(console, process);
1023 try {
1024 MavenArtifactDownloader.download(this, Collections.singletonList(mavenProject), true, embedder, process);
1025 fireArtifactsDownloaded(mavenProject);
1027 finally {
1028 embeddersManager.release(embedder);
1032 public MavenArtifact downloadArtifact(MavenProject mavenProject,
1033 MavenId id,
1034 MavenEmbeddersManager embeddersManager,
1035 MavenConsole console,
1036 MavenProgressIndicator process) throws MavenProcessCanceledException {
1037 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(MavenEmbeddersManager.EmbedderKind.FOR_DOWNLOAD);
1038 embedder.customizeForResolve(console, process);
1040 try {
1041 Artifact artifact = embedder.createArtifact(id.getGroupId(),
1042 id.getArtifactId(),
1043 id.getVersion(),
1044 MavenConstants.TYPE_JAR,
1045 null);
1046 artifact.setScope(Artifact.SCOPE_COMPILE);
1047 embedder.resolve(artifact, mavenProject.getRemoteRepositories());
1048 return new MavenArtifact(artifact, mavenProject.getLocalRepository());
1050 finally {
1051 embeddersManager.release(embedder);
1055 public void doWithEmbedder(MavenProject mavenProject,
1056 MavenEmbeddersManager embeddersManager,
1057 MavenEmbeddersManager.EmbedderKind embedderKind,
1058 MavenConsole console,
1059 MavenProgressIndicator process,
1060 EmbedderTask task) throws MavenProcessCanceledException {
1061 MavenEmbedderWrapper embedder = embeddersManager.getEmbedder(embedderKind);
1062 embedder.customizeForStrictResolve(getProjectIdToFileMapping(), console, process);
1063 embedder.clearCachesFor(mavenProject);
1064 try {
1065 task.run(embedder);
1067 finally {
1068 embeddersManager.release(embedder);
1072 public <Result> Result visit(Visitor<Result> visitor) {
1073 for (MavenProject each : getRootProjects()) {
1074 if (visitor.isDone()) break;
1075 doVisit(each, visitor);
1077 return visitor.getResult();
1080 private <Result> void doVisit(MavenProject project, Visitor<Result> visitor) {
1081 if (!visitor.isDone() && visitor.shouldVisit(project)) {
1082 visitor.visit(project);
1083 for (MavenProject each : getModules(project)) {
1084 if (visitor.isDone()) break;
1085 doVisit(each, visitor);
1087 visitor.leave(project);
1091 private void writeLock() {
1092 myStructureWriteLock.lock();
1095 private void writeUnlock() {
1096 myStructureWriteLock.unlock();
1099 private void readLock() {
1100 myStructureReadLock.lock();
1103 private void readUnlock() {
1104 myStructureReadLock.unlock();
1107 public void addListener(Listener l) {
1108 myListeners.add(l);
1111 private void fireProfilesChanged(List<String> profiles) {
1112 for (Listener each : myListeners) {
1113 each.profilesChanged(profiles);
1117 private void fireProjectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, Object fromImport) {
1118 for (Listener each : myListeners) {
1119 each.projectsIgnoredStateChanged(ignored, unignored, fromImport);
1123 private void fireProjectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted, Object message) {
1124 for (Listener each : myListeners) {
1125 each.projectsUpdated(updated, deleted, message);
1129 private void fireProjectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1130 org.apache.maven.project.MavenProject nativeMavenProject,
1131 Object message) {
1132 for (Listener each : myListeners) {
1133 each.projectResolved(projectWithChanges, nativeMavenProject, message);
1137 private void firePluginsResolved(MavenProject project) {
1138 for (Listener each : myListeners) {
1139 each.pluginsResolved(project);
1143 private void fireFoldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges, Object message) {
1144 for (Listener each : myListeners) {
1145 each.foldersResolved(projectWithChanges, message);
1149 private void fireArtifactsDownloaded(MavenProject project) {
1150 for (Listener each : myListeners) {
1151 each.artifactsDownloaded(project);
1155 private class UpdateContext {
1156 public final Map<MavenProject, MavenProjectChanges> updatedProjectsWithChanges = new LinkedHashMap<MavenProject, MavenProjectChanges>();
1157 public final Set<MavenProject> deletedProjects = new LinkedHashSet<MavenProject>();
1159 public void update(MavenProject project, MavenProjectChanges changes) {
1160 deletedProjects.remove(project);
1161 updatedProjectsWithChanges.put(project, changes.mergedWith(updatedProjectsWithChanges.get(project)));
1164 public void deleted(MavenProject project) {
1165 updatedProjectsWithChanges.remove(project);
1166 deletedProjects.add(project);
1169 public void deleted(Collection<MavenProject> projects) {
1170 for (MavenProject each : projects) {
1171 deleted(each);
1175 public void fireUpdatedIfNecessary(Object message) {
1176 if (updatedProjectsWithChanges.isEmpty() && deletedProjects.isEmpty()) return;
1177 fireProjectsUpdated(updatedProjectsWithChanges.isEmpty()
1178 ? Collections.EMPTY_LIST
1179 : MavenUtil.mapToList(updatedProjectsWithChanges),
1180 deletedProjects.isEmpty()
1181 ? Collections.EMPTY_LIST
1182 : new ArrayList<MavenProject>(deletedProjects),
1183 message);
1187 public interface EmbedderTask {
1188 void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException;
1191 public static abstract class Visitor<Result> {
1192 private Result result;
1194 public boolean shouldVisit(MavenProject project) {
1195 return true;
1198 public abstract void visit(MavenProject project);
1200 public void leave(MavenProject node) {
1203 public void setResult(Result result) {
1204 this.result = result;
1207 public Result getResult() {
1208 return result;
1211 public boolean isDone() {
1212 return result != null;
1216 public static abstract class SimpleVisitor extends Visitor<Object> {
1219 private static class MavenProjectTimestamp {
1220 private final long myPomTimestamp;
1221 private final long myParentLastReadStamp;
1222 private final long myProfilesTimestamp;
1223 private final long myUserSettingsTimestamp;
1224 private final long myGlobalSettingsTimestamp;
1225 private final long myActiveProfilesHashCode;
1227 private MavenProjectTimestamp(long pomTimestamp,
1228 long parentLastReadStamp,
1229 long profilesTimestamp,
1230 long userSettingsTimestamp,
1231 long globalSettingsTimestamp,
1232 long activeProfilesHashCode) {
1233 myPomTimestamp = pomTimestamp;
1234 myParentLastReadStamp = parentLastReadStamp;
1235 myProfilesTimestamp = profilesTimestamp;
1236 myUserSettingsTimestamp = userSettingsTimestamp;
1237 myGlobalSettingsTimestamp = globalSettingsTimestamp;
1238 myActiveProfilesHashCode = activeProfilesHashCode;
1241 public static MavenProjectTimestamp read(DataInputStream in) throws IOException {
1242 return new MavenProjectTimestamp(in.readLong(),
1243 in.readLong(),
1244 in.readLong(),
1245 in.readLong(),
1246 in.readLong(),
1247 in.readLong());
1250 public void write(DataOutputStream out) throws IOException {
1251 out.writeLong(myPomTimestamp);
1252 out.writeLong(myParentLastReadStamp);
1253 out.writeLong(myProfilesTimestamp);
1254 out.writeLong(myUserSettingsTimestamp);
1255 out.writeLong(myGlobalSettingsTimestamp);
1256 out.writeLong(myActiveProfilesHashCode);
1259 @Override
1260 public String toString() {
1261 return "(" + myPomTimestamp
1262 + ":" + myParentLastReadStamp
1263 + ":" + myProfilesTimestamp
1264 + ":" + myUserSettingsTimestamp
1265 + ":" + myGlobalSettingsTimestamp
1266 + ":" + myActiveProfilesHashCode + ")";
1269 @Override
1270 public boolean equals(Object o) {
1271 if (this == o) return true;
1272 if (o == null || getClass() != o.getClass()) return false;
1274 MavenProjectTimestamp timestamp = (MavenProjectTimestamp)o;
1276 if (myPomTimestamp != timestamp.myPomTimestamp) return false;
1277 if (myParentLastReadStamp != timestamp.myParentLastReadStamp) return false;
1278 if (myProfilesTimestamp != timestamp.myProfilesTimestamp) return false;
1279 if (myUserSettingsTimestamp != timestamp.myUserSettingsTimestamp) return false;
1280 if (myGlobalSettingsTimestamp != timestamp.myGlobalSettingsTimestamp) return false;
1281 if (myActiveProfilesHashCode != timestamp.myActiveProfilesHashCode) return false;
1283 return true;
1286 @Override
1287 public int hashCode() {
1288 int result = 0;
1289 result = 31 * result + (int)(myPomTimestamp ^ (myPomTimestamp >>> 32));
1290 result = 31 * result + (int)(myParentLastReadStamp ^ (myParentLastReadStamp >>> 32));
1291 result = 31 * result + (int)(myProfilesTimestamp ^ (myProfilesTimestamp >>> 32));
1292 result = 31 * result + (int)(myUserSettingsTimestamp ^ (myUserSettingsTimestamp >>> 32));
1293 result = 31 * result + (int)(myGlobalSettingsTimestamp ^ (myGlobalSettingsTimestamp >>> 32));
1294 result = 31 * result + (int)(myActiveProfilesHashCode ^ (myActiveProfilesHashCode >>> 32));
1295 return result;
1299 public interface Listener extends EventListener {
1300 void profilesChanged(List<String> profiles);
1302 void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, Object message);
1304 void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted, Object message);
1306 void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1307 org.apache.maven.project.MavenProject nativeMavenProject,
1308 Object message);
1310 void pluginsResolved(MavenProject project);
1312 void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges, Object message);
1314 void artifactsDownloaded(MavenProject project);
1317 public static class ListenerAdapter implements Listener {
1318 public void profilesChanged(List<String> profiles) {
1321 public void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, Object message) {
1324 public void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted, Object message) {
1327 public void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
1328 org.apache.maven.project.MavenProject nativeMavenProject,
1329 Object message) {
1332 public void pluginsResolved(MavenProject project) {
1335 public void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges, Object message) {
1338 public void artifactsDownloaded(MavenProject project) {