SVN: do not log process canceled...
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / SvnFileUrlMappingImpl.java
blobd244f2269dbd3fa27f5bdb84ea81435ad2c02000
1 package org.jetbrains.idea.svn;
3 import com.intellij.lifecycle.AtomicSectionsAware;
4 import com.intellij.openapi.components.PersistentStateComponent;
5 import com.intellij.openapi.components.ProjectComponent;
6 import com.intellij.openapi.components.State;
7 import com.intellij.openapi.components.Storage;
8 import com.intellij.openapi.diagnostic.Logger;
9 import com.intellij.openapi.progress.ProcessCanceledException;
10 import com.intellij.openapi.project.Project;
11 import com.intellij.openapi.util.Getter;
12 import com.intellij.openapi.util.Ref;
13 import com.intellij.openapi.util.io.FileUtil;
14 import com.intellij.openapi.vcs.ObjectsConvertor;
15 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
16 import com.intellij.openapi.vcs.changes.ChangeListManager;
17 import com.intellij.openapi.vcs.changes.InvokeAfterUpdateMode;
18 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
19 import com.intellij.openapi.vcs.impl.StringLenComparator;
20 import com.intellij.openapi.vfs.LocalFileSystem;
21 import com.intellij.openapi.vfs.VfsUtil;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.util.Consumer;
24 import com.intellij.util.containers.Convertor;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
27 import org.tmatesoft.svn.core.SVNDepth;
28 import org.tmatesoft.svn.core.SVNException;
29 import org.tmatesoft.svn.core.SVNURL;
30 import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
31 import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
32 import org.tmatesoft.svn.core.wc.*;
34 import java.io.File;
35 import java.util.*;
37 @State(
38 name = "SvnFileUrlMappingImpl",
39 storages = {
40 @Storage(
41 id ="other",
42 file = "$WORKSPACE_FILE$"
45 class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentStateComponent<SvnMappingSavedPart>, ProjectComponent {
46 private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileUrlMappingImpl");
48 private final SvnCompatibilityChecker myChecker;
49 private final Object myMonitor = new Object();
50 // strictly: what real roots are under what vcs mappings
51 private final SvnMapping myMapping;
52 // grouped; if there are several mappings one under another, will return the upmost
53 private final SvnMapping myMoreRealMapping;
54 private final MyRootsHelper myHelper;
55 private final Project myProject;
56 private final NestedCopiesSink myTempSink;
57 private boolean myInitialized;
59 private class MyRootsHelper extends ThreadLocalDefendedInvoker<VirtualFile[]> {
60 private final ProjectLevelVcsManager myPlVcsManager;
62 private MyRootsHelper(final ProjectLevelVcsManager vcsManager) {
63 myPlVcsManager = vcsManager;
66 protected VirtualFile[] execute() {
67 return myPlVcsManager.getRootsUnderVcs(SvnVcs.getInstance(myProject));
71 public static SvnFileUrlMappingImpl getInstance(final Project project) {
72 return project.getComponent(SvnFileUrlMappingImpl.class);
75 private SvnFileUrlMappingImpl(final Project project, final ProjectLevelVcsManager vcsManager) {
76 myProject = project;
77 myMapping = new SvnMapping();
78 myMoreRealMapping = new SvnMapping();
79 myHelper = new MyRootsHelper(vcsManager);
80 myChecker = new SvnCompatibilityChecker(project);
81 myTempSink = new NestedCopiesSink();
84 @Nullable
85 public SVNURL getUrlForFile(final File file) {
86 final RootUrlInfo rootUrlInfo = getWcRootForFilePath(file);
87 if (rootUrlInfo == null) {
88 return null;
91 final String absolutePath = file.getAbsolutePath();
92 final String rootAbsPath = rootUrlInfo.getIoFile().getAbsolutePath();
93 if (absolutePath.length() < rootAbsPath.length()) {
94 // remove last separator from etalon name
95 if (absolutePath.equals(rootAbsPath.substring(0, rootAbsPath.length() - 1))) {
96 return rootUrlInfo.getAbsoluteUrlAsUrl();
98 return null;
100 final String relativePath = absolutePath.substring(rootAbsPath.length());
101 try {
102 return rootUrlInfo.getAbsoluteUrlAsUrl().appendPath(FileUtil.toSystemIndependentName(relativePath), true);
104 catch (SVNException e) {
105 LOG.info(e);
106 return null;
110 @Nullable
111 public String getLocalPath(final String url) {
112 synchronized (myMonitor) {
113 final String rootUrl = getUrlRootForUrl(url);
114 if (rootUrl == null) {
115 return null;
117 final RootUrlInfo parentInfo = myMapping.byUrl(rootUrl);
118 if (parentInfo == null) {
119 return null;
122 return fileByUrl(parentInfo.getIoFile().getAbsolutePath(), rootUrl, url).getAbsolutePath();
126 public static File fileByUrl(final String parentPath, final String parentUrl, final String childUrl) {
127 return new File(parentPath, childUrl.substring(parentUrl.length()));
130 @Nullable
131 public RootUrlInfo getWcRootForFilePath(final File file) {
132 synchronized (myMonitor) {
133 final String root = getRootForPath(file);
134 if (root == null) {
135 return null;
138 return myMapping.byFile(root);
142 public boolean rootsDiffer() {
143 synchronized (myMonitor) {
144 return myMapping.isRootsDifferFromSettings();
148 @Nullable
149 public RootUrlInfo getWcRootForUrl(final String url) {
150 synchronized (myMonitor) {
151 final String rootUrl = getUrlRootForUrl(url);
152 if (rootUrl == null) {
153 return null;
156 final RootUrlInfo result = myMapping.byUrl(rootUrl);
157 if (result == null) {
158 LOG.info("Inconsistent maps for url:" + url + " found root url: " + rootUrl);
159 return null;
161 return result;
166 * Returns real working copies roots - if there is <Project Root> -> Subversion setting,
167 * and there is one working copy, will return one root
169 public List<RootUrlInfo> getAllWcInfos() {
170 synchronized (myMonitor) {
171 // a copy is created inside
172 return myMoreRealMapping.getAllCopies();
176 public List<VirtualFile> convertRoots(final List<VirtualFile> result) {
177 if (myHelper.isInside()) return result;
179 synchronized (myMonitor) {
180 final List<VirtualFile> cachedRoots = myMapping.getUnderVcsRoots();
181 final List<VirtualFile> lonelyRoots = myMapping.getLonelyRoots();
182 if (! lonelyRoots.isEmpty()) {
183 myChecker.reportNoRoots(lonelyRoots);
185 if (cachedRoots.isEmpty()) {
186 // todo +-
187 return result;
189 return cachedRoots;
193 public void acceptNestedData(final Set<NestedCopiesBuilder.MyPointInfo> set) {
194 myTempSink.add(set);
197 private boolean init() {
198 synchronized (myMonitor) {
199 final boolean result = myInitialized;
200 myInitialized = true;
201 return result;
205 public void realRefresh(final AtomicSectionsAware atomicSectionsAware) {
206 final SvnVcs vcs = SvnVcs.getInstance(myProject);
207 final VirtualFile[] roots = myHelper.executeDefended();
209 final CopiesApplier copiesApplier = new CopiesApplier();
210 final CopiesDetector copiesDetector = new CopiesDetector(atomicSectionsAware, vcs, copiesApplier, new Getter<NestedCopiesData>() {
211 public NestedCopiesData get() {
212 return myTempSink.receive();
215 // do not send additional request for nested copies when in init state
216 copiesDetector.detectCopyRoots(roots, init());
219 private class CopiesApplier {
220 public void apply(final SvnVcs vcs, final AtomicSectionsAware atomicSectionsAware, final List<RootUrlInfo> roots,
221 final List<VirtualFile> lonelyRoots) {
222 final SvnMapping mapping = new SvnMapping();
223 mapping.addAll(roots);
224 mapping.reportLonelyRoots(lonelyRoots);
226 final SvnMapping groupedMapping = new SvnMapping();
227 final List<RootUrlInfo> filtered = new ArrayList<RootUrlInfo>();
228 ForNestedRootChecker.filterOutSuperfluousChildren(vcs, roots, filtered);
230 groupedMapping.addAll(filtered);
232 // apply
233 try {
234 atomicSectionsAware.enter();
235 synchronized (myMonitor) {
236 myMapping.copyFrom(mapping);
237 myMoreRealMapping.copyFrom(groupedMapping);
239 } finally {
240 atomicSectionsAware.exit();
242 myProject.getMessageBus().syncPublisher(SvnVcs.ROOTS_RELOADED).run();
246 private static class CopiesDetector {
247 private final AtomicSectionsAware myAtomicSectionsAware;
248 private final SvnVcs myVcs;
249 private final CopiesApplier myApplier;
250 private final List<VirtualFile> myLonelyRoots;
251 private final List<RootUrlInfo> myTopRoots;
252 private final RepositoryRoots myRepositoryRoots;
253 private final Getter<NestedCopiesData> myGate;
255 private CopiesDetector(final AtomicSectionsAware atomicSectionsAware, final SvnVcs vcs, final CopiesApplier applier,
256 final Getter<NestedCopiesData> gate) {
257 myAtomicSectionsAware = atomicSectionsAware;
258 myVcs = vcs;
259 myApplier = applier;
260 myGate = gate;
261 myTopRoots = new ArrayList<RootUrlInfo>();
262 myLonelyRoots = new ArrayList<VirtualFile>();
263 myRepositoryRoots = new RepositoryRoots(myVcs);
266 public void detectCopyRoots(final VirtualFile[] roots, final boolean clearState) {
267 final Getter<Boolean> cancelGetter = new Getter<Boolean>() {
268 public Boolean get() {
269 return myAtomicSectionsAware.shouldExitAsap();
273 for (final VirtualFile vcsRoot : roots) {
274 final List<Real> foundRoots = ForNestedRootChecker.getAllNestedWorkingCopies(vcsRoot, myVcs, false, cancelGetter);
275 if (foundRoots.isEmpty()) {
276 myLonelyRoots.add(vcsRoot);
278 // filter out bad(?) items
279 for (Real foundRoot : foundRoots) {
280 final SVNURL repoRoot = foundRoot.getInfo().getRepositoryRootURL();
281 if (repoRoot == null) {
282 LOG.info("Error: cannot find repository URL for versioned folder: " + foundRoot.getFile().getPath());
283 } else {
284 myRepositoryRoots.register(repoRoot);
285 myTopRoots.add(new RootUrlInfo(repoRoot, foundRoot.getInfo().getURL(),
286 SvnFormatSelector.getWorkingCopyFormat(foundRoot.getInfo().getFile()), foundRoot.getFile(), vcsRoot));
291 if (! SvnConfiguration.getInstance(myVcs.getProject()).DETECT_NESTED_COPIES) {
292 myApplier.apply(myVcs, myAtomicSectionsAware, myTopRoots, myLonelyRoots);
293 } else {
294 addNestedRoots(clearState);
298 private void addNestedRoots(final boolean clearState) {
299 final List<VirtualFile> basicVfRoots = ObjectsConvertor.convert(myTopRoots, new Convertor<RootUrlInfo, VirtualFile>() {
300 public VirtualFile convert(final RootUrlInfo real) {
301 return real.getVirtualFile();
305 final ChangeListManager clManager = ChangeListManager.getInstance(myVcs.getProject());
307 if (clearState) {
308 // clear what was reported before (could be for currently-not-existing roots)
309 myGate.get();
311 clManager.invokeAfterUpdate(new Runnable() {
312 public void run() {
313 final List<RootUrlInfo> nestedRoots = new ArrayList<RootUrlInfo>();
315 for (NestedCopiesBuilder.MyPointInfo info : myGate.get().getSet()) {
316 if (NestedCopyType.external.equals(info.getType())) {
317 try {
318 final SVNStatus svnStatus = SvnUtil.getStatus(myVcs, new File(info.getFile().getPath()));
319 if (svnStatus.getURL() == null) continue;
320 info.setUrl(svnStatus.getURL());
321 info.setFormat(WorkingCopyFormat.getInstance(svnStatus.getWorkingCopyFormat()));
323 catch (Exception e) {
324 continue;
327 for (RootUrlInfo topRoot : myTopRoots) {
328 if (VfsUtil.isAncestor(topRoot.getVirtualFile(), info.getFile(), true)) {
329 final SVNURL repoRoot = myRepositoryRoots.ask(info.getUrl());
330 if (repoRoot != null) {
331 nestedRoots.add(new RootUrlInfo(repoRoot, info.getUrl(), info.getFormat(), info.getFile(), topRoot.getRoot()));
333 break;
338 myTopRoots.addAll(nestedRoots);
339 myApplier.apply(myVcs, myAtomicSectionsAware, myTopRoots, myLonelyRoots);
341 }, InvokeAfterUpdateMode.SILENT_CALLBACK_POOLED, null, new Consumer<VcsDirtyScopeManager>() {
342 public void consume(VcsDirtyScopeManager vcsDirtyScopeManager) {
343 if (clearState) {
344 vcsDirtyScopeManager.filesDirty(null, basicVfRoots);
347 }, null);
351 private static SVNStatus getExternalItemStatus(final SvnVcs vcs, final File file) {
352 final SVNStatusClient statusClient = vcs.createStatusClient();
353 try {
354 if (file.isDirectory()) {
355 return statusClient.doStatus(file, false);
356 } else {
357 final File parent = file.getParentFile();
358 if (parent != null) {
359 statusClient.setFilesProvider(new ISVNStatusFileProvider() {
360 public Map getChildrenFiles(File parent) {
361 return Collections.singletonMap(file.getAbsolutePath(), file);
364 final Ref<SVNStatus> refStatus = new Ref<SVNStatus>();
365 statusClient.doStatus(parent, SVNRevision.WORKING, SVNDepth.FILES, false, true, false, false, new ISVNStatusHandler() {
366 public void handleStatus(final SVNStatus status) throws SVNException {
367 if (file.equals(status.getFile())) {
368 refStatus.set(status);
371 }, null);
372 return refStatus.get();
376 catch (SVNException e) {
379 return null;
382 private static class RepositoryRoots {
383 private final SvnVcs myVcs;
384 private final Set<SVNURL> myRoots;
386 private RepositoryRoots(final SvnVcs vcs) {
387 myVcs = vcs;
388 myRoots = new HashSet<SVNURL>();
391 public void register(final SVNURL url) {
392 myRoots.add(url);
395 public SVNURL ask(final SVNURL url) {
396 for (SVNURL root : myRoots) {
397 if (root.equals(SVNURLUtil.getCommonURLAncestor(root, url))) {
398 return root;
401 final SVNURL newRoot = SvnUtil.getRepositoryRoot(myVcs, url);
402 if (newRoot != null) {
403 myRoots.add(newRoot);
405 return newRoot;
409 @Nullable
410 public String getUrlRootForUrl(final String currentUrl) {
411 for (String url : myMapping.getUrls()) {
412 if (SVNPathUtil.isAncestor(url, currentUrl)) {
413 return url;
416 return null;
419 @Nullable
420 public String getRootForPath(final File currentPath) {
421 String convertedPath = currentPath.getAbsolutePath();
422 convertedPath = (currentPath.isDirectory() && (! convertedPath.endsWith(File.separator))) ? convertedPath + File.separator :
423 convertedPath;
424 final List<String> paths;
425 synchronized (myMonitor) {
426 paths = new ArrayList<String>(myMapping.getFileRoots());
428 Collections.sort(paths, StringLenComparator.getDescendingInstance());
430 for (String path : paths) {
431 if (FileUtil.startsWith(convertedPath, path)) {
432 return path;
435 return null;
438 public VirtualFile[] getNotFilteredRoots() {
439 return myHelper.executeDefended();
442 public boolean isEmpty() {
443 synchronized (myMonitor) {
444 return myMapping.isEmpty();
448 public SvnMappingSavedPart getState() {
449 final SvnMappingSavedPart result = new SvnMappingSavedPart();
451 final SvnMapping mapping = new SvnMapping();
452 final SvnMapping realMapping = new SvnMapping();
453 synchronized (myMonitor) {
454 mapping.copyFrom(myMapping);
455 realMapping.copyFrom(myMoreRealMapping);
458 for (RootUrlInfo info : mapping.getAllCopies()) {
459 result.add(convert(info));
461 for (RootUrlInfo info : realMapping.getAllCopies()) {
462 result.addReal(convert(info));
464 return result;
467 private SvnCopyRootSimple convert(final RootUrlInfo info) {
468 final SvnCopyRootSimple copy = new SvnCopyRootSimple();
469 copy.myVcsRoot = FileUtil.toSystemDependentName(info.getRoot().getPath());
470 copy.myCopyRoot = info.getIoFile().getAbsolutePath();
471 return copy;
474 public void loadState(final SvnMappingSavedPart state) {
475 final SvnMapping mapping = new SvnMapping();
476 final SvnMapping realMapping = new SvnMapping();
478 try {
479 fillMapping(mapping, state.getMappingRoots());
480 fillMapping(realMapping, state.getMoreRealMappingRoots());
481 } catch (ProcessCanceledException e) {
482 throw e;
483 } catch (Throwable t) {
484 LOG.info(t);
485 return;
488 synchronized (myMonitor) {
489 myMapping.copyFrom(mapping);
490 myMoreRealMapping.copyFrom(realMapping);
494 private void fillMapping(final SvnMapping mapping, final List<SvnCopyRootSimple> list) {
495 final LocalFileSystem lfs = LocalFileSystem.getInstance();
497 for (SvnCopyRootSimple simple : list) {
498 final VirtualFile copyRoot = lfs.findFileByIoFile(new File(simple.myCopyRoot));
499 final VirtualFile vcsRoot = lfs.findFileByIoFile(new File(simple.myVcsRoot));
501 if (copyRoot == null || vcsRoot == null) continue;
503 final SvnVcs vcs = SvnVcs.getInstance(myProject);
504 final SVNInfo svnInfo = vcs.getInfo(copyRoot);
505 if ((svnInfo == null) || (svnInfo.getRepositoryRootURL() == null)) continue;
507 mapping.add(new RootUrlInfo(svnInfo.getRepositoryRootURL(), svnInfo.getURL(),
508 SvnFormatSelector.getWorkingCopyFormat(svnInfo.getFile()), copyRoot, vcsRoot));
512 public void projectOpened() {
515 public void projectClosed() {
518 @NotNull
519 public String getComponentName() {
520 return "SvnFileUrlMappingImpl";
523 public void initComponent() {
526 public void disposeComponent() {