1 package com
.intellij
.openapi
.vcs
.changes
;
3 import com
.intellij
.openapi
.application
.ApplicationManager
;
4 import com
.intellij
.openapi
.components
.ProjectComponent
;
5 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
6 import com
.intellij
.openapi
.project
.Project
;
7 import com
.intellij
.openapi
.startup
.StartupManager
;
8 import com
.intellij
.openapi
.util
.Computable
;
9 import com
.intellij
.openapi
.util
.Ref
;
10 import com
.intellij
.openapi
.vcs
.AbstractVcs
;
11 import com
.intellij
.openapi
.vcs
.FilePath
;
12 import com
.intellij
.openapi
.vcs
.ProjectLevelVcsManager
;
13 import com
.intellij
.openapi
.vcs
.VcsRoot
;
14 import com
.intellij
.openapi
.vfs
.VirtualFile
;
15 import com
.intellij
.util
.Consumer
;
16 import org
.jetbrains
.annotations
.NonNls
;
17 import org
.jetbrains
.annotations
.NotNull
;
18 import org
.jetbrains
.annotations
.Nullable
;
20 import java
.util
.ArrayList
;
21 import java
.util
.Collection
;
26 public class VcsDirtyScopeManagerImpl
extends VcsDirtyScopeManager
implements ProjectComponent
{
27 private final Project myProject
;
28 private final ChangeListManager myChangeListManager
;
29 private final ProjectLevelVcsManager myVcsManager
;
31 private final DirtBuilder myDirtBuilder
;
32 private final VcsGuess myGuess
;
33 private final SynchronizedLife myLife
;
35 public VcsDirtyScopeManagerImpl(Project project
, ChangeListManager changeListManager
, ProjectLevelVcsManager vcsManager
) {
37 myChangeListManager
= changeListManager
;
38 myVcsManager
= vcsManager
;
40 myLife
= new SynchronizedLife();
41 myGuess
= new VcsGuess(myProject
);
42 myDirtBuilder
= new DirtBuilder(myGuess
);
45 public void projectOpened() {
46 if (ApplicationManager
.getApplication().isUnitTestMode()) {
48 final AbstractVcs
[] vcss
= myVcsManager
.getAllActiveVcss();
49 if (vcss
.length
> 0) {
50 markEverythingDirty();
54 StartupManager
.getInstance(myProject
).registerPostStartupActivity(new DumbAwareRunnable() {
57 markEverythingDirty();
63 public void suspendMe() {
67 public void reanimate() {
68 final Ref
<Boolean
> wasNotEmptyRef
= new Ref
<Boolean
>();
69 myLife
.releaseMe(new Runnable() {
71 wasNotEmptyRef
.set(! myDirtBuilder
.isEmpty());
74 if (Boolean
.TRUE
.equals(wasNotEmptyRef
.get())) {
75 myChangeListManager
.scheduleUpdate();
79 public void markEverythingDirty() {
80 if (myProject
.isDisposed()) return;
82 final LifeDrop lifeDrop
= myLife
.doIfAlive(new Runnable() {
84 myDirtBuilder
.everythingDirty();
88 if (lifeDrop
.isDone() && (! lifeDrop
.isSuspened())) {
89 myChangeListManager
.scheduleUpdate();
93 public void projectClosed() {
98 public String
getComponentName() {
99 return "VcsDirtyScopeManager";
102 public void initComponent() {}
104 private void killSelf() {
105 myLife
.kill(new Runnable() {
107 myDirtBuilder
.reset();
112 public void disposeComponent() {
116 private void convertPaths(@Nullable final Collection
<FilePath
> from
, final Collection
<FilePathUnderVcs
> to
) {
118 for (FilePath fp
: from
) {
119 final AbstractVcs vcs
= myGuess
.getVcsForDirty(fp
);
121 to
.add(new FilePathUnderVcs(fp
, vcs
));
127 public boolean filePathsDirty(@Nullable final Collection
<FilePath
> filesDirty
, @Nullable final Collection
<FilePath
> dirsRecursivelyDirty
) {
128 final ArrayList
<FilePathUnderVcs
> filesConverted
= filesDirty
== null ?
null : new ArrayList
<FilePathUnderVcs
>(filesDirty
.size());
129 final ArrayList
<FilePathUnderVcs
> dirsConverted
= dirsRecursivelyDirty
== null ?
null : new ArrayList
<FilePathUnderVcs
>(dirsRecursivelyDirty
.size());
131 ApplicationManager
.getApplication().runReadAction(new Runnable() {
133 convertPaths(filesDirty
, filesConverted
);
134 convertPaths(dirsRecursivelyDirty
, dirsConverted
);
137 final boolean haveStuff
= ((filesConverted
!= null) && (! filesConverted
.isEmpty())) ||
138 ((dirsConverted
!= null) && (! dirsConverted
.isEmpty()));
139 if (! haveStuff
) return false;
141 return takeDirt(new Consumer
<DirtBuilder
>() {
142 public void consume(final DirtBuilder dirt
) {
143 if (filesConverted
!= null) {
144 for (FilePathUnderVcs root
: filesConverted
) {
145 dirt
.addDirtyFile(root
);
148 if (dirsConverted
!= null) {
149 for (FilePathUnderVcs root
: dirsConverted
) {
150 dirt
.addDirtyDirRecursively(root
);
157 private boolean takeDirt(final Consumer
<DirtBuilder
> filler
) {
158 final Ref
<Boolean
> wasNotEmptyRef
= new Ref
<Boolean
>();
159 final Runnable runnable
= new Runnable() {
161 filler
.consume(myDirtBuilder
);
162 wasNotEmptyRef
.set(!myDirtBuilder
.isEmpty());
165 final LifeDrop lifeDrop
= myLife
.doIfAlive(runnable
);
167 if (lifeDrop
.isDone() && (! lifeDrop
.isSuspened()) && (Boolean
.TRUE
.equals(wasNotEmptyRef
.get()))) {
168 myChangeListManager
.scheduleUpdate();
170 // no sense in checking correct here any more: vcs is searched for asynchronously
171 return (! lifeDrop
.isDone());
174 private void convert(@Nullable final Collection
<VirtualFile
> from
, final Collection
<VcsRoot
> to
) {
176 for (VirtualFile vf
: from
) {
177 final AbstractVcs vcs
= myGuess
.getVcsForDirty(vf
);
179 to
.add(new VcsRoot(vcs
, vf
));
185 public boolean filesDirty(@Nullable final Collection
<VirtualFile
> filesDirty
, @Nullable final Collection
<VirtualFile
> dirsRecursivelyDirty
) {
186 final ArrayList
<VcsRoot
> filesConverted
= filesDirty
== null ?
null : new ArrayList
<VcsRoot
>(filesDirty
.size());
187 final ArrayList
<VcsRoot
> dirsConverted
= dirsRecursivelyDirty
== null ?
null : new ArrayList
<VcsRoot
>(dirsRecursivelyDirty
.size());
189 ApplicationManager
.getApplication().runReadAction(new Runnable() {
191 convert(filesDirty
, filesConverted
);
192 convert(dirsRecursivelyDirty
, dirsConverted
);
195 final boolean haveStuff
= ((filesConverted
!= null) && (! filesConverted
.isEmpty())) ||
196 ((dirsConverted
!= null) && (! dirsConverted
.isEmpty()));
197 if (! haveStuff
) return false;
199 return takeDirt(new Consumer
<DirtBuilder
>() {
200 public void consume(final DirtBuilder dirt
) {
201 if (filesConverted
!= null) {
202 for (VcsRoot root
: filesConverted
) {
203 dirt
.addDirtyFile(root
);
206 if (dirsConverted
!= null) {
207 for (VcsRoot root
: dirsConverted
) {
208 dirt
.addDirtyDirRecursively(root
);
215 public void fileDirty(final VirtualFile file
) {
216 final AbstractVcs vcs
= myGuess
.getVcsForDirty(file
);
217 if (vcs
== null) return;
218 final VcsRoot root
= new VcsRoot(vcs
, file
);
219 takeDirt(new Consumer
<DirtBuilder
>() {
220 public void consume(DirtBuilder dirtBuilder
) {
221 dirtBuilder
.addDirtyFile(root
);
226 public void fileDirty(final FilePath file
) {
227 final AbstractVcs vcs
= myGuess
.getVcsForDirty(file
);
228 if (vcs
== null) return;
229 final FilePathUnderVcs root
= new FilePathUnderVcs(file
, vcs
);
230 takeDirt(new Consumer
<DirtBuilder
>() {
231 public void consume(DirtBuilder dirtBuilder
) {
232 dirtBuilder
.addDirtyFile(root
);
237 public void dirDirtyRecursively(final VirtualFile dir
, final boolean scheduleUpdate
) {
238 dirDirtyRecursively(dir
);
241 public void dirDirtyRecursively(final VirtualFile dir
) {
242 final AbstractVcs vcs
= myGuess
.getVcsForDirty(dir
);
243 if (vcs
== null) return;
244 final VcsRoot root
= new VcsRoot(vcs
, dir
);
245 takeDirt(new Consumer
<DirtBuilder
>() {
246 public void consume(DirtBuilder dirtBuilder
) {
247 dirtBuilder
.addDirtyDirRecursively(root
);
252 public void dirDirtyRecursively(final FilePath path
) {
253 final AbstractVcs vcs
= myGuess
.getVcsForDirty(path
);
254 if (vcs
== null) return;
255 final FilePathUnderVcs root
= new FilePathUnderVcs(path
, vcs
);
256 takeDirt(new Consumer
<DirtBuilder
>() {
257 public void consume(DirtBuilder dirtBuilder
) {
258 dirtBuilder
.addDirtyDirRecursively(root
);
264 public VcsInvalidated
retrieveScopes() {
265 final Ref
<DirtBuilder
> dirtCopyRef
= new Ref
<DirtBuilder
>();
267 final LifeDrop lifeDrop
= myLife
.doIfAlive(new Runnable() {
269 dirtCopyRef
.set(new DirtBuilder(myDirtBuilder
));
270 myDirtBuilder
.reset();
274 if (lifeDrop
.isDone() && (! dirtCopyRef
.isNull())) {
275 return ApplicationManager
.getApplication().runReadAction(new Computable
<VcsInvalidated
>() {
276 public VcsInvalidated
compute() {
277 final Scopes scopes
= new Scopes(myProject
, myGuess
);
278 scopes
.takeDirt(dirtCopyRef
.get());
279 return scopes
.retrieveAndClear();
286 private String
toStringScopes(final VcsInvalidated vcsInvalidated
) {
287 final StringBuilder sb
= new StringBuilder();
288 sb
.append("is everything dirty: ").append(vcsInvalidated
.isEverythingDirty()).append(";\n");
289 for (VcsDirtyScope scope
: vcsInvalidated
.getScopes()) {
290 sb
.append("|\nFiles: ");
291 for (FilePath path
: scope
.getDirtyFiles()) {
292 sb
.append(path
).append('\n');
294 sb
.append("\nDirs: ");
295 for (FilePath filePath
: scope
.getRecursivelyDirtyDirectories()) {
296 sb
.append(filePath
).append('\n');
299 sb
.append("-------------");
300 return sb
.toString();
303 private static class LifeDrop
{
304 private final boolean myDone
;
305 private final boolean mySuspened
;
307 private LifeDrop(boolean done
, boolean suspened
) {
309 mySuspened
= suspened
;
312 public boolean isDone() {
316 public boolean isSuspened() {
321 private static class SynchronizedLife
{
322 private LifeStages myStage
;
323 private final Object myLock
;
324 private boolean mySuspended
;
326 private SynchronizedLife() {
327 myStage
= LifeStages
.NOT_BORN
;
328 myLock
= new Object();
332 synchronized (myLock
) {
333 myStage
= LifeStages
.ALIVE
;
337 public void kill(Runnable runnable
) {
338 synchronized (myLock
) {
339 myStage
= LifeStages
.DEAD
;
344 public void suspendMe() {
345 synchronized (myLock
) {
346 if (LifeStages
.ALIVE
.equals(myStage
)) {
352 public void releaseMe(final Runnable runnable
) {
353 synchronized (myLock
) {
354 if (LifeStages
.ALIVE
.equals(myStage
)) {
361 public LifeDrop
doIfAliveAndNotSuspended(final Runnable runnable
) {
362 synchronized (myLock
) {
363 synchronized (myLock
) {
364 if (LifeStages
.ALIVE
.equals(myStage
) && (! mySuspended
)) {
366 return new LifeDrop(true, mySuspended
);
368 return new LifeDrop(false, mySuspended
);
373 // allow work under inner lock: inner class, not wide scope
374 public LifeDrop
doIfAlive(final Runnable runnable
) {
375 synchronized (myLock
) {
376 if (LifeStages
.ALIVE
.equals(myStage
)) {
378 return new LifeDrop(true, mySuspended
);
380 return new LifeDrop(false, mySuspended
);
384 private static enum LifeStages
{