optimisation: way to get file boolean attributes in batch
[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 import org.jetbrains.annotations.NotNull;
29
30 import java.util.*;
31
32 /**
33  * @author max
34  */
35 public class RefreshWorker {
36   private final VirtualFile myRefreshRoot;
37   private final boolean myIsRecursive;
38   private final Queue<VirtualFile> myRefreshQueue = new Queue<VirtualFile>(100);
39
40   private final List<VFileEvent> myEvents = new ArrayList<VFileEvent>();
41
42   public RefreshWorker(final VirtualFile refreshRoot, final boolean isRecursive) {
43     myRefreshRoot = refreshRoot;
44     myIsRecursive = isRecursive;
45     myRefreshQueue.addLast(refreshRoot);
46   }
47
48   public void scan() {
49     final NewVirtualFile root = (NewVirtualFile)myRefreshRoot;
50     NewVirtualFileSystem delegate = root.getFileSystem();
51     int attributes = delegate.getBooleanAttributes(root, NewVirtualFileSystem.BA_EXISTS | NewVirtualFileSystem.BA_DIRECTORY);
52
53     if (root.isDirty() && (attributes & NewVirtualFileSystem.BA_EXISTS) == 0) {
54       scheduleDeletion(root);
55       root.markClean();
56     }
57     else {
58       boolean isDir = (attributes & NewVirtualFileSystem.BA_DIRECTORY) != 0;
59       if (isDir) {
60         delegate = PersistentFS.replaceWithNativeFS(delegate);
61       }
62
63       final PersistentFS persistence = (PersistentFS)ManagingFS.getInstance();
64
65       while (!myRefreshQueue.isEmpty()) {
66         final VirtualFileSystemEntry file = (VirtualFileSystemEntry)myRefreshQueue.pullFirst();
67         if (!file.isDirty()) continue;
68
69         if (file.isDirectory()) {
70           VirtualDirectoryImpl dir = (VirtualDirectoryImpl)file;
71           final boolean fullSync = dir.allChildrenLoaded();
72           if (fullSync) {
73             Set<String> currentNames = new HashSet<String>(Arrays.asList(persistence.list(file)));
74             Set<String> upToDateNames = new HashSet<String>(Arrays.asList(VfsUtil.filterNames(delegate.list(file))));
75
76             Set<String> newNames = new HashSet<String>(upToDateNames);
77             newNames.removeAll(currentNames);
78
79             Set<String> deletedNames = new HashSet<String>(currentNames);
80             deletedNames.removeAll(upToDateNames);
81
82             for (String name : deletedNames) {
83               scheduleDeletion(file.findChild(name));
84             }
85
86             for (String name : newNames) {
87               boolean isDirectory = delegate.isDirectory(new FakeVirtualFile(file, name));
88               scheduleCreation(file, name, isDirectory);
89             }
90
91             for (VirtualFile child : file.getChildren()) {
92               if (!deletedNames.contains(child.getName())) {
93                 int childAttributes = delegate.getBooleanAttributes(child, -1);
94                 scheduleChildRefresh(file, child, delegate, childAttributes);
95               }
96             }
97           }
98           else {
99             for (VirtualFile child : file.getCachedChildren()) {
100               int childAttributes = delegate.getBooleanAttributes(child, -1);
101               if ((childAttributes & NewVirtualFileSystem.BA_EXISTS) != 0) {
102                 scheduleChildRefresh(file, child, delegate, childAttributes);
103               }
104               else {
105                 scheduleDeletion(child);
106               }
107             }
108
109             final List<String> names = dir.getSuspiciousNames();
110             for (String name : names) {
111               if (name.isEmpty()) continue;
112
113               final VirtualFile fake = new FakeVirtualFile(file, name);
114               int attrs = delegate.getBooleanAttributes(fake, NewVirtualFileSystem.BA_EXISTS | NewVirtualFileSystem.BA_DIRECTORY);
115               if ((attrs & NewVirtualFileSystem.BA_EXISTS) != 0) {
116                 boolean isDira = (attrs & NewVirtualFileSystem.BA_DIRECTORY) != 0;
117                 scheduleCreation(file, name, isDira);
118               }
119             }
120           }
121         }
122         else {
123           long currentTimestamp = persistence.getTimeStamp(file);
124           long upToDateTimestamp = delegate.getTimeStamp(file);
125
126           if (currentTimestamp != upToDateTimestamp) {
127             scheduleUpdateContent(file);
128           }
129         }
130
131         boolean currentWritable = persistence.isWritable(file);
132         boolean upToDateWritable = delegate.isWritable(file);
133
134         if (currentWritable != upToDateWritable) {
135           scheduleWritableAttributeChange(file, currentWritable, upToDateWritable);
136         }
137
138         file.markClean();
139       }
140     }
141   }
142
143   private void scheduleChildRefresh(@NotNull VirtualFileSystemEntry file,
144                                     @NotNull VirtualFile child,
145                                     @NotNull NewVirtualFileSystem delegate,
146                                     @NewVirtualFileSystem.FileBooleanAttributes int childAttributes) {
147     final boolean currentIsDirectory = child.isDirectory();
148     final boolean currentIsSymlink = child.isSymLink();
149     final boolean currentIsSpecial = child.isSpecialFile();
150     final boolean upToDateIsDirectory = (childAttributes & NewVirtualFileSystem.BA_DIRECTORY) != 0;
151     final boolean upToDateIsSymlink = delegate.isSymLink(child);
152     final boolean upToDateIsSpecial = (childAttributes & (NewVirtualFileSystem.BA_REGULAR | NewVirtualFileSystem.BA_DIRECTORY | NewVirtualFileSystem.BA_EXISTS)) == NewVirtualFileSystem.BA_EXISTS;
153     if (currentIsDirectory != upToDateIsDirectory || currentIsSymlink != upToDateIsSymlink || currentIsSpecial != upToDateIsSpecial) {
154       scheduleDeletion(child);
155       scheduleReCreation(file, child.getName(), upToDateIsDirectory);
156     }
157     else if (myIsRecursive || !currentIsDirectory) {
158       myRefreshQueue.addLast(child);
159     }
160   }
161
162   private void scheduleWritableAttributeChange(final VirtualFileSystemEntry file,
163                                                final boolean currentWritable,
164                                                final boolean upToDateWritable) {
165     myEvents.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_WRITABLE, currentWritable, upToDateWritable, true));
166   }
167
168   private void scheduleUpdateContent(final VirtualFileSystemEntry file) {
169     myEvents.add(new VFileContentChangeEvent(null, file, file.getModificationStamp(), -1, true));
170   }
171
172   private void scheduleCreation(final VirtualFileSystemEntry parent, final String childName, final boolean isDirectory) {
173     myEvents.add(new VFileCreateEvent(null, parent, childName, isDirectory, true, false));
174   }
175
176   private void scheduleReCreation(final VirtualFileSystemEntry parent, final String childName, final boolean isDirectory) {
177     myEvents.add(new VFileCreateEvent(null, parent, childName, isDirectory, true, true));
178   }
179
180   private void scheduleDeletion(final VirtualFile file) {
181     if (file == null) return;
182     myEvents.add(new VFileDeleteEvent(null, file, true));
183   }
184
185   public List<VFileEvent> getEvents() {
186     return myEvents;
187   }
188 }