blob: 223c69ab1c6f14793a103fbc398b94494ab23bfa [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.actions;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diff.DiffManager;
import com.intellij.openapi.diff.SimpleContent;
import com.intellij.openapi.diff.SimpleDiffRequest;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.MarkerVcsContentRevision;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnRevisionNumber;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision;
import org.jetbrains.idea.svn.properties.PropertyClient;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.ISVNPropertyHandler;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import javax.swing.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public abstract class AbstractShowPropertiesDiffAction extends AnAction implements DumbAware {
protected AbstractShowPropertiesDiffAction(String name) {
super(name);
}
protected abstract DataKey<Change[]> getChangesKey();
@Nullable
protected abstract SVNRevision getBeforeRevisionValue(final Change change, final SvnVcs vcs) throws SVNException;
@Nullable
protected abstract SVNRevision getAfterRevisionValue(final Change change, final SvnVcs vcs) throws SVNException;
@Override
public void update(final AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final Presentation presentation = e.getPresentation();
final Change[] data = VcsDataKeys.CHANGES.getData(dataContext);
boolean showAction = checkThatChangesAreUnderSvn(data);
presentation.setVisible(data != null && showAction);
presentation.setEnabled(showAction);
}
private boolean checkThatChangesAreUnderSvn(Change[] data) {
boolean showAction = false;
if (data != null) {
for (Change change : data) {
final ContentRevision before = change.getBeforeRevision();
if (before != null) {
showAction = showAction || before instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision)before).getVcsKey());
}
final ContentRevision after = change.getAfterRevision();
if (after != null) {
showAction = showAction || after instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision)after).getVcsKey());
}
if (showAction) break;
}
}
return showAction;
}
private boolean enabled(final Project project, final Change[] changes) {
final boolean noChange = (project == null) || (changes == null) || (changes.length != 1);
if (noChange) {
return false;
} else {
final Change change = changes[0];
final ContentRevision revision = (change.getBeforeRevision() != null) ? change.getBeforeRevision() : change.getAfterRevision();
if ((revision == null) || (! (revision.getRevisionNumber() instanceof SvnRevisionNumber))) {
return false;
}
return checkVcs(project, change);
}
}
protected boolean checkVcs(final Project project, final Change change) {
final VirtualFile virtualFile = ChangesUtil.getFilePath(change).getVirtualFile();
if (virtualFile == null) {
return false;
}
final AbstractVcs vcs = ChangesUtil.getVcsForFile(virtualFile, project);
return (vcs != null) && SvnVcs.VCS_NAME.equals(vcs.getName());
}
public void actionPerformed(final AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final Change[] changes = e.getData(getChangesKey());
if (! checkThatChangesAreUnderSvn(changes)) {
return;
}
final Change change = changes[0];
final CalculateAndShow worker = new CalculateAndShow(project, change, e.getPresentation().getText());
ProgressManager.getInstance().run(worker);
}
private class CalculateAndShow extends Task.Backgroundable {
private final Change myChange;
private String myBeforeContent;
private String myAfterContent;
private SVNRevision myBeforeRevisionValue;
private SVNRevision myAfterRevision;
private Exception myException;
private final String myErrorTitle;
private CalculateAndShow(@Nullable final Project project, final Change change, final String errorTitle) {
super(project, SvnBundle.message("fetching.properties.contents.progress.title"), true, Backgroundable.DEAF);
myChange = change;
myErrorTitle = errorTitle;
}
public void run(@NotNull final ProgressIndicator indicator) {
final SvnVcs vcs = SvnVcs.getInstance(myProject);
try {
myBeforeRevisionValue = getBeforeRevisionValue(myChange, vcs);
myAfterRevision = getAfterRevisionValue(myChange, vcs);
myBeforeContent = getPropertyList(vcs, myChange.getBeforeRevision(), myBeforeRevisionValue);
indicator.checkCanceled();
// gets exactly WORKING revision property
myAfterContent = getPropertyList(vcs, myChange.getAfterRevision(), myAfterRevision);
}
catch(SVNException exc) {
myException = exc;
}
catch (VcsException exc) {
myException = exc;
}
// since sometimes called from modal dialog (commit changes dialog)
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (myException != null) {
Messages.showErrorDialog(myException.getMessage(), myErrorTitle);
return;
}
if (myBeforeContent != null && myAfterContent != null && myBeforeRevisionValue != null && myAfterRevision != null) {
final SimpleDiffRequest diffRequest = new SimpleDiffRequest(myProject, getDiffWindowTitle(myChange));
if (compareRevisions(myBeforeRevisionValue, myAfterRevision) >= 0) {
// before ahead
diffRequest.setContents(new SimpleContent(myAfterContent), new SimpleContent(myBeforeContent));
diffRequest.setContentTitles(revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue));
} else {
diffRequest.setContents(new SimpleContent(myBeforeContent), new SimpleContent(myAfterContent));
diffRequest.setContentTitles(revisionToString(myBeforeRevisionValue), revisionToString(myAfterRevision));
}
DiffManager.getInstance().getDiffTool().show(diffRequest);
}
}
});
}
}
private String getDiffWindowTitle(final Change change) {
if (change.isMoved() || change.isRenamed()) {
final FilePath beforeFilePath = ChangesUtil.getBeforePath(change);
final FilePath afterFilePath = ChangesUtil.getAfterPath(change);
final String beforePath = beforeFilePath == null ? "" : beforeFilePath.getIOFile().getAbsolutePath();
final String afterPath = afterFilePath == null ? "" : afterFilePath.getIOFile().getAbsolutePath();
return SvnBundle.message("action.Subversion.properties.difference.diff.for.move.title", beforePath, afterPath);
} else {
return SvnBundle.message("action.Subversion.properties.difference.diff.title", ChangesUtil.getFilePath(change).getIOFile().getAbsolutePath());
}
}
private int compareRevisions(@NonNls final SVNRevision revision1, @NonNls final SVNRevision revision2) {
if (revision1.equals(revision2)) {
return 0;
}
// working(local) ahead of head
if (SVNRevision.WORKING.equals(revision1)) {
return 1;
}
if (SVNRevision.WORKING.equals(revision2)) {
return -1;
}
if (SVNRevision.HEAD.equals(revision1)) {
return 1;
}
if (SVNRevision.HEAD.equals(revision2)) {
return -1;
}
return revision1.getNumber() > revision2.getNumber() ? 1 : -1;
}
private String revisionToString(final SVNRevision revision) {
if (revision == null) {
return "not exists";
}
return revision.toString();
}
private final static String ourPropertiesDelimiter = "\n";
private static String getPropertyList(@NotNull SvnVcs vcs,
@Nullable final ContentRevision contentRevision,
@Nullable final SVNRevision revision)
throws SVNException, VcsException {
if (contentRevision == null) {
return "";
}
SvnTarget target;
if (contentRevision instanceof SvnRepositoryContentRevision) {
final SvnRepositoryContentRevision svnRevision = (SvnRepositoryContentRevision)contentRevision;
target = SvnTarget.fromURL(SVNURL.parseURIEncoded(svnRevision.getFullPath()), revision);
} else {
final File ioFile = contentRevision.getFile().getIOFile();
target = SvnTarget.fromFile(ioFile, revision);
}
return getPropertyList(vcs, target, revision);
}
public static String getPropertyList(@NotNull SvnVcs vcs, @NotNull final SVNURL url, @Nullable final SVNRevision revision)
throws VcsException {
return getPropertyList(vcs, SvnTarget.fromURL(url, revision), revision);
}
public static String getPropertyList(@NotNull SvnVcs vcs, @NotNull final File ioFile, @Nullable final SVNRevision revision)
throws SVNException {
try {
return getPropertyList(vcs, SvnTarget.fromFile(ioFile, revision), revision);
}
catch (VcsException e) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, e), e);
}
}
private static String getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision)
throws VcsException {
final List<SVNPropertyData> lines = new ArrayList<SVNPropertyData>();
final ISVNPropertyHandler propertyHandler = createHandler(revision, lines);
vcs.getFactory(target).createPropertyClient().list(target, revision, SVNDepth.EMPTY, propertyHandler);
return toSortedStringPresentation(lines);
}
private static ISVNPropertyHandler createHandler(SVNRevision revision, final List<SVNPropertyData> lines) {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator != null) {
indicator.checkCanceled();
indicator.setText(SvnBundle.message("show.properties.diff.progress.text.revision.information", revision.toString()));
}
return new ISVNPropertyHandler() {
public void handleProperty(final File path, final SVNPropertyData property) throws SVNException {
registerProperty(property);
}
public void handleProperty(final SVNURL url, final SVNPropertyData property) throws SVNException {
registerProperty(property);
}
public void handleProperty(final long revision, final SVNPropertyData property) throws SVNException {
// revision properties here
}
private void registerProperty(@NotNull SVNPropertyData property) {
if (indicator != null) {
indicator.checkCanceled();
indicator.setText2(SvnBundle.message("show.properties.diff.progress.text2.property.information", property.getName()));
}
lines.add(property);
}
};
}
private static String toSortedStringPresentation(List<SVNPropertyData> lines) {
StringBuilder sb = new StringBuilder();
Collections.sort(lines, new Comparator<SVNPropertyData>() {
public int compare(final SVNPropertyData o1, final SVNPropertyData o2) {
return o1.getName().compareTo(o2.getName());
}
});
for (SVNPropertyData line : lines) {
addPropertyPresentation(line, sb);
}
return sb.toString();
}
private static void addPropertyPresentation(final SVNPropertyData property, final StringBuilder sb) {
if (sb.length() != 0) {
sb.append(ourPropertiesDelimiter);
}
sb.append(property.getName()).append("=").append((property.getValue() == null) ? "" : SVNPropertyValue.getPropertyAsString(property.getValue()));
}
}