blob: 33ecf625c05da21393f83c9f38e1f324dd49b7be [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.ex.temp;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.local.LocalFileSystemBase;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.openapi.vfs.newvfs.VfsImplUtil;
import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile;
import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.LocalTimeCounter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* @author max
*/
public class TempFileSystem extends LocalFileSystemBase {
private final FSItem myRoot = new FSDir(null, "/");
@SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
public static TempFileSystem getInstance() {
return ApplicationManager.getApplication().getComponent(TempFileSystem.class);
}
@NotNull
@Override
protected String extractRootPath(@NotNull final String path) {
return "/";
}
@Nullable
private FSItem convert(VirtualFile file) {
final VirtualFile parentFile = file.getParent();
if (parentFile == null) return myRoot;
FSItem parentItem = convert(parentFile);
if (parentItem == null || !parentItem.isDirectory()) {
return null;
}
return parentItem.findChild(file.getName());
}
@Override
@NotNull
public VirtualFile createChildDirectory(Object requestor, @NotNull VirtualFile parent, @NotNull String dir) throws IOException {
final FSItem fsItem = convert(parent);
assert fsItem != null && fsItem.isDirectory();
final FSDir fsDir = (FSDir)fsItem;
final FSItem existingDir = fsDir.findChild(dir);
if (existingDir == null) {
fsDir.addChild(new FSDir(fsDir, dir));
}
else if (!existingDir.isDirectory()) {
throw new IOException("Directory already contains a file named " + dir);
}
return new FakeVirtualFile(parent, dir);
}
@NotNull
@Override
public VirtualFile createChildFile(Object requestor, @NotNull VirtualFile parent, @NotNull String file) throws IOException {
final FSItem fsItem = convert(parent);
if (fsItem == null) {
FSRecords.invalidateCaches();
throw new IllegalStateException("cannot find parent directory: " + parent.getPath());
}
assert fsItem.isDirectory() : "parent is not a directory: " + parent.getPath();
final FSDir fsDir = (FSDir)fsItem;
assert fsDir.findChild(file) == null : "File " + file + " already exists in " + parent.getPath();
fsDir.addChild(new FSFile(fsDir, file));
return new FakeVirtualFile(parent, file);
}
@NotNull
@Override
public VirtualFile copyFile(Object requestor,
@NotNull VirtualFile file,
@NotNull VirtualFile newParent,
@NotNull String copyName) throws IOException {
return VfsUtilCore.copyFile(requestor, file, newParent, copyName);
}
@Override
public void deleteFile(final Object requestor, @NotNull final VirtualFile file) throws IOException {
final FSItem fsItem = convert(file);
if (fsItem == null) {
FSRecords.invalidateCaches();
throw new IllegalStateException("failed to delete file " + file.getPath());
}
fsItem.getParent().removeChild(fsItem);
}
@Override
public void moveFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final VirtualFile newParent) throws IOException {
final FSItem fsItem = convert(file);
assert fsItem != null : "failed to move file " + file.getPath();
final FSItem newParentItem = convert(newParent);
assert newParentItem != null && newParentItem.isDirectory() : "failed to find move target " + file.getPath();
FSDir newDir = (FSDir)newParentItem;
if (newDir.findChild(file.getName()) != null) {
throw new IOException("Directory already contains a file named " + file.getName());
}
fsItem.getParent().removeChild(fsItem);
newDir.addChild(fsItem);
fsItem.myParent = newDir;
}
@Override
public void renameFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final String newName) throws IOException {
final FSItem fsItem = convert(file);
assert fsItem != null;
fsItem.setName(newName);
}
@Override
@NotNull
public String getProtocol() {
return "temp";
}
@Override
public boolean exists(@NotNull final VirtualFile fileOrDirectory) {
return convert(fileOrDirectory) != null;
}
@Override
@NotNull
public String[] list(@NotNull final VirtualFile file) {
final FSItem fsItem = convert(file);
assert fsItem != null;
return fsItem.list();
}
@Override
public boolean isDirectory(@NotNull final VirtualFile file) {
return convert(file) instanceof FSDir;
}
@Override
public long getTimeStamp(@NotNull final VirtualFile file) {
final FSItem fsItem = convert(file);
assert fsItem != null : "cannot find item for path " + file.getPath();
return fsItem.myTimestamp;
}
@Override
public void setTimeStamp(@NotNull final VirtualFile file, final long timeStamp) {
final FSItem fsItem = convert(file);
assert fsItem != null;
fsItem.myTimestamp = timeStamp > 0 ? timeStamp : LocalTimeCounter.currentTime();
}
@Override
public boolean isWritable(@NotNull final VirtualFile file) {
final FSItem fsItem = convert(file);
assert fsItem != null;
return fsItem.myWritable;
}
@Override
public void setWritable(@NotNull final VirtualFile file, final boolean writableFlag) throws IOException {
final FSItem fsItem = convert(file);
assert fsItem != null;
fsItem.myWritable = writableFlag;
}
@Override
@NotNull
public byte[] contentsToByteArray(@NotNull final VirtualFile file) throws IOException {
final FSItem fsItem = convert(file);
if (fsItem == null) throw new FileNotFoundException("Cannot find temp for " + file.getPath());
assert fsItem instanceof FSFile : fsItem;
return ((FSFile)fsItem).myContent;
}
@Override
@NotNull
public InputStream getInputStream(@NotNull final VirtualFile file) throws IOException {
return new BufferExposingByteArrayInputStream(contentsToByteArray(file));
}
@Override
@NotNull
public OutputStream getOutputStream(@NotNull final VirtualFile file,
final Object requestor,
final long modStamp,
final long timeStamp) throws IOException {
return new ByteArrayOutputStream() {
@Override
public void close() throws IOException {
super.close();
final FSItem fsItem = convert(file);
assert fsItem instanceof FSFile;
((FSFile)fsItem).myContent = toByteArray();
setTimeStamp(file, modStamp);
}
};
}
@Override
public void refresh(final boolean asynchronous) {
RefreshQueue.getInstance().refresh(asynchronous, true, null, ManagingFS.getInstance().getRoots(this));
}
@Override
public VirtualFile findFileByPath(@NotNull @NonNls String path) {
return VfsImplUtil.findFileByPath(this, path);
}
@Override
public VirtualFile findFileByPathIfCached(@NotNull @NonNls String path) {
return VfsImplUtil.findFileByPathIfCached(this, path);
}
@Override
public VirtualFile refreshAndFindFileByPath(@NotNull String path) {
return VfsImplUtil.refreshAndFindFileByPath(this, path);
}
@Override
public long getLength(@NotNull final VirtualFile file) {
try {
return contentsToByteArray(file).length;
}
catch (IOException e) {
return 0;
}
}
private abstract static class FSItem {
private FSDir myParent;
private String myName;
private long myTimestamp;
private boolean myWritable;
protected FSItem(final FSDir parent, final String name) {
myParent = parent;
myName = name;
myTimestamp = LocalTimeCounter.currentTime();
myWritable = true;
}
public abstract boolean isDirectory();
@Nullable
public FSItem findChild(final String name) {
return null;
}
public void setName(final String name) {
myName = name;
}
public FSDir getParent() {
return myParent;
}
public String[] list() {
return ArrayUtil.EMPTY_STRING_ARRAY;
}
@Override
public String toString() {
return getClass().getSimpleName() + ": " + myName;
}
}
private static class FSDir extends FSItem {
private final List<FSItem> myChildren = new ArrayList<FSItem>();
public FSDir(final FSDir parent, final String name) {
super(parent, name);
}
@Override
@Nullable
public FSItem findChild(final String name) {
for (FSItem child : myChildren) {
if (name.equals(child.myName)) {
return child;
}
}
return null;
}
@Override
public boolean isDirectory() {
return true;
}
public void addChild(final FSItem item) {
myChildren.add(item);
}
public void removeChild(final FSItem fsItem) {
if (fsItem.myName.equals("src") && getParent() == null) {
throw new RuntimeException("removing src directory");
}
myChildren.remove(fsItem);
}
@Override
public String[] list() {
String[] names = ArrayUtil.newStringArray(myChildren.size());
for (int i = 0; i < names.length; i++) {
names[i] = myChildren.get(i).myName;
}
return names;
}
}
private static class FSFile extends FSItem {
public FSFile(final FSDir parent, final String name) {
super(parent, name);
}
private byte[] myContent = new byte[0];
@Override
public boolean isDirectory() {
return false;
}
}
@Override
public FileAttributes getAttributes(@NotNull final VirtualFile file) {
final FSItem item = convert(file);
if (item == null) return null;
final long length = item instanceof FSFile ? ((FSFile)item).myContent.length : 0;
return new FileAttributes(item.isDirectory(), false, false, false, length, item.myTimestamp, item.myWritable);
}
@NotNull
@Override
public Set<WatchRequest> addRootsToWatch(@NotNull Collection<String> rootPaths, boolean watchRecursively) {
throw new IncorrectOperationException();
}
@Override
public void removeWatchedRoots(@NotNull Collection<WatchRequest> watchRequests) {
throw new IncorrectOperationException();
}
@Override
public Set<WatchRequest> replaceWatchedRoots(@NotNull Collection<WatchRequest> watchRequests,
@Nullable Collection<String> recursiveRoots,
@Nullable Collection<String> flatRoots) {
throw new IncorrectOperationException();
}
@Nullable
@Override
protected String normalize(@NotNull String path) {
return path;
}
}