mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
a case of two pointers merging into one
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,25 +24,30 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.newvfs.ManagingFS;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
// all file pointers we store in the tree with nodes corresponding to the file structure on disk
|
||||
class FilePointerPartNode {
|
||||
private static final FilePointerPartNode[] EMPTY_ARRAY = new FilePointerPartNode[0];
|
||||
@NotNull private String part; // common prefix of all file pointers beneath
|
||||
@NotNull private FilePointerPartNode[] children;
|
||||
private FilePointerPartNode parent;
|
||||
VirtualFilePointerImpl leaf; // file pointer for this exact path (e.g. concatenation of all "part" fields down from the root)
|
||||
@NotNull FilePointerPartNode[] children;
|
||||
FilePointerPartNode parent;
|
||||
// file pointers for this exact path (e.g. concatenation of all "part" fields down from the root).
|
||||
// Either VirtualFilePointerImpl or VirtualFilePointerImpl[] (when it so happened that several pointers merged into one node - e.g. after file rename onto existing pointer)
|
||||
private Object leaves;
|
||||
|
||||
// in case there is file pointer exists for this part, its info is saved here
|
||||
volatile Pair<VirtualFile, String> myFileAndUrl; // must not be both null
|
||||
private volatile long myLastUpdated = -1;
|
||||
volatile int useCount;
|
||||
|
||||
private int pointersUnder = 1; // number of alive pointers in this node plus all nodes beneath
|
||||
int pointersUnder; // number of alive pointers in this node plus all nodes beneath
|
||||
private static final VirtualFileManager ourFileManager = VirtualFileManager.getInstance();
|
||||
private static final ManagingFS ourManagingFS = ManagingFS.getInstance();
|
||||
|
||||
@@ -59,11 +64,11 @@ class FilePointerPartNode {
|
||||
}
|
||||
|
||||
// returns the node and length of matched characters in that node, or null if there is no match
|
||||
int position(@Nullable VirtualFile parent,
|
||||
@Nullable CharSequence parentName,
|
||||
boolean separator,
|
||||
@NotNull CharSequence childName,
|
||||
@NotNull FilePointerPartNode[] outNode) {
|
||||
private int position(@Nullable VirtualFile parent,
|
||||
@Nullable CharSequence parentName,
|
||||
boolean separator,
|
||||
@NotNull CharSequence childName,
|
||||
@NotNull FilePointerPartNode[] outNode) {
|
||||
checkConsistency();
|
||||
|
||||
int partStart;
|
||||
@@ -107,7 +112,7 @@ class FilePointerPartNode {
|
||||
}
|
||||
|
||||
// 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 getPointersUnder(@Nullable VirtualFile parent,
|
||||
void addPointersUnder(@Nullable VirtualFile parent,
|
||||
boolean separator,
|
||||
@NotNull CharSequence childName,
|
||||
@NotNull List<FilePointerPartNode> out) {
|
||||
@@ -121,33 +126,14 @@ class FilePointerPartNode {
|
||||
}
|
||||
|
||||
private static void addAllPointersUnder(@NotNull FilePointerPartNode node, @NotNull List<FilePointerPartNode> out) {
|
||||
if (node.leaf != null) out.add(node);
|
||||
if (node.leaves != null) {
|
||||
out.add(node);
|
||||
}
|
||||
for (FilePointerPartNode child : node.children) {
|
||||
addAllPointersUnder(child, out);
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
boolean getPointersUnder(@NotNull String path, int start, @NotNull List<FilePointerPartNode> out) {
|
||||
checkConsistency();
|
||||
if (pointersUnder == 0) return false;
|
||||
// invariant: upper nodes are matched
|
||||
int index = indexOfFirstDifferentChar(path, start);
|
||||
if (index - start == part.length() // part matched entirely, check children
|
||||
|| index == path.length() // query matched entirely, add all children to matches
|
||||
) {
|
||||
if (index == path.length() && leaf != null) {
|
||||
out.add(this);
|
||||
}
|
||||
|
||||
for (FilePointerPartNode child : children) {
|
||||
child.getPointersUnder(path, index, out);
|
||||
}
|
||||
}
|
||||
// else there is no match
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final boolean UNIT_TEST = ApplicationManager.getApplication().isUnitTestMode();
|
||||
void checkConsistency() {
|
||||
if (UNIT_TEST && !ApplicationInfoImpl.isInPerformanceTest()) {
|
||||
@@ -162,19 +148,23 @@ class FilePointerPartNode {
|
||||
child.doCheckConsistency();
|
||||
assert child.parent == this;
|
||||
}
|
||||
if (leaf != null) childSum++;
|
||||
assert (useCount == 0) == (leaf == null) : useCount + " - " +leaf;
|
||||
childSum += leavesNumber();
|
||||
assert (useCount == 0) == (leaves == null) : useCount + " - " + (leaves instanceof VirtualFilePointerImpl ? leaves : Arrays.toString((VirtualFilePointerImpl[])leaves));
|
||||
assert pointersUnder == childSum : "expected: "+pointersUnder+"; actual: "+childSum;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
FilePointerPartNode findPointerOrCreate(@NotNull String path, int start, @NotNull Pair<VirtualFile, String> fileAndUrl) {
|
||||
FilePointerPartNode findPointerOrCreate(@NotNull String path,
|
||||
int start,
|
||||
@NotNull Pair<VirtualFile, String> fileAndUrl,
|
||||
int pointersToStore) {
|
||||
// invariant: upper nodes are matched
|
||||
int index = indexOfFirstDifferentChar(path, start);
|
||||
if (index == path.length() // query matched entirely
|
||||
&& index - start == part.length()
|
||||
) {
|
||||
if (leaf == null) pointersUnder++;
|
||||
&& index - start == part.length()) {
|
||||
if (leaves == null) {
|
||||
pointersUnder+=pointersToStore; // the pointer is going to be written here
|
||||
}
|
||||
return this;
|
||||
}
|
||||
if (index - start == part.length() // part matched entirely, check children
|
||||
@@ -183,16 +173,19 @@ class FilePointerPartNode {
|
||||
// find the right child (its part should start with ours)
|
||||
int i = child.indexOfFirstDifferentChar(path, index);
|
||||
if (i != index && (i > index+1 || path.charAt(index) != '/' || index == 0)) {
|
||||
FilePointerPartNode node = child.findPointerOrCreate(path, index, fileAndUrl);
|
||||
if (node.leaf == null) pointersUnder++; // the new node's been created
|
||||
FilePointerPartNode node = child.findPointerOrCreate(path, index, fileAndUrl, pointersToStore);
|
||||
if (node.leaves == null) {
|
||||
pointersUnder+=pointersToStore; // the new node's been created
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
// cannot insert to children, create child node manually
|
||||
String pathRest = path.substring(index);
|
||||
FilePointerPartNode newNode = new FilePointerPartNode(pathRest, this, fileAndUrl);
|
||||
newNode.pointersUnder+=pointersToStore;
|
||||
children = ArrayUtil.append(children, newNode);
|
||||
pointersUnder++;
|
||||
pointersUnder+=pointersToStore;
|
||||
return newNode;
|
||||
}
|
||||
// else there is no match
|
||||
@@ -201,6 +194,9 @@ class FilePointerPartNode {
|
||||
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;
|
||||
}
|
||||
String commonPredecessor = StringUtil.first(part, index - start, false);
|
||||
FilePointerPartNode splittedAway = new FilePointerPartNode(part.substring(index - start), this, myFileAndUrl);
|
||||
splittedAway.children = children;
|
||||
@@ -209,30 +205,31 @@ class FilePointerPartNode {
|
||||
}
|
||||
splittedAway.pointersUnder = pointersUnder;
|
||||
splittedAway.useCount = useCount;
|
||||
splittedAway.associate(leaf, myFileAndUrl);
|
||||
splittedAway.associate(leaves, myFileAndUrl);
|
||||
associate(null, null);
|
||||
useCount = 0;
|
||||
part = commonPredecessor;
|
||||
children = newNode == this ? new FilePointerPartNode[]{splittedAway} : new FilePointerPartNode[]{splittedAway, newNode};
|
||||
pointersUnder++;
|
||||
pointersUnder+=pointersToStore;
|
||||
return newNode;
|
||||
}
|
||||
|
||||
// return true if the root node must be deleted also
|
||||
boolean remove() {
|
||||
assert leaf != null : toString();
|
||||
// returns root node
|
||||
@NotNull
|
||||
FilePointerPartNode remove() {
|
||||
int pointersNumber = leavesNumber();
|
||||
assert leaves != null : toString();
|
||||
associate(null, null);
|
||||
useCount = 0;
|
||||
myLastUpdated = -1;
|
||||
FilePointerPartNode node;
|
||||
for (node = this; node.parent != null; node = node.parent) {
|
||||
node.pointersUnder--;
|
||||
node.pointersUnder-=pointersNumber;
|
||||
}
|
||||
if (--node.pointersUnder == 0) {
|
||||
if ((node.pointersUnder-=pointersNumber) == 0) {
|
||||
node.children = EMPTY_ARRAY; // clear root node, especially in tests
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return node;
|
||||
}
|
||||
|
||||
private int indexOfFirstDifferentChar(@NotNull CharSequence path, int start) {
|
||||
@@ -305,11 +302,18 @@ class FilePointerPartNode {
|
||||
return start1;
|
||||
}
|
||||
|
||||
void associate(VirtualFilePointerImpl pointer, Pair<VirtualFile, String> fileAndUrl) {
|
||||
if (pointer != null) {
|
||||
pointer.myNode = this;
|
||||
void associate(Object leaves, Pair<VirtualFile, String> fileAndUrl) {
|
||||
if (leaves != null) {
|
||||
if (leaves instanceof VirtualFilePointerImpl) {
|
||||
((VirtualFilePointerImpl)leaves).myNode = this;
|
||||
}
|
||||
else {
|
||||
for (VirtualFilePointerImpl pointer : (VirtualFilePointerImpl[])leaves) {
|
||||
pointer.myNode = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
leaf = pointer;
|
||||
this.leaves = leaves;
|
||||
myFileAndUrl = fileAndUrl;
|
||||
myLastUpdated = -1;
|
||||
}
|
||||
@@ -318,7 +322,30 @@ class FilePointerPartNode {
|
||||
return useCount+=delta;
|
||||
}
|
||||
|
||||
int getPointersUnder() {
|
||||
int numberOfPointersUnder() {
|
||||
return pointersUnder;
|
||||
}
|
||||
|
||||
VirtualFilePointerImpl getAnyPointer() {
|
||||
Object leaves = this.leaves;
|
||||
return leaves == null ? null : leaves instanceof VirtualFilePointerImpl ? (VirtualFilePointerImpl)leaves : ((VirtualFilePointerImpl[])leaves)[0];
|
||||
}
|
||||
|
||||
private int leavesNumber() {
|
||||
Object leaves = this.leaves;
|
||||
return leaves == null ? 0 : leaves instanceof VirtualFilePointerImpl ? 1 : ((VirtualFilePointerImpl[])leaves).length;
|
||||
}
|
||||
|
||||
void addAllPointersTo(@NotNull Collection<? super VirtualFilePointerImpl> outList) {
|
||||
Object leaves = this.leaves;
|
||||
if (leaves == null) {
|
||||
return;
|
||||
}
|
||||
if (leaves instanceof VirtualFilePointerImpl) {
|
||||
outList.add((VirtualFilePointerImpl)leaves);
|
||||
}
|
||||
else {
|
||||
ContainerUtil.addAll(outList, (VirtualFilePointerImpl[])leaves);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+61
-57
@@ -31,7 +31,7 @@ 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.ConcurrencyUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.URLUtil;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
@@ -122,21 +122,17 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static VirtualFilePointer[] toPointers(@NotNull List<FilePointerPartNode> pointers) {
|
||||
if (pointers.isEmpty()) return VirtualFilePointer.EMPTY_ARRAY;
|
||||
List<VirtualFilePointer> list = ContainerUtil
|
||||
.mapNotNull(pointers, new Function<FilePointerPartNode, VirtualFilePointer>() {
|
||||
@Override
|
||||
public VirtualFilePointer fun(FilePointerPartNode pair) {
|
||||
return pair.leaf;
|
||||
}
|
||||
});
|
||||
|
||||
private static VirtualFilePointer[] toPointers(@NotNull List<FilePointerPartNode> nodes) {
|
||||
if (nodes.isEmpty()) return VirtualFilePointer.EMPTY_ARRAY;
|
||||
List<VirtualFilePointer> list = new ArrayList<VirtualFilePointer>(nodes.size());
|
||||
for (FilePointerPartNode node : nodes) {
|
||||
node.addAllPointersTo(list);
|
||||
}
|
||||
return list.toArray(new VirtualFilePointer[list.size()]);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public VirtualFilePointer[] getPointersUnder(VirtualFile parent, String childName) {
|
||||
VirtualFilePointer[] getPointersUnder(VirtualFile parent, String childName) {
|
||||
List<FilePointerPartNode> nodes = new ArrayList<FilePointerPartNode>();
|
||||
addPointersUnder(parent, true, childName, nodes);
|
||||
return toPointers(nodes);
|
||||
@@ -147,7 +143,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
@NotNull CharSequence childName,
|
||||
@NotNull List<FilePointerPartNode> out) {
|
||||
for (FilePointerPartNode root : myPointers.values()) {
|
||||
root.getPointersUnder(parent, separator, childName, out);
|
||||
root.addPointersUnder(parent, separator, childName, out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,21 +251,19 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
FilePointerPartNode node;
|
||||
if (root == null) {
|
||||
root = new FilePointerPartNode(path, null, fileAndUrl);
|
||||
root.pointersUnder++;
|
||||
myPointers.put(listener, root);
|
||||
node = root;
|
||||
}
|
||||
else {
|
||||
node = root.findPointerOrCreate(path, 0, fileAndUrl);
|
||||
node = root.findPointerOrCreate(path, 0, fileAndUrl, 1);
|
||||
}
|
||||
|
||||
VirtualFilePointerImpl pointer;
|
||||
if (node.leaf == null) {
|
||||
VirtualFilePointerImpl pointer = node.getAnyPointer();
|
||||
if (pointer == null) {
|
||||
pointer = new VirtualFilePointerImpl(listener, parentDisposable, fileAndUrl);
|
||||
node.associate(pointer, fileAndUrl);
|
||||
}
|
||||
else {
|
||||
pointer = node.leaf;
|
||||
}
|
||||
pointer.myNode.incrementUsageCount(1);
|
||||
|
||||
root.checkConsistency();
|
||||
@@ -288,16 +282,19 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
private synchronized void assertAllPointersDisposed() {
|
||||
for (Map.Entry<VirtualFilePointerListener, FilePointerPartNode> entry : myPointers.entrySet()) {
|
||||
FilePointerPartNode root = entry.getValue();
|
||||
ArrayList<FilePointerPartNode> left = new ArrayList<FilePointerPartNode>();
|
||||
root.getPointersUnder(null, false, "", left);
|
||||
if (!left.isEmpty()) {
|
||||
VirtualFilePointerImpl p = left.get(0).leaf;
|
||||
List<FilePointerPartNode> left = new ArrayList<FilePointerPartNode>();
|
||||
List<VirtualFilePointerImpl> pointers = new ArrayList<VirtualFilePointerImpl>();
|
||||
root.addPointersUnder(null, false, "", left);
|
||||
for (FilePointerPartNode node : left) {
|
||||
node.addAllPointersTo(pointers);
|
||||
}
|
||||
if (!pointers.isEmpty()) {
|
||||
VirtualFilePointerImpl p = pointers.get(0);
|
||||
try {
|
||||
p.throwDisposalError("Not disposed pointer: "+p);
|
||||
}
|
||||
finally {
|
||||
for (FilePointerPartNode pair : left) {
|
||||
VirtualFilePointerImpl pointer = pair.leaf;
|
||||
for (VirtualFilePointerImpl pointer : pointers) {
|
||||
pointer.dispose();
|
||||
}
|
||||
}
|
||||
@@ -317,13 +314,13 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
@TestOnly
|
||||
public void storePointers() {
|
||||
myStoredPointers.clear();
|
||||
addAllPointers(myStoredPointers);
|
||||
addAllPointersTo(myStoredPointers);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void assertPointersAreDisposed() {
|
||||
List<VirtualFilePointerImpl> pointers = new ArrayList<VirtualFilePointerImpl>();
|
||||
addAllPointers(pointers);
|
||||
addAllPointersTo(pointers);
|
||||
try {
|
||||
for (VirtualFilePointerImpl pointer : pointers) {
|
||||
if (!myStoredPointers.contains(pointer)) {
|
||||
@@ -336,13 +333,14 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
}
|
||||
}
|
||||
|
||||
private void addAllPointers(@NotNull Collection<VirtualFilePointerImpl> pointers) {
|
||||
@TestOnly
|
||||
private void addAllPointersTo(@NotNull Collection<VirtualFilePointerImpl> pointers) {
|
||||
List<FilePointerPartNode> out = new ArrayList<FilePointerPartNode>();
|
||||
for (FilePointerPartNode root : myPointers.values()) {
|
||||
root.getPointersUnder(null, false, "", out);
|
||||
root.addPointersUnder(null, false, "", out);
|
||||
}
|
||||
for (FilePointerPartNode node : out) {
|
||||
pointers.add(node.leaf);
|
||||
node.addAllPointersTo(pointers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,8 +389,8 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
}
|
||||
|
||||
private List<EventDescriptor> myEvents = Collections.emptyList();
|
||||
private List<FilePointerPartNode> myPointersToUpdateUrl = Collections.emptyList();
|
||||
private List<FilePointerPartNode> myPointersToFire = Collections.emptyList();
|
||||
private List<FilePointerPartNode> myNodesToUpdateUrl = Collections.emptyList();
|
||||
private List<FilePointerPartNode> myNodesToFire = Collections.emptyList();
|
||||
|
||||
@Override
|
||||
public void before(@NotNull final List<? extends VFileEvent> events) {
|
||||
@@ -423,10 +421,11 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
|
||||
List<FilePointerPartNode> nodes = new ArrayList<FilePointerPartNode>();
|
||||
addPointersUnder(eventFile, false, "", nodes);
|
||||
for (FilePointerPartNode pair : nodes) {
|
||||
VirtualFile file = pair.leaf.getFile();
|
||||
for (FilePointerPartNode node : nodes) {
|
||||
VirtualFilePointerImpl pointer = node.getAnyPointer();
|
||||
VirtualFile file = pointer == null ? null : pointer.getFile();
|
||||
if (file != null) {
|
||||
toUpdateUrl.add(pair);
|
||||
toUpdateUrl.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -439,10 +438,11 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
|
||||
List<FilePointerPartNode> nodes = new ArrayList<FilePointerPartNode>();
|
||||
addPointersUnder(eventFile, false, "", nodes);
|
||||
for (FilePointerPartNode pair : nodes) {
|
||||
VirtualFile file = pair.leaf.getFile();
|
||||
for (FilePointerPartNode node : nodes) {
|
||||
VirtualFilePointerImpl pointer = node.getAnyPointer();
|
||||
VirtualFile file = pointer == null ? null : pointer.getFile();
|
||||
if (file != null) {
|
||||
toUpdateUrl.add(pair);
|
||||
toUpdateUrl.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,41 +474,44 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
myBus.syncPublisher(VirtualFilePointerListener.TOPIC).beforeValidityChanged(toFirePointers);
|
||||
}
|
||||
|
||||
myPointersToFire = toFireEvents;
|
||||
myPointersToUpdateUrl = toUpdateUrl;
|
||||
myNodesToFire = toFireEvents;
|
||||
myNodesToUpdateUrl = toUpdateUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after(@NotNull final List<? extends VFileEvent> events) {
|
||||
incModificationCount();
|
||||
|
||||
for (FilePointerPartNode node : myPointersToUpdateUrl) {
|
||||
for (FilePointerPartNode node : myNodesToUpdateUrl) {
|
||||
synchronized (this) {
|
||||
VirtualFilePointerImpl pointer = node.leaf;
|
||||
String urlBefore = pointer.getUrlNoUpdate();
|
||||
String urlBefore = node.myFileAndUrl.second;
|
||||
Pair<VirtualFile,String> after = node.update();
|
||||
String urlAfter = after.second;
|
||||
if (URL_COMPARATOR.compare(urlBefore, urlAfter) != 0) {
|
||||
List<VirtualFilePointerImpl> myPointers = new SmartList<VirtualFilePointerImpl>();
|
||||
node.addAllPointersTo(myPointers);
|
||||
|
||||
// url has changed, reinsert
|
||||
FilePointerPartNode root = myPointers.get(pointer.getListener());
|
||||
int useCount = node.useCount;
|
||||
node.remove();
|
||||
FilePointerPartNode newNode = root.findPointerOrCreate(VfsUtilCore.urlToPath(urlAfter), 0, after);
|
||||
VirtualFilePointerImpl existingPointer = newNode.leaf;
|
||||
FilePointerPartNode root = node.remove();
|
||||
FilePointerPartNode newNode = root.findPointerOrCreate(VfsUtilCore.urlToPath(urlAfter), 0, after, myPointers.size());
|
||||
VirtualFilePointer existingPointer = newNode.getAnyPointer();
|
||||
if (existingPointer != null) {
|
||||
// can happen when e.g. file renamed to the existing file
|
||||
// merge two pointers
|
||||
pointer.myNode = newNode;
|
||||
}
|
||||
else {
|
||||
newNode.associate(pointer, after);
|
||||
for (FilePointerPartNode n = newNode; n != null; n = n.parent) {
|
||||
n.pointersUnder += myPointers.size();
|
||||
}
|
||||
}
|
||||
newNode.addAllPointersTo(myPointers);
|
||||
VirtualFilePointerImpl[] newMyPointers = myPointers.toArray(new VirtualFilePointerImpl[myPointers.size()]);
|
||||
newNode.associate(newMyPointers, after);
|
||||
newNode.incrementUsageCount(useCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFilePointer[] pointersToFireArray = toPointers(myPointersToFire);
|
||||
VirtualFilePointer[] pointersToFireArray = toPointers(myNodesToFire);
|
||||
for (VirtualFilePointer pointer : pointersToFireArray) {
|
||||
((VirtualFilePointerImpl)pointer).myNode.update();
|
||||
}
|
||||
@@ -521,16 +524,17 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
myBus.syncPublisher(VirtualFilePointerListener.TOPIC).validityChanged(pointersToFireArray);
|
||||
}
|
||||
|
||||
myPointersToUpdateUrl = Collections.emptyList();
|
||||
myNodesToUpdateUrl = Collections.emptyList();
|
||||
myEvents = Collections.emptyList();
|
||||
myPointersToFire = Collections.emptyList();
|
||||
myNodesToFire = Collections.emptyList();
|
||||
for (FilePointerPartNode root : myPointers.values()) {
|
||||
root.checkConsistency();
|
||||
}
|
||||
}
|
||||
|
||||
void removeNode(@NotNull FilePointerPartNode node, VirtualFilePointerListener listener) {
|
||||
boolean rootNodeEmpty = node.remove();
|
||||
FilePointerPartNode root = node.remove();
|
||||
boolean rootNodeEmpty = root.children.length == 0 ;
|
||||
if (rootNodeEmpty) {
|
||||
myPointers.remove(listener);
|
||||
}
|
||||
@@ -549,7 +553,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
myParent = parent;
|
||||
}
|
||||
|
||||
static void registerDisposable(@NotNull Disposable parentDisposable, @NotNull VirtualFilePointerImpl pointer) {
|
||||
private static void registerDisposable(@NotNull Disposable parentDisposable, @NotNull VirtualFilePointerImpl pointer) {
|
||||
DelegatingDisposable result = ourInstances.get(parentDisposable);
|
||||
if (result == null) {
|
||||
DelegatingDisposable newDisposable = new DelegatingDisposable(parentDisposable);
|
||||
@@ -585,7 +589,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp
|
||||
int numberOfPointers() {
|
||||
int number = 0;
|
||||
for (FilePointerPartNode root : myPointers.values()) {
|
||||
number = root.getPointersUnder();
|
||||
number = root.numberOfPointersUnder();
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
+115
-134
@@ -35,9 +35,8 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
|
||||
import com.intellij.testFramework.*;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ThrowableRunnable;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -49,7 +48,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -88,7 +86,7 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
}
|
||||
|
||||
private static class LoggingListener implements VirtualFilePointerListener {
|
||||
private final ArrayList<String> myLog = new ArrayList<String>();
|
||||
private final ArrayList<String> myLog = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void beforeValidityChanged(@NotNull VirtualFilePointer[] pointers) {
|
||||
@@ -138,13 +136,10 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
final VirtualFilePointer fileToCreatePointer = createPointerByFile(fileToCreate, fileToCreateListener);
|
||||
assertFalse(fileToCreatePointer.isValid());
|
||||
fileToCreate.createNewFile();
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VirtualFileManager.getInstance().syncRefresh();
|
||||
final VirtualFile virtualFile = getVirtualFile(tempDirectory);
|
||||
virtualFile.refresh(false, true);
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
VirtualFileManager.getInstance().syncRefresh();
|
||||
final VirtualFile virtualFile = getVirtualFile(tempDirectory);
|
||||
virtualFile.refresh(false, true);
|
||||
});
|
||||
assertTrue(fileToCreatePointer.isValid());
|
||||
assertEquals("[before:false, after:true]", fileToCreateListener.getLog().toString());
|
||||
@@ -228,19 +223,16 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
final LoggingListener fileToMoveListener = new LoggingListener();
|
||||
final VirtualFilePointer fileToMovePointer = createPointerByFile(fileToMove, fileToMoveListener);
|
||||
assertTrue(fileToMovePointer.isValid());
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final VirtualFile virtualFile = getVirtualFile(fileToMove);
|
||||
assertTrue(virtualFile.isValid());
|
||||
final VirtualFile target = getVirtualFile(moveTarget);
|
||||
assertTrue(target.isValid());
|
||||
try {
|
||||
virtualFile.move(null, target);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
final VirtualFile virtualFile = getVirtualFile(fileToMove);
|
||||
assertTrue(virtualFile.isValid());
|
||||
final VirtualFile target = getVirtualFile(moveTarget);
|
||||
assertTrue(target.isValid());
|
||||
try {
|
||||
virtualFile.move(null, target);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertTrue(fileToMovePointer.isValid());
|
||||
@@ -257,19 +249,16 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
final LoggingListener listener = new LoggingListener();
|
||||
final VirtualFilePointer fileToMoveTargetPointer = createPointerByFile(new File(moveTarget, fileToMove.getName()), listener);
|
||||
assertFalse(fileToMoveTargetPointer.isValid());
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final VirtualFile virtualFile = getVirtualFile(fileToMove);
|
||||
assertTrue(virtualFile.isValid());
|
||||
final VirtualFile target = getVirtualFile(moveTarget);
|
||||
assertTrue(target.isValid());
|
||||
try {
|
||||
virtualFile.move(null, target);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
final VirtualFile virtualFile = getVirtualFile(fileToMove);
|
||||
assertTrue(virtualFile.isValid());
|
||||
final VirtualFile target = getVirtualFile(moveTarget);
|
||||
assertTrue(target.isValid());
|
||||
try {
|
||||
virtualFile.move(null, target);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertTrue(fileToMoveTargetPointer.isValid());
|
||||
@@ -290,19 +279,16 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
final VirtualFilePointer fileToMoveTargetPointer = createPointerByFile(new File(moveTarget, fileToMove.getName()), targetListener);
|
||||
|
||||
assertFalse(fileToMoveTargetPointer.isValid());
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final VirtualFile virtualFile = getVirtualFile(fileToMove);
|
||||
assertTrue(virtualFile.isValid());
|
||||
final VirtualFile target = getVirtualFile(moveTarget);
|
||||
assertTrue(target.isValid());
|
||||
try {
|
||||
virtualFile.move(null, target);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
final VirtualFile virtualFile = getVirtualFile(fileToMove);
|
||||
assertTrue(virtualFile.isValid());
|
||||
final VirtualFile target = getVirtualFile(moveTarget);
|
||||
assertTrue(target.isValid());
|
||||
try {
|
||||
virtualFile.move(null, target);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertTrue(fileToMovePointer.isValid());
|
||||
@@ -370,26 +356,20 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
final VirtualFilePointer fileToCreatePointer = createPointerByFile(fileToCreate, fileToCreateListener);
|
||||
assertFalse(fileToCreatePointer.isValid());
|
||||
fileToCreate.createNewFile();
|
||||
final Runnable postRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertTrue(fileToCreatePointer.isValid());
|
||||
assertEquals("[before:false, after:true]", fileToCreateListener.getLog().toString());
|
||||
try {
|
||||
String expectedUrl = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, fileToCreate.getCanonicalPath().replace(File.separatorChar, '/'));
|
||||
assertEquals(expectedUrl.toUpperCase(), fileToCreatePointer.getUrl().toUpperCase());
|
||||
} catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
final Runnable postRunnable = () -> {
|
||||
assertTrue(fileToCreatePointer.isValid());
|
||||
assertEquals("[before:false, after:true]", fileToCreateListener.getLog().toString());
|
||||
try {
|
||||
String expectedUrl = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, fileToCreate.getCanonicalPath().replace(File.separatorChar, '/'));
|
||||
assertEquals(expectedUrl.toUpperCase(), fileToCreatePointer.getUrl().toUpperCase());
|
||||
} catch (IOException e) {
|
||||
fail();
|
||||
}
|
||||
};
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VirtualFileManager.getInstance().syncRefresh();
|
||||
final VirtualFile virtualFile = getVirtualFile(tempDirectory);
|
||||
virtualFile.refresh(false, true);
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
VirtualFileManager.getInstance().syncRefresh();
|
||||
final VirtualFile virtualFile = getVirtualFile(tempDirectory);
|
||||
virtualFile.refresh(false, true);
|
||||
});
|
||||
postRunnable.run();
|
||||
}
|
||||
@@ -405,11 +385,8 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
assertFalse(pointer_f2.isValid());
|
||||
file_f1.createNewFile();
|
||||
file_f2.createNewFile();
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LocalFileSystem.getInstance().refresh(false);
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
LocalFileSystem.getInstance().refresh(false);
|
||||
});
|
||||
assertEquals("[before:false:false, after:true:true]", listener.getLog().toString());
|
||||
}
|
||||
@@ -534,11 +511,8 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
}
|
||||
|
||||
private static void refreshVFS() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VirtualFileManager.getInstance().syncRefresh();
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
VirtualFileManager.getInstance().syncRefresh();
|
||||
});
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
}
|
||||
@@ -582,15 +556,12 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
}
|
||||
|
||||
public void testContainerCreateDeletePerformance() throws Exception {
|
||||
PlatformTestUtil.startPerformanceTest("VF container create/delete",200, new ThrowableRunnable() {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
Disposable parent = Disposer.newDisposable();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
myVirtualFilePointerManager.createContainer(parent);
|
||||
}
|
||||
Disposer.dispose(parent);
|
||||
PlatformTestUtil.startPerformanceTest("VF container create/delete", 200, () -> {
|
||||
Disposable parent = Disposer.newDisposable();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
myVirtualFilePointerManager.createContainer(parent);
|
||||
}
|
||||
Disposer.dispose(parent);
|
||||
}).cpuBound().assertTiming();
|
||||
}
|
||||
|
||||
@@ -627,12 +598,7 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
}
|
||||
|
||||
private static VirtualFile refreshAndFind(@NotNull final String url) {
|
||||
return WriteCommandAction.runWriteCommandAction(null, new Computable<VirtualFile>() {
|
||||
@Override
|
||||
public VirtualFile compute() {
|
||||
return VirtualFileManager.getInstance().refreshAndFindFileByUrl(url);
|
||||
}
|
||||
});
|
||||
return WriteCommandAction.runWriteCommandAction(null, (Computable<VirtualFile>)() -> VirtualFileManager.getInstance().refreshAndFindFileByUrl(url));
|
||||
}
|
||||
|
||||
public void testThreadsPerformance() throws IOException, InterruptedException, TimeoutException, ExecutionException {
|
||||
@@ -706,32 +672,21 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
|
||||
private static void stressRead(@NotNull final VirtualFilePointer pointer, @NotNull final Collection<Job<Void>> reads) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
final AtomicReference<Job<Void>> reference = new AtomicReference<Job<Void>>();
|
||||
reference.set(JobLauncher.getInstance().submitToJobThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VirtualFile file = pointer.getFile();
|
||||
if (file != null && !file.isValid()) {
|
||||
throw new IncorrectOperationException("I've caught it. I am that good");
|
||||
}
|
||||
}
|
||||
});
|
||||
final AtomicReference<Job<Void>> reference = new AtomicReference<>();
|
||||
reference.set(JobLauncher.getInstance().submitToJobThread(() -> ApplicationManager.getApplication().runReadAction(() -> {
|
||||
VirtualFile file = pointer.getFile();
|
||||
if (file != null && !file.isValid()) {
|
||||
throw new IncorrectOperationException("I've caught it. I am that good");
|
||||
}
|
||||
}, new Consumer<Future>() {
|
||||
@Override
|
||||
public void consume(Future future) {
|
||||
try {
|
||||
future.get();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finally {
|
||||
reads.remove(reference.get());
|
||||
}
|
||||
}), future -> {
|
||||
try {
|
||||
future.get();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finally {
|
||||
reads.remove(reference.get());
|
||||
}
|
||||
}));
|
||||
reads.add(reference.get());
|
||||
@@ -740,21 +695,18 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
|
||||
public void testManyPointersUpdatePerformance() throws IOException {
|
||||
LoggingListener listener = new LoggingListener();
|
||||
final List<VFileEvent> events = new ArrayList<VFileEvent>();
|
||||
final List<VFileEvent> events = new ArrayList<>();
|
||||
final File ioTempDir = createTempDirectory();
|
||||
final VirtualFile temp = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioTempDir);
|
||||
for (int i=0; i<100000; i++) {
|
||||
myVirtualFilePointerManager.create(VfsUtilCore.pathToUrl("/a/b/c/d/" + i), disposable, listener);
|
||||
events.add(new VFileCreateEvent(this, temp, "xxx" + i, false, true));
|
||||
}
|
||||
PlatformTestUtil.startPerformanceTest("vfp update", 10000, new ThrowableRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
for (int i=0; i<100; i++) {
|
||||
// simulate VFS refresh events since launching the actual refresh is too slow
|
||||
myVirtualFilePointerManager.before(events);
|
||||
myVirtualFilePointerManager.after(events);
|
||||
}
|
||||
PlatformTestUtil.startPerformanceTest("vfp update", 10000, () -> {
|
||||
for (int i=0; i<100; i++) {
|
||||
// simulate VFS refresh events since launching the actual refresh is too slow
|
||||
myVirtualFilePointerManager.before(events);
|
||||
myVirtualFilePointerManager.after(events);
|
||||
}
|
||||
}).assertTiming();
|
||||
}
|
||||
@@ -764,13 +716,10 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
final String url = VfsUtilCore.pathToUrl("/a/b/c/d/e");
|
||||
final VirtualFilePointer thePointer = myVirtualFilePointerManager.create(url, disposable, listener);
|
||||
TempFileSystem.getInstance();
|
||||
PlatformTestUtil.startPerformanceTest("same url vfp create", 5000, new ThrowableRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
for (int i=0; i<10000000; i++) {
|
||||
VirtualFilePointer pointer = myVirtualFilePointerManager.create(url, disposable, listener);
|
||||
assertSame(pointer, thePointer);
|
||||
}
|
||||
PlatformTestUtil.startPerformanceTest("same url vfp create", 5000, () -> {
|
||||
for (int i=0; i<10000000; i++) {
|
||||
VirtualFilePointer pointer = myVirtualFilePointerManager.create(url, disposable, listener);
|
||||
assertSame(pointer, thePointer);
|
||||
}
|
||||
}).assertTiming();
|
||||
}
|
||||
@@ -783,7 +732,7 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
VirtualFile dir2 = createChildDirectory(root, "dir2");
|
||||
|
||||
PsiTestUtil.addSourceRoot(getModule(), dir1);
|
||||
PsiTestUtil.addLibrary(getModule(), "mylib", "", new String[]{dir2.getPath()}, new String[0]);
|
||||
PsiTestUtil.addLibrary(getModule(), "mylib", "", new String[]{dir2.getPath()}, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
|
||||
assertSourceIs(dir1);
|
||||
assertLibIs(dir2);
|
||||
@@ -801,6 +750,7 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
assertLibIs(dir2); // libDir renamed, libs are "dir2" now
|
||||
|
||||
dir1 = createChildDirectory(root, "dir1");
|
||||
assertNotNull(dir1);
|
||||
assertSourceIs(dir2); // srcDir stays the same
|
||||
assertLibIs(dir2); // libDir stays the same
|
||||
|
||||
@@ -818,4 +768,35 @@ public class VirtualFilePointerTest extends PlatformTestCase {
|
||||
VirtualFile[] expected = dir1 == null ? VirtualFile.EMPTY_ARRAY : new VirtualFile[]{dir1};
|
||||
assertOrderedEquals(roots, expected);
|
||||
}
|
||||
|
||||
public void testTwoPointersMergingIntoOne() throws IOException {
|
||||
File tempDirectory = createTempDirectory();
|
||||
final VirtualFile root = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDirectory);
|
||||
|
||||
VirtualFile dir1 = createChildDirectory(root, "dir1");
|
||||
VirtualFile dir2 = createChildDirectory(root, "dir2");
|
||||
|
||||
VirtualFilePointer p1 = myVirtualFilePointerManager.create(dir1, disposable, null);
|
||||
VirtualFilePointer p2 = myVirtualFilePointerManager.create(dir2, disposable, null);
|
||||
assertTrue(p1.isValid());
|
||||
assertEquals(dir1, p1.getFile());
|
||||
assertTrue(p2.isValid());
|
||||
assertEquals(dir2, p2.getFile());
|
||||
|
||||
delete(dir1);
|
||||
assertEquals(null, p1.getFile());
|
||||
assertEquals(dir2, p2.getFile());
|
||||
|
||||
rename(dir2, "dir1");
|
||||
assertEquals(dir2, p1.getFile());
|
||||
assertEquals(dir2, p2.getFile());
|
||||
|
||||
rename(dir2, "dir2");
|
||||
assertEquals(dir2, p1.getFile());
|
||||
assertEquals(dir2, p2.getFile());
|
||||
|
||||
createChildDirectory(root, "dir1");
|
||||
assertEquals(dir2, p1.getFile());
|
||||
assertEquals(dir2, p2.getFile());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user