39f9da022ab9bc7c22355d3d0682697a794505b0
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / vfs / newvfs / persistent / RefreshWorker.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.openapi.vfs.newvfs.persistent;
17
18 import com.intellij.openapi.vfs.VfsUtil;
19 import com.intellij.openapi.vfs.VirtualFile;
20 import com.intellij.openapi.vfs.newvfs.ManagingFS;
21 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
22 import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem;
23 import com.intellij.openapi.vfs.newvfs.events.*;
24 import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile;
25 import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
26 import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry;
27 import com.intellij.util.containers.Queue;
28
29 import java.util.*;
30
31 /**
32  * @author max
33  */
34 public class RefreshWorker {
35   private final VirtualFile myRefreshRoot;
36   private final boolean myIsRecursive;
37   private final Queue<VirtualFile> myRefreshQueue = new Queue<VirtualFile>(100);
38
39   private final List<VFileEvent> myEvents = new ArrayList<VFileEvent>();
40
41   public RefreshWorker(final VirtualFile refreshRoot, final boolean isRecursive) {
42     myRefreshRoot = refreshRoot;
43     myIsRecursive = isRecursive;
44     myRefreshQueue.addLast(refreshRoot);
45   }
46
47   public void scan() {
48     final NewVirtualFile root = (NewVirtualFile)myRefreshRoot;
49     NewVirtualFileSystem delegate = root.getFileSystem();
50     if (root.isDirty() && !delegate.exists(root)) {
51       scheduleDeletion(root);
52       root.markClean();
53     }
54     else {
55       if (root.isDirectory()) {
56         delegate = PersistentFS.replaceWithNativeFS(delegate);
57       }
58
59       final PersistentFS persistence = (PersistentFS)ManagingFS.getInstance();
60
61       while (!myRefreshQueue.isEmpty()) {
62         final VirtualFileSystemEntry file = (VirtualFileSystemEntry)myRefreshQueue.pullFirst();
63         if (!file.isDirty()) continue;
64
65         if (file.isDirectory()) {
66           VirtualDirectoryImpl dir = (VirtualDirectoryImpl)file;
67           final boolean fullSync = dir.allChildrenLoaded();
68           if (fullSync) {
69             Set<String> currentNames = new HashSet<String>(Arrays.asList(persistence.list(file)));
70             Set<String> upToDateNames = new HashSet<String>(Arrays.asList(VfsUtil.filterNames(delegate.list(file))));
71
72             Set<String> newNames = new HashSet<String>(upToDateNames);
73             newNames.removeAll(currentNames);
74
75             Set<String> deletedNames = new HashSet<String>(currentNames);
76             deletedNames.removeAll(upToDateNames);
77
78             for (String name : deletedNames) {
79               scheduleDeletion(file.findChild(name));
80             }
81
82             for (String name : newNames) {
83               boolean isDirectory = delegate.isDirectory(new FakeVirtualFile(file, name));
84               scheduleCreation(file, name, isDirectory);
85             }
86
87             for (VirtualFile child : file.getChildren()) {
88               if (!deletedNames.contains(child.getName())) {
89                 scheduleChildRefresh(file, child, delegate);
90               }
91             }
92           }
93           else {
94             for (VirtualFile child : file.getCachedChildren()) {
95               if (delegate.exists(child)) {
96                 scheduleChildRefresh(file, child, delegate);
97               }
98               else {
99                 scheduleDeletion(child);
100               }
101             }
102
103             final List<String> names = dir.getSuspiciousNames();
104             for (String name : names) {
105               if (name.length() == 0) continue;
106
107               final VirtualFile fake = new FakeVirtualFile(file, name);
108               if (delegate.exists(fake)) {
109                 scheduleCreation(file, name, delegate.isDirectory(fake));
110               }
111             }
112           }
113         }
114         else {
115           long currentTimestamp = persistence.getTimeStamp(file);
116           long upToDateTimestamp = delegate.getTimeStamp(file);
117
118           if (currentTimestamp != upToDateTimestamp) {
119             scheduleUpdateContent(file);
120           }
121         }
122
123         boolean currentWritable = persistence.isWritable(file);
124         boolean upToDateWritable = delegate.isWritable(file);
125
126         if (currentWritable != upToDateWritable) {
127           scheduleWritableAttributeChange(file, currentWritable, upToDateWritable);
128         }
129
130         file.markClean();
131       }
132     }
133   }
134
135   private void scheduleChildRefresh(final VirtualFileSystemEntry file, final VirtualFile child, final NewVirtualFileSystem delegate) {
136     final boolean currentIsDirectory = child.isDirectory();
137     final boolean currentIsSymlink = child.isSymLink();
138     final boolean currentIsSpecial = child.isSpecialFile();
139     final boolean upToDateIsDirectory = delegate.isDirectory(child);
140     final boolean upToDateIsSymlink = delegate.isSymLink(child);
141     final boolean upToDateIsSpecial = delegate.isSpecialFile(child);
142     if (currentIsDirectory != upToDateIsDirectory || currentIsSymlink != upToDateIsSymlink || currentIsSpecial != upToDateIsSpecial) {
143       scheduleDeletion(child);
144       scheduleReCreation(file, child.getName(), upToDateIsDirectory);
145     }
146     else if (myIsRecursive || !currentIsDirectory) {
147       myRefreshQueue.addLast(child);
148     }
149   }
150
151   private void scheduleWritableAttributeChange(final VirtualFileSystemEntry file,
152                                                final boolean currentWritable,
153                                                final boolean upToDateWritable) {
154     myEvents.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_WRITABLE, currentWritable, upToDateWritable, true));
155   }
156
157   private void scheduleUpdateContent(final VirtualFileSystemEntry file) {
158     myEvents.add(new VFileContentChangeEvent(null, file, file.getModificationStamp(), -1, true));
159   }
160
161   private void scheduleCreation(final VirtualFileSystemEntry parent, final String childName, final boolean isDirectory) {
162     myEvents.add(new VFileCreateEvent(null, parent, childName, isDirectory, true, false));
163   }
164
165   private void scheduleReCreation(final VirtualFileSystemEntry parent, final String childName, final boolean isDirectory) {
166     myEvents.add(new VFileCreateEvent(null, parent, childName, isDirectory, true, true));
167   }
168
169   private void scheduleDeletion(final VirtualFile file) {
170     if (file == null) return;
171     myEvents.add(new VFileDeleteEvent(null, file, true));
172   }
173
174   public List<VFileEvent> getEvents() {
175     return myEvents;
176   }
177 }