From 098cee3bd7b9b97600e253f60a979deff2190ccf Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 10 Nov 2015 14:41:05 +0300 Subject: [PATCH] a case of two pointers merging into one --- .../openapi/vfs/impl/FilePointerPartNode.java | 141 ++++++---- .../impl/VirtualFilePointerManagerImpl.java | 118 +++++---- .../vfs/impl/VirtualFilePointerTest.java | 249 ++++++++---------- 3 files changed, 260 insertions(+), 248 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java index 9a427a579d72..30414040dd45 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/FilePointerPartNode.java @@ -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 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 out) { @@ -121,33 +126,14 @@ class FilePointerPartNode { } private static void addAllPointersUnder(@NotNull FilePointerPartNode node, @NotNull List 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 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 fileAndUrl) { + FilePointerPartNode findPointerOrCreate(@NotNull String path, + int start, + @NotNull Pair 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 fileAndUrl) { - if (pointer != null) { - pointer.myNode = this; + void associate(Object leaves, Pair 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 outList) { + Object leaves = this.leaves; + if (leaves == null) { + return; + } + if (leaves instanceof VirtualFilePointerImpl) { + outList.add((VirtualFilePointerImpl)leaves); + } + else { + ContainerUtil.addAll(outList, (VirtualFilePointerImpl[])leaves); + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java index 62594200cb1f..99f7a8542130 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java @@ -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 pointers) { - if (pointers.isEmpty()) return VirtualFilePointer.EMPTY_ARRAY; - List list = ContainerUtil - .mapNotNull(pointers, new Function() { - @Override - public VirtualFilePointer fun(FilePointerPartNode pair) { - return pair.leaf; - } - }); - + private static VirtualFilePointer[] toPointers(@NotNull List nodes) { + if (nodes.isEmpty()) return VirtualFilePointer.EMPTY_ARRAY; + List list = new ArrayList(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 nodes = new ArrayList(); addPointersUnder(parent, true, childName, nodes); return toPointers(nodes); @@ -147,7 +143,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp @NotNull CharSequence childName, @NotNull List 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 entry : myPointers.entrySet()) { FilePointerPartNode root = entry.getValue(); - ArrayList left = new ArrayList(); - root.getPointersUnder(null, false, "", left); - if (!left.isEmpty()) { - VirtualFilePointerImpl p = left.get(0).leaf; + List left = new ArrayList(); + List pointers = new ArrayList(); + 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 pointers = new ArrayList(); - 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 pointers) { + @TestOnly + private void addAllPointersTo(@NotNull Collection pointers) { List out = new ArrayList(); 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 myEvents = Collections.emptyList(); - private List myPointersToUpdateUrl = Collections.emptyList(); - private List myPointersToFire = Collections.emptyList(); + private List myNodesToUpdateUrl = Collections.emptyList(); + private List myNodesToFire = Collections.emptyList(); @Override public void before(@NotNull final List events) { @@ -423,10 +421,11 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp List nodes = new ArrayList(); 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 nodes = new ArrayList(); 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 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 after = node.update(); String urlAfter = after.second; if (URL_COMPARATOR.compare(urlBefore, urlAfter) != 0) { + List myPointers = new SmartList(); + 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; } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java index 9f63d41871da..22fab8855cc6 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java @@ -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 myLog = new ArrayList(); + private final ArrayList 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() { - @Override - public VirtualFile compute() { - return VirtualFileManager.getInstance().refreshAndFindFileByUrl(url); - } - }); + return WriteCommandAction.runWriteCommandAction(null, (Computable)() -> 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> reads) { for (int i = 0; i < 10; i++) { - final AtomicReference> reference = new AtomicReference>(); - 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> 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() { - @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 events = new ArrayList(); + final List 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()); + } }