VCS: changes view, for VirtualFiles in tree, don't wrap them into Filepath just to...
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / FilePathImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.vcs;
17
18 import com.intellij.openapi.editor.Document;
19 import com.intellij.openapi.fileEditor.FileDocumentManager;
20 import com.intellij.openapi.fileTypes.FileType;
21 import com.intellij.openapi.fileTypes.FileTypeManager;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.io.FileUtil;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.openapi.vfs.LocalFileSystem;
26 import com.intellij.openapi.vfs.VfsUtil;
27 import com.intellij.openapi.vfs.VirtualFile;
28 import com.intellij.openapi.vfs.encoding.EncodingManager;
29 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
30 import org.jetbrains.annotations.NonNls;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.lang.reflect.Constructor;
37 import java.nio.charset.Charset;
38
39 public class FilePathImpl implements FilePath {
40   private VirtualFile myVirtualFile;
41   private VirtualFile myVirtualParent;
42   private final String myName;
43   @NotNull private final File myFile;
44   private boolean myIsDirectory;
45   private boolean myNonLocal;
46
47   private FilePathImpl(VirtualFile virtualParent, String name, final boolean isDirectory, VirtualFile child, final boolean forDeleted) {
48     myVirtualParent = virtualParent;
49     myName = name;
50     myIsDirectory = isDirectory;
51     if (myVirtualParent == null) {
52       myFile = new File(myName);
53     }
54     else {
55       myFile = new File(new File(myVirtualParent.getPath()), myName);
56     }
57
58     if (! forDeleted) {
59       if (child == null) {
60         refresh();
61       }
62       else {
63         myVirtualFile = child;
64       }
65     }
66   }
67
68   @Heavy
69   public FilePathImpl(VirtualFile virtualParent, String name, final boolean isDirectory) {
70     this(virtualParent, name, isDirectory, null, false);
71   }
72
73   @Heavy
74   private FilePathImpl(VirtualFile virtualParent, String name, final boolean isDirectory, final boolean forDeleted) {
75     this(virtualParent, name, isDirectory, null, forDeleted);
76   }
77
78   public FilePathImpl(final File file, final boolean isDirectory) {
79     myFile = file;
80     myName = file.getName();
81     myIsDirectory = isDirectory;
82   }
83
84   public FilePathImpl(@NotNull VirtualFile virtualFile) {
85     this(virtualFile.getParent(), virtualFile.getName(), virtualFile.isDirectory(), virtualFile, false);
86   }
87
88   public int hashCode() {
89     return StringUtil.stringHashCodeInsensitive(myFile.getPath());
90   }
91
92   public boolean equals(Object o) {
93     if (!(o instanceof FilePath)) {
94       return false;
95     }
96     else {
97       return myFile.equals(((FilePath)o).getIOFile());
98     }
99   }
100
101   public void refresh() {
102     if (!myNonLocal) {
103       if (myVirtualParent == null) {
104         myVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile);
105       }
106       else {
107         myVirtualFile = myVirtualParent.findChild(myName);
108       }
109     }
110   }
111
112   public String getPath() {
113     final VirtualFile virtualFile = myVirtualFile;
114     if (virtualFile != null && virtualFile.isValid()) {
115       return virtualFile.getPath();
116     }
117     else {
118       return myFile.getPath();
119     }
120   }
121
122   public boolean isDirectory() {
123     if (myVirtualFile == null) {
124       return myIsDirectory;
125     }
126     else {
127       return myVirtualFile.isDirectory();
128     }
129   }
130
131   public boolean isUnder(FilePath parent, boolean strict) {
132     if (myVirtualFile != null && parent.getVirtualFile() != null) {
133       return VfsUtil.isAncestor(parent.getVirtualFile(), myVirtualFile, strict);
134     }
135
136     try {
137       return FileUtil.isAncestor(parent.getIOFile(), getIOFile(), strict);
138     }
139     catch (IOException e) {
140       return false;
141     }
142   }
143
144   public FilePath getParentPath() {
145     if (myVirtualParent != null && myVirtualParent.isValid()) {
146       return new FilePathImpl(myVirtualParent);
147     }
148
149     // can't use File.getParentPath() because the path may not correspond to an actual file on disk,
150     // and adding a drive letter would not be appropriate (IDEADEV-7405)
151     // path containing exactly one separator is assumed to be root path
152     final String path = myFile.getPath();
153     int pos = path.lastIndexOf(File.separatorChar);
154     if (pos < 0 || pos == path.indexOf(File.separatorChar)) {
155       return null;
156     }
157     return new FilePathImpl(new File(path.substring(0, pos)), true);
158   }
159
160   public VirtualFile getVirtualFile() {
161     if (myVirtualFile != null && !myVirtualFile.isValid()) {
162       myVirtualFile = null;
163     }
164     return myVirtualFile;
165   }
166
167   public VirtualFile getVirtualFileParent() {
168     if (myVirtualParent != null && !myVirtualParent.isValid()) {
169       myVirtualParent = null;
170     }
171     return myVirtualParent;
172   }
173
174   @NotNull
175   public File getIOFile() {
176     return myFile;
177   }
178
179   public String getName() {
180     return myName;
181   }
182
183   public String getPresentableUrl() {
184     if (myVirtualFile == null) {
185       return myFile.getAbsolutePath();
186     }
187     else {
188       return myVirtualFile.getPresentableUrl();
189     }
190   }
191
192   @Nullable
193   public Document getDocument() {
194     if ((myVirtualFile == null) || (myVirtualFile.getFileType().isBinary())) {
195       return null;
196     }
197     return FileDocumentManager.getInstance().getDocument(myVirtualFile);
198   }
199
200   public Charset getCharset() {
201     return getCharset(null);
202   }
203
204   public Charset getCharset(Project project) {
205     // try to find existing virtual file
206     VirtualFile existing = myVirtualFile != null && myVirtualFile.isValid() ? myVirtualFile : null;
207     if (existing == null) {
208       LocalFileSystem lfs = LocalFileSystem.getInstance();
209       for (File f = myFile; f != null; f = f.getParentFile()) {
210         existing = lfs.findFileByIoFile(f);
211         if (existing != null && existing.isValid()) {
212           break;
213         }
214       }
215     }
216     if (existing != null) {
217       Charset rc = existing.getCharset();
218       if (rc != null) {
219         return rc;
220       }
221     }
222     EncodingManager e = project != null ? EncodingProjectManager.getInstance(project) : null;
223     if (e == null) {
224       e = EncodingManager.getInstance();
225     }
226     return e.getDefaultCharset();
227   }
228
229   public FileType getFileType() {
230     return myVirtualFile != null ? myVirtualFile.getFileType() : FileTypeManager.getInstance().getFileTypeByFileName(myFile.getName());
231   }
232
233   public static FilePathImpl create(File selectedFile) {
234     return create(selectedFile, false);
235   }
236
237   public static FilePathImpl create(File selectedFile, boolean isDirectory) {
238     if (selectedFile == null) {
239       return null;
240     }
241
242     LocalFileSystem lfs = LocalFileSystem.getInstance();
243
244     VirtualFile virtualFile = lfs.findFileByIoFile(selectedFile);
245     if (virtualFile != null) {
246       return new FilePathImpl(virtualFile);
247     }
248
249     return createForDeletedFile(selectedFile, isDirectory);
250   }
251
252   public static FilePathImpl createForDeletedFile(final File selectedFile, final boolean isDirectory) {
253     LocalFileSystem lfs = LocalFileSystem.getInstance();
254
255     File parentFile = selectedFile.getParentFile();
256     if (parentFile == null) {
257       return new FilePathImpl(selectedFile, isDirectory);
258     }
259
260     VirtualFile virtualFileParent = lfs.findFileByIoFile(parentFile);
261     if (virtualFileParent != null) {
262       return new FilePathImpl(virtualFileParent, selectedFile.getName(), isDirectory, true);
263     }
264     else {
265       return new FilePathImpl(selectedFile, isDirectory);
266     }
267   }
268
269   public static FilePath createOn(String s) {
270     File ioFile = new File(s);
271     final LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
272     VirtualFile virtualFile = localFileSystem.findFileByIoFile(ioFile);
273     if (virtualFile != null) {
274       return new FilePathImpl(virtualFile);
275     }
276     else {
277       VirtualFile virtualFileParent = localFileSystem.findFileByIoFile(ioFile.getParentFile());
278       if (virtualFileParent != null) {
279         return new FilePathImpl(virtualFileParent, ioFile.getName(), false);
280       }
281       else {
282         return null;
283       }
284     }
285   }
286
287   private static Constructor<File> ourFileStringConstructor;
288   private static boolean ourFileStringConstructorInitialized;
289
290   @NotNull
291   public static FilePath createNonLocal(String path, final boolean directory) {
292     path = path.replace('/', File.separatorChar);
293     // avoid filename normalization (IDEADEV-10548)
294     if (!ourFileStringConstructorInitialized) {
295       ourFileStringConstructorInitialized = true;
296       try {
297         ourFileStringConstructor = File.class.getDeclaredConstructor(String.class, int.class);
298         ourFileStringConstructor.setAccessible(true);
299       }
300       catch (Exception ex) {
301         ourFileStringConstructor = null;
302       }
303     }
304     File file = null;
305     try {
306       if (ourFileStringConstructor != null) {
307         file = ourFileStringConstructor.newInstance(path, 1);
308       }
309     }
310     catch (Exception ex) {
311       // reflection call failed, try regular call
312     }
313     if (file == null) {
314       file = new File(path);
315     }
316     FilePathImpl result = new FilePathImpl(file, directory);
317     result.myNonLocal = true;
318     return result;
319   }
320
321   @Override
322   @NonNls
323   public String toString() {
324     return "FilePath[" + myFile + "]";
325   }
326
327   public boolean isNonLocal() {
328     return myNonLocal;
329   }
330 }