virt dir more concurrency
[fedora-idea.git] / platform-impl / src / com / intellij / openapi / vfs / newvfs / impl / VirtualDirectoryImpl.java
blob14b6324d210916ea7f62a695c78177840cea9f79
1 /*
2 * @author max
3 */
4 package com.intellij.openapi.vfs.newvfs.impl;
6 import com.intellij.openapi.vfs.VirtualFile;
7 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
8 import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem;
9 import com.intellij.openapi.vfs.newvfs.RefreshQueue;
10 import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
11 import com.intellij.util.ArrayUtil;
12 import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
13 import gnu.trove.THashMap;
14 import gnu.trove.THashSet;
15 import org.jetbrains.annotations.NotNull;
16 import org.jetbrains.annotations.Nullable;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.util.*;
23 public class VirtualDirectoryImpl extends VirtualFileSystemEntry {
24 private final NewVirtualFileSystem myFS;
25 private Object myChildren; // Either HashMap<String, VFile> or VFile[]
27 public VirtualDirectoryImpl(final String name, final VirtualDirectoryImpl parent, final NewVirtualFileSystem fs, final int id) {
28 super(name, parent, id);
29 myFS = fs;
32 @NotNull
33 public NewVirtualFileSystem getFileSystem() {
34 return myFS;
37 @Nullable
38 private NewVirtualFile findChild(final String name, final boolean createIfNotFound) {
39 if (name.length() == 0) {
40 return null;
43 final VirtualFile[] a = asArray();
44 if (a != null) {
45 for (VirtualFile file : a) {
46 if (namesEqual(name, file.getName())) return (NewVirtualFile)file;
49 return createIfNotFound ? createAndFindChildWithEventFire(name) : null;
52 final Map<String, VirtualFile> map;
53 final VirtualFile file;
54 synchronized (this) {
55 map = ensureAsMap();
56 file = map.get(name);
59 if (file == NullVirtualFile.INSTANCE) {
60 return createIfNotFound ? createAndFindChildWithEventFire(name) : null;
63 if (file != null) return (NewVirtualFile)file;
65 final NewVirtualFileSystem delegate = getFileSystem();
66 VirtualFile fake = new FakeVirtualFile(name, this);
67 String name1 = delegate.getCanonicallyCasedName(fake);
69 int id = ourPersistence.getId(this, name1);
70 if (id > 0) {
71 synchronized (this) {
72 VirtualFile alreadyCreated = map.get(name1);
73 if (alreadyCreated != null) {
74 if (alreadyCreated == NullVirtualFile.INSTANCE) {
75 return createIfNotFound ? createAndFindChildWithEventFire(name) : null;
77 return (NewVirtualFile)alreadyCreated;
79 NewVirtualFile child = createChild(name1, id);
80 map.put(name1, child);
81 return child;
85 synchronized (this) {
86 if (myChildren instanceof Map) {
87 ensureAsMap().put(name1, NullVirtualFile.INSTANCE);
91 return null;
94 public VirtualFileSystemEntry createChild(String name, int id) {
95 final NewVirtualFileSystem fs = getFileSystem();
96 final VirtualFileSystemEntry child = ourPersistence.isDirectory(id) ?
97 new VirtualDirectoryImpl(name, this, fs, id) :
98 new VirtualFileImpl(name, this, id);
99 if (fs.markNewFilesAsDirty()) {
100 child.markDirty();
103 return child;
106 @Nullable
107 private NewVirtualFile createAndFindChildWithEventFire(final String name) {
108 final NewVirtualFileSystem delegate = getFileSystem();
109 VirtualFile fake = new FakeVirtualFile(name, this);
110 if (delegate.exists(fake)) {
111 final String realName = delegate.getCanonicallyCasedName(fake);
112 VFileCreateEvent event = new VFileCreateEvent(null, this, realName, delegate.isDirectory(fake), true);
113 RefreshQueue.getInstance().processSingleEvent(event);
114 return findChild(realName);
116 else {
117 return null;
121 @Nullable
122 public NewVirtualFile refreshAndFindChild(final String name) {
123 return findChild(name, true);
126 @Nullable
127 public synchronized NewVirtualFile findChildIfCached(final String name) {
128 final VirtualFile[] a = asArray();
129 if (a != null) {
130 for (VirtualFile file : a) {
131 if (namesEqual(name, file.getName())) return (NewVirtualFile)file;
134 return null;
137 final Map<String, VirtualFile> map = asMap();
138 if (map != null) {
139 final VirtualFile file = map.get(name);
140 return file instanceof NewVirtualFile ? (NewVirtualFile)file : null;
143 return null;
146 @NotNull
147 public synchronized Collection<VirtualFile> getInDbChildren() {
148 if (myChildren instanceof VirtualFile[]) {
149 return Arrays.asList((VirtualFile[])myChildren);
152 if (!ourPersistence.wereChildrenAccessed(this)) {
153 return Collections.emptyList();
156 if (ourPersistence.areChildrenLoaded(this)) {
157 return Arrays.asList(getChildren());
160 final String[] names = ourPersistence.listPersisted(this);
161 for (String name : names) {
162 findChild(name, false);
165 return ensureAsMap().values();
168 @NotNull
169 public synchronized VirtualFile[] getChildren() {
170 if (myChildren instanceof VirtualFile[]) {
171 return (VirtualFile[])myChildren;
174 final int[] childrenIds = ourPersistence.listIds(this);
175 VirtualFile[] children;
176 if (childrenIds.length == 0) {
177 children = EMPTY_ARRAY;
179 else {
180 children = new VirtualFile[childrenIds.length];
181 final Map<String, VirtualFile> map = asMap();
182 for (int i = 0; i < children.length; i++) {
183 final int childId = childrenIds[i];
184 final String name = ourPersistence.getName(childId);
185 VirtualFile child = map != null ? map.get(name) : null;
187 children[i] = child != null && child != NullVirtualFile.INSTANCE ? child : createChild(name, childId);
191 if (getId() > 0) {
192 myChildren = children;
195 return children;
198 @Nullable
199 public NewVirtualFile findChild(@NotNull final String name) {
200 return findChild(name, false);
203 @Nullable
204 private synchronized VirtualFile[] asArray() {
205 if (myChildren instanceof VirtualFile[]) return (VirtualFile[])myChildren;
206 return null;
209 @Nullable
210 @SuppressWarnings({"unchecked"})
211 private synchronized Map<String, VirtualFile> asMap() {
212 if (myChildren instanceof Map) return (Map<String, VirtualFile>)myChildren;
213 return null;
216 @NotNull
217 @SuppressWarnings({"unchecked"})
218 private synchronized Map<String, VirtualFile> ensureAsMap() {
219 assert !(myChildren instanceof VirtualFile[]);
221 if (myChildren == null) {
222 myChildren = createMap();
225 return (Map<String, VirtualFile>)myChildren;
228 public synchronized void addChild(VirtualFile file) {
229 final VirtualFile[] a = asArray();
230 if (a != null) {
231 myChildren = ArrayUtil.append(a, file);
233 else {
234 final Map<String, VirtualFile> m = ensureAsMap();
235 m.put(file.getName(), file);
239 public synchronized void removeChild(VirtualFile file) {
240 final VirtualFile[] a = asArray();
241 if (a != null) {
242 myChildren = ArrayUtil.remove(a, file);
244 else {
245 final Map<String, VirtualFile> m = ensureAsMap();
246 m.put(file.getName(), NullVirtualFile.INSTANCE);
250 public synchronized boolean allChildrenLoaded() {
251 return myChildren instanceof VirtualFile[];
254 public synchronized List<String> getSuspicousNames() {
255 final Map<String, VirtualFile> map = asMap();
256 if (map == null) return Collections.emptyList();
258 List<String> names = new ArrayList<String>();
259 for (Map.Entry<String, VirtualFile> entry : map.entrySet()) {
260 if (entry.getValue() == NullVirtualFile.INSTANCE) {
261 names.add(entry.getKey());
265 return names;
268 public boolean isDirectory() {
269 return true;
272 @NotNull
273 public synchronized Collection<VirtualFile> getCachedChildren() {
274 final Map<String, VirtualFile> map = asMap();
275 if (map != null) {
276 Set<VirtualFile> files = new THashSet<VirtualFile>(map.values());
277 files.remove(NullVirtualFile.INSTANCE);
278 return files;
281 final VirtualFile[] a = asArray();
282 if (a != null) return Arrays.asList(a);
284 return Collections.emptyList();
287 private boolean namesEqual(final String name1, final String name2) {
288 return getFileSystem().isCaseSensitive() ? name1.equals(name2) : name1.equalsIgnoreCase(name2);
291 private Map<String, VirtualFile> createMap() {
292 return getFileSystem().isCaseSensitive()
293 ? new THashMap<String, VirtualFile>()
294 : new THashMap<String, VirtualFile>(CaseInsensitiveStringHashingStrategy.INSTANCE);
297 public synchronized void cleanupCachedChildren() {
298 // For tests only!!!
299 myChildren = null;
302 public InputStream getInputStream() throws IOException {
303 throw new IOException("getInputStream() must not be called against a directory: " + getUrl());
306 @NotNull
307 public OutputStream getOutputStream(final Object requestor, final long newModificationStamp, final long newTimeStamp) throws IOException {
308 throw new IOException("getOutputStream() must not be called against a directory: " + getUrl());