a case of two pointers merging into one

This commit is contained in:
Alexey Kudravtsev
2015-11-10 14:43:47 +03:00
parent 39df36d294
commit 098cee3bd7
3 changed files with 260 additions and 248 deletions
@@ -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);
}
}
}
@@ -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;
}
@@ -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());
}
}