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
.*;
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();
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
));
89 IOException ioException
= new IOException();
90 ioException
.initCause(e
);
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());
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) {
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
);
133 public void save(File file
) throws IOException
{
134 synchronized (myStateLock
) {
137 file
.getParentFile().mkdirs();
138 FileOutputStream fs
= new FileOutputStream(file
);
139 DataOutputStream out
= new DataOutputStream(fs
);
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
);
159 private static void writeList(DataOutputStream out
, List
<String
> list
) throws IOException
{
160 out
.writeInt(list
.size());
161 for (String each
: list
) {
166 private void writeProjectsRecursively(DataOutputStream out
, List
<MavenProject
> list
) throws IOException
{
167 out
.writeInt(list
.size());
168 for (MavenProject each
: list
) {
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
);
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
);
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() {
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() {
252 myIgnoredFilesPaths
.addAll(paths
);
255 myIgnoredFilesPaths
.removeAll(paths
);
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() {
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();
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
);
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
,
369 MavenGeneralSettings generalSettings
,
370 MavenProgressIndicator process
,
372 update(files
, false, force
, getActiveProfiles(), new MavenProjectReader(), generalSettings
, process
, message
);
375 private void update(Collection
<VirtualFile
> files
,
378 List
<String
> activeProfiles
,
379 MavenProjectReader projectReader
,
380 MavenGeneralSettings generalSettings
,
381 MavenProgressIndicator process
,
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
);
394 doUpdate(mavenProject
,
395 findAggregator(mavenProject
),
408 updateActiveProfiles();
409 updateContext
.fireUpdatedIfNecessary(message
);
412 private void doAdd(final VirtualFile f
,
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
;
430 doUpdate(newMavenProject
,
443 private void doUpdate(MavenProject mavenProject
,
444 MavenProject aggregator
,
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());
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
;
476 myMavenIdToProjectMapping
.remove(mavenProject
.getMavenId());
477 myMavenIdToFileMapping
.remove(mavenProject
.getMavenId());
483 MavenId oldParentId
= mavenProject
.getParentId();
484 changes
= changes
.mergedWith(mavenProject
.read(generalSettings
, activeProfiles
, reader
, myProjectLocator
));
488 myVirtualFileToProjectMapping
.put(mavenProject
.getFile(), mavenProject
);
489 myMavenIdToProjectMapping
.put(mavenProject
.getMavenId(), mavenProject
);
490 myMavenIdToFileMapping
.put(mavenProject
.getMavenId(), mavenProject
.getFile());
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
;
505 connect(aggregator
, mavenProject
);
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
);
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;
544 module
= new MavenProject(each
);
547 MavenProject currentAggregator
= findAggregator(module
);
548 if (currentAggregator
!= null && currentAggregator
!= mavenProject
) {
549 MavenLog
.LOG
.info("Module " + each
+ " is already included into " + mavenProject
.getFile());
554 if (isChanged
|| isNewModule
|| recursive
) {
559 recursive ? force
: false, // do not force update modules if only this project was requested to be updated
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
) {
578 findAggregator(each
),
580 false, // no need to go recursively in case of inheritance, only when updating modules
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
,
610 userSettingsTimestamp
,
611 globalSettingsTimestamp
,
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;
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;
641 public void delete(List
<VirtualFile
> files
,
642 MavenGeneralSettings generalSettings
,
643 MavenProgressIndicator process
,
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
,
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
);
685 doDelete(project
, each
, updateContext
);
691 if (aggregator
!= null) {
692 removeModule(aggregator
, project
);
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
);
708 updateContext
.deleted(project
);
711 private void connect(MavenProject newAggregator
, MavenProject project
) {
714 if (newAggregator
!= null) {
715 addModule(newAggregator
, project
);
718 myRootProjects
.add(project
);
726 private boolean reconnect(MavenProject newAggregator
, MavenProject project
) {
727 MavenProject prevAggregator
= findAggregator(project
);
729 if (prevAggregator
== newAggregator
) return false;
733 if (prevAggregator
!= null) {
734 removeModule(prevAggregator
, project
);
737 myRootProjects
.remove(project
);
740 if (newAggregator
!= null) {
741 addModule(newAggregator
, project
);
744 myRootProjects
.add(project
);
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() {
765 return !myRootProjects
.isEmpty();
772 public List
<MavenProject
> getRootProjects() {
775 return new ArrayList
<MavenProject
>(myRootProjects
);
782 public List
<VirtualFile
> getRootProjectsFiles() {
783 return MavenUtil
.collectFiles(getRootProjects());
786 public List
<MavenProject
> getProjects() {
789 return new ArrayList
<MavenProject
>(myVirtualFileToProjectMapping
.values());
796 public List
<MavenProject
> getNonIgnoredProjects() {
799 List
<MavenProject
> result
= new ArrayList
<MavenProject
>();
800 for (MavenProject each
: myVirtualFileToProjectMapping
.values()) {
801 if (!isIgnored(each
)) result
.add(each
);
810 public List
<VirtualFile
> getProjectsFiles() {
813 return new ArrayList
<VirtualFile
>(myVirtualFileToProjectMapping
.keySet());
820 public MavenProject
findProject(VirtualFile f
) {
823 return myVirtualFileToProjectMapping
.get(f
);
830 public MavenProject
findProject(MavenId id
) {
833 return myMavenIdToProjectMapping
.get(id
);
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() {
851 return new THashMap
<MavenId
, VirtualFile
>(myMavenIdToFileMapping
);
858 public MavenProject
findAggregator(MavenProject project
) {
861 return myModuleToAggregatorMapping
.get(project
);
868 public List
<MavenProject
> getModules(MavenProject aggregator
) {
871 List
<MavenProject
> modules
= myAggregatorToModuleMapping
.get(aggregator
);
872 return modules
== null
873 ? Collections
.<MavenProject
>emptyList()
874 : new ArrayList
<MavenProject
>(modules
);
881 private void addModule(MavenProject aggregator
, MavenProject module
) {
884 List
<MavenProject
> modules
= myAggregatorToModuleMapping
.get(aggregator
);
885 if (modules
== null) {
886 modules
= new ArrayList
<MavenProject
>();
887 myAggregatorToModuleMapping
.put(aggregator
, modules
);
891 myModuleToAggregatorMapping
.put(module
, aggregator
);
898 private void removeModule(MavenProject aggregator
, MavenProject module
) {
901 List
<MavenProject
> modules
= myAggregatorToModuleMapping
.get(aggregator
);
902 if (modules
== null) return;
903 modules
.remove(module
);
904 myModuleToAggregatorMapping
.remove(module
);
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
);
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
);
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
);
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
,
956 new MavenProjectReader(),
959 fireProjectResolved(Pair
.create(mavenProject
, resolveResult
.first
), resolveResult
.second
, message
);
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
);
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
);
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
,
996 MavenEmbeddersManager
.EmbedderKind
.FOR_FOLDERS_RESOLVE
,
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
,
1007 new MavenProjectReader(),
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
);
1024 MavenArtifactDownloader
.download(this, Collections
.singletonList(mavenProject
), true, embedder
, process
);
1025 fireArtifactsDownloaded(mavenProject
);
1028 embeddersManager
.release(embedder
);
1032 public MavenArtifact
downloadArtifact(MavenProject mavenProject
,
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
);
1041 Artifact artifact
= embedder
.createArtifact(id
.getGroupId(),
1044 MavenConstants
.TYPE_JAR
,
1046 artifact
.setScope(Artifact
.SCOPE_COMPILE
);
1047 embedder
.resolve(artifact
, mavenProject
.getRemoteRepositories());
1048 return new MavenArtifact(artifact
, mavenProject
.getLocalRepository());
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
);
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
) {
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
,
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
) {
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
),
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
) {
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() {
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(),
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
);
1260 public String
toString() {
1261 return "(" + myPomTimestamp
1262 + ":" + myParentLastReadStamp
1263 + ":" + myProfilesTimestamp
1264 + ":" + myUserSettingsTimestamp
1265 + ":" + myGlobalSettingsTimestamp
1266 + ":" + myActiveProfilesHashCode
+ ")";
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;
1287 public int hashCode() {
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));
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
,
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
,
1332 public void pluginsResolved(MavenProject project
) {
1335 public void foldersResolved(Pair
<MavenProject
, MavenProjectChanges
> projectWithChanges
, Object message
) {
1338 public void artifactsDownloaded(MavenProject project
) {