2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.jetbrains.idea.svn.actions;
18 import com.intellij.diff.DiffManager;
19 import com.intellij.openapi.actionSystem.*;
20 import com.intellij.openapi.progress.PerformInBackgroundOption;
21 import com.intellij.openapi.progress.ProgressIndicator;
22 import com.intellij.openapi.progress.ProgressManager;
23 import com.intellij.openapi.progress.Task;
24 import com.intellij.openapi.project.DumbAware;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.ui.Messages;
27 import com.intellij.openapi.util.Condition;
28 import com.intellij.openapi.util.text.StringUtil;
29 import com.intellij.openapi.vcs.FilePath;
30 import com.intellij.openapi.vcs.VcsDataKeys;
31 import com.intellij.openapi.vcs.VcsException;
32 import com.intellij.openapi.vcs.changes.Change;
33 import com.intellij.openapi.vcs.changes.ChangesUtil;
34 import com.intellij.openapi.vcs.changes.ContentRevision;
35 import com.intellij.openapi.vcs.changes.MarkerVcsContentRevision;
36 import com.intellij.util.containers.ContainerUtil;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39 import org.jetbrains.idea.svn.SvnBundle;
40 import org.jetbrains.idea.svn.SvnVcs;
41 import org.jetbrains.idea.svn.api.Depth;
42 import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest;
43 import org.jetbrains.idea.svn.difftool.properties.SvnPropertiesDiffRequest.PropertyContent;
44 import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision;
45 import org.jetbrains.idea.svn.properties.PropertyConsumer;
46 import org.jetbrains.idea.svn.properties.PropertyData;
47 import org.jetbrains.idea.svn.properties.PropertyValue;
48 import org.tmatesoft.svn.core.SVNErrorCode;
49 import org.tmatesoft.svn.core.SVNErrorMessage;
50 import org.tmatesoft.svn.core.SVNException;
51 import org.tmatesoft.svn.core.SVNURL;
52 import org.tmatesoft.svn.core.wc.SVNRevision;
53 import org.tmatesoft.svn.core.wc2.SvnTarget;
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.Comparator;
59 import java.util.List;
61 public abstract class AbstractShowPropertiesDiffAction extends AnAction implements DumbAware {
62 protected AbstractShowPropertiesDiffAction(String name) {
66 protected abstract DataKey<Change[]> getChangesKey();
68 protected abstract SVNRevision getBeforeRevisionValue(final Change change, final SvnVcs vcs) throws SVNException;
70 protected abstract SVNRevision getAfterRevisionValue(final Change change, final SvnVcs vcs) throws SVNException;
73 public void update(final AnActionEvent e) {
74 final DataContext dataContext = e.getDataContext();
75 final Presentation presentation = e.getPresentation();
76 final Change[] data = VcsDataKeys.CHANGES.getData(dataContext);
77 boolean showAction = checkThatChangesAreUnderSvn(data);
78 presentation.setVisible(data != null && showAction);
79 presentation.setEnabled(showAction);
82 private static boolean checkThatChangesAreUnderSvn(@Nullable Change[] changes) {
83 boolean result = false;
85 if (changes != null) {
86 result = ContainerUtil.or(changes, new Condition<Change>() {
88 public boolean value(Change change) {
89 return isUnderSvn(change.getBeforeRevision()) || isUnderSvn(change.getAfterRevision());
97 private static boolean isUnderSvn(@Nullable ContentRevision revision) {
98 return revision instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision)revision).getVcsKey());
101 public void actionPerformed(final AnActionEvent e) {
102 final DataContext dataContext = e.getDataContext();
103 final Project project = CommonDataKeys.PROJECT.getData(dataContext);
104 final Change[] changes = e.getData(getChangesKey());
106 if (! checkThatChangesAreUnderSvn(changes)) {
110 final Change change = changes[0];
112 final CalculateAndShow worker = new CalculateAndShow(project, change, e.getPresentation().getText());
113 ProgressManager.getInstance().run(worker);
116 private class CalculateAndShow extends Task.Backgroundable {
117 private final Change myChange;
118 private List<PropertyData> myBeforeContent;
119 private List<PropertyData> myAfterContent;
120 private SVNRevision myBeforeRevisionValue;
121 private SVNRevision myAfterRevision;
122 private Exception myException;
123 private final String myErrorTitle;
125 private CalculateAndShow(@Nullable final Project project, final Change change, final String errorTitle) {
126 super(project, SvnBundle.message("fetching.properties.contents.progress.title"), true, PerformInBackgroundOption.DEAF);
128 myErrorTitle = errorTitle;
131 public void run(@NotNull final ProgressIndicator indicator) {
132 final SvnVcs vcs = SvnVcs.getInstance(myProject);
135 myBeforeRevisionValue = getBeforeRevisionValue(myChange, vcs);
136 myAfterRevision = getAfterRevisionValue(myChange, vcs);
138 myBeforeContent = getPropertyList(vcs, myChange.getBeforeRevision(), myBeforeRevisionValue);
139 indicator.checkCanceled();
140 // gets exactly WORKING revision property
141 myAfterContent = getPropertyList(vcs, myChange.getAfterRevision(), myAfterRevision);
143 catch (SVNException exc) {
146 catch (VcsException exc) {
152 public void onSuccess() {
153 if (myException != null) {
154 Messages.showErrorDialog(myException.getMessage(), myErrorTitle);
157 if (myBeforeContent != null && myAfterContent != null && myBeforeRevisionValue != null && myAfterRevision != null) {
158 SvnPropertiesDiffRequest diffRequest;
159 if (compareRevisions(myBeforeRevisionValue, myAfterRevision) > 0) {
160 diffRequest = new SvnPropertiesDiffRequest(getDiffWindowTitle(myChange),
161 new PropertyContent(myAfterContent), new PropertyContent(myBeforeContent),
162 revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue));
165 diffRequest = new SvnPropertiesDiffRequest(getDiffWindowTitle(myChange),
166 new PropertyContent(myBeforeContent), new PropertyContent(myAfterContent),
167 revisionToString(myBeforeRevisionValue), revisionToString(myAfterRevision));
169 DiffManager.getInstance().showDiff(myProject, diffRequest);
175 private static String getDiffWindowTitle(@NotNull Change change) {
176 if (change.isMoved() || change.isRenamed()) {
177 final FilePath beforeFilePath = ChangesUtil.getBeforePath(change);
178 final FilePath afterFilePath = ChangesUtil.getAfterPath(change);
180 final String beforePath = beforeFilePath == null ? "" : beforeFilePath.getPath();
181 final String afterPath = afterFilePath == null ? "" : afterFilePath.getPath();
182 return SvnBundle.message("action.Subversion.properties.difference.diff.for.move.title", beforePath, afterPath);
184 return SvnBundle.message("action.Subversion.properties.difference.diff.title", ChangesUtil.getFilePath(change).getPath());
188 private static int compareRevisions(@NotNull SVNRevision revision1, @NotNull SVNRevision revision2) {
189 if (revision1.equals(revision2)) {
192 // working(local) ahead of head
193 if (SVNRevision.WORKING.equals(revision1)) {
196 if (SVNRevision.WORKING.equals(revision2)) {
199 if (SVNRevision.HEAD.equals(revision1)) {
202 if (SVNRevision.HEAD.equals(revision2)) {
205 return revision1.getNumber() > revision2.getNumber() ? 1 : -1;
209 private static String revisionToString(@Nullable SVNRevision revision) {
210 return revision == null ? "not exists" : revision.toString();
213 private final static String ourPropertiesDelimiter = "\n";
216 private static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs,
217 @Nullable final ContentRevision contentRevision,
218 @Nullable final SVNRevision revision)
219 throws SVNException, VcsException {
220 if (contentRevision == null) {
221 return Collections.emptyList();
225 if (contentRevision instanceof SvnRepositoryContentRevision) {
226 final SvnRepositoryContentRevision svnRevision = (SvnRepositoryContentRevision)contentRevision;
227 target = SvnTarget.fromURL(SVNURL.parseURIEncoded(svnRevision.getFullPath()), revision);
229 final File ioFile = contentRevision.getFile().getIOFile();
230 target = SvnTarget.fromFile(ioFile, revision);
233 return getPropertyList(vcs, target, revision);
237 public static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs, @NotNull final SVNURL url, @Nullable final SVNRevision revision)
238 throws VcsException {
239 return getPropertyList(vcs, SvnTarget.fromURL(url, revision), revision);
243 public static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs, @NotNull final File ioFile, @Nullable final SVNRevision revision)
244 throws SVNException {
246 return getPropertyList(vcs, SvnTarget.fromFile(ioFile, revision), revision);
248 catch (VcsException e) {
249 throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, e), e);
254 private static List<PropertyData> getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision)
255 throws VcsException {
256 final List<PropertyData> lines = new ArrayList<PropertyData>();
257 final PropertyConsumer propertyHandler = createHandler(revision, lines);
259 vcs.getFactory(target).createPropertyClient().list(target, revision, Depth.EMPTY, propertyHandler);
265 private static PropertyConsumer createHandler(SVNRevision revision, @NotNull final List<PropertyData> lines) {
266 final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
267 if (indicator != null) {
268 indicator.checkCanceled();
269 indicator.setText(SvnBundle.message("show.properties.diff.progress.text.revision.information", revision.toString()));
272 return new PropertyConsumer() {
273 public void handleProperty(final File path, final PropertyData property) throws SVNException {
274 registerProperty(property);
277 public void handleProperty(final SVNURL url, final PropertyData property) throws SVNException {
278 registerProperty(property);
281 public void handleProperty(final long revision, final PropertyData property) throws SVNException {
282 // revision properties here
285 private void registerProperty(@NotNull PropertyData property) {
286 if (indicator != null) {
287 indicator.checkCanceled();
288 indicator.setText2(SvnBundle.message("show.properties.diff.progress.text2.property.information", property.getName()));
296 public static String toSortedStringPresentation(@NotNull List<PropertyData> lines) {
297 StringBuilder sb = new StringBuilder();
299 Collections.sort(lines, new Comparator<PropertyData>() {
300 public int compare(final PropertyData o1, final PropertyData o2) {
301 return o1.getName().compareTo(o2.getName());
305 for (PropertyData line : lines) {
306 addPropertyPresentation(line, sb);
309 return sb.toString();
312 private static void addPropertyPresentation(@NotNull PropertyData property, @NotNull StringBuilder sb) {
313 if (sb.length() != 0) {
314 sb.append(ourPropertiesDelimiter);
316 sb.append(property.getName()).append("=").append(StringUtil.notNullize(PropertyValue.toString(property.getValue())));