blob: faa1c63c60198acd322e50bf3827e586b7830514 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vfs.newvfs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.ZipFileCache;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VFileProperty;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.impl.ArchiveHandler;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class VfsImplUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.VfsImplUtil");
private static final String FILE_SEPARATORS = "/" + File.separator;
private VfsImplUtil() { }
@Nullable
public static NewVirtualFile findFileByPath(@NotNull NewVirtualFileSystem vfs, @NotNull @NonNls String path) {
Pair<NewVirtualFile, Iterable<String>> data = prepare(vfs, path);
if (data == null) {
return null;
}
NewVirtualFile file = data.first;
for (String pathElement : data.second) {
if (pathElement.isEmpty() || ".".equals(pathElement)) continue;
if ("..".equals(pathElement)) {
if (file.is(VFileProperty.SYMLINK)) {
final NewVirtualFile canonicalFile = file.getCanonicalFile();
file = canonicalFile != null ? canonicalFile.getParent() : null;
}
else {
file = file.getParent();
}
}
else {
file = file.findChild(pathElement);
}
if (file == null) return null;
}
return file;
}
@Nullable
public static NewVirtualFile findFileByPathIfCached(@NotNull NewVirtualFileSystem vfs, @NotNull @NonNls String path) {
Pair<NewVirtualFile, Iterable<String>> data = prepare(vfs, path);
if (data == null) {
return null;
}
NewVirtualFile file = data.first;
for (String pathElement : data.second) {
if (pathElement.isEmpty() || ".".equals(pathElement)) continue;
if ("..".equals(pathElement)) {
if (file.is(VFileProperty.SYMLINK)) {
final String canonicalPath = file.getCanonicalPath();
final NewVirtualFile canonicalFile = canonicalPath != null ? findFileByPathIfCached(vfs, canonicalPath) : null;
file = canonicalFile != null ? canonicalFile.getParent() : null;
}
else {
file = file.getParent();
}
}
else {
file = file.findChildIfCached(pathElement);
}
if (file == null) return null;
}
return file;
}
@Nullable
public static NewVirtualFile refreshAndFindFileByPath(@NotNull NewVirtualFileSystem vfs, @NotNull @NonNls String path) {
Pair<NewVirtualFile, Iterable<String>> data = prepare(vfs, path);
if (data == null) {
return null;
}
NewVirtualFile file = data.first;
for (String pathElement : data.second) {
if (pathElement.isEmpty() || ".".equals(pathElement)) continue;
if ("..".equals(pathElement)) {
if (file.is(VFileProperty.SYMLINK)) {
final String canonicalPath = file.getCanonicalPath();
final NewVirtualFile canonicalFile = canonicalPath != null ? refreshAndFindFileByPath(vfs, canonicalPath) : null;
file = canonicalFile != null ? canonicalFile.getParent() : null;
}
else {
file = file.getParent();
}
}
else {
file = file.refreshAndFindChild(pathElement);
}
if (file == null) return null;
}
return file;
}
@Nullable
private static Pair<NewVirtualFile, Iterable<String>> prepare(@NotNull NewVirtualFileSystem vfs, @NotNull String path) {
String normalizedPath = normalize(vfs, path);
if (StringUtil.isEmptyOrSpaces(normalizedPath)) {
return null;
}
String basePath = vfs.extractRootPath(normalizedPath);
if (basePath.length() > normalizedPath.length()) {
LOG.error(vfs + " failed to extract root path '" + basePath + "' from '" + normalizedPath + "' (original '" + path + "')");
return null;
}
NewVirtualFile root = ManagingFS.getInstance().findRoot(basePath, vfs);
if (root == null || !root.exists()) {
return null;
}
Iterable<String> parts = StringUtil.tokenize(normalizedPath.substring(basePath.length()), FILE_SEPARATORS);
return Pair.create(root, parts);
}
public static void refresh(@NotNull NewVirtualFileSystem vfs, boolean asynchronous) {
VirtualFile[] roots = ManagingFS.getInstance().getRoots(vfs);
if (roots.length > 0) {
RefreshQueue.getInstance().refresh(asynchronous, true, null, roots);
}
}
@Nullable
public static String normalize(@NotNull NewVirtualFileSystem vfs, @NotNull String path) {
return vfs.normalize(path);
}
private static final AtomicBoolean ourSubscribed = new AtomicBoolean(false);
private static final Object ourLock = new Object();
private static final Map<String, Pair<ArchiveFileSystem, ArchiveHandler>> ourHandlers = ContainerUtil.newTroveMap(FileUtil.PATH_HASHING_STRATEGY);
@NotNull
public static <T extends ArchiveHandler> T getHandler(@NotNull VirtualFile entryFile,
@NotNull ArchiveFileSystem vfs,
@NotNull Function<String, T> producer) {
checkSubscription();
String rootPath = vfs.extractRootPath(entryFile.getPath());
ArchiveHandler handler;
boolean refresh = false;
synchronized (ourLock) {
Pair<ArchiveFileSystem, ArchiveHandler> record = ourHandlers.get(rootPath);
if (record == null) {
handler = producer.fun(rootPath);
record = Pair.create(vfs, handler);
ourHandlers.put(rootPath, record);
refresh = true;
}
handler = record.second;
}
if (refresh) {
final File file = handler.getFile();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
}
}, ModalityState.defaultModalityState());
}
@SuppressWarnings("unchecked") T t = (T)handler;
return t;
}
private static void checkSubscription() {
if (ourSubscribed.getAndSet(true)) return;
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
bus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
Map<String, VirtualFile> rootsToRefresh = null;
synchronized (ourLock) {
String[] rootPaths = ArrayUtil.toStringArray(ourHandlers.keySet());
for (VFileEvent event : events) {
if (!(event.getFileSystem() instanceof LocalFileSystem)) continue;
String path = event.getPath();
for (int i = 0; i < rootPaths.length; i++) {
String rootPath = rootPaths[i];
if (rootPath == null) continue;
ArchiveFileSystem vfs = ourHandlers.get(rootPath).first;
String localPath = vfs.extractLocalPath(rootPath);
if (FileUtil.startsWith(localPath, path)) {
ourHandlers.remove(rootPath);
NewVirtualFile root = ManagingFS.getInstance().findRoot(rootPath, vfs);
if (root != null) {
root.markDirtyRecursively();
if (rootsToRefresh == null) rootsToRefresh = ContainerUtil.newHashMap();
rootsToRefresh.put(localPath, root);
}
rootPaths[i] = null;
}
}
}
}
if (rootsToRefresh != null) {
ZipFileCache.reset(rootsToRefresh.keySet());
boolean async = !ApplicationManager.getApplication().isUnitTestMode();
RefreshQueue.getInstance().refresh(async, true, null, rootsToRefresh.values());
}
}
});
}
}