/* * Copyright 2000-2012 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.vcs; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.encoding.EncodingProjectManager; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.lang.reflect.Constructor; import java.nio.charset.Charset; public class FilePathImpl implements FilePath { private VirtualFile myVirtualFile; private VirtualFile myVirtualParent; private final String myName; @NotNull private final File myFile; private boolean myIsDirectory; private final boolean myLocal; private FilePathImpl(VirtualFile virtualParent, @NotNull String name, final boolean isDirectory, VirtualFile child, final boolean forDeleted) { this(fileFromVirtual(virtualParent, child, name), isDirectory, true); myVirtualParent = virtualParent; if (!forDeleted) { if (child == null) { refresh(); } else { myVirtualFile = child; } } } private static File fileFromVirtual(VirtualFile virtualParent, final VirtualFile child, String name) { assert virtualParent != null || child != null; if (virtualParent != null) { return new File(virtualParent.getPath(), name); } return new File(child.getPath()); } private void detectFileType() { VirtualFile file = myVirtualFile; if (file == null || !file.isValid() || file.isDirectory()) return; FileType fileType = file.getFileType(); if (fileType == UnknownFileType.INSTANCE) { FileTypeRegistry.getInstance().detectFileTypeFromContent(file); } } @Heavy public FilePathImpl(@NotNull VirtualFile virtualParent, String name, final boolean isDirectory) { this(virtualParent, name, isDirectory, null, false); } @Heavy private FilePathImpl(@NotNull VirtualFile virtualParent, String name, final boolean isDirectory, final boolean forDeleted) { this(virtualParent, name, isDirectory, null, forDeleted); } public FilePathImpl(@NotNull File file, final boolean isDirectory) { this(file, isDirectory, true); } private FilePathImpl(@NotNull File file, final boolean isDirectory, boolean local) { myFile = file; myName = file.getName(); myIsDirectory = isDirectory; myLocal = local; } public FilePathImpl(@NotNull VirtualFile virtualFile) { this(virtualFile.getParent(), virtualFile.getName(), virtualFile.isDirectory(), virtualFile, false); } public FilePath createChild(final String subPath, final boolean isDirectory) { if (StringUtil.isEmptyOrSpaces(subPath)) return this; if (getVirtualFile() != null && subPath.indexOf('/') == -1 && subPath.indexOf('\\') == -1) { return new FilePathImpl(getVirtualFile(), subPath, isDirectory, true); } else { return new FilePathImpl(new File(getIOFile(), subPath), isDirectory); } } public int hashCode() { return StringUtil.stringHashCodeInsensitive(myFile.getPath()); } public boolean equals(Object o) { if (!(o instanceof FilePath)) { return false; } else { if (! isSpecialName(myName) && ! isSpecialName(((FilePath)o).getName()) && ! Comparing.equal(myName, ((FilePath)o).getName())) return false; return myFile.equals(((FilePath)o).getIOFile()); } } private static boolean isSpecialName(final String name) { return ".".equals(name) || "..".equals(name); } @Override public void refresh() { if (myLocal) { if (myVirtualParent == null) { myVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile); } else { myVirtualFile = myVirtualParent.findChild(myName); } } } @Override public void hardRefresh() { if (myLocal && (myVirtualFile == null || ! myVirtualFile.isValid())) { myVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myFile); } } @Override public String getPath() { final VirtualFile virtualFile = myVirtualFile; if (virtualFile != null && virtualFile.isValid()) { return virtualFile.getPath(); } else { return myFile.getPath(); } } public void setIsDirectory(boolean isDirectory) { myIsDirectory = isDirectory; } @Override public boolean isDirectory() { if (myVirtualFile == null) { return myIsDirectory; } else { return myVirtualFile.isDirectory(); } } @Override public boolean isUnder(FilePath parent, boolean strict) { if (myVirtualFile != null) { final VirtualFile parentFile = parent.getVirtualFile(); if (parentFile != null) { return VfsUtilCore.isAncestor(parentFile, myVirtualFile, strict); } } return FileUtil.isAncestor(parent.getIOFile(), getIOFile(), strict); } @Override public FilePath getParentPath() { if (myVirtualParent != null && myVirtualParent.isValid() && myVirtualParent.getParent() != null) { return new FilePathImpl(myVirtualParent); } // can't use File.getParentPath() because the path may not correspond to an actual file on disk, // and adding a drive letter would not be appropriate (IDEADEV-7405) // path containing exactly one separator is assumed to be root path final String path = myFile.getPath(); int pos = path.lastIndexOf(File.separatorChar); if (pos < 0 || pos == path.indexOf(File.separatorChar)) { return null; } return new FilePathImpl(new File(path.substring(0, pos)), true); } @Override @Nullable public VirtualFile getVirtualFile() { if (myVirtualFile != null && !myVirtualFile.isValid()) { myVirtualFile = null; } detectFileType(); return myVirtualFile; } @Override @Nullable public VirtualFile getVirtualFileParent() { if (myVirtualParent != null && !myVirtualParent.isValid()) { myVirtualParent = null; } return myVirtualParent; } @Override @NotNull public File getIOFile() { return myFile; } @Override public String getName() { return myName; } @Override public String getPresentableUrl() { if (myVirtualFile == null || !myVirtualFile.isValid()) { return myFile.getAbsolutePath(); } else { return myVirtualFile.getPresentableUrl(); } } @Override @Nullable public Document getDocument() { if (myVirtualFile == null || myVirtualFile.getFileType().isBinary()) { return null; } return FileDocumentManager.getInstance().getDocument(myVirtualFile); } @Override public Charset getCharset() { return getCharset(null); } @Override public Charset getCharset(Project project) { // try to find existing virtual file VirtualFile existing = myVirtualFile != null && myVirtualFile.isValid() ? myVirtualFile : null; if (existing == null) { LocalFileSystem lfs = LocalFileSystem.getInstance(); for (File f = myFile; f != null; f = f.getParentFile()) { existing = lfs.findFileByIoFile(f); if (existing != null && existing.isValid()) { break; } } } if (existing != null) { Charset rc = existing.getCharset(); if (rc != null) { return rc; } } EncodingManager e = project != null ? EncodingProjectManager.getInstance(project) : null; if (e == null) { e = EncodingManager.getInstance(); } return e.getDefaultCharset(); } @Override public FileType getFileType() { return myVirtualFile != null ? myVirtualFile.getFileType() : FileTypeManager.getInstance().getFileTypeByFileName(myFile.getName()); } public static FilePathImpl create(VirtualFile file) { return create(VfsUtilCore.virtualToIoFile(file), file.isDirectory()); } public static FilePathImpl create(File selectedFile) { return create(selectedFile, false); } public static FilePathImpl create(File selectedFile, boolean isDirectory) { if (selectedFile == null) { return null; } LocalFileSystem lfs = LocalFileSystem.getInstance(); VirtualFile virtualFile = lfs.findFileByIoFile(selectedFile); if (virtualFile != null) { return new FilePathImpl(virtualFile); } return createForDeletedFile(selectedFile, isDirectory); } public static FilePathImpl createForDeletedFile(final File selectedFile, final boolean isDirectory) { LocalFileSystem lfs = LocalFileSystem.getInstance(); File parentFile = selectedFile.getParentFile(); if (parentFile == null) { return new FilePathImpl(selectedFile, isDirectory); } VirtualFile virtualFileParent = lfs.findFileByIoFile(parentFile); if (virtualFileParent != null) { return new FilePathImpl(virtualFileParent, selectedFile.getName(), isDirectory, true); } else { return new FilePathImpl(selectedFile, isDirectory); } } public static FilePath createOn(String s) { File ioFile = new File(s); final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); VirtualFile virtualFile = localFileSystem.findFileByIoFile(ioFile); if (virtualFile != null) { return new FilePathImpl(virtualFile); } else { VirtualFile virtualFileParent = localFileSystem.findFileByIoFile(ioFile.getParentFile()); if (virtualFileParent != null) { return new FilePathImpl(virtualFileParent, ioFile.getName(), false); } else { return null; } } } private static Constructor<File> ourFileStringConstructor; private static boolean ourFileStringConstructorInitialized; @NotNull public static FilePath createNonLocal(String path, final boolean directory) { path = path.replace('/', File.separatorChar); // avoid filename normalization (IDEADEV-10548) if (!ourFileStringConstructorInitialized) { ourFileStringConstructorInitialized = true; try { ourFileStringConstructor = File.class.getDeclaredConstructor(String.class, int.class); ourFileStringConstructor.setAccessible(true); } catch (Exception ex) { ourFileStringConstructor = null; } } File file = null; try { if (ourFileStringConstructor != null) { file = ourFileStringConstructor.newInstance(path, 1); } } catch (Exception ex) { // reflection call failed, try regular call } if (file == null) { file = new File(path); } return new FilePathImpl(file, directory, false); } @Override @NonNls public String toString() { return "FilePath[" + myFile.getName() + "] (" + myFile.getParent() + ")"; } @Override public boolean isNonLocal() { return !myLocal; } }