ExcludedFileIndex -> FileIndexFacade; use it in PsiClassImplUtil
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / impl / ProjectLevelVcsManagerImpl.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.impl;
17
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.application.ModalityState;
20 import com.intellij.openapi.components.ProjectComponent;
21 import com.intellij.openapi.components.StorageScheme;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.editor.Editor;
24 import com.intellij.openapi.editor.EditorFactory;
25 import com.intellij.openapi.editor.EditorSettings;
26 import com.intellij.openapi.editor.markup.TextAttributes;
27 import com.intellij.openapi.progress.ProcessCanceledException;
28 import com.intellij.openapi.project.DumbAwareRunnable;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.project.ex.ProjectEx;
31 import com.intellij.openapi.roots.FileIndexFacade;
32 import com.intellij.openapi.startup.StartupManager;
33 import com.intellij.openapi.util.*;
34 import com.intellij.openapi.util.io.FileUtil;
35 import com.intellij.openapi.util.registry.Registry;
36 import com.intellij.openapi.vcs.*;
37 import com.intellij.openapi.vcs.changes.ChangesUtil;
38 import com.intellij.openapi.vcs.checkout.CompositeCheckoutListener;
39 import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx;
40 import com.intellij.openapi.vcs.history.VcsHistoryCache;
41 import com.intellij.openapi.vcs.impl.projectlevelman.*;
42 import com.intellij.openapi.vcs.update.ActionInfo;
43 import com.intellij.openapi.vcs.update.UpdateInfoTree;
44 import com.intellij.openapi.vcs.update.UpdatedFiles;
45 import com.intellij.openapi.vcs.update.UpdatedFilesListener;
46 import com.intellij.openapi.vfs.LocalFileSystem;
47 import com.intellij.openapi.vfs.VfsUtil;
48 import com.intellij.openapi.vfs.VirtualFile;
49 import com.intellij.openapi.wm.ToolWindow;
50 import com.intellij.openapi.wm.ToolWindowAnchor;
51 import com.intellij.openapi.wm.ToolWindowId;
52 import com.intellij.openapi.wm.ToolWindowManager;
53 import com.intellij.ui.content.Content;
54 import com.intellij.ui.content.ContentFactory;
55 import com.intellij.ui.content.ContentManager;
56 import com.intellij.util.ContentsUtil;
57 import com.intellij.util.PairProcessor;
58 import com.intellij.util.PlatformIcons;
59 import com.intellij.util.Processor;
60 import com.intellij.util.containers.Convertor;
61 import com.intellij.util.messages.MessageBus;
62 import com.intellij.util.messages.MessageBusConnection;
63 import com.intellij.util.ui.EditorAdapter;
64 import org.jdom.Attribute;
65 import org.jdom.DataConversionException;
66 import org.jdom.Element;
67 import org.jetbrains.annotations.NonNls;
68 import org.jetbrains.annotations.NotNull;
69 import org.jetbrains.annotations.Nullable;
70
71 import javax.swing.*;
72 import java.awt.*;
73 import java.util.*;
74 import java.util.List;
75
76 public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx implements ProjectComponent, JDOMExternalizable {
77   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl");
78   public static final String SETTINGS_EDITED_MANUALLY = "settingsEditedManually";
79
80   private final ProjectLevelVcsManagerSerialization mySerialization;
81   private final OptionsAndConfirmations myOptionsAndConfirmations;
82
83   private final NewMappings myMappings;
84   private final Project myProject;
85   private final MessageBus myMessageBus;
86   private final MappingsToRoots myMappingsToRoots;
87
88   private ContentManager myContentManager;
89   private EditorAdapter myEditorAdapter;
90
91   private final VcsInitialization myInitialization;
92
93   @NonNls private static final String ELEMENT_MAPPING = "mapping";
94   @NonNls private static final String ATTRIBUTE_DIRECTORY = "directory";
95   @NonNls private static final String ATTRIBUTE_VCS = "vcs";
96   @NonNls private static final String ATTRIBUTE_DEFAULT_PROJECT = "defaultProject";
97   @NonNls private static final String ELEMENT_ROOT_SETTINGS = "rootSettings";
98   @NonNls private static final String ATTRIBUTE_CLASS = "class";
99
100   private boolean myMappingsLoaded = false;
101   private boolean myHaveLegacyVcsConfiguration = false;
102   private final DefaultVcsRootPolicy myDefaultVcsRootPolicy;
103
104   private volatile int myBackgroundOperationCounter = 0;
105
106   private final Map<VcsBackgroundableActions, BackgroundableActionEnabledHandler> myBackgroundableActionHandlerMap;
107
108   private final List<Pair<String, TextAttributes>> myPendingOutput = new ArrayList<Pair<String, TextAttributes>>();
109   private VcsEventsListenerManagerImpl myVcsEventListenerManager;
110
111   private final VcsHistoryCache myVcsHistoryCache;
112   private final ContentRevisionCache myContentRevisionCache;
113   private MessageBusConnection myConnect;
114   private VcsListener myVcsListener;
115   private final FileIndexFacade myExcludedIndex;
116
117   public ProjectLevelVcsManagerImpl(Project project, final FileStatusManager manager, MessageBus messageBus, final FileIndexFacade excludedFileIndex) {
118     myProject = project;
119     myMessageBus = messageBus;
120     mySerialization = new ProjectLevelVcsManagerSerialization();
121     myOptionsAndConfirmations = new OptionsAndConfirmations();
122
123     myDefaultVcsRootPolicy = DefaultVcsRootPolicy.getInstance(project);
124
125     myBackgroundableActionHandlerMap = new HashMap<VcsBackgroundableActions, BackgroundableActionEnabledHandler>();
126     myInitialization = new VcsInitialization(myProject);
127     myMappings = new NewMappings(myProject, myMessageBus, this, manager);
128     myMappingsToRoots = new MappingsToRoots(myMappings, myProject);
129
130     if (! myProject.isDefault()) {
131       myVcsEventListenerManager = new VcsEventsListenerManagerImpl();
132     }
133
134     myVcsHistoryCache = new VcsHistoryCache();
135     myContentRevisionCache = new ContentRevisionCache();
136     myConnect = myMessageBus.connect();
137     myVcsListener = new VcsListener() {
138       @Override
139       public void directoryMappingChanged() {
140         myVcsHistoryCache.clear();
141       }
142     };
143     myConnect.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, myVcsListener);
144     myConnect.subscribe(UpdatedFilesListener.UPDATED_FILES, new UpdatedFilesListener() {
145       @Override
146       public void consume(Set<String> strings) {
147         myContentRevisionCache.clearCurrent(strings);
148       }
149     });
150     myExcludedIndex = excludedFileIndex;
151   }
152
153   public void initComponent() {
154     myOptionsAndConfirmations.init(new Convertor<String, VcsShowConfirmationOption.Value>() {
155       public VcsShowConfirmationOption.Value convert(String o) {
156         return mySerialization.getInitOptionValue(o);
157       }
158     });
159   }
160
161   public void registerVcs(AbstractVcs vcs) {
162     AllVcses.getInstance(myProject).registerManually(vcs);
163   }
164
165   @Nullable
166   public AbstractVcs findVcsByName(String name) {
167     if (name == null) return null;
168     if (myProject.isDisposed()) return null;
169     return AllVcses.getInstance(myProject).getByName(name);
170   }
171
172   @Nullable
173   public VcsDescriptor getDescriptor(final String name) {
174     if (name == null) return null;
175     if (myProject.isDisposed()) return null;
176     return AllVcses.getInstance(myProject).getDescriptor(name);
177   }
178
179   public VcsDescriptor[] getAllVcss() {
180     return AllVcses.getInstance(myProject).getAll();
181   }
182
183   public boolean haveVcses() {
184     return ! AllVcses.getInstance(myProject).isEmpty();
185   }
186
187   public void disposeComponent() {
188     if (myEditorAdapter != null) {
189       final Editor editor = myEditorAdapter.getEditor();
190       if (! editor.isDisposed()) {
191         EditorFactory.getInstance().releaseEditor(editor);
192       }
193     }
194     myMappings.disposeMe();
195     myConnect.disconnect();
196     myContentManager = null;
197
198     ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
199     if (toolWindowManager != null && toolWindowManager.getToolWindow(ToolWindowId.VCS) != null) {
200       toolWindowManager.unregisterToolWindow(ToolWindowId.VCS);
201     }
202   }
203
204   public void projectOpened() {
205     final StartupManager manager = StartupManager.getInstance(myProject);
206     manager.registerPostStartupActivity(new DumbAwareRunnable() {
207       public void run() {
208         ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
209         if (toolWindowManager != null) { // Can be null in tests
210           ToolWindow toolWindow =
211             toolWindowManager.registerToolWindow(ToolWindowId.VCS, true, ToolWindowAnchor.BOTTOM, myProject, true);
212           myContentManager = toolWindow.getContentManager();
213           toolWindow.setIcon(PlatformIcons.VCS_SMALL_TAB);
214           toolWindow.installWatcher(myContentManager);
215         } else {
216           myContentManager = ContentFactory.SERVICE.getInstance().createContentManager(true, myProject);
217         }
218       }
219     });
220   }
221
222   public void projectClosed() {
223   }
224
225   @NotNull
226   public String getComponentName() {
227     return "ProjectLevelVcsManager";
228   }
229
230   public boolean checkAllFilesAreUnder(AbstractVcs abstractVcs, VirtualFile[] files) {
231     if (files == null) return false;
232     for (VirtualFile file : files) {
233       if (getVcsFor(file) != abstractVcs) {
234         return false;
235       }
236     }
237     return true;
238   }
239
240   @Nullable
241   public AbstractVcs getVcsFor(@NotNull VirtualFile file) {
242     final String vcsName = myMappings.getVcsFor(file);
243     if (vcsName == null || vcsName.length() == 0) {
244       return null;
245     }
246     return AllVcses.getInstance(myProject).getByName(vcsName);
247   }
248
249   @Nullable
250   public AbstractVcs getVcsFor(final FilePath file) {
251     return ApplicationManager.getApplication().runReadAction(new Computable<AbstractVcs>() {
252       @Nullable
253       public AbstractVcs compute() {
254         if (!ApplicationManager.getApplication().isUnitTestMode() && !myProject.isInitialized()) return null;
255         if (myProject.isDisposed()) throw new ProcessCanceledException();
256         VirtualFile vFile = ChangesUtil.findValidParent(file);
257         if (vFile != null) {
258           return getVcsFor(vFile);
259         }
260         return null;
261       }
262     });
263   }
264
265   @Nullable
266   public VirtualFile getVcsRootFor(final @Nullable VirtualFile file) {
267     final VcsDirectoryMapping mapping = myMappings.getMappingFor(file);
268     if (mapping == null) {
269       return null;
270     }
271     final String directory = mapping.getDirectory();
272     if (directory.length() == 0) {
273       return myDefaultVcsRootPolicy.getVcsRootFor(file);
274     }
275     return LocalFileSystem.getInstance().findFileByPath(directory);
276   }
277
278   @Nullable
279   public VcsRoot getVcsRootObjectFor(final VirtualFile file) {
280     final VcsDirectoryMapping mapping = myMappings.getMappingFor(file);
281     if (mapping == null) {
282       return null;
283     }
284     final String directory = mapping.getDirectory();
285     final AbstractVcs vcs = findVcsByName(mapping.getVcs());
286     if (directory.length() == 0) {
287       return new VcsRoot(vcs, myDefaultVcsRootPolicy.getVcsRootFor(file));
288     }
289     return new VcsRoot(vcs, LocalFileSystem.getInstance().findFileByPath(directory));
290   }
291
292   @Nullable
293   public VirtualFile getVcsRootFor(final FilePath file) {
294     return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
295       @Nullable
296       public VirtualFile compute() {
297         if (myProject.isDisposed()) return null;
298         VirtualFile vFile = ChangesUtil.findValidParent(file);
299         if (vFile != null) {
300           return getVcsRootFor(vFile);
301         }
302         return null;
303       }
304     });
305   }
306
307   @Override
308   public VcsRoot getVcsRootObjectFor(final FilePath file) {
309     return ApplicationManager.getApplication().runReadAction(new Computable<VcsRoot>() {
310       @Nullable
311       public VcsRoot compute() {
312         VirtualFile vFile = ChangesUtil.findValidParent(file);
313         if (vFile != null) {
314           return getVcsRootObjectFor(vFile);
315         }
316         return null;
317       }
318     });
319   }
320
321   public void unregisterVcs(AbstractVcs vcs) {
322     if (! ApplicationManager.getApplication().isUnitTestMode() && myMappings.haveActiveVcs(vcs.getName())) {
323       // unlikely
324       LOG.warn("Active vcs '" + vcs.getName() + "' is being unregistered. Remove from mappings first.");
325     }
326     myMappings.beingUnregistered(vcs.getName());
327     AllVcses.getInstance(myProject).unregisterManually(vcs);
328   }
329
330   public ContentManager getContentManager() {
331     return myContentManager;
332   }
333
334   public boolean checkVcsIsActive(AbstractVcs vcs) {
335     return checkVcsIsActive(vcs.getName());
336   }
337
338   public boolean checkVcsIsActive(final String vcsName) {
339     return myMappings.haveActiveVcs(vcsName);
340   }
341
342   public AbstractVcs[] getAllActiveVcss() {
343     return myMappings.getActiveVcses();
344   }
345
346   public boolean hasActiveVcss() {
347     return myMappings.hasActiveVcss();
348   }
349
350   public boolean hasAnyMappings() {
351     return ! myMappings.isEmpty();
352   }
353
354 public void addMessageToConsoleWindow(final String message, final TextAttributes attributes) {
355     if (!Registry.is("vcs.showConsole")) return;
356
357     ApplicationManager.getApplication().invokeLater(new Runnable() {
358       public void run() {
359         // for default and disposed projects the ContentManager is not available.
360         if (myProject.isDisposed() || myProject.isDefault()) return;
361         final ContentManager contentManager = getContentManager();
362         if (contentManager == null) {
363           myPendingOutput.add(new Pair<String, TextAttributes>(message, attributes));
364         }
365         else {
366           getOrCreateConsoleContent(contentManager);
367           myEditorAdapter.appendString(message, attributes);
368         }
369       }
370     }, ModalityState.defaultModalityState());
371   }
372
373   private Content getOrCreateConsoleContent(final ContentManager contentManager) {
374     final String displayName = VcsBundle.message("vcs.console.toolwindow.display.name");
375     Content content = contentManager.findContent(displayName);
376     if (content == null) {
377       final EditorFactory editorFactory = EditorFactory.getInstance();
378       final Editor editor = editorFactory.createViewer(editorFactory.createDocument(""), myProject);
379       EditorSettings editorSettings = editor.getSettings();
380       editorSettings.setLineMarkerAreaShown(false);
381       editorSettings.setIndentGuidesShown(false);
382       editorSettings.setLineNumbersShown(false);
383       editorSettings.setFoldingOutlineShown(false);
384
385       myEditorAdapter = new EditorAdapter(editor, myProject);
386       final JPanel panel = new JPanel(new BorderLayout());
387       panel.add(editor.getComponent(), BorderLayout.CENTER);
388
389       content = ContentFactory.SERVICE.getInstance().createContent(panel, displayName, true);
390       contentManager.addContent(content);
391
392       for (Pair<String, TextAttributes> pair : myPendingOutput) {
393         myEditorAdapter.appendString(pair.first, pair.second);
394       }
395       myPendingOutput.clear();
396     }
397     return content;
398   }
399
400   @NotNull
401   public VcsShowSettingOption getOptions(VcsConfiguration.StandardOption option) {
402     return myOptionsAndConfirmations.getOptions(option);
403   }
404
405   public List<VcsShowOptionsSettingImpl> getAllOptions() {
406     return myOptionsAndConfirmations.getAllOptions();
407   }
408
409   @NotNull
410   public VcsShowSettingOption getStandardOption(@NotNull VcsConfiguration.StandardOption option, @NotNull AbstractVcs vcs) {
411     final VcsShowOptionsSettingImpl options = (VcsShowOptionsSettingImpl) getOptions(option);
412     options.addApplicableVcs(vcs);
413     return options;
414   }
415
416   @NotNull
417   public VcsShowSettingOption getOrCreateCustomOption(@NotNull String vcsActionName, @NotNull AbstractVcs vcs) {
418     return myOptionsAndConfirmations.getOrCreateCustomOption(vcsActionName, vcs);
419   }
420
421   public void showProjectOperationInfo(final UpdatedFiles updatedFiles, String displayActionName) {
422     showUpdateProjectInfo(updatedFiles, displayActionName, ActionInfo.STATUS, false);
423   }
424
425   public UpdateInfoTree showUpdateProjectInfo(UpdatedFiles updatedFiles, String displayActionName, ActionInfo actionInfo, boolean canceled) {
426     if (! myProject.isOpen() || myProject.isDisposed()) return null;
427     ContentManager contentManager = getContentManager();
428     if (contentManager == null) {
429       return null;  // content manager is made null during dispose; flag is set later
430     }
431     final UpdateInfoTree updateInfoTree = new UpdateInfoTree(contentManager, myProject, updatedFiles, displayActionName, actionInfo);
432     Content content = ContentFactory.SERVICE.getInstance().createContent(updateInfoTree, canceled ?
433       VcsBundle.message("toolwindow.title.update.action.canceled.info", displayActionName) :
434       VcsBundle.message("toolwindow.title.update.action.info", displayActionName), true);
435     Disposer.register(content, updateInfoTree);
436     ContentsUtil.addContent(contentManager, content, true);
437     ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.VCS).activate(null);
438     updateInfoTree.expandRootChildren();
439     return updateInfoTree;
440   }
441
442   public void cleanupMappings() {
443     myMappings.cleanupMappings();
444   }
445
446   public List<VcsDirectoryMapping> getDirectoryMappings() {
447     return myMappings.getDirectoryMappings();
448   }
449
450   public List<VcsDirectoryMapping> getDirectoryMappings(final AbstractVcs vcs) {
451     return myMappings.getDirectoryMappings(vcs.getName());
452   }
453
454   @Nullable
455   public VcsDirectoryMapping getDirectoryMappingFor(final FilePath path) {
456     return ApplicationManager.getApplication().runReadAction(new Computable<VcsDirectoryMapping>() {
457       @Nullable
458       public VcsDirectoryMapping compute() {
459         VirtualFile vFile = ChangesUtil.findValidParent(path);
460         if (vFile != null) {
461           return myMappings.getMappingFor(vFile);
462         }
463         return null;
464       }
465     });
466   }
467
468   public boolean hasExplicitMapping(final FilePath f) {
469     VirtualFile vFile = ChangesUtil.findValidParent(f);
470     if (vFile == null) return false;
471     return hasExplicitMapping(vFile);
472   }
473
474   public boolean hasExplicitMapping(final VirtualFile vFile) {
475     final VcsDirectoryMapping mapping = myMappings.getMappingFor(vFile);
476     return mapping != null && ! mapping.isDefaultMapping();
477   }
478
479   public void setDirectoryMapping(final String path, final String activeVcsName) {
480     if (myMappingsLoaded) return;            // ignore per-module VCS settings if the mapping table was loaded from .ipr
481     myHaveLegacyVcsConfiguration = true;
482     myMappings.setMapping(FileUtil.toSystemIndependentName(path), activeVcsName);
483   }
484
485   public void setAutoDirectoryMapping(String path, String activeVcsName) {
486     final List<VirtualFile> defaultRoots = myMappings.getDefaultRoots();
487     if (defaultRoots.size() == 1 && "".equals(myMappings.haveDefaultMapping())) {
488       myMappings.removeDirectoryMapping(new VcsDirectoryMapping("", ""));
489     }
490     myMappings.setMapping(path, activeVcsName);
491   }
492
493   public void removeDirectoryMapping(VcsDirectoryMapping mapping) {
494     myMappings.removeDirectoryMapping(mapping);
495   }
496
497   public void setDirectoryMappings(final List<VcsDirectoryMapping> items) {
498     myHaveLegacyVcsConfiguration = true;
499     myMappings.setDirectoryMappings(items);
500   }
501
502   public void iterateVcsRoot(final VirtualFile root, final Processor<FilePath> iterator) {
503     VcsRootIterator.iterateVcsRoot(myProject, root, iterator);
504   }
505
506   @Override
507   public void iterateVcsRoot(VirtualFile root,
508                              Processor<FilePath> iterator,
509                              @Nullable PairProcessor<VirtualFile, VirtualFile[]> directoryFilter) {
510     VcsRootIterator.iterateVcsRoot(myProject, root, iterator, directoryFilter);
511   }
512
513   public void readExternal(Element element) throws InvalidDataException {
514     mySerialization.readExternalUtil(element, myOptionsAndConfirmations);
515     final Attribute attribute = element.getAttribute(SETTINGS_EDITED_MANUALLY);
516     if (attribute != null) {
517       try {
518         myHaveLegacyVcsConfiguration = attribute.getBooleanValue();
519       }
520       catch (DataConversionException e) {
521         //
522       }
523     }
524   }
525
526   public void writeExternal(Element element) throws WriteExternalException {
527     mySerialization.writeExternalUtil(element, myOptionsAndConfirmations);
528     element.setAttribute(SETTINGS_EDITED_MANUALLY, String.valueOf(myHaveLegacyVcsConfiguration));
529   }
530
531   @NotNull
532   public VcsShowConfirmationOption getStandardConfirmation(@NotNull VcsConfiguration.StandardConfirmation option,
533                                                            @NotNull AbstractVcs vcs) {
534     final VcsShowConfirmationOptionImpl result = getConfirmation(option);
535     result.addApplicableVcs(vcs);
536     return result;
537   }
538
539   public List<VcsShowConfirmationOptionImpl> getAllConfirmations() {
540     return myOptionsAndConfirmations.getAllConfirmations();
541   }
542
543   @NotNull
544   public VcsShowConfirmationOptionImpl getConfirmation(VcsConfiguration.StandardConfirmation option) {
545     return myOptionsAndConfirmations.getConfirmation(option);
546   }
547
548   private final Map<VcsListener, MessageBusConnection> myAdapters = new HashMap<VcsListener, MessageBusConnection>();
549
550   public void addVcsListener(VcsListener listener) {
551     final MessageBusConnection connection = myMessageBus.connect();
552     connection.subscribe(VCS_CONFIGURATION_CHANGED, listener);
553     myAdapters.put(listener, connection);
554   }
555
556   public void removeVcsListener(VcsListener listener) {
557     final MessageBusConnection connection = myAdapters.remove(listener);
558     if (connection != null) {
559       connection.disconnect();
560     }
561   }
562
563   public void startBackgroundVcsOperation() {
564     myBackgroundOperationCounter++;
565   }
566
567   public void stopBackgroundVcsOperation() {
568     // in fact, the condition is "should not be called under ApplicationManager.invokeLater() and similiar"
569     assert ! ApplicationManager.getApplication().isDispatchThread();
570     LOG.assertTrue(myBackgroundOperationCounter > 0, "myBackgroundOperationCounter > 0");
571     myBackgroundOperationCounter--;
572   }
573
574   public boolean isBackgroundVcsOperationRunning() {
575     return myBackgroundOperationCounter > 0;
576   }
577
578   public List<VirtualFile> getRootsUnderVcsWithoutFiltering(final AbstractVcs vcs) {
579     return myMappings.getMappingsAsFilesUnderVcs(vcs);
580   }
581
582   @NotNull
583   public VirtualFile[] getRootsUnderVcs(AbstractVcs vcs) {
584     return myMappingsToRoots.getRootsUnderVcs(vcs);
585   }
586
587   public List<VirtualFile> getDetailedVcsMappings(final AbstractVcs vcs) {
588     return myMappingsToRoots.getDetailedVcsMappings(vcs);
589   }
590
591   public VirtualFile[] getAllVersionedRoots() {
592     List<VirtualFile> vFiles = new ArrayList<VirtualFile>();
593     final AbstractVcs[] vcses = myMappings.getActiveVcses();
594     for (AbstractVcs vcs : vcses) {
595       Collections.addAll(vFiles, getRootsUnderVcs(vcs));
596     }
597     return VfsUtil.toVirtualFileArray(vFiles);
598   }
599
600   @NotNull
601   public VcsRoot[] getAllVcsRoots() {
602     List<VcsRoot> vcsRoots = new ArrayList<VcsRoot>();
603     final AbstractVcs[] vcses = myMappings.getActiveVcses();
604     for (AbstractVcs vcs : vcses) {
605       final VirtualFile[] roots = getRootsUnderVcs(vcs);
606       for(VirtualFile root: roots) {
607         vcsRoots.add(new VcsRoot(vcs, root));
608       }
609     }
610     return vcsRoots.toArray(new VcsRoot[vcsRoots.size()]);
611   }
612
613   public void updateActiveVcss() {
614     // not needed
615   }
616
617   public void notifyDirectoryMappingChanged() {
618     myMessageBus.syncPublisher(VCS_CONFIGURATION_CHANGED).directoryMappingChanged();
619   }
620
621   public void readDirectoryMappings(final Element element) {
622     myMappings.clear();
623
624     final List<VcsDirectoryMapping> mappingsList = new ArrayList<VcsDirectoryMapping>();
625     final List list = element.getChildren(ELEMENT_MAPPING);
626     boolean haveNonEmptyMappings = false;
627     for(Object childObj: list) {
628       Element child = (Element) childObj;
629       final String vcs = child.getAttributeValue(ATTRIBUTE_VCS);
630       if (vcs != null && vcs.length() > 0) {
631         haveNonEmptyMappings = true;
632       }
633       VcsDirectoryMapping mapping = new VcsDirectoryMapping(child.getAttributeValue(ATTRIBUTE_DIRECTORY), vcs);
634       mappingsList.add(mapping);
635
636       Element rootSettingsElement = child.getChild(ELEMENT_ROOT_SETTINGS);
637       if (rootSettingsElement != null) {
638         String className = rootSettingsElement.getAttributeValue(ATTRIBUTE_CLASS);
639         AbstractVcs vcsInstance = findVcsByName(mapping.getVcs());
640         if (vcsInstance != null && className != null) {
641           final VcsRootSettings rootSettings = vcsInstance.createEmptyVcsRootSettings();
642           if (rootSettings != null) {
643             try {
644               rootSettings.readExternal(rootSettingsElement);
645               mapping.setRootSettings(rootSettings);
646             } catch (InvalidDataException e) {
647               LOG.error("Failed to load VCS root settings class "+ className + " for VCS " + vcsInstance.getClass().getName(), e);
648             }
649           }
650         }
651       }
652     }
653     boolean defaultProject = Boolean.TRUE.toString().equals(element.getAttributeValue(ATTRIBUTE_DEFAULT_PROJECT));
654     // run autodetection if there's no VCS in default project and 
655     if (haveNonEmptyMappings || !defaultProject) {
656       myMappingsLoaded = true;
657     }
658     myMappings.setDirectoryMappings(mappingsList);
659   }
660
661   public void writeDirectoryMappings(final Element element) {
662     if (myProject.isDefault()) {
663       element.setAttribute(ATTRIBUTE_DEFAULT_PROJECT, Boolean.TRUE.toString());
664     }
665     for(VcsDirectoryMapping mapping: getDirectoryMappings()) {
666       Element child = new Element(ELEMENT_MAPPING);
667       child.setAttribute(ATTRIBUTE_DIRECTORY, mapping.getDirectory());
668       child.setAttribute(ATTRIBUTE_VCS, mapping.getVcs());
669       final VcsRootSettings rootSettings = mapping.getRootSettings();
670       if (rootSettings != null) {
671         Element rootSettingsElement = new Element(ELEMENT_ROOT_SETTINGS);
672         rootSettingsElement.setAttribute(ATTRIBUTE_CLASS, rootSettings.getClass().getName());
673         try {
674           rootSettings.writeExternal(rootSettingsElement);
675           child.addContent(rootSettingsElement);
676         }
677         catch (WriteExternalException e) {
678           // don't add element
679         }
680       }
681       element.addContent(child);
682     }
683   }
684
685   public boolean needAutodetectMappings() {
686     return !myHaveLegacyVcsConfiguration && !myMappingsLoaded;
687   }
688
689   /**
690    * Used to guess VCS for automatic mapping through a look into a working copy
691    */
692   @Nullable
693   public AbstractVcs findVersioningVcs(VirtualFile file) {
694     final VcsDescriptor[] vcsDescriptors = getAllVcss();
695     VcsDescriptor probableVcs = null;
696     for (VcsDescriptor vcsDescriptor : vcsDescriptors) {
697       if (vcsDescriptor.probablyUnderVcs(file)) {
698         if (probableVcs != null) {
699           return null;
700         }
701         probableVcs = vcsDescriptor;
702       }
703     }
704     return probableVcs == null ? null : findVcsByName(probableVcs.getName());
705   }
706
707   public CheckoutProvider.Listener getCompositeCheckoutListener() {
708     return new CompositeCheckoutListener(myProject);
709   }
710
711   @Override
712   public VcsEventsListenerManager getVcsEventsListenerManager() {
713     return myVcsEventListenerManager;
714   }
715
716   public void fireDirectoryMappingsChanged() {
717     if (myProject.isOpen() && ! myProject.isDisposed()) {
718       myMappings.mappingsChanged();
719     }
720   }
721
722   public String haveDefaultMapping() {
723     return myMappings.haveDefaultMapping();
724   }
725
726   @Override
727   protected VcsEnvironmentsProxyCreator getProxyCreator() {
728     return myVcsEventListenerManager;
729   }
730
731   public BackgroundableActionEnabledHandler getBackgroundableActionHandler(final VcsBackgroundableActions action) {
732     ApplicationManager.getApplication().assertIsDispatchThread();
733
734     BackgroundableActionEnabledHandler result = myBackgroundableActionHandlerMap.get(action);
735     if (result == null) {
736       result = new BackgroundableActionEnabledHandler();
737       myBackgroundableActionHandlerMap.put(action, result);
738     }
739     return result;
740   }
741
742   public void addInitializationRequest(final VcsInitObject vcsInitObject, final Runnable runnable) {
743     ApplicationManager.getApplication().runReadAction(new Runnable() {
744       public void run() {
745         myInitialization.add(vcsInitObject, runnable);
746       }
747     });
748   }
749
750   public boolean isFileInContent(final VirtualFile vf) {
751     return vf != null && (myExcludedIndex.isInContent(vf) || isFileInBaseDir(vf) || vf.equals(myProject.getBaseDir()) ||
752                             hasExplicitMapping(vf) || isInDirectoryBasedRoot(vf)) && ! myExcludedIndex.isExcludedFile(vf);
753   }
754
755   @Override
756   public boolean dvcsUsedInProject() {
757     AbstractVcs[] allActiveVcss = getAllActiveVcss();
758     for (AbstractVcs activeVcs : allActiveVcss) {
759       if (VcsType.distibuted.equals(activeVcs.getType())) {
760         return true;
761       }
762     }
763     return false;
764   }
765
766   private boolean isInDirectoryBasedRoot(final VirtualFile file) {
767     if (file == null) return false;
768     final StorageScheme storageScheme = ((ProjectEx) myProject).getStateStore().getStorageScheme();
769     if (StorageScheme.DIRECTORY_BASED.equals(storageScheme)) {
770       final VirtualFile baseDir = myProject.getBaseDir();
771       if (baseDir == null) return false;
772       final VirtualFile ideaDir = baseDir.findChild(Project.DIRECTORY_STORE_FOLDER);
773       return ideaDir != null && ideaDir.isValid() && ideaDir.isDirectory() && VfsUtil.isAncestor(ideaDir, file, false);
774     }
775     return false;
776   }
777
778   private boolean isFileInBaseDir(final VirtualFile file) {
779     VirtualFile parent = file.getParent();
780     return !file.isDirectory() && parent != null && parent.equals(myProject.getBaseDir());
781   }
782
783   // inner roots disclosed
784   public static List<VirtualFile> getRootsUnder(final List<VirtualFile> roots, final VirtualFile underWhat) {
785     final List<VirtualFile> result = new ArrayList<VirtualFile>(roots.size());
786     for (VirtualFile root : roots) {
787       if (VfsUtil.isAncestor(underWhat, root, false)) {
788         result.add(root);
789       }
790     }
791     return result;
792   }
793
794   public VcsHistoryCache getVcsHistoryCache() {
795     return myVcsHistoryCache;
796   }
797
798   @Override
799   public ContentRevisionCache getContentRevisionCache() {
800     return myContentRevisionCache;
801   }
802 }