import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.impl.LightFilePointer;
import com.intellij.ui.JBColor;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.PathUtil;
import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
import com.intellij.openapi.roots.libraries.LibraryProperties;
import com.intellij.openapi.roots.libraries.LibraryType;
-import com.intellij.openapi.roots.ui.LightFilePointer;
-import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.impl.LightFilePointer;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
}
result.add(file);
}
- return VfsUtil.toVirtualFileArray(result);
+ return VfsUtilCore.toVirtualFileArray(result);
}
@Override
myExcludedRoots.add(new LightFilePointer(url));
}
+ @Override
public void removeExcludedRoot(@NotNull String url) {
myExcludedRoots.remove(new LightFilePointer(url));
}
import com.intellij.openapi.Disposable;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
@NotNull
@Override
public VirtualFilePointer create(@NotNull String url, @NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) {
- VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(url);
- return new IdentityVirtualFilePointer(vFile, url);
+ return new LightFilePointer(url);
}
@NotNull
@Override
public VirtualFilePointer create(@NotNull VirtualFile file, @NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) {
- return new IdentityVirtualFilePointer(file, file.getUrl());
+ return new LightFilePointer(file);
}
@NotNull
public VirtualFilePointer duplicate(@NotNull VirtualFilePointer pointer,
@NotNull Disposable parent,
@Nullable VirtualFilePointerListener listener) {
- return new IdentityVirtualFilePointer(pointer.getFile(), pointer.getUrl());
+ return new LightFilePointer(pointer.getUrl());
}
@NotNull
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.openapi.roots.ui;
+package com.intellij.openapi.vfs.impl;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.StandardFileSystems;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
+import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.File;
-
public class LightFilePointer implements VirtualFilePointer {
private final String myUrl;
private VirtualFile myFile;
return toPresentableUrl(myUrl);
}
- public static String toPresentableUrl(String url) {
+ @NotNull
+ private static String toPresentableUrl(@NotNull String url) {
String path = VirtualFileManager.extractPath(url);
- path = StringUtil.trimEnd(path, JarFileSystem.JAR_SEPARATOR);
- return path.replace('/', File.separatorChar);
+ String protocol = VirtualFileManager.extractProtocol(url);
+ VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
+ return ObjectUtils.notNull(fileSystem, StandardFileSystems.local()).extractPresentableUrl(path);
}
@Override
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ui.LightFilePointer;
+import com.intellij.openapi.vfs.impl.LightFilePointer;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * Copyright 2000-2016 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.
*/
package com.intellij.openapi.vfs.impl;
+import com.intellij.openapi.Disposable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener;
import org.jetbrains.annotations.NotNull;
/**
* @author cdr
*/
-class IdentityVirtualFilePointer implements VirtualFilePointer {
+class IdentityVirtualFilePointer extends VirtualFilePointerImpl implements VirtualFilePointer, Disposable {
private final VirtualFile myFile;
private final String myUrl;
+ private volatile int useCount;
- IdentityVirtualFilePointer(VirtualFile file, @NotNull String url) {
+ IdentityVirtualFilePointer(VirtualFile file, @NotNull String url, VirtualFilePointerListener listener) {
+ super(listener);
myFile = file;
myUrl = url;
}
public boolean isValid() {
return myFile == null || myFile.isValid();
}
+
+ @Override
+ int incrementUsageCount(int delta) {
+ return useCount += delta;
+ }
+
+ @Override
+ public void dispose() {
+ incrementUsageCount(-1);
+ }
}
\ No newline at end of file
*/
package com.intellij.openapi.vfs.impl;
-import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
class VirtualFilePointerImpl extends TraceableDisposable implements VirtualFilePointer {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.VirtualFilePointerImpl");
volatile FilePointerPartNode myNode; // null means disposed
- VirtualFilePointerImpl(VirtualFilePointerListener listener, @NotNull Disposable parentDisposable, Pair<VirtualFile, String> fileAndUrl) {
+ VirtualFilePointerImpl(@Nullable VirtualFilePointerListener listener) {
super(TRACE_CREATION);
myListener = listener;
}
}
@NotNull
- String getUrlNoUpdate() {
+ private String getUrlNoUpdate() {
return isDisposed() ? "" : myNode.myFileAndUrl.second;
}
VirtualFilePointerListener getListener() {
return myListener;
}
+
+ int incrementUsageCount(int delta) {
+ return myNode.incrementUsageCount(delta);
+ }
}
if (fileSystem == TEMP_FILE_SYSTEM) {
// for tests, recreate always
VirtualFile found = file == null ? VirtualFileManager.getInstance().findFileByUrl(url) : file;
- return new IdentityVirtualFilePointer(found, url);
+ return found == null ? new LightFilePointer(url) : new LightFilePointer(found);
}
boolean isJar = fileSystem == JAR_FILE_SYSTEM;
// we are unable to track alien file systems for now
VirtualFile found = fileSystem == null ? null : file != null ? file : VirtualFileManager.getInstance().findFileByUrl(url);
// if file is null, this pointer will never be alive
- return getOrCreateIdentity(url, found);
+ return getOrCreateIdentity(url, found, parentDisposable, listener);
}
if (file == null) {
}
}
// else url has come from VirtualFile.getPath() and is good enough
-
- VirtualFilePointerImpl pointer = getOrCreate(parentDisposable, listener, path, Pair.create(file, url));
+ VirtualFilePointerImpl pointer = getOrCreate(listener, path, Pair.create(file, url));
DelegatingDisposable.registerDisposable(parentDisposable, pointer);
return pointer;
}
private final Map<String, IdentityVirtualFilePointer> myUrlToIdentity = new THashMap<>();
@NotNull
- private IdentityVirtualFilePointer getOrCreateIdentity(@NotNull String url, @Nullable VirtualFile found) {
- return myUrlToIdentity.computeIfAbsent(url, __ -> new IdentityVirtualFilePointer(found, url));
+ private IdentityVirtualFilePointer getOrCreateIdentity(@NotNull String url,
+ @Nullable VirtualFile found,
+ @NotNull Disposable parentDisposable,
+ VirtualFilePointerListener listener) {
+ IdentityVirtualFilePointer pointer = myUrlToIdentity.get(url);
+ if (pointer == null) {
+ pointer = new IdentityVirtualFilePointer(found, url,listener){
+ @Override
+ public void dispose() {
+ synchronized (VirtualFilePointerManagerImpl.this) {
+ super.dispose();
+ myUrlToIdentity.remove(url);
+ }
+ }
+ };
+ myUrlToIdentity.put(url, pointer);
+
+ DelegatingDisposable.registerDisposable(parentDisposable, pointer);
+ }
+ pointer.incrementUsageCount(1);
+ return pointer;
}
@NotNull
}
@NotNull
- private VirtualFilePointerImpl getOrCreate(@NotNull Disposable parentDisposable,
- @Nullable VirtualFilePointerListener listener,
+ private VirtualFilePointerImpl getOrCreate(@Nullable VirtualFilePointerListener listener,
@NotNull String path,
@NotNull Pair<VirtualFile, String> fileAndUrl) {
FilePointerPartNode root = myPointers.get(listener);
VirtualFilePointerImpl pointer = node.getAnyPointer();
if (pointer == null) {
- pointer = new VirtualFilePointerImpl(listener, parentDisposable, fileAndUrl);
+ pointer = new VirtualFilePointerImpl(listener);
node.associate(pointer, fileAndUrl);
}
- pointer.myNode.incrementUsageCount(1);
+ pointer.incrementUsageCount(1);
root.checkConsistency();
return pointer;
private static class DelegatingDisposable implements Disposable {
private static final ConcurrentMap<Disposable, DelegatingDisposable> ourInstances = ContainerUtil.newConcurrentMap(ContainerUtil.<Disposable>identityStrategy());
- private final TObjectIntHashMap<VirtualFilePointerImpl> myCounts = new TObjectIntHashMap<>();
+ private final TObjectIntHashMap<VirtualFilePointerImpl> myCounts = new TObjectIntHashMap<>(); // guarded by this
private final Disposable myParent;
private DelegatingDisposable(@NotNull Disposable parent) {
ourInstances.remove(myParent);
synchronized (this) {
myCounts.forEachEntry((pointer, disposeCount) -> {
- int after = pointer.myNode.incrementUsageCount(-disposeCount + 1);
+ int after = pointer.incrementUsageCount(-disposeCount + 1);
LOG.assertTrue(after > 0, after);
pointer.dispose();
return true;
}
return number;
}
+
@TestOnly
int numberOfListeners() {
return myPointers.keySet().size();
}
+
+ @TestOnly
+ int numberOfCachedUrlToIdentity() {
+ return myUrlToIdentity.size();
+ }
}
import com.intellij.concurrency.Job;
import com.intellij.concurrency.JobLauncher;
+import com.intellij.mock.MockVirtualFile;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.PathManagerEx;
VirtualFilePointer pointer = myVirtualFilePointerManager.create(dir2.getUrl() + "/../" + dir1.getName() + "/" + file.getName(), disposable, null);
assertEquals(file, pointer.getFile());
}
+
+ public void testAlienVirtualFileSystemPointerRemovedFromUrlToIdentityCacheOnDispose() throws IOException {
+ VirtualFile mockVirtualFile = new MockVirtualFile("test_name", "test_text");
+ Disposable disposable1 = Disposer.newDisposable();
+ final VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(mockVirtualFile, disposable1, null);
+
+ assertInstanceOf(pointer, IdentityVirtualFilePointer.class);
+ assertTrue(pointer.isValid());
+
+ VirtualFile virtualFileWithSameUrl = new MockVirtualFile("test_name", "test_text");
+ VirtualFilePointer updatedPointer = VirtualFilePointerManager.getInstance().create(virtualFileWithSameUrl, disposable1, null);
+
+ assertInstanceOf(updatedPointer, IdentityVirtualFilePointer.class);
+
+ assertEquals(1, ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).numberOfCachedUrlToIdentity());
+
+ Disposer.dispose(disposable1);
+
+ assertEquals(0, ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).numberOfCachedUrlToIdentity());
+ }
}