Directory recursive pointers support (for libraries jar directories)

This commit is contained in:
Alexey Kudravtsev
2017-09-19 16:12:14 +03:00
parent e7a59cb711
commit 7a2de543a2
9 changed files with 265 additions and 69 deletions
@@ -35,4 +35,12 @@ public interface VirtualFilePointer {
String getPresentableUrl();
boolean isValid();
/**
* @return true if {@link VirtualFilePointerListener#validityChanged(VirtualFilePointer[])}
* should fired for every change beneath this directory (Used for jar directories in libraries)
*/
default boolean isRecursive() {
return false;
}
}
@@ -17,6 +17,7 @@ package com.intellij.openapi.vfs.pointers;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
@@ -36,15 +37,19 @@ public interface VirtualFilePointerContainer {
void remove(@NotNull VirtualFilePointer pointer);
@NotNull List<VirtualFilePointer> getList();
@NotNull
List<VirtualFilePointer> getList();
void addAll(@NotNull VirtualFilePointerContainer that);
@NotNull String[] getUrls();
@NotNull
String[] getUrls();
@NotNull VirtualFile[] getFiles();
@NotNull
VirtualFile[] getFiles();
@NotNull VirtualFile[] getDirectories();
@NotNull
VirtualFile[] getDirectories();
@Nullable
VirtualFilePointer findByUrl(@NotNull String url);
@@ -53,6 +58,15 @@ public interface VirtualFilePointerContainer {
int size();
/**
* For example, to read from the xml below, call {@code readExternal(myRootTag, "childElementName"); }
* <pre>{@code
* <myroot>
* <childElementName url="xxx1"/>
* <childElementName url="xxx2"/>
* </myroot>
* }</pre>
*/
void readExternal(@NotNull Element rootChild, @NotNull String childElementName) throws InvalidDataException;
void writeExternal(@NotNull Element element, @NotNull String childElementName);
@@ -61,7 +75,28 @@ public interface VirtualFilePointerContainer {
void moveDown(@NotNull String url);
@NotNull VirtualFilePointerContainer clone(@NotNull Disposable parent);
@NotNull
VirtualFilePointerContainer clone(@NotNull Disposable parent);
@NotNull VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener);
@NotNull
VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener);
/**
* Adds {@code directory} as a root of jar files.
* After that the {@link #getFiles()} and {@link #getUrls()} etc will return jar files in this directory
* (and, if {@code recursively} was set, the jar files in all-subdirectories).
*/
void addJarDirectory(@NotNull String directoryUrl, boolean recursively);
/**
* Removes {@code directory} from the roots of jar files.
* After that the {@link #getFiles()} and {@link #getUrls()} etc will not return jar files in this directory anymore.
* @return true if removed
*/
boolean removeJarDirectory(@NotNull String directoryUrl);
/**
* Returns list of (directory url, isRecursive) which were added via {@link #addJarDirectory(String, boolean)} }
*/
@NotNull
List<Pair<String, Boolean>> getJarDirectories();
}
@@ -42,4 +42,10 @@ public abstract class VirtualFilePointerManager extends SimpleModificationTracke
@NotNull
public abstract VirtualFilePointerContainer createContainer(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener);
@NotNull
public abstract VirtualFilePointer createDirectoryPointer(@NotNull String url,
boolean recursively,
@NotNull Disposable parent,
@NotNull VirtualFilePointerListener listener);
}
@@ -59,4 +59,12 @@ public class CoreVirtualFilePointerManager extends VirtualFilePointerManager {
public VirtualFilePointerContainer createContainer(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) {
return new VirtualFilePointerContainerImpl(this, parent, listener);
}
@NotNull
@Override
public VirtualFilePointer createDirectoryPointer(@NotNull String url,
boolean recursively,
@NotNull Disposable parent, @NotNull VirtualFilePointerListener listener) {
return create(url, parent, listener);
}
}
@@ -15,21 +15,28 @@
*/
package com.intellij.openapi.vfs.impl;
import com.intellij.ide.highlighter.ArchiveFileType;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TraceableDisposable;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.URLUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -45,6 +52,8 @@ import java.util.List;
class VirtualFilePointerContainerImpl extends TraceableDisposable implements VirtualFilePointerContainer, Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer");
@NotNull private final List<VirtualFilePointer> myList = ContainerUtil.createLockFreeCopyOnWriteList();
@NotNull private final List<VirtualFilePointer> myJarDirectories = ContainerUtil.createLockFreeCopyOnWriteList();
@NotNull private final List<VirtualFilePointer> myJarRecursiveDirectories = ContainerUtil.createLockFreeCopyOnWriteList();
@NotNull private final VirtualFilePointerManager myVirtualFilePointerManager;
@NotNull private final Disposable myParent;
private final VirtualFilePointerListener myListener;
@@ -56,7 +65,6 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
VirtualFilePointerContainerImpl(@NotNull VirtualFilePointerManager manager,
@NotNull Disposable parentDisposable,
@Nullable VirtualFilePointerListener listener) {
//noinspection HardCodedStringLiteral
super(TRACE_CREATION && !ApplicationInfoImpl.isInStressTest());
myVirtualFilePointerManager = manager;
myParent = parentDisposable;
@@ -64,8 +72,8 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
}
@Override
public void readExternal(@NotNull final Element rootChild, @NotNull final String childElements) throws InvalidDataException {
final List<Element> urls = rootChild.getChildren(childElements);
public void readExternal(@NotNull final Element rootChild, @NotNull final String childName) throws InvalidDataException {
final List<Element> urls = rootChild.getChildren(childName);
for (Element url : urls) {
final String urlAttribute = url.getAttributeValue(URL_ATTR);
if (urlAttribute == null) throw new InvalidDataException("path element without url");
@@ -119,16 +127,14 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
public void add(@NotNull VirtualFile file) {
assert !myDisposed;
dropCaches();
final VirtualFilePointer pointer = create(file);
myList.add(pointer);
myList.add(create(file));
}
@Override
public void add(@NotNull String url) {
assert !myDisposed;
dropCaches();
final VirtualFilePointer pointer = create(url);
myList.add(pointer);
myList.add(create(url));
}
@Override
@@ -154,6 +160,12 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
for (final VirtualFilePointer pointer : that.getList()) {
myList.add(duplicate(pointer));
}
for (VirtualFilePointer jarDirectory : ((VirtualFilePointerContainerImpl)that).myJarDirectories) {
myJarDirectories.add(duplicate(jarDirectory));
}
for (VirtualFilePointer jarRecursiveDirectory : ((VirtualFilePointerContainerImpl)that).myJarRecursiveDirectories) {
myJarRecursiveDirectories.add(duplicate(jarRecursiveDirectory));
}
}
private void dropCaches() {
@@ -181,7 +193,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
@NotNull
private Trinity<String[], VirtualFile[], VirtualFile[]> cacheThings() {
Trinity<String[], VirtualFile[], VirtualFile[]> result;
if (myList.isEmpty()) {
if (myList.isEmpty() && myJarDirectories.isEmpty() && myJarRecursiveDirectories.isEmpty()) {
result = EMPTY;
}
else {
@@ -203,6 +215,40 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
}
}
}
for (VirtualFilePointer jarDirectoryPtr : myJarDirectories) {
VirtualFile jarDirectory = jarDirectoryPtr.getFile();
if (jarDirectory != null) {
VirtualFile[] children = jarDirectory.getChildren();
for (VirtualFile file : children) {
if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) {
VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR);
if (jarRoot != null) {
cachedFiles.add(jarRoot);
cachedDirectories.add(file);
}
}
}
}
}
for (VirtualFilePointer jarDirectoryPtr : myJarRecursiveDirectories) {
VirtualFile jarDirectory = jarDirectoryPtr.getFile();
if (jarDirectory != null) {
VfsUtilCore.visitChildrenRecursively(jarDirectory, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile file) {
if (!file.isDirectory() && FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ArchiveFileType.INSTANCE) {
VirtualFile jarRoot = StandardFileSystems.jar().findFileByPath(file.getPath() + URLUtil.JAR_SEPARATOR);
if (jarRoot != null) {
cachedFiles.add(jarRoot);
cachedDirectories.add(file);
return false;
}
}
return true;
}
});
}
}
VirtualFile[] directories = VfsUtilCore.toVirtualFileArray(cachedDirectories);
VirtualFile[] files = allFilesAreDirs ? directories : VfsUtilCore.toVirtualFileArray(cachedFiles);
String[] urlsArray = ArrayUtil.toStringArray(cachedUrls);
@@ -229,7 +275,7 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
@Nullable
public VirtualFilePointer findByUrl(@NotNull String url) {
assert !myDisposed;
for (VirtualFilePointer pointer : myList) {
for (VirtualFilePointer pointer : ContainerUtil.concat(myList, myJarDirectories, myJarRecursiveDirectories)) {
if (url.equals(pointer.getUrl())) return pointer;
}
return null;
@@ -243,16 +289,18 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
@Override
public int size() {
return myList.size();
return myList.size()+myJarDirectories.size()+myJarRecursiveDirectories.size();
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof VirtualFilePointerContainerImpl)) return false;
final VirtualFilePointerContainerImpl virtualFilePointerContainer = (VirtualFilePointerContainerImpl)o;
VirtualFilePointerContainerImpl impl = (VirtualFilePointerContainerImpl)o;
return myList.equals(virtualFilePointerContainer.myList);
return myList.equals(impl.myList) &&
myJarDirectories.equals(impl.myJarDirectories) &&
myJarRecursiveDirectories.equals(impl.myJarRecursiveDirectories);
}
public int hashCode() {
@@ -278,7 +326,9 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
@NonNls
@Override
public String toString() {
return "VFPContainer: " + myList/*+"; parent:"+myParent*/;
return "VFPContainer: " + myList
+ (myJarDirectories.isEmpty() ? "" : ", jars: "+myJarDirectories)
+ (myJarRecursiveDirectories.isEmpty() ? "" : ", jars(recursively): "+myJarRecursiveDirectories);
}
@Override
@@ -291,10 +341,16 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
@NotNull
public VirtualFilePointerContainer clone(@NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) {
assert !myDisposed;
VirtualFilePointerContainer clone = myVirtualFilePointerManager.createContainer(parent, listener);
VirtualFilePointerContainerImpl clone = (VirtualFilePointerContainerImpl)myVirtualFilePointerManager.createContainer(parent, listener);
for (VirtualFilePointer pointer : myList) {
clone.add(pointer.getUrl());
}
for (VirtualFilePointer jarDirectoryPtr : myJarDirectories) {
clone.addJarDirectory(jarDirectoryPtr.getUrl(), false);
}
for (VirtualFilePointer jarDirectoryPtr : myJarRecursiveDirectories) {
clone.addJarDirectory(jarDirectoryPtr.getUrl(), true);
}
return clone;
}
@@ -305,4 +361,27 @@ class VirtualFilePointerContainerImpl extends TraceableDisposable implements Vir
kill(null);
clear();
}
@Override
public void addJarDirectory(@NotNull String directoryUrl, boolean recursively) {
VirtualFilePointer pointer = myVirtualFilePointerManager.createDirectoryPointer(directoryUrl, recursively, myParent, myListener);
(recursively ? myJarRecursiveDirectories : myJarDirectories).add(pointer);
dropCaches();
}
@Override
public boolean removeJarDirectory(@NotNull String directoryUrl) {
dropCaches();
//noinspection NonShortCircuitBooleanExpression
return myJarDirectories.removeIf(ptr-> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl))
| myJarRecursiveDirectories.removeIf(ptr-> FileUtil.pathsEqual(ptr.getUrl(), directoryUrl));
}
@NotNull
@Override
public List<Pair<String, Boolean>> getJarDirectories() {
List<Pair<String, Boolean>> jars = ContainerUtil.map(myJarDirectories, ptr -> Pair.create(ptr.getUrl(), false));
List<Pair<String, Boolean>> recJars = ContainerUtil.map(myJarRecursiveDirectories, ptr -> Pair.create(ptr.getUrl(), true));
return ContainerUtil.concat(jars, recJars);
}
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
@@ -51,11 +52,12 @@ class FilePointerPartNode {
int pointersUnder; // number of alive pointers in this node plus all nodes beneath
private static final VirtualFileManager ourFileManager = VirtualFileManager.getInstance();
FilePointerPartNode(@NotNull String part, FilePointerPartNode parent, Pair<VirtualFile,String> fileAndUrl) {
FilePointerPartNode(@NotNull String part, FilePointerPartNode parent, Pair<VirtualFile,String> fileAndUrl, int pointersToStore) {
this.part = part;
this.parent = parent;
children = EMPTY_ARRAY;
myFileAndUrl = fileAndUrl;
pointersUnder = pointersToStore;
}
@Override
@@ -67,8 +69,9 @@ class FilePointerPartNode {
private int position(@Nullable VirtualFile parent,
@Nullable CharSequence parentName,
boolean separator,
@NotNull CharSequence childName,
@NotNull FilePointerPartNode[] outNode) {
@NotNull CharSequence childName, int childStart, int childEnd,
@NotNull FilePointerPartNode[] outNode,
@NotNull List<FilePointerPartNode> dirs) {
int partStart;
if (parent == null) {
partStart = 0;
@@ -77,52 +80,77 @@ class FilePointerPartNode {
else {
VirtualFile gParent = parent.getParent();
CharSequence gParentName = gParent == null ? null : gParent.getNameSequence();
partStart = position(gParent, gParentName, gParentName != null && !StringUtil.equals(gParentName, "/"), parentName, outNode);
partStart = position(gParent, gParentName, gParentName != null && !StringUtil.equals(gParentName, "/"), parentName, 0, parentName.length(), outNode, dirs);
if (partStart == -1) return -1;
}
FilePointerPartNode found = outNode[0];
boolean childSeparator = false;
if (separator) {
if (partStart == outNode[0].part.length()) {
if (partStart == found.part.length()) {
childSeparator = true;
}
else {
int sepIndex = indexOfFirstDifferentChar("/", 0, outNode[0].part, partStart);
int sepIndex = indexOfFirstDifferentChar("/", 0, found.part, partStart);
if (sepIndex != 1) return -1;
partStart++;
}
}
int index = indexOfFirstDifferentChar(childName, 0, outNode[0].part, partStart);
int index = indexOfFirstDifferentChar(childName, childStart, found.part, partStart);
if (index == childEnd) {
addRecursiveDirectoryPtr(dirs);
if (index == childName.length()) {
return partStart+index;
}
if (partStart + index == outNode[0].part.length()) {
if (partStart + index-childStart == found.part.length()) {
// go to children
for (FilePointerPartNode child : outNode[0].children) {
int childPos = child.position(null, null, childSeparator, childName.subSequence(index, childName.length()), outNode);
if (childPos != -1) return childPos;
for (FilePointerPartNode child : found.children) {
int childPos = child.position(null, null, childSeparator, childName, index, childEnd, outNode, dirs);
if (childPos != -1) {
addRecursiveDirectoryPtr(dirs);
return childPos;
}
}
}
// else there is no match
return -1;
}
private void addRecursiveDirectoryPtr(@NotNull List<FilePointerPartNode> dirs) {
if(hasRecursiveDirectoryPointer() && (dirs.isEmpty() || dirs.get(dirs.size()-1) != this)) {
dirs.add(this);
}
}
// appends to "out" all nodes under this node whose path (beginning from this node) starts in prefix.subSequence(start), then parent.getPath(), then childName
void addPointersUnder(@Nullable VirtualFile parent,
boolean separator,
@NotNull CharSequence childName,
@NotNull List<FilePointerPartNode> out) {
FilePointerPartNode[] outNode = new FilePointerPartNode[1];
void addRelevantPointersFrom(@Nullable VirtualFile parent,
boolean separator,
@NotNull CharSequence childName,
@NotNull List<FilePointerPartNode> out) {
CharSequence parentName = parent == null ? null : parent.getNameSequence();
int position = position(parent, parentName, separator, childName, outNode);
FilePointerPartNode[] outNode = new FilePointerPartNode[1];
int position = position(parent, parentName, separator, childName, 0, childName.length(), outNode, out);
if (position != -1) {
FilePointerPartNode node = outNode[0];
addAllPointersUnder(node, out);
}
}
private boolean hasRecursiveDirectoryPointer() {
if (leaves == null) return false;
if (leaves instanceof VirtualFilePointer) {
return ((VirtualFilePointer)leaves).isRecursive();
}
VirtualFilePointerImpl[] leaves = (VirtualFilePointerImpl[])this.leaves;
for (VirtualFilePointerImpl leaf : leaves) {
if (leaf.isRecursive()) return true;
}
return false;
}
private static void addAllPointersUnder(@NotNull FilePointerPartNode node, @NotNull List<FilePointerPartNode> out) {
if (node.leaves != null) {
out.add(node);
@@ -190,8 +218,7 @@ class FilePointerPartNode {
}
// cannot insert to children, create child node manually
String pathRest = path.substring(index);
FilePointerPartNode newNode = new FilePointerPartNode(pathRest, this, fileAndUrl);
newNode.pointersUnder += pointersToStore;
FilePointerPartNode newNode = new FilePointerPartNode(pathRest, this, fileAndUrl, pointersToStore);
children = ArrayUtil.append(children, newNode);
pointersUnder += pointersToStore;
return newNode;
@@ -200,17 +227,13 @@ class FilePointerPartNode {
// try to make "/" start the splitted part
if (index > start + 1 && index != path.length() && path.charAt(index - 1) == '/') index--;
String pathRest = path.substring(index);
FilePointerPartNode newNode = pathRest.isEmpty() ? this : new FilePointerPartNode(pathRest, this, fileAndUrl);
if (newNode != this) {
newNode.pointersUnder = pointersToStore;
}
FilePointerPartNode newNode = pathRest.isEmpty() ? this : new FilePointerPartNode(pathRest, this, fileAndUrl, pointersToStore);
String commonPredecessor = StringUtil.first(part, index - start, false);
FilePointerPartNode splittedAway = new FilePointerPartNode(part.substring(index - start), this, myFileAndUrl);
FilePointerPartNode splittedAway = new FilePointerPartNode(part.substring(index - start), this, myFileAndUrl, pointersUnder);
splittedAway.children = children;
for (FilePointerPartNode child : children) {
child.parent = splittedAway;
}
splittedAway.pointersUnder = pointersUnder;
splittedAway.useCount = useCount;
splittedAway.associate(leaves, myFileAndUrl);
useCount = 0;
@@ -35,6 +35,7 @@ class VirtualFilePointerImpl extends TraceableDisposable implements VirtualFileP
private static final boolean TRACE_CREATION = LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode();
volatile FilePointerPartNode myNode; // null means disposed
boolean recursive; // true if the validityChanged() event should be fired for any change under this directory. Used for library jar directories.
VirtualFilePointerImpl(@Nullable VirtualFilePointerListener listener) {
super(TRACE_CREATION);
@@ -132,4 +133,9 @@ class VirtualFilePointerImpl extends TraceableDisposable implements VirtualFileP
int incrementUsageCount(int delta) {
return myNode.incrementUsageCount(delta);
}
@Override
public boolean isRecursive() {
return recursive;
}
}
@@ -117,16 +117,16 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
@TestOnly
synchronized VirtualFilePointer[] getPointersUnder(VirtualFile parent, String childName) {
List<FilePointerPartNode> nodes = new ArrayList<>();
addPointersUnder(parent, true, childName, nodes);
addRelevantPointers(parent, true, childName, nodes);
return toPointers(nodes);
}
private void addPointersUnder(VirtualFile parent,
boolean separator,
@NotNull CharSequence childName,
@NotNull List<FilePointerPartNode> out) {
private void addRelevantPointers(VirtualFile parent,
boolean separator,
@NotNull CharSequence childName,
@NotNull List<FilePointerPartNode> out) {
for (FilePointerPartNode root : myPointers.values()) {
root.addPointersUnder(parent, separator, childName, out);
root.addRelevantPointersFrom(parent, separator, childName, out);
}
}
@@ -202,7 +202,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
}
}
// else url has come from VirtualFile.getPath() and is good enough
VirtualFilePointerImpl pointer = getOrCreate(listener, path, Pair.create(file, url));
VirtualFilePointerImpl pointer = getOrCreate(path, Pair.create(file, url), listener);
DelegatingDisposable.registerDisposable(parentDisposable, pointer);
return pointer;
}
@@ -215,7 +215,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
VirtualFilePointerListener listener) {
IdentityVirtualFilePointer pointer = myUrlToIdentity.get(url);
if (pointer == null) {
pointer = new IdentityVirtualFilePointer(found, url,listener){
pointer = new IdentityVirtualFilePointer(found, url,listener) {
@Override
public void dispose() {
synchronized (VirtualFilePointerManagerImpl.this) {
@@ -247,14 +247,13 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
}
@NotNull
private VirtualFilePointerImpl getOrCreate(@Nullable VirtualFilePointerListener listener,
@NotNull String path,
@NotNull Pair<VirtualFile, String> fileAndUrl) {
private VirtualFilePointerImpl getOrCreate(@NotNull String path,
@NotNull Pair<VirtualFile, String> fileAndUrl,
@Nullable VirtualFilePointerListener listener) {
FilePointerPartNode root = myPointers.get(listener);
FilePointerPartNode node;
if (root == null) {
root = new FilePointerPartNode(path, null, fileAndUrl);
root.pointersUnder++;
root = new FilePointerPartNode(path, null, fileAndUrl, 1);
myPointers.put(listener, root);
node = root;
}
@@ -286,7 +285,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
for (Map.Entry<VirtualFilePointerListener, FilePointerPartNode> entry : myPointers.entrySet()) {
FilePointerPartNode root = entry.getValue();
List<FilePointerPartNode> left = new ArrayList<>();
root.addPointersUnder(null, false, "", left);
root.addRelevantPointersFrom(null, false, "", left);
List<VirtualFilePointerImpl> pointers = new ArrayList<>();
for (FilePointerPartNode node : left) {
node.addAllPointersTo(pointers);
@@ -316,7 +315,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
synchronized void addAllPointersTo(@NotNull Collection<VirtualFilePointerImpl> pointers) {
List<FilePointerPartNode> out = new ArrayList<>();
for (FilePointerPartNode root : myPointers.values()) {
root.addPointersUnder(null, false, "", out);
root.addRelevantPointersFrom(null, false, "", out);
}
for (FilePointerPartNode node : out) {
node.addAllPointersTo(pointers);
@@ -378,24 +377,23 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
for (VFileEvent event : events) {
if (event instanceof VFileDeleteEvent) {
final VFileDeleteEvent deleteEvent = (VFileDeleteEvent)event;
addPointersUnder(deleteEvent.getFile(), false, "", toFireEvents);
addRelevantPointers(deleteEvent.getFile(), false, "", toFireEvents);
}
else if (event instanceof VFileCreateEvent) {
final VFileCreateEvent createEvent = (VFileCreateEvent)event;
addPointersUnder(createEvent.getParent(), true, createEvent.getChildName(), toFireEvents);
addRelevantPointers(createEvent.getParent(), true, createEvent.getChildName(), toFireEvents);
}
else if (event instanceof VFileCopyEvent) {
final VFileCopyEvent copyEvent = (VFileCopyEvent)event;
addPointersUnder(copyEvent.getNewParent(), true, copyEvent.getFile().getName(), toFireEvents);
addRelevantPointers(copyEvent.getNewParent(), true, copyEvent.getFile().getName(), toFireEvents);
}
else if (event instanceof VFileMoveEvent) {
final VFileMoveEvent moveEvent = (VFileMoveEvent)event;
VirtualFile eventFile = moveEvent.getFile();
addPointersUnder(moveEvent.getNewParent(), true, eventFile.getName(), toFireEvents);
addRelevantPointers(moveEvent.getNewParent(), true, eventFile.getName(), toFireEvents);
List<FilePointerPartNode> nodes = new ArrayList<>();
addPointersUnder(eventFile, false, "", nodes);
addRelevantPointers(eventFile, false, "", nodes);
toFireEvents.addAll(nodes); // files deleted from eventFile and created in moveEvent.getNewParent()
for (FilePointerPartNode node : nodes) {
VirtualFilePointerImpl pointer = node.getAnyPointer();
@@ -411,10 +409,10 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
&& !Comparing.equal(change.getOldValue(), change.getNewValue())) {
VirtualFile eventFile = change.getFile();
VirtualFile parent = eventFile.getParent(); // e.g. for LightVirtualFiles
addPointersUnder(parent, true, change.getNewValue().toString(), toFireEvents);
addRelevantPointers(parent, true, change.getNewValue().toString(), toFireEvents);
List<FilePointerPartNode> nodes = new ArrayList<>();
addPointersUnder(eventFile, false, "", nodes);
addRelevantPointers(eventFile, false, "", nodes);
for (FilePointerPartNode node : nodes) {
VirtualFilePointerImpl pointer = node.getAnyPointer();
VirtualFile file = pointer == null ? null : pointer.getFile();
@@ -567,6 +565,18 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
}
}
@NotNull
@Override
public synchronized VirtualFilePointer createDirectoryPointer(@NotNull String url,
boolean recursively,
@NotNull Disposable parent, @NotNull VirtualFilePointerListener listener) {
VirtualFilePointer pointer = create(null, url, parent, listener);
if (pointer instanceof VirtualFilePointerImpl) {
((VirtualFilePointerImpl)pointer).recursive = true;
}
return pointer;
}
@TestOnly
synchronized int numberOfPointers() {
int number = 0;
@@ -585,4 +595,5 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
int numberOfCachedUrlToIdentity() {
return myUrlToIdentity.size();
}
}
@@ -1004,4 +1004,24 @@ public class VirtualFilePointerTest extends PlatformTestCase {
}
LOG.debug("final i = " + i);
}
public void testDirectoryPointersWork() throws Exception {
final File dir = createTempDirectory();
VirtualFile vDir = LocalFileSystem.getInstance().findFileByIoFile(dir);
assertNotNull(vDir);
VirtualFile deep = createChildDirectory(vDir, "deep");
LoggingListener listener = new LoggingListener();
Disposable disposable = Disposer.newDisposable();
VirtualFilePointer ptr = myVirtualFilePointerManager.createDirectoryPointer(vDir.getUrl(), false, disposable, listener);
createChildData(vDir, "1");
assertEquals("[before:true, after:true]", listener.getLog().toString());
Disposer.dispose(disposable);
listener = new LoggingListener();
myVirtualFilePointerManager.createDirectoryPointer(vDir.getUrl(), true, this.disposable, listener);
createChildData(deep, "1");
assertEquals("[before:true, after:true]", listener.getLog().toString());
}
}