SVN: reuse proper way for checking for presence of admin directory (.svn) in dir
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / SvnVcs.java
blob924540d3a77e592c64c5f7408f49e899c022c73e
1 /**
2 * @copyright
3 * ====================================================================
4 * Copyright (c) 2003-2004 QintSoft. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://svnup.tigris.org/.
15 * ====================================================================
16 * @endcopyright
19 * Copyright 2000-2005 JetBrains s.r.o.
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the License.
23 * You may obtain a copy of the License at
25 * http://www.apache.org/licenses/LICENSE-2.0
27 * Unless required by applicable law or agreed to in writing, software
28 * distributed under the License is distributed on an "AS IS" BASIS,
29 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 * See the License for the specific language governing permissions and
31 * limitations under the License.
33 package org.jetbrains.idea.svn;
35 import com.intellij.ide.FrameStateListener;
36 import com.intellij.ide.FrameStateManager;
37 import com.intellij.openapi.actionSystem.AnAction;
38 import com.intellij.openapi.application.Application;
39 import com.intellij.openapi.application.ApplicationManager;
40 import com.intellij.openapi.diagnostic.Logger;
41 import com.intellij.openapi.options.Configurable;
42 import com.intellij.openapi.progress.ProcessCanceledException;
43 import com.intellij.openapi.progress.ProgressManager;
44 import com.intellij.openapi.project.DumbAwareRunnable;
45 import com.intellij.openapi.project.Project;
46 import com.intellij.openapi.startup.StartupManager;
47 import com.intellij.openapi.ui.DialogWrapper;
48 import com.intellij.openapi.ui.Messages;
49 import com.intellij.openapi.util.Pair;
50 import com.intellij.openapi.util.SystemInfo;
51 import com.intellij.openapi.util.Trinity;
52 import com.intellij.openapi.vcs.*;
53 import com.intellij.openapi.vcs.annotate.AnnotationProvider;
54 import com.intellij.openapi.vcs.changes.*;
55 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
56 import com.intellij.openapi.vcs.diff.DiffProvider;
57 import com.intellij.openapi.vcs.history.VcsHistoryProvider;
58 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
59 import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
60 import com.intellij.openapi.vcs.merge.MergeProvider;
61 import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
62 import com.intellij.openapi.vcs.update.UpdateEnvironment;
63 import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
64 import com.intellij.openapi.vfs.VirtualFile;
65 import com.intellij.openapi.vfs.VirtualFileManager;
66 import com.intellij.util.containers.Convertor;
67 import com.intellij.util.containers.SoftHashMap;
68 import com.intellij.util.messages.MessageBus;
69 import com.intellij.util.messages.MessageBusConnection;
70 import com.intellij.util.messages.Topic;
71 import org.jetbrains.annotations.NonNls;
72 import org.jetbrains.annotations.NotNull;
73 import org.jetbrains.annotations.Nullable;
74 import org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction;
75 import org.jetbrains.idea.svn.actions.SvnMergeProvider;
76 import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider;
77 import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment;
78 import org.jetbrains.idea.svn.dialogs.SvnFormatWorker;
79 import org.jetbrains.idea.svn.dialogs.WCInfo;
80 import org.jetbrains.idea.svn.history.LoadedRevisionsCache;
81 import org.jetbrains.idea.svn.history.SvnChangeList;
82 import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider;
83 import org.jetbrains.idea.svn.history.SvnHistoryProvider;
84 import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment;
85 import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment;
86 import org.jetbrains.idea.svn.update.SvnUpdateEnvironment;
87 import org.tmatesoft.svn.core.*;
88 import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
89 import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
90 import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
91 import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
92 import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
93 import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea14;
94 import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory;
95 import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
96 import org.tmatesoft.svn.core.io.SVNRepository;
97 import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
98 import org.tmatesoft.svn.core.wc.*;
99 import org.tmatesoft.svn.util.SVNDebugLog;
100 import org.tmatesoft.svn.util.SVNDebugLogAdapter;
101 import org.tmatesoft.svn.util.SVNLogType;
103 import java.io.File;
104 import java.io.UnsupportedEncodingException;
105 import java.util.*;
106 import java.util.logging.Level;
108 @SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
109 public class SvnVcs extends AbstractVcs {
110 private final static Logger REFRESH_LOG = Logger.getInstance("#svn_refresh");
112 private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnVcs");
113 private final Map<String, Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>> myPropertyCache = new SoftHashMap<String, Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>>();
115 private final SvnConfiguration myConfiguration;
116 private final SvnEntriesFileListener myEntriesFileListener;
118 private CheckinEnvironment myCheckinEnvironment;
119 private RollbackEnvironment myRollbackEnvironment;
120 private UpdateEnvironment mySvnUpdateEnvironment;
121 private UpdateEnvironment mySvnIntegrateEnvironment;
122 private VcsHistoryProvider mySvnHistoryProvider;
123 private AnnotationProvider myAnnotationProvider;
124 private DiffProvider mySvnDiffProvider;
125 private final VcsShowConfirmationOption myAddConfirmation;
126 private final VcsShowConfirmationOption myDeleteConfirmation;
127 private EditFileProvider myEditFilesProvider;
128 private SvnCommittedChangesProvider myCommittedChangesProvider;
129 private final VcsShowSettingOption myCheckoutOptions;
131 private ChangeProvider myChangeProvider;
132 private MergeProvider myMergeProvider;
134 @NonNls public static final String LOG_PARAMETER_NAME = "javasvn.log";
135 @NonNls public static final String VCS_NAME = "svn";
136 public static final String pathToEntries = SvnUtil.SVN_ADMIN_DIR_NAME + File.separatorChar + SvnUtil.ENTRIES_FILE_NAME;
137 public static final String pathToDirProps = SvnUtil.SVN_ADMIN_DIR_NAME + File.separatorChar + SvnUtil.DIR_PROPS_FILE_NAME;
138 private final SvnChangelistListener myChangeListListener;
139 private MessageBusConnection myMessageBusConnection;
141 private SvnCopiesRefreshManager myCopiesRefreshManager;
142 private SvnFileUrlMappingImpl myMapping;
143 private final MyFrameStateListener myFrameStateListener;
145 public static final Topic<Runnable> ROOTS_RELOADED = new Topic<Runnable>("ROOTS_RELOADED", Runnable.class);
147 static {
148 //noinspection UseOfArchaicSystemPropertyAccessors
149 final JavaSVNDebugLogger logger = new JavaSVNDebugLogger(Boolean.getBoolean(LOG_PARAMETER_NAME), LOG);
150 SVNDebugLog.setDefaultLog(logger);
151 SVNAdminAreaFactory.setSelector(new SvnFormatSelector());
153 DAVRepositoryFactory.setup();
154 SVNRepositoryFactoryImpl.setup();
155 FSRepositoryFactory.setup();
157 // non-optimized writing is fast enough on Linux/MacOS, and somewhat more reliable
158 if (SystemInfo.isWindows) {
159 SVNAdminArea14.setOptimizedWritingEnabled(true);
163 private static Boolean booleanProperty(final String systemParameterName) {
164 return Boolean.valueOf(System.getProperty(systemParameterName));
167 public SvnVcs(final Project project, MessageBus bus, SvnConfiguration svnConfiguration, final ChangeListManager changeListManager,
168 final VcsDirtyScopeManager vcsDirtyScopeManager) {
169 super(project);
170 LOG.debug("ct");
171 myConfiguration = svnConfiguration;
173 dumpFileStatus(FileStatus.ADDED);
174 dumpFileStatus(FileStatus.DELETED);
175 dumpFileStatus(FileStatus.MERGE);
176 dumpFileStatus(FileStatus.MODIFIED);
177 dumpFileStatus(FileStatus.NOT_CHANGED);
178 dumpFileStatus(FileStatus.UNKNOWN);
180 dumpFileStatus(SvnFileStatus.REPLACED);
181 dumpFileStatus(SvnFileStatus.EXTERNAL);
182 dumpFileStatus(SvnFileStatus.OBSTRUCTED);
184 myEntriesFileListener = new SvnEntriesFileListener(project);
186 final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project);
187 myAddConfirmation = vcsManager.getStandardConfirmation(VcsConfiguration.StandardConfirmation.ADD, this);
188 myDeleteConfirmation = vcsManager.getStandardConfirmation(VcsConfiguration.StandardConfirmation.REMOVE, this);
189 myCheckoutOptions = vcsManager.getStandardOption(VcsConfiguration.StandardOption.CHECKOUT, this);
191 if (myProject.isDefault()) {
192 myChangeListListener = null;
193 } else {
194 upgradeIfNeeded(bus);
196 myChangeListListener = new SvnChangelistListener(myProject, createChangelistClient());
197 changeListManager.addChangeListListener(myChangeListListener);
199 myMessageBusConnection = bus.connect();
200 myMessageBusConnection.subscribe(ProjectLevelVcsManagerImpl.VCS_MAPPING_CHANGED, new Runnable() {
201 public void run() {
202 invokeRefreshSvnRoots(true);
207 // do one time after project loaded
208 StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
209 public void run() {
210 postStartup();
212 // for IDEA, it takes 2 minutes - and anyway this can be done in background, no sence...
213 // once it could be mistaken about copies for 2 minutes on start...
215 /*if (! myMapping.getAllWcInfos().isEmpty()) {
216 invokeRefreshSvnRoots();
217 return;
219 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
220 public void run() {
221 myCopiesRefreshManager.getCopiesRefresh().ensureInit();
223 }, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject);*/
227 myFrameStateListener = new MyFrameStateListener(changeListManager, vcsDirtyScopeManager);
230 public void postStartup() {
231 myMapping = SvnFileUrlMappingImpl.getInstance(myProject);
232 // only mapping for default
233 if (myProject.isDefault()) return;
234 myCopiesRefreshManager = new SvnCopiesRefreshManager(myProject, myMapping);
236 invokeRefreshSvnRoots(true);
239 public void invokeRefreshSvnRoots(final boolean asynchronous) {
240 REFRESH_LOG.debug("refresh: ", new Throwable());
241 if (myCopiesRefreshManager != null) {
242 if (asynchronous) {
243 myCopiesRefreshManager.getCopiesRefresh().asynchRequest();
244 } else {
245 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
246 public void run() {
247 myCopiesRefreshManager.getCopiesRefresh().synchRequest();
249 }, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject);
254 private void upgradeIfNeeded(final MessageBus bus) {
255 final MessageBusConnection connection = bus.connect();
256 connection.subscribe(ChangeListManagerImpl.LISTS_LOADED, new LocalChangeListsLoadedListener() {
257 public void processLoadedLists(final List<LocalChangeList> lists) {
258 try {
259 ChangeListManager.getInstanceChecked(myProject).setReadOnly(SvnChangeProvider.ourDefaultListName, true);
261 final SvnConfiguration.SvnSupportOptions supportOptions = myConfiguration.getSupportOptions();
263 upgradeTo15(supportOptions);
264 if (! supportOptions.changeListsSynchronized()) {
265 processChangeLists(lists);
266 supportOptions.upgradeToChangeListsSynchronized();
269 catch (ProcessCanceledException e) {
272 connection.disconnect();
277 public void processChangeLists(final List<LocalChangeList> lists) {
278 final ProjectLevelVcsManager plVcsManager = ProjectLevelVcsManager.getInstanceChecked(myProject);
279 plVcsManager.startBackgroundVcsOperation();
280 try {
281 final SVNChangelistClient client = createChangelistClient();
282 for (LocalChangeList list : lists) {
283 if (! list.isDefault()) {
284 final Collection<Change> changes = list.getChanges();
285 for (Change change : changes) {
286 correctListForRevision(plVcsManager, change.getBeforeRevision(), client, list.getName());
287 correctListForRevision(plVcsManager, change.getAfterRevision(), client, list.getName());
292 finally {
293 final Application appManager = ApplicationManager.getApplication();
294 if (appManager.isDispatchThread()) {
295 appManager.executeOnPooledThread(new Runnable() {
296 public void run() {
297 plVcsManager.stopBackgroundVcsOperation();
300 } else {
301 plVcsManager.stopBackgroundVcsOperation();
306 private void correctListForRevision(final ProjectLevelVcsManager plVcsManager, final ContentRevision revision,
307 final SVNChangelistClient client, final String name) {
308 if (revision != null) {
309 final FilePath path = revision.getFile();
310 final AbstractVcs vcs = plVcsManager.getVcsFor(path);
311 if ((vcs != null) && VCS_NAME.equals(vcs.getName())) {
312 try {
313 client.doAddToChangelist(new File[] {path.getIOFile()}, SVNDepth.EMPTY, name, null);
315 catch (SVNException e) {
316 // left in default list
322 private void upgradeTo15(final SvnConfiguration.SvnSupportOptions supportOptions) {
323 if (! supportOptions.upgradeTo15Asked()) {
324 final SvnWorkingCopyChecker workingCopyChecker = new SvnWorkingCopyChecker();
326 if (workingCopyChecker.upgradeNeeded()) {
327 ApplicationManager.getApplication().invokeLater(new Runnable() {
328 public void run() {
329 // ask for upgrade
330 final int upgradeAnswer = Messages.showYesNoDialog(SvnBundle.message("upgrade.format.to15.question.text",
331 SvnBundle.message("label.where.svn.format.can.be.changed.text", SvnBundle.message("action.show.svn.map.text"))),
332 SvnBundle.message("upgrade.format.to15.question.title"), Messages.getWarningIcon());
333 if (DialogWrapper.OK_EXIT_CODE == upgradeAnswer) {
334 workingCopyChecker.doUpgrade();
342 @Override
343 public void activate() {
344 super.activate();
345 SvnApplicationSettings.getInstance().svnActivated();
346 VirtualFileManager.getInstance().addVirtualFileListener(myEntriesFileListener);
347 // this will initialize its inner listener for committed changes upload
348 LoadedRevisionsCache.getInstance(myProject);
349 FrameStateManager.getInstance().addListener(myFrameStateListener);
352 @Override
353 public void deactivate() {
354 FrameStateManager.getInstance().removeListener(myFrameStateListener);
355 if (myMessageBusConnection != null) {
356 myMessageBusConnection.disconnect();
358 VirtualFileManager.getInstance().removeVirtualFileListener(myEntriesFileListener);
359 SvnApplicationSettings.getInstance().svnDeactivated();
360 new DefaultSVNRepositoryPool(null, null).shutdownConnections(true);
361 if (myCommittedChangesProvider != null) {
362 myCommittedChangesProvider.deactivate();
364 if (myChangeListListener != null && (! myProject.isDefault())) {
365 ChangeListManager.getInstance(myProject).removeChangeListListener(myChangeListListener);
367 super.deactivate();
370 public VcsShowConfirmationOption getAddConfirmation() {
371 return myAddConfirmation;
374 public VcsShowConfirmationOption getDeleteConfirmation() {
375 return myDeleteConfirmation;
378 public VcsShowSettingOption getCheckoutOptions() {
379 return myCheckoutOptions;
382 public EditFileProvider getEditFileProvider() {
383 if (myEditFilesProvider == null) {
384 myEditFilesProvider = new SvnEditFileProvider(this);
386 return myEditFilesProvider;
389 @NotNull
390 public ChangeProvider getChangeProvider() {
391 if (myChangeProvider == null) {
392 myChangeProvider = new SvnChangeProvider(this);
394 return myChangeProvider;
397 public SVNRepository createRepository(String url) throws SVNException {
398 SVNRepository repos = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
399 repos.setAuthenticationManager(myConfiguration.getAuthenticationManager(myProject));
400 repos.setTunnelProvider(myConfiguration.getOptions(myProject));
401 return repos;
404 public SVNRepository createRepository(SVNURL url) throws SVNException {
405 SVNRepository repos = SVNRepositoryFactory.create(url);
406 repos.setAuthenticationManager(myConfiguration.getAuthenticationManager(myProject));
407 repos.setTunnelProvider(myConfiguration.getOptions(myProject));
408 return repos;
411 public SVNUpdateClient createUpdateClient() {
412 return new SVNUpdateClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
415 public SVNStatusClient createStatusClient() {
416 return new SVNStatusClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
419 public SVNWCClient createWCClient() {
420 return new SVNWCClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
423 public SVNCopyClient createCopyClient() {
424 return new SVNCopyClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
427 public SVNMoveClient createMoveClient() {
428 return new SVNMoveClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
431 public SVNLogClient createLogClient() {
432 return new SVNLogClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
435 public SVNCommitClient createCommitClient() {
436 return new SVNCommitClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
439 public SVNDiffClient createDiffClient() {
440 return new SVNDiffClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
443 public SVNChangelistClient createChangelistClient() {
444 return new SVNChangelistClient(myConfiguration.getAuthenticationManager(myProject), myConfiguration.getOptions(myProject));
447 public SVNWCAccess createWCAccess() {
448 final SVNWCAccess access = SVNWCAccess.newInstance(null);
449 access.setOptions(myConfiguration.getOptions(myProject));
450 return access;
453 public ISVNOptions getSvnOptions() {
454 return myConfiguration.getOptions(myProject);
457 public ISVNAuthenticationManager getSvnAuthenticationManager() {
458 return myConfiguration.getAuthenticationManager(myProject);
461 void dumpFileStatus(FileStatus fs) {
462 if (LOG.isDebugEnabled()) {
463 LOG.debug("FileStatus:" + fs.getText() + " " + fs.getColor() + " " + " " + fs.getClass().getName());
467 public UpdateEnvironment getIntegrateEnvironment() {
468 if (mySvnIntegrateEnvironment == null) {
469 mySvnIntegrateEnvironment = new SvnIntegrateEnvironment(this);
471 return mySvnIntegrateEnvironment;
474 public UpdateEnvironment getUpdateEnvironment() {
475 if (mySvnUpdateEnvironment == null) {
476 mySvnUpdateEnvironment = new SvnUpdateEnvironment(this);
478 return mySvnUpdateEnvironment;
481 public String getName() {
482 LOG.debug("getName");
483 return VCS_NAME;
486 public String getDisplayName() {
487 LOG.debug("getDisplayName");
488 return "Subversion";
491 public Configurable getConfigurable() {
492 LOG.debug("createConfigurable");
493 return new SvnConfigurable(myProject);
496 public Project getProject() {
497 return myProject;
500 public SvnConfiguration getSvnConfiguration() {
501 return myConfiguration;
504 public static SvnVcs getInstance(Project project) {
505 return (SvnVcs) ProjectLevelVcsManager.getInstance(project).findVcsByName(VCS_NAME);
508 @NotNull
509 public CheckinEnvironment getCheckinEnvironment() {
510 if (myCheckinEnvironment == null) {
511 myCheckinEnvironment = new SvnCheckinEnvironment(this);
513 return myCheckinEnvironment;
516 @NotNull
517 public RollbackEnvironment getRollbackEnvironment() {
518 if (myRollbackEnvironment == null) {
519 myRollbackEnvironment = new SvnRollbackEnvironment(this);
521 return myRollbackEnvironment;
524 public VcsHistoryProvider getVcsHistoryProvider() {
525 // no heavy state, but it would be useful to have place to keep state in -> do not reuse instance
526 return new SvnHistoryProvider(this);
529 public VcsHistoryProvider getVcsBlockHistoryProvider() {
530 return getVcsHistoryProvider();
533 public AnnotationProvider getAnnotationProvider() {
534 if (myAnnotationProvider == null) {
535 myAnnotationProvider = new SvnAnnotationProvider(this);
537 return myAnnotationProvider;
540 public SvnEntriesFileListener getSvnEntriesFileListener() {
541 return myEntriesFileListener;
544 public DiffProvider getDiffProvider() {
545 if (mySvnDiffProvider == null) {
546 mySvnDiffProvider = new SvnDiffProvider(this);
548 return mySvnDiffProvider;
551 private Trinity<Long, Long, Long> getTimestampForPropertiesChange(final File ioFile, final boolean isDir) {
552 final File dir = isDir ? ioFile : ioFile.getParentFile();
553 final String relPath = SVNAdminUtil.getPropPath(ioFile.getName(), isDir ? SVNNodeKind.DIR : SVNNodeKind.FILE, false);
554 final String relPathBase = SVNAdminUtil.getPropBasePath(ioFile.getName(), isDir ? SVNNodeKind.DIR : SVNNodeKind.FILE, false);
555 final String relPathRevert = SVNAdminUtil.getPropRevertPath(ioFile.getName(), isDir ? SVNNodeKind.DIR : SVNNodeKind.FILE, false);
556 return new Trinity<Long, Long, Long>(new File(dir, relPath).lastModified(), new File(dir, relPathBase).lastModified(),
557 new File(dir, relPathRevert).lastModified());
560 private boolean trinitiesEqual(final Trinity<Long, Long, Long> t1, final Trinity<Long, Long, Long> t2) {
561 if (t2.first == 0 && t2.second == 0 && t2.third == 0) return false;
562 return t1.equals(t2);
565 @Nullable
566 public SVNPropertyValue getPropertyWithCaching(final VirtualFile file, final String propName) throws SVNException {
567 Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>> cachedMap = myPropertyCache.get(keyForVf(file));
568 final Pair<SVNPropertyValue, Trinity<Long, Long, Long>> cachedValue = (cachedMap == null) ? null : cachedMap.get(propName);
570 final File ioFile = new File(file.getPath());
571 final Trinity<Long, Long, Long> tsTrinity = getTimestampForPropertiesChange(ioFile, file.isDirectory());
573 if (cachedValue != null) {
574 // zero means that a file was not found
575 if (trinitiesEqual(cachedValue.getSecond(), tsTrinity)) {
576 return cachedValue.getFirst();
580 final SVNPropertyData value = createWCClient().doGetProperty(ioFile, propName, SVNRevision.WORKING, SVNRevision.WORKING);
581 final SVNPropertyValue propValue = (value == null) ? null : value.getValue();
583 if (cachedMap == null) {
584 cachedMap = new HashMap<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>();
585 myPropertyCache.put(keyForVf(file), cachedMap);
588 cachedMap.put(propName, new Pair<SVNPropertyValue, Trinity<Long, Long, Long>>(propValue, tsTrinity));
590 return propValue;
593 public boolean fileExistsInVcs(FilePath path) {
594 File file = path.getIOFile();
595 SVNStatus status;
596 try {
597 status = createStatusClient().doStatus(file, false);
598 if (status != null) {
599 final SVNStatusType statusType = status.getContentsStatus();
600 if (statusType == SVNStatusType.STATUS_ADDED) {
601 return status.isCopied();
603 return !(status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED ||
604 status.getContentsStatus() == SVNStatusType.STATUS_IGNORED ||
605 status.getContentsStatus() == SVNStatusType.STATUS_OBSTRUCTED);
608 catch (SVNException e) {
611 return false;
614 public boolean fileIsUnderVcs(FilePath path) {
615 final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
616 return (! SvnStatusUtil.isIgnoredInAnySense(clManager, path.getVirtualFile())) && (! clManager.isUnversioned(path.getVirtualFile()));
619 private static File getEntriesFile(File file) {
620 return file.isDirectory() ? new File(file, pathToEntries) : new File(file.getParentFile(), pathToEntries);
623 private static File getDirPropsFile(File file) {
624 return new File(file, pathToDirProps);
627 @Nullable
628 public SVNInfo getInfo(final VirtualFile file) {
629 try {
630 SVNWCClient wcClient = new SVNWCClient(getSvnAuthenticationManager(), getSvnOptions());
631 SVNInfo info = wcClient.doInfo(new File(file.getPath()), SVNRevision.WORKING);
632 if (info == null || info.getRepositoryRootURL() == null) {
633 info = wcClient.doInfo(new File(file.getPath()), SVNRevision.HEAD);
635 return info;
637 catch (SVNException e) {
638 return null;
642 public static class SVNStatusHolder {
644 private final SVNStatus myValue;
645 private final long myEntriesTimestamp;
646 private final long myFileTimestamp;
647 private final boolean myIsLocked;
649 public SVNStatusHolder(long entriesStamp, long fileStamp, SVNStatus value) {
650 myValue = value;
651 myEntriesTimestamp = entriesStamp;
652 myFileTimestamp = fileStamp;
653 myIsLocked = value != null && value.isLocked();
656 public long getEntriesTimestamp() {
657 return myEntriesTimestamp;
660 public long getFileTimestamp() {
661 return myFileTimestamp;
664 public boolean isLocked() {
665 return myIsLocked;
668 public SVNStatus getStatus() {
669 return myValue;
673 public static class SVNInfoHolder {
675 private final SVNInfo myValue;
676 private final long myEntriesTimestamp;
677 private final long myFileTimestamp;
679 public SVNInfoHolder(long entriesStamp, long fileStamp, SVNInfo value) {
680 myValue = value;
681 myEntriesTimestamp = entriesStamp;
682 myFileTimestamp = fileStamp;
685 public long getEntriesTimestamp() {
686 return myEntriesTimestamp;
689 public long getFileTimestamp() {
690 return myFileTimestamp;
693 public SVNInfo getInfo() {
694 return myValue;
698 private static class JavaSVNDebugLogger extends SVNDebugLogAdapter {
699 private final boolean myLoggingEnabled;
700 private final Logger myLog;
701 @NonNls public static final String TRACE_LOG_PARAMETER_NAME = "javasvn.log.trace";
703 public JavaSVNDebugLogger(boolean loggingEnabled, Logger log) {
704 myLoggingEnabled = loggingEnabled;
705 myLog = log;
708 public void log(final SVNLogType logType, final Throwable th, final Level logLevel) {
709 if (myLoggingEnabled) {
710 myLog.info(th);
714 public void log(final SVNLogType logType, final String message, final Level logLevel) {
715 if (myLoggingEnabled) {
716 myLog.info(message);
720 public void log(final SVNLogType logType, final String message, final byte[] data) {
721 if (myLoggingEnabled) {
722 if (data != null) {
723 try {
724 myLog.info(message + "\n" + new String(data, "UTF-8"));
726 catch (UnsupportedEncodingException e) {
727 myLog.info(message + "\n" + new String(data));
729 } else {
730 myLog.info(message);
736 public FileStatus[] getProvidedStatuses() {
737 return new FileStatus[]{SvnFileStatus.EXTERNAL,
738 SvnFileStatus.OBSTRUCTED,
739 SvnFileStatus.REPLACED};
743 @Override @NotNull
744 public CommittedChangesProvider<SvnChangeList, ChangeBrowserSettings> getCommittedChangesProvider() {
745 if (myCommittedChangesProvider == null) {
746 myCommittedChangesProvider = new SvnCommittedChangesProvider(myProject);
748 return myCommittedChangesProvider;
751 @Nullable
752 @Override
753 public VcsRevisionNumber parseRevisionNumber(final String revisionNumberString) {
754 final SVNRevision revision = SVNRevision.parse(revisionNumberString);
755 if (revision.equals(SVNRevision.UNDEFINED)) {
756 return null;
758 return new SvnRevisionNumber(revision);
761 @Override
762 public String getRevisionPattern() {
763 return ourIntegerPattern;
766 @Override
767 public boolean isVersionedDirectory(final VirtualFile dir) {
768 return SvnUtil.seemsLikeVersionedDir(dir);
771 @NotNull
772 public SvnFileUrlMapping getSvnFileUrlMapping() {
773 return myMapping;
777 * Returns real working copies roots - if there is <Project Root> -> Subversion setting,
778 * and there is one working copy, will return one root
780 public List<WCInfo> getAllWcInfos() {
781 final SvnFileUrlMapping urlMapping = getSvnFileUrlMapping();
783 final List<RootUrlInfo> infoList = urlMapping.getAllWcInfos();
784 final List<WCInfo> infos = new ArrayList<WCInfo>();
785 for (RootUrlInfo info : infoList) {
786 final File file = info.getIoFile();
787 infos.add(new WCInfo(file.getAbsolutePath(), info.getAbsoluteUrlAsUrl(),
788 SvnFormatSelector.getWorkingCopyFormat(file), info.getRepositoryUrl(), SvnUtil.isWorkingCopyRoot(file)));
790 return infos;
793 private class SvnWorkingCopyChecker {
794 private List<WCInfo> myAllWcInfos;
796 public boolean upgradeNeeded() {
797 myAllWcInfos = getAllWcInfos();
798 for (WCInfo info : myAllWcInfos) {
799 if (! WorkingCopyFormat.ONE_DOT_FIVE.equals(info.getFormat())) {
800 return true;
803 return false;
806 public void doUpgrade() {
807 ApplicationManager.getApplication().invokeLater(new Runnable() {
808 public void run() {
809 final SvnFormatWorker formatWorker = new SvnFormatWorker(myProject, WorkingCopyFormat.ONE_DOT_FIVE, myAllWcInfos);
810 // additionally ask about working copies with roots above the project root
811 formatWorker.checkForOutsideCopies();
812 if (formatWorker.haveStuffToConvert()) {
813 ProgressManager.getInstance().run(formatWorker);
820 @Override
821 public RootsConvertor getCustomConvertor() {
822 return myMapping;
825 @Override
826 public MergeProvider getMergeProvider() {
827 if (myMergeProvider == null) {
828 myMergeProvider = new SvnMergeProvider(myProject);
830 return myMergeProvider;
833 @Override
834 public List<AnAction> getAdditionalActionsForLocalChange() {
835 return Arrays.<AnAction>asList(new ShowPropertiesDiffWithLocalAction());
838 public void pathChanged(final File from, final File to) throws SVNException {
839 myChangeListListener.pathChanged(from, to);
842 private String keyForVf(final VirtualFile vf) {
843 return vf.getUrl();
846 @Override
847 public boolean allowsNestedRoots() {
848 return SvnConfiguration.getInstance(myProject).DETECT_NESTED_COPIES;
851 @Override
852 public List<VirtualFile> filterUniqueRoots(final List<VirtualFile> in) {
853 if (in.size() <= 1) return in;
855 final List<RootUrlPair> infos = new ArrayList<RootUrlPair>(in.size());
856 final SvnFileUrlMappingImpl mapping = myMapping;
857 for (VirtualFile vf : in) {
858 final File ioFile = new File(vf.getPath());
859 final SVNURL url = mapping.getUrlForFile(ioFile);
860 if (url == null) continue;
861 /*if ((info == null) || (! ioFile.getAbsolutePath().equals(info.getIoFile().getAbsolutePath()))) {
862 // we get one of roots there, there shouldn't be other paths
863 continue;
865 infos.add(new MyPair(vf, url.toString()));
867 final List<RootUrlPair> filtered = new ArrayList<RootUrlPair>(infos.size());
868 ForNestedRootChecker.filterOutSuperfluousChildren(this, infos, filtered);
870 return ObjectsConvertor.convert(filtered, new Convertor<RootUrlPair, VirtualFile>() {
871 public VirtualFile convert(RootUrlPair o) {
872 return o.getVirtualFile();
877 private static class MyPair implements RootUrlPair {
878 private final VirtualFile myFile;
879 private final String myUrl;
881 private MyPair(VirtualFile file, String url) {
882 myFile = file;
883 myUrl = url;
886 public VirtualFile getVirtualFile() {
887 return myFile;
890 public String getUrl() {
891 return myUrl;
895 private static class MyFrameStateListener implements FrameStateListener {
896 private final ChangeListManager myClManager;
897 private final VcsDirtyScopeManager myDirtyScopeManager;
899 private MyFrameStateListener(ChangeListManager clManager, VcsDirtyScopeManager dirtyScopeManager) {
900 myClManager = clManager;
901 myDirtyScopeManager = dirtyScopeManager;
904 public void onFrameDeactivated() {
907 public void onFrameActivated() {
908 final List<VirtualFile> folders = ((ChangeListManagerImpl)myClManager).getLockedFolders();
909 if (! folders.isEmpty()) {
910 myDirtyScopeManager.filesDirty(null, folders);