VCS: vcs dirty scope manager to use application-level vfs listener and check what...
[fedora-idea.git] / vcs-impl / src / com / intellij / openapi / vcs / changes / VcsDirtyScopeManagerImpl.java
blobe13ee181c34e93b4de0e7ec8b8845e5c8674ad1f
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;
23 /**
24 * @author max
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) {
36 myProject = project;
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()) {
47 myLife.born();
48 final AbstractVcs[] vcss = myVcsManager.getAllActiveVcss();
49 if (vcss.length > 0) {
50 markEverythingDirty();
53 else {
54 StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
55 public void run() {
56 myLife.born();
57 markEverythingDirty();
59 });
63 public void suspendMe() {
64 myLife.suspendMe();
67 public void reanimate() {
68 final Ref<Boolean> wasNotEmptyRef = new Ref<Boolean>();
69 myLife.releaseMe(new Runnable() {
70 public void run() {
71 wasNotEmptyRef.set(! myDirtBuilder.isEmpty());
73 });
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() {
83 public void run() {
84 myDirtBuilder.everythingDirty();
86 });
88 if (lifeDrop.isDone() && (! lifeDrop.isSuspened())) {
89 myChangeListManager.scheduleUpdate();
93 public void projectClosed() {
94 killSelf();
97 @NotNull @NonNls
98 public String getComponentName() {
99 return "VcsDirtyScopeManager";
102 public void initComponent() {}
104 private void killSelf() {
105 myLife.kill(new Runnable() {
106 public void run() {
107 myDirtBuilder.reset();
112 public void disposeComponent() {
113 killSelf();
116 private void convertPaths(@Nullable final Collection<FilePath> from, final Collection<FilePathUnderVcs> to) {
117 if (from != null) {
118 for (FilePath fp : from) {
119 final AbstractVcs vcs = myGuess.getVcsForDirty(fp);
120 if (vcs != null) {
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() {
132 public void run() {
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() {
160 public void run() {
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) {
175 if (from != null) {
176 for (VirtualFile vf : from) {
177 final AbstractVcs vcs = myGuess.getVcsForDirty(vf);
178 if (vcs != null) {
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() {
190 public void run() {
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);
263 @Nullable
264 public VcsInvalidated retrieveScopes() {
265 final Ref<DirtBuilder> dirtCopyRef = new Ref<DirtBuilder>();
267 final LifeDrop lifeDrop = myLife.doIfAlive(new Runnable() {
268 public void run() {
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();
283 return null;
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) {
308 myDone = done;
309 mySuspened = suspened;
312 public boolean isDone() {
313 return myDone;
316 public boolean isSuspened() {
317 return mySuspened;
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();
331 public void born() {
332 synchronized (myLock) {
333 myStage = LifeStages.ALIVE;
337 public void kill(Runnable runnable) {
338 synchronized (myLock) {
339 myStage = LifeStages.DEAD;
340 runnable.run();
344 public void suspendMe() {
345 synchronized (myLock) {
346 if (LifeStages.ALIVE.equals(myStage)) {
347 mySuspended = true;
352 public void releaseMe(final Runnable runnable) {
353 synchronized (myLock) {
354 if (LifeStages.ALIVE.equals(myStage)) {
355 mySuspended = false;
356 runnable.run();
361 public LifeDrop doIfAliveAndNotSuspended(final Runnable runnable) {
362 synchronized (myLock) {
363 synchronized (myLock) {
364 if (LifeStages.ALIVE.equals(myStage) && (! mySuspended)) {
365 runnable.run();
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)) {
377 runnable.run();
378 return new LifeDrop(true, mySuspended);
380 return new LifeDrop(false, mySuspended);
384 private static enum LifeStages {
385 NOT_BORN,
386 ALIVE,
387 DEAD