ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / SvnUtil.java
blobd507e645f56555e7c4a7863544646f25b08e9818
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.jetbrains.idea.svn;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.progress.ProcessCanceledException;
20 import com.intellij.openapi.progress.ProgressIndicator;
21 import com.intellij.openapi.progress.ProgressManager;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.Computable;
24 import com.intellij.openapi.vcs.AbstractVcsHelper;
25 import com.intellij.openapi.vcs.RepositoryLocation;
26 import com.intellij.openapi.vcs.VcsException;
27 import com.intellij.openapi.vfs.LocalFileSystem;
28 import com.intellij.openapi.vfs.VfsUtil;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.openapi.vfs.VirtualFileManager;
31 import com.intellij.openapi.wm.WindowManager;
32 import com.intellij.util.ArrayUtil;
33 import com.intellij.util.NotNullFunction;
34 import org.jetbrains.annotations.NonNls;
35 import org.jetbrains.annotations.Nullable;
36 import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew;
37 import org.jetbrains.idea.svn.dialogs.LockDialog;
38 import org.jetbrains.idea.svn.dialogs.WCInfo;
39 import org.tmatesoft.svn.core.SVNException;
40 import org.tmatesoft.svn.core.SVNURL;
41 import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
42 import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
43 import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
44 import org.tmatesoft.svn.core.io.SVNCapability;
45 import org.tmatesoft.svn.core.io.SVNRepository;
46 import org.tmatesoft.svn.core.wc.*;
48 import java.io.File;
49 import java.util.*;
51 public class SvnUtil {
52 @NonNls public static final String SVN_ADMIN_DIR_NAME = SVNFileUtil.getAdminDirectoryName();
53 @NonNls public static final String ENTRIES_FILE_NAME = "entries";
54 @NonNls public static final String DIR_PROPS_FILE_NAME = "dir-props";
55 @NonNls public static final String PATH_TO_LOCK_FILE = SVN_ADMIN_DIR_NAME + "/lock";
56 @NonNls public static final String LOCK_FILE_NAME = "lock";
58 private SvnUtil() {
61 public static void crawlWCRoots(VirtualFile path, NotNullFunction<VirtualFile, Collection<VirtualFile>> callback) {
62 if (path == null) {
63 return;
65 final boolean isDirectory = path.isDirectory();
66 final VirtualFile parentVFile = (!isDirectory) || (!path.exists()) ? path.getParent() : path;
67 if (parentVFile == null) {
68 return;
70 final File parent = new File(parentVFile.getPath());
72 if (SVNWCUtil.isVersionedDirectory(parent)) {
73 checkCanceled();
74 final Collection<VirtualFile> pending = callback.fun(path);
75 checkCanceled();
76 for (VirtualFile virtualFile : pending) {
77 crawlWCRoots(virtualFile, callback);
80 else if (isDirectory) {
81 checkCanceled();
82 VirtualFile[] children = path.getChildren();
83 for (int i = 0; children != null && i < children.length; i++) {
84 checkCanceled();
85 VirtualFile child = children[i];
86 if (child.isDirectory()) {
87 crawlWCRoots(child, callback);
93 public static Collection<File> crawlWCRoots(File path, SvnWCRootCrawler callback, ProgressIndicator progress) {
94 final Collection<File> result = new HashSet<File>();
95 File parent = path.isFile() || !path.exists() ? path.getParentFile() : path;
96 if (SVNWCUtil.isVersionedDirectory(parent)) {
97 checkCanceled(progress);
98 final Collection<File> pending = callback.handleWorkingCopyRoot(path, progress);
99 checkCanceled(progress);
100 for (final File aPending : pending) {
101 result.addAll(crawlWCRoots(aPending, callback, progress));
103 result.add(path);
105 else if (path.isDirectory()) {
106 checkCanceled(progress);
107 File[] children = path.listFiles();
108 for (int i = 0; children != null && i < children.length; i++) {
109 checkCanceled(progress);
110 File child = children[i];
111 if (child.isDirectory()) {
112 result.addAll(crawlWCRoots(child, callback, progress));
116 return result;
119 private static void checkCanceled() {
120 final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
121 checkCanceled(indicator);
124 private static void checkCanceled(final ProgressIndicator progress) {
125 if (progress != null && progress.isCanceled()) {
126 throw new ProcessCanceledException();
130 public static String[] getLocationsForModule(final SvnVcs vcs, File path, ProgressIndicator progress) {
131 LocationsCrawler crawler = new LocationsCrawler(vcs);
132 crawlWCRoots(path, crawler, progress);
133 return crawler.getLocations();
136 public static Map<String, File> getLocationInfoForModule(final SvnVcs vcs, File path, ProgressIndicator progress) {
137 final LocationsCrawler crawler = new LocationsCrawler(vcs);
138 crawlWCRoots(path, crawler, progress);
139 return crawler.getLocationInfos();
142 public static void doLockFiles(Project project, final SvnVcs activeVcs, final File[] ioFiles) throws VcsException {
143 final String lockMessage;
144 final boolean force;
145 // TODO[yole]: check for shift pressed
146 if (activeVcs.getCheckoutOptions().getValue()) {
147 LockDialog dialog = new LockDialog(project, true, ioFiles != null && ioFiles.length > 1);
148 dialog.show();
149 if (!dialog.isOK()) {
150 return;
152 lockMessage = dialog.getComment();
153 force = dialog.isForce();
155 else {
156 lockMessage = "";
157 force = false;
160 final SVNException[] exception = new SVNException[1];
161 final Collection<String> failedLocks = new ArrayList<String>();
162 final int[] count = new int[]{ioFiles.length};
163 final ISVNEventHandler eventHandler = new ISVNEventHandler() {
164 public void handleEvent(SVNEvent event, double progress) {
165 if (event.getAction() == SVNEventAction.LOCK_FAILED) {
166 failedLocks.add(event.getErrorMessage() != null ?
167 event.getErrorMessage().getFullMessage() :
168 event.getFile().getAbsolutePath());
169 count[0]--;
173 public void checkCancelled() {
177 Runnable command = new Runnable() {
178 public void run() {
179 ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
180 SVNWCClient wcClient;
182 try {
183 wcClient = activeVcs.createWCClient();
184 wcClient.setEventHandler(eventHandler);
185 if (progress != null) {
186 progress.setText(SvnBundle.message("progress.text.locking.files"));
188 for (File ioFile : ioFiles) {
189 if (progress != null) {
190 progress.checkCanceled();
192 File file = ioFile;
193 if (progress != null) {
194 progress.setText2(SvnBundle.message("progress.text2.processing.file", file.getName()));
196 wcClient.doLock(new File[]{file}, force, lockMessage);
199 catch (SVNException e) {
200 exception[0] = e;
205 ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.lock.files"), false, project);
206 if (!failedLocks.isEmpty()) {
207 String[] failedFiles = ArrayUtil.toStringArray(failedLocks);
208 List<VcsException> exceptions = new ArrayList<VcsException>();
210 for (String file : failedFiles) {
211 exceptions.add(new VcsException(SvnBundle.message("exception.text.locking.file.failed", file)));
213 AbstractVcsHelper.getInstance(project).showErrors(exceptions, SvnBundle.message("message.title.lock.failures"));
216 WindowManager.getInstance().getStatusBar(project).setInfo(SvnBundle.message("message.text.files.locked", count[0]));
217 if (exception[0] != null) {
218 throw new VcsException(exception[0]);
222 public static void doUnlockFiles(Project project, final SvnVcs activeVcs, final File[] ioFiles) throws VcsException {
223 final boolean force = true;
224 final SVNException[] exception = new SVNException[1];
225 final Collection<String> failedUnlocks = new ArrayList<String>();
226 final int[] count = new int[]{ioFiles.length};
227 final ISVNEventHandler eventHandler = new ISVNEventHandler() {
228 public void handleEvent(SVNEvent event, double progress) {
229 if (event.getAction() == SVNEventAction.UNLOCK_FAILED) {
230 failedUnlocks.add(event.getErrorMessage() != null ?
231 event.getErrorMessage().getFullMessage() :
232 event.getFile().getAbsolutePath());
233 count[0]--;
237 public void checkCancelled() {
241 Runnable command = new Runnable() {
242 public void run() {
243 ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
244 SVNWCClient wcClient;
246 try {
247 wcClient = activeVcs.createWCClient();
248 wcClient.setEventHandler(eventHandler);
249 if (progress != null) {
250 progress.setText(SvnBundle.message("progress.text.unlocking.files"));
252 for (File ioFile : ioFiles) {
253 if (progress != null) {
254 progress.checkCanceled();
256 File file = ioFile;
257 if (progress != null) {
258 progress.setText2(SvnBundle.message("progress.text2.processing.file", file.getName()));
260 wcClient.doUnlock(new File[]{file}, force);
263 catch (SVNException e) {
264 exception[0] = e;
269 ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.unlock.files"), false, project);
270 if (!failedUnlocks.isEmpty()) {
271 String[] failedFiles = ArrayUtil.toStringArray(failedUnlocks);
272 List<VcsException> exceptions = new ArrayList<VcsException>();
274 for (String file : failedFiles) {
275 exceptions.add(new VcsException(SvnBundle.message("exception.text.failed.to.unlock.file", file)));
277 AbstractVcsHelper.getInstance(project).showErrors(exceptions, SvnBundle.message("message.title.unlock.failures"));
280 WindowManager.getInstance().getStatusBar(project).setInfo(SvnBundle.message("message.text.files.unlocked", count[0]));
281 if (exception[0] != null) {
282 throw new VcsException(exception[0]);
286 public static String formatRepresentation(final WorkingCopyFormat format) {
287 if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) {
288 return SvnBundle.message("dialog.show.svn.map.table.version16.text");
289 } else if (WorkingCopyFormat.ONE_DOT_FIVE.equals(format)) {
290 return SvnBundle.message("dialog.show.svn.map.table.version15.text");
291 } else if (WorkingCopyFormat.ONE_DOT_FOUR.equals(format)) {
292 return SvnBundle.message("dialog.show.svn.map.table.version14.text");
293 } else if (WorkingCopyFormat.ONE_DOT_THREE.equals(format)) {
294 return SvnBundle.message("dialog.show.svn.map.table.version13.text");
296 return "";
299 private static class LocationsCrawler implements SvnWCRootCrawler {
300 private final SvnVcs myVcs;
301 private final Map<String, File> myLocations;
303 public LocationsCrawler(SvnVcs vcs) {
304 myVcs = vcs;
305 myLocations = new HashMap<String, File>();
308 public String[] getLocations() {
309 final Set<String> set = myLocations.keySet();
310 return ArrayUtil.toStringArray(set);
313 public Map<String, File> getLocationInfos() {
314 return Collections.unmodifiableMap(myLocations);
317 public Collection<File> handleWorkingCopyRoot(File root, ProgressIndicator progress) {
318 final Collection<File> result = new HashSet<File>();
319 if (progress != null) {
320 progress.setText(SvnBundle.message("progress.text.discovering.location", root.getAbsolutePath()));
322 try {
323 SVNWCClient wcClient = myVcs.createWCClient();
324 SVNInfo info = wcClient.doInfo(root, SVNRevision.WORKING);
325 if (info != null && info.getURL() != null) {
326 myLocations.put(info.getURL().toString(), info.getFile());
329 catch (SVNException e) {
332 return result;
336 @Nullable
337 public static String getRepositoryUUID(final SvnVcs vcs, final File file) {
338 final SVNWCClient client = vcs.createWCClient();
339 try {
340 final SVNInfo info = client.doInfo(file, SVNRevision.WORKING);
341 return (info == null) ? null : info.getRepositoryUUID();
342 } catch (SVNException e) {
343 return null;
347 @Nullable
348 public static String getRepositoryUUID(final SvnVcs vcs, final SVNURL url) {
349 final SVNWCClient client = vcs.createWCClient();
350 try {
351 final SVNInfo info = client.doInfo(url, SVNRevision.WORKING, SVNRevision.WORKING);
352 return (info == null) ? null : info.getRepositoryUUID();
353 } catch (SVNException e) {
354 return null;
358 @Nullable
359 public static SVNURL getRepositoryRoot(final SvnVcs vcs, final File file) {
360 final SVNWCClient client = vcs.createWCClient();
361 try {
362 final SVNInfo info = client.doInfo(file, SVNRevision.WORKING);
363 return (info == null) ? null : info.getRepositoryRootURL();
364 } catch (SVNException e) {
365 return null;
369 @Nullable
370 public static SVNURL getRepositoryRoot(final SvnVcs vcs, final String url) {
371 try {
372 return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url));
374 catch (SVNException e) {
375 return null;
379 @Nullable
380 public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url) {
381 final SVNWCClient client = vcs.createWCClient();
382 try {
383 SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD);
384 return (info == null) ? null : info.getRepositoryRootURL();
385 } catch (SVNException e) {
386 return null;
390 public static boolean isWorkingCopyRoot(final File file) {
391 try {
392 return SVNWCUtil.isWorkingCopyRoot(file);
393 } catch (SVNException e) {
394 return false;
398 @Nullable
399 public static File getWorkingCopyRoot(final File inFile) {
400 File file = inFile;
401 while ((file != null) && (file.isFile() || (! file.exists()))) {
402 file = file.getParentFile();
405 if (file == null) {
406 return null;
409 try {
410 return SVNWCUtil.getWorkingCopyRoot(file, true);
411 } catch (SVNException e) {
412 return null;
416 @Nullable
417 public static SVNURL getWorkingCopyUrl(final SvnVcs vcs, final File file) {
418 try {
419 if(SVNWCUtil.isWorkingCopyRoot(file)) {
420 final SVNWCClient client = vcs.createWCClient();
421 final SVNInfo info = client.doInfo(file, SVNRevision.WORKING);
422 return info.getURL();
424 } catch (SVNException e) {
427 return null;
430 public static File fileFromUrl(final File baseDir, final String baseUrl, final String fullUrl) throws SVNException {
431 assert fullUrl.startsWith(baseUrl);
433 final String part = fullUrl.substring(baseUrl.length()).replace('/', File.separatorChar).replace('\\', File.separatorChar);
434 return new File(baseDir, part);
437 public static VirtualFile getVirtualFile(final String filePath) {
438 @NonNls final String path = VfsUtil.pathToUrl(filePath.replace(File.separatorChar, '/'));
439 return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
440 @Nullable
441 public VirtualFile compute() {
442 return VirtualFileManager.getInstance().findFileByUrl(path);
447 @Nullable
448 public static SVNURL getBranchForUrl(final SvnVcs vcs, final VirtualFile vcsRoot, final String urlPath) {
449 final SvnBranchConfigurationNew configuration;
450 try {
451 final SVNURL url = SVNURL.parseURIEncoded(urlPath);
452 configuration = SvnBranchConfigurationManager.getInstance(vcs.getProject()).get(vcsRoot);
453 return (configuration == null) ? null : configuration.getWorkingBranch(url);
455 catch (SVNException e) {
456 return null;
457 } catch (VcsException e1) {
458 return null;
462 @Nullable
463 public static String getPathForProgress(final SVNEvent event) {
464 if (event.getFile() != null) {
465 return event.getFile().getName();
467 if (event.getURL() != null) {
468 return event.getURL().toString();
470 return null;
473 @Nullable
474 public static VirtualFile correctRoot(final Project project, final String path) {
475 if (path.length() == 0) {
476 // project root
477 return project.getBaseDir();
479 return LocalFileSystem.getInstance().findFileByPath(path);
482 @Nullable
483 public static VirtualFile correctRoot(final Project project, final VirtualFile file) {
484 if (file.getPath().length() == 0) {
485 // project root
486 return project.getBaseDir();
488 return file;
491 public static boolean isOneDotFiveAvailable(final Project project, final RepositoryLocation location) {
492 final SvnVcs vcs = SvnVcs.getInstance(project);
493 final List<WCInfo> infos = vcs.getAllWcInfos();
495 for (WCInfo info : infos) {
496 if (! info.getFormat().supportsMergeInfo()) {
497 continue;
500 final String url = info.getUrl().toString();
501 if ((location != null) && (! location.toPresentableString().startsWith(url)) &&
502 (! url.startsWith(location.toPresentableString()))) {
503 continue;
505 if (! checkRepositoryVersion15(vcs, url)) {
506 continue;
508 return true;
510 return false;
513 public static boolean checkRepositoryVersion15(final SvnVcs vcs, final String url) {
514 SVNRepository repository = null;
515 try {
516 repository = vcs.createRepository(url);
517 return repository.hasCapability(SVNCapability.MERGE_INFO);
519 catch (SVNException e) {
520 return false;
522 finally {
523 if (repository != null) {
524 repository.closeSession();
529 public static SVNStatus getStatus(final SvnVcs vcs, final File file) {
530 final SVNStatusClient statusClient = vcs.createStatusClient();
531 try {
532 return statusClient.doStatus(file, false);
534 catch (SVNException e) {
535 return null;
539 public static boolean seemsLikeVersionedDir(final VirtualFile file) {
540 final String adminName = SVNFileUtil.getAdminDirectoryName();
541 final VirtualFile[] children = file.getChildren();
542 for (VirtualFile child : children) {
543 if (adminName.equals(child.getName()) && child.isDirectory()) {
544 return true;
547 return false;
550 public static boolean isAdminDirectory(final VirtualFile file) {
551 return isAdminDirectory(file.getParent(), file.getName());
554 public static boolean isAdminDirectory(File parent, final String name) {
555 if (name.equals(SVN_ADMIN_DIR_NAME)) {
556 return true;
558 if (parent != null) {
559 if (parent.getName().equals(SVN_ADMIN_DIR_NAME)) {
560 return true;
562 parent = parent.getParentFile();
563 if (parent != null && parent.getName().equals(SVN_ADMIN_DIR_NAME)) {
564 return true;
567 return false;
570 public static boolean isAdminDirectory(VirtualFile parent, String name) {
571 // never allow to delete admin directories by themselves (this can happen during LVCS undo,
572 // which deletes created directories from bottom to top)
573 if (name.equals(SVN_ADMIN_DIR_NAME)) {
574 return true;
576 if (parent != null) {
577 if (parent.getName().equals(SVN_ADMIN_DIR_NAME)) {
578 return true;
580 parent = parent.getParent();
581 if (parent != null && parent.getName().equals(SVN_ADMIN_DIR_NAME)) {
582 return true;
585 return false;
588 @Nullable
589 public static SVNURL getUrl(final File file) {
590 SVNWCAccess wcAccess = SVNWCAccess.newInstance(null);
591 try {
592 wcAccess.probeOpen(file, false, 0);
593 SVNEntry entry = wcAccess.getVersionedEntry(file, false);
594 return entry.getSVNURL();
595 } catch (SVNException e) {
597 } finally {
598 try {
599 wcAccess.close();
601 catch (SVNException e) {
605 return null;