reverted
[idea/community.git] / plugins / hg4idea / src / org / zmlx / hg4idea / HgUtil.java
1 // Copyright 2010 Victor Iacoban
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software distributed under
10 // the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 // either express or implied. See the License for the specific language governing permissions and
12 // limitations under the License.
13 package org.zmlx.hg4idea;
14
15 import com.intellij.openapi.application.ApplicationManager;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.openapi.util.ShutDownTracker;
18 import com.intellij.openapi.vcs.FilePath;
19 import com.intellij.openapi.vcs.VcsException;
20 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
21 import com.intellij.openapi.vfs.VirtualFile;
22 import com.intellij.ui.GuiUtils;
23 import com.intellij.vcsUtil.VcsUtil;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
26 import org.zmlx.hg4idea.command.HgRemoveCommand;
27 import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand;
28
29 import java.io.*;
30 import java.lang.reflect.InvocationTargetException;
31 import java.util.List;
32
33 /**
34  * <strong><font color="#FF0000">TODO JavaDoc.</font></strong>
35  */
36 public abstract class HgUtil {
37   
38   public static File copyResourceToTempFile(String basename, String extension) throws IOException {
39     final InputStream in = HgUtil.class.getClassLoader().getResourceAsStream("python/" + basename + extension);
40
41     final File tempFile = File.createTempFile(basename, extension);
42     final byte[] buffer = new byte[4096];
43
44     OutputStream out = null;
45     try {
46       out = new FileOutputStream(tempFile, false);
47       int bytesRead;
48       while ((bytesRead = in.read(buffer)) != -1)
49         out.write(buffer, 0, bytesRead);
50     } finally {
51       try {
52         out.close();
53       }
54       catch (IOException e) {
55         // ignore
56       }
57     }
58     try {
59       in.close();
60     }
61     catch (IOException e) {
62       // ignore
63     }
64     tempFile.deleteOnExit();
65     return tempFile;
66   }
67
68   public static void markDirectoryDirty(final Project project, final VirtualFile file) throws InvocationTargetException, InterruptedException {
69     ApplicationManager.getApplication().runReadAction(new Runnable() {
70       public void run() {
71         VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(file);
72       }
73     });
74     runWriteActionAndWait(new Runnable() {
75       public void run() {
76         file.refresh(true, true);
77       }
78     });
79   }
80
81   public static void markFileDirty( final Project project, final VirtualFile file ) throws InvocationTargetException, InterruptedException {
82     ApplicationManager.getApplication().runReadAction(new Runnable() {
83       public void run() {
84         VcsDirtyScopeManager.getInstance(project).fileDirty(file);
85       }
86     } );
87     runWriteActionAndWait(new Runnable() {
88       public void run() {
89         file.refresh(true, false);
90       }
91     });
92   }
93
94   /**
95    * Runs the given task as a write action in the event dispatching thread and waits for its completion.
96    */
97   public static void runWriteActionAndWait(@NotNull final Runnable runnable) throws InvocationTargetException, InterruptedException {
98     GuiUtils.runOrInvokeAndWait(new Runnable() {
99       public void run() {
100         ApplicationManager.getApplication().runWriteAction(runnable);
101       }
102     });
103   }
104
105   /**
106    * Schedules the given task to be run as a write action in the event dispatching thread.
107    */
108   public static void runWriteActionLater(@NotNull final Runnable runnable) {
109     ApplicationManager.getApplication().invokeLater(new Runnable() {
110       public void run() {
111         ApplicationManager.getApplication().runWriteAction(runnable);
112       }
113     });
114   }
115
116   /**
117    * Returns a temporary python file that will be deleted on exit.
118    * 
119    * Also all compiled version of the python file will be deleted.
120    * 
121    * @param base The basename of the file to copy
122    * @return The temporary copy the specified python file, with all the necessary hooks installed
123    * to make sure it is completely removed at shutdown
124    */
125   @Nullable
126   public static File getTemporaryPythonFile(String base) {
127     try {
128       final File file = copyResourceToTempFile(base, ".py");
129       final String fileName = file.getName();
130       ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
131         public void run() {
132           File[] files = file.getParentFile().listFiles(new FilenameFilter() {
133             public boolean accept(File dir, String name) {
134               return name.startsWith(fileName);
135             }
136           });
137           if (files != null) {
138             for (File file1 : files) {
139               file1.delete();
140             }
141           }
142         }
143       });
144       return file;
145     } catch (IOException e) {
146       return null;
147     }
148   }
149
150   /**
151    * Calls 'hg remove' to remove given files from the VCS.
152    * @param project
153    * @param files files to be removed from the VCS.
154    */
155   public static void removeFilesFromVcs(Project project, List<FilePath> files) {
156     final HgRemoveCommand command = new HgRemoveCommand(project);
157     for (FilePath filePath : files) {
158       final VirtualFile vcsRoot = VcsUtil.getVcsRootFor(project, filePath);
159       if (vcsRoot == null) {
160         continue;
161       }
162       command.execute(new HgFile(vcsRoot, filePath));
163     }
164   }
165
166
167   /**
168    * Finds the nearest parent directory which is an hg root.
169    * @param dir Directory which parent will be checked.
170    * @return Directory which is the nearest hg root being a parent of this directory,
171    * or <code>null</code> if this directory is not under hg.
172    * @see com.intellij.openapi.vcs.AbstractVcs#isVersionedDirectory(com.intellij.openapi.vfs.VirtualFile)
173    */
174   @Nullable
175   public static VirtualFile getNearestHgRoot(VirtualFile dir) {
176     VirtualFile currentDir = dir;
177     while (currentDir != null) {
178       if (isHgRoot(currentDir)) {
179         return currentDir;
180       }
181       currentDir = currentDir.getParent();
182     }
183     return null;
184   }
185
186   /**
187    * Checks if the given directory is an hg root.
188    */
189   public static boolean isHgRoot(VirtualFile dir) {
190     return dir.findChild(".hg") != null;
191   }
192
193   /**
194    * Gets the Mercurial root for the given file path or null if non exists:
195    * the root should not only be in directory mappings, but also the .hg repository folder should exist.
196    * @see #getHgRootOrThrow(com.intellij.openapi.project.Project, com.intellij.openapi.vcs.FilePath)
197    */
198   @Nullable
199   public static VirtualFile getHgRootOrNull(Project project, FilePath filePath) {
200     return getNearestHgRoot(VcsUtil.getVcsRootFor(project, filePath));
201   }
202
203   /**
204    * Gets the Mercurial root for the given file path or null if non exists:
205    * the root should not only be in directory mappings, but also the .hg repository folder should exist.
206    * @see #getHgRootOrThrow(com.intellij.openapi.project.Project, com.intellij.openapi.vcs.FilePath)
207    * @see #getHgRootOrNull(com.intellij.openapi.project.Project, com.intellij.openapi.vcs.FilePath) 
208    */
209   @Nullable
210   public static VirtualFile getHgRootOrNull(Project project, VirtualFile file) {
211     return getHgRootOrNull(project, VcsUtil.getFilePath(file.getPath()));
212   }
213
214   /**
215    * Gets the Mercurial root for the given file path or throws a VcsException if non exists:
216    * the root should not only be in directory mappings, but also the .hg repository folder should exist.
217    * @see #getHgRootOrNull(com.intellij.openapi.project.Project, com.intellij.openapi.vcs.FilePath)
218    */
219   @NotNull
220   public static VirtualFile getHgRootOrThrow(Project project, FilePath filePath) throws VcsException {
221     final VirtualFile vf = getHgRootOrNull(project, filePath);
222     if (vf == null) {
223       throw new VcsException(HgVcsMessages.message("hg4idea.exception.file.not.under.hg", filePath.getPresentableUrl()));
224     }
225     return vf;
226   }
227
228   /**
229    * Gets the Mercurial root for the given file or throws a VcsException if non exists:
230    * the root should not only be in directory mappings, but also the .hg repository folder should exist.
231    * @see #getHgRootOrNull(com.intellij.openapi.project.Project, com.intellij.openapi.vcs.FilePath)
232    */
233   @NotNull
234   public static VirtualFile getHgRootOrThrow(Project project, VirtualFile vf) throws VcsException {
235     return getHgRootOrThrow(project, VcsUtil.getFilePath(vf.getPath()));
236   }
237
238   /**
239    * Checks is a merge operation is in progress on the given repository.
240    * Actually gets the number of parents of the current revision. If there are 2 parents, then a merge is going on. Otherwise there is
241    * only one parent. 
242    * @param project    project to work on.
243    * @param repository repository which is checked on merge.
244    * @return True if merge operation is in progress, false if there is no merge operation.
245    */
246   public static boolean isMergeInProgress(@NotNull Project project, VirtualFile repository) {
247     return new HgWorkingCopyRevisionsCommand(project).parents(repository).size() > 1;
248   }
249
250 }