blob: c7422ef2a22081d22cdc4b949aa53bc432d19a18 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.checkout;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.CheckoutProvider;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx;
import com.intellij.openapi.vcs.update.RefreshVFsSynchronously;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.actions.ExclusiveBackgroundVcsAction;
import org.jetbrains.idea.svn.actions.SvnExcludingIgnoredOperation;
import org.jetbrains.idea.svn.api.ClientFactory;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.api.ProgressTracker;
import org.jetbrains.idea.svn.checkin.CommitEventHandler;
import org.jetbrains.idea.svn.checkin.IdeaCommitHandler;
import org.jetbrains.idea.svn.dialogs.CheckoutDialog;
import org.jetbrains.idea.svn.dialogs.UpgradeFormatDialog;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import javax.swing.*;
import java.io.File;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class SvnCheckoutProvider implements CheckoutProvider {
public void doCheckout(@NotNull final Project project, Listener listener) {
// TODO: Several dialogs is invoked while dialog.show() - seems code should be rewritten to be more transparent
CheckoutDialog dialog = new CheckoutDialog(project, listener);
dialog.show();
}
public static void doCheckout(@NotNull Project project, @NotNull File target, final String url, final SVNRevision revision,
final Depth depth, final boolean ignoreExternals, @Nullable final Listener listener) {
if (! target.exists()) {
target.mkdirs();
}
final WorkingCopyFormat selectedFormat = promptForWCopyFormat(target, project);
// UNKNOWN here means operation was cancelled
if (selectedFormat != WorkingCopyFormat.UNKNOWN) {
checkout(project, target, url, revision, depth, ignoreExternals, listener, selectedFormat);
}
}
@NotNull
public static ClientFactory getFactory(@NotNull SvnVcs vcs, @NotNull WorkingCopyFormat format) throws VcsException {
ClientFactory settingsFactory = vcs.getFactoryFromSettings();
ClientFactory otherFactory = vcs.getOtherFactory();
List<WorkingCopyFormat> settingsFactoryFormats = settingsFactory.createCheckoutClient().getSupportedFormats();
List<WorkingCopyFormat> otherFactoryFormats = CheckoutFormatFromUserProvider.getOtherFactoryFormats(otherFactory);
return settingsFactoryFormats.contains(format) || !otherFactoryFormats.contains(format) ? settingsFactory : otherFactory;
}
public static void checkout(final Project project,
final File target,
final String url,
final SVNRevision revision,
final Depth depth,
final boolean ignoreExternals,
final Listener listener, final WorkingCopyFormat selectedFormat) {
final Ref<Boolean> checkoutSuccessful = new Ref<Boolean>();
final Exception[] exception = new Exception[1];
final Task.Backgroundable checkoutBackgroundTask = new Task.Backgroundable(project,
SvnBundle.message("message.title.check.out"), true, VcsConfiguration.getInstance(project).getCheckoutOption()) {
public void run(@NotNull final ProgressIndicator indicator) {
final WorkingCopyFormat format = selectedFormat == null ? WorkingCopyFormat.UNKNOWN : selectedFormat;
SvnWorkingCopyFormatHolder.setPresetFormat(format);
SvnVcs vcs = SvnVcs.getInstance(project);
ProgressTracker handler = new CheckoutEventHandler(vcs, false, ProgressManager.getInstance().getProgressIndicator());
ProgressManager.progress(SvnBundle.message("progress.text.checking.out", target.getAbsolutePath()));
try {
getFactory(vcs, format).createCheckoutClient()
.checkout(SvnTarget.fromURL(SVNURL.parseURIEncoded(url)), target, revision, depth, ignoreExternals, true, format, handler);
ProgressManager.checkCanceled();
checkoutSuccessful.set(Boolean.TRUE);
}
catch (SVNCancelException ignore) {
}
catch (SVNException e) {
exception[0] = e;
}
catch (VcsException e) {
exception[0] = e;
}
finally {
SvnWorkingCopyFormatHolder.setPresetFormat(null);
}
}
public void onCancel() {
onSuccess();
}
public void onSuccess() {
if (exception[0] != null) {
Messages.showErrorDialog(SvnBundle.message("message.text.cannot.checkout", exception[0].getMessage()), SvnBundle.message("message.title.check.out"));
}
final VirtualFile vf = RefreshVFsSynchronously.findCreatedFile(target);
if (vf != null) {
vf.refresh(true, true, new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
notifyListener();
}
});
}
});
}
else {
notifyListener();
}
}
private void notifyListener() {
notifyRootManagerIfUnderProject(project, target);
if (listener != null) {
if (!checkoutSuccessful.isNull()) {
listener.directoryCheckedOut(target, SvnVcs.getKey());
}
listener.checkoutCompleted();
}
}
};
ProgressManager.getInstance().run(checkoutBackgroundTask);
}
private static void notifyRootManagerIfUnderProject(final Project project, final File directory) {
if (project.isDefault()) return;
final ProjectLevelVcsManagerEx plVcsManager = ProjectLevelVcsManagerEx.getInstanceEx(project);
final SvnVcs vcs = (SvnVcs) plVcsManager.findVcsByName(SvnVcs.VCS_NAME);
final VirtualFile[] files = vcs.getSvnFileUrlMapping().getNotFilteredRoots();
for (VirtualFile file : files) {
if (FileUtil.isAncestor(new File(file.getPath()), directory, false)) {
// todo: should be done like auto detection
plVcsManager.fireDirectoryMappingsChanged();
return;
}
}
}
@CalledInAwt
@NotNull
public static WorkingCopyFormat promptForWCopyFormat(@NotNull File target, @NotNull Project project) {
return new CheckoutFormatFromUserProvider(project, target).prompt();
}
public static void doExport(final Project project, final File target, final SVNURL url, final Depth depth,
final boolean ignoreExternals, final boolean force, final String eolStyle) {
try {
final VcsException[] exception = new VcsException[1];
final SvnVcs vcs = SvnVcs.getInstance(project);
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
ProgressTracker handler = new CheckoutEventHandler(vcs, true, progressIndicator);
try {
progressIndicator.setText(SvnBundle.message("progress.text.export", target.getAbsolutePath()));
SvnTarget from = SvnTarget.fromURL(url);
ExportClient client = vcs.getFactoryFromSettings().createExportClient();
client.export(from, target, SVNRevision.HEAD, depth, eolStyle, force, ignoreExternals, handler);
}
catch (VcsException e) {
exception[0] = e;
}
}
}, SvnBundle.message("message.title.export"), true, project);
if (exception[0] != null) {
throw exception[0];
}
}
catch (VcsException e1) {
Messages.showErrorDialog(SvnBundle.message("message.text.cannot.export", e1.getMessage()), SvnBundle.message("message.title.export"));
}
}
public static void doImport(final Project project, final File target, final SVNURL url, final Depth depth,
final boolean includeIgnored, final String message) {
final Ref<String> errorMessage = new Ref<String>();
final SvnVcs vcs = SvnVcs.getInstance(project);
final String targetPath = FileUtil.toSystemIndependentName(target.getAbsolutePath());
ExclusiveBackgroundVcsAction.run(project, new Runnable() {
public void run() {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
final FileIndexFacade facade = PeriodicalTasksCloser.getInstance().safeGetService(project, FileIndexFacade.class);
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
try {
progressIndicator.setText(SvnBundle.message("progress.text.import", target.getAbsolutePath()));
final VirtualFile targetVf = SvnUtil.getVirtualFile(targetPath);
if (targetVf == null) {
errorMessage.set("Can not find file: " + targetPath);
} else {
final boolean isInContent = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return facade.isInContent(targetVf);
}
});
CommitEventHandler handler = new IdeaCommitHandler(progressIndicator);
boolean useFileFilter = !project.isDefault() && isInContent;
ISVNCommitHandler commitHandler =
useFileFilter ? new MyFilter(LocalFileSystem.getInstance(), new SvnExcludingIgnoredOperation.Filter(project)) : null;
long revision = vcs.getFactoryFromSettings().createImportClient()
.doImport(target, url, depth, message, includeIgnored, handler, commitHandler);
if (revision > 0) {
StatusBar.Info.set(SvnBundle.message("status.text.comitted.revision", revision), project);
}
}
}
catch (VcsException e) {
errorMessage.set(e.getMessage());
}
}
}, SvnBundle.message("message.title.import"), true, project);
}
});
if (! errorMessage.isNull()) {
Messages.showErrorDialog(SvnBundle.message("message.text.cannot.import", errorMessage.get()), SvnBundle.message("message.title.import"));
}
}
private static class MyFilter extends DefaultSVNCommitHandler implements ISVNFileFilter {
private final LocalFileSystem myLfs;
private final SvnExcludingIgnoredOperation.Filter myFilter;
private MyFilter(LocalFileSystem lfs, SvnExcludingIgnoredOperation.Filter filter) {
myLfs = lfs;
myFilter = filter;
}
public boolean accept(final File file) throws SVNException {
final VirtualFile vf = myLfs.findFileByIoFile(file);
return vf != null && myFilter.accept(vf);
}
}
public String getVcsName() {
return "_Subversion";
}
public static class CheckoutFormatFromUserProvider {
private static final Logger LOG = Logger.getInstance(CheckoutFormatFromUserProvider.class);
@NotNull private final Project myProject;
@NotNull private final SvnVcs myVcs;
@NotNull private final File myPath;
@NotNull private final AtomicReference<String> error;
public CheckoutFormatFromUserProvider(@NotNull Project project, @NotNull File path) {
myProject = project;
myVcs = SvnVcs.getInstance(project);
myPath = path;
error = new AtomicReference<String>();
}
@CalledInAwt
public WorkingCopyFormat prompt() {
assert !ApplicationManager.getApplication().isUnitTestMode();
final WorkingCopyFormat result = displayUpgradeDialog();
ApplicationManager.getApplication().getMessageBus().syncPublisher(SvnVcs.WC_CONVERTED).run();
return result;
}
@NotNull
private WorkingCopyFormat displayUpgradeDialog() {
final UpgradeFormatDialog dialog = new UpgradeFormatDialog(myProject, myPath, false);
final ModalityState dialogState = ModalityState.any();
dialog.startLoading();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final List<WorkingCopyFormat> formats = loadSupportedFormats();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
final String errorMessage = error.get();
if (errorMessage != null) {
dialog.doCancelAction();
Messages.showErrorDialog(SvnBundle.message("message.text.cannot.load.supported.formats", errorMessage),
SvnBundle.message("message.title.check.out"));
}
else {
dialog.setSupported(formats);
dialog.setData(ContainerUtil.getFirstItem(formats, WorkingCopyFormat.UNKNOWN));
dialog.stopLoading();
}
}
}, dialogState);
}
});
dialog.show();
return dialog.isOK() ? dialog.getUpgradeMode() : WorkingCopyFormat.UNKNOWN;
}
@NotNull
private List<WorkingCopyFormat> loadSupportedFormats() {
List<WorkingCopyFormat> result = ContainerUtil.newArrayList();
try {
result.addAll(myVcs.getFactoryFromSettings().createCheckoutClient().getSupportedFormats());
result.addAll(getOtherFactoryFormats(myVcs.getOtherFactory()));
}
catch (VcsException e) {
error.set(e.getMessage());
}
return result;
}
@NotNull
private static List<WorkingCopyFormat> getOtherFactoryFormats(@NotNull ClientFactory otherFactory) {
List<WorkingCopyFormat> result;
try {
result = otherFactory.createCheckoutClient().getSupportedFormats();
}
catch (VcsException e) {
// do not add error as it is just usability fix and "other factory" could be incorrectly configured (for instance, invalid
// executable path)
result = ContainerUtil.newArrayList();
LOG.info("Failed to get checkout formats from other factory", e);
}
return result;
}
}
}