cfbb0c06b09da212d788eef486b7eb240fcd74ae
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / shelf / DiffShelvedChangesAction.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.changes.shelf;
17
18 import com.intellij.openapi.actionSystem.*;
19 import com.intellij.openapi.diff.DiffRequestFactory;
20 import com.intellij.openapi.diff.MergeRequest;
21 import com.intellij.openapi.diff.impl.patch.*;
22 import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch;
23 import com.intellij.openapi.project.DumbAware;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.ui.MessageType;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.openapi.vcs.FilePath;
28 import com.intellij.openapi.vcs.VcsException;
29 import com.intellij.openapi.vcs.changes.Change;
30 import com.intellij.openapi.vcs.changes.ContentRevision;
31 import com.intellij.openapi.vcs.changes.actions.ChangeDiffRequestPresentable;
32 import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentable;
33 import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentableProxy;
34 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
35 import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts;
36 import com.intellij.openapi.vcs.changes.patch.MergedDiffRequestPresentable;
37 import com.intellij.openapi.vcs.changes.patch.PatchMergeRequestFactory;
38 import com.intellij.openapi.vcs.changes.ui.ChangesComparator;
39 import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import java.io.IOException;
45 import java.util.*;
46
47 /**
48  * @author yole
49  */
50 public class DiffShelvedChangesAction extends AnAction implements DumbAware {
51   public void actionPerformed(final AnActionEvent e) {
52     showShelvedChangesDiff(e.getDataContext());
53   }
54
55   public static void showShelvedChangesDiff(final DataContext dc) {
56     final Project project = PlatformDataKeys.PROJECT.getData(dc);
57     ShelvedChangeList[] changeLists = ShelvedChangesViewManager.SHELVED_CHANGELIST_KEY.getData(dc);
58     if (changeLists == null) {
59       changeLists = ShelvedChangesViewManager.SHELVED_RECYCLED_CHANGELIST_KEY.getData(dc);
60     }
61
62     // selected changes inside lists
63     List<ShelvedChange> shelvedChanges = ShelvedChangesViewManager.SHELVED_CHANGE_KEY.getData(dc);
64
65     if (changeLists == null) return;
66
67     final List<ShelvedChange> changesFromFirstList = changeLists[0].getChanges();
68
69     Collections.sort(changesFromFirstList, new MyComparator(project));
70
71     int toSelectIdx = 0;
72     final ArrayList<DiffRequestPresentable> diffRequestPresentables = new ArrayList<DiffRequestPresentable>();
73     final ApplyPatchContext context = new ApplyPatchContext(project.getBaseDir(), 0, false, false);
74     final PatchesPreloader preloader = new PatchesPreloader();
75
76     final List<String> missing = new LinkedList<String>();
77     for (final ShelvedChange shelvedChange : changesFromFirstList) {
78       final Change change = shelvedChange.getChange(project);
79       final String beforePath = shelvedChange.getBeforePath();
80       try {
81         final VirtualFile f = ApplyTextFilePatch.findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), beforePath == null);
82         if ((f == null) || (! f.exists())) {
83           if (beforePath != null) {
84             missing.add(beforePath);
85           }
86           continue;
87         }
88
89         diffRequestPresentables.add(new DiffRequestPresentableProxy() {
90           @Override
91           protected DiffRequestPresentable init() {
92             if (isConflictingChange(change)) {
93               TextFilePatch patch = preloader.getPatch(shelvedChange);
94               if (patch == null) return null;
95
96               final FilePath pathBeforeRename = context.getPathBeforeRename(f);
97
98               final ApplyPatchForBaseRevisionTexts threeTexts = ApplyPatchForBaseRevisionTexts.create(project, f, pathBeforeRename, patch);
99               if ((threeTexts == null) || (threeTexts.getStatus() == null) || (ApplyPatchStatus.FAILURE.equals(threeTexts.getStatus()))) {
100                 return null;
101               }
102
103               return new MergedDiffRequestPresentable(project, threeTexts, f, "Shelved Version");
104             }
105             else {
106               return new ChangeDiffRequestPresentable(project, change);
107             }
108           }
109         });
110       }
111       catch (IOException e) {
112         continue;
113       }
114       if ((shelvedChanges != null) && shelvedChanges.contains(shelvedChange)) {
115         // just added
116         toSelectIdx = diffRequestPresentables.size() - 1;
117       }
118     }
119     if (! missing.isEmpty()) {
120       // 7-8
121       ChangesViewBalloonProblemNotifier.showMe(project, "Show Diff: Cannot find base for: " + StringUtil.join(missing, ",\n"), MessageType.WARNING);
122     }
123
124     ShowDiffAction.showDiffImpl(project, diffRequestPresentables, toSelectIdx, ShowDiffAction.DiffExtendUIFactory.NONE, false);
125   }
126
127   private static class PatchesPreloader {
128     private final Map<String, List<TextFilePatch>> myFilePatchesMap;
129
130     private PatchesPreloader() {
131       myFilePatchesMap = new HashMap<String, List<TextFilePatch>>();
132     }
133
134     @Nullable
135     public TextFilePatch getPatch(final ShelvedChange shelvedChange) {
136       List<TextFilePatch> textFilePatches = myFilePatchesMap.get(shelvedChange.getPatchPath());
137       if (textFilePatches == null) {
138         try {
139           textFilePatches = ShelveChangesManager.loadPatches(shelvedChange.getPatchPath());
140         }
141         catch (IOException e) {
142           return null;
143         }
144         catch (PatchSyntaxException e) {
145           return null;
146         }
147         myFilePatchesMap.put(shelvedChange.getPatchPath(), textFilePatches);
148       }
149       for (TextFilePatch textFilePatch : textFilePatches) {
150         if (shelvedChange.getBeforePath().equals(textFilePatch.getBeforeName())) {
151           return textFilePatch;
152         }
153       }
154       return null;
155     }
156   }
157
158   private final static class MyComparator implements Comparator<ShelvedChange> {
159     private final Project myProject;
160
161     public MyComparator(Project project) {
162       myProject = project;
163     }
164
165     public int compare(final ShelvedChange o1, final ShelvedChange o2) {
166       return ChangesComparator.getInstance().compare(o1.getChange(myProject), o2.getChange(myProject));
167     }
168   }
169
170   private static boolean isConflictingChange(final Change change) {
171     ContentRevision afterRevision = change.getAfterRevision();
172     if (afterRevision == null) return false;
173     try {
174       afterRevision.getContent();
175     }
176     catch(VcsException e) {
177       if (e.getCause() instanceof ApplyPatchException) {
178         return true;
179       }
180     }
181     return false;
182   }
183
184   public void update(final AnActionEvent e) {
185     ActionManager.getInstance().getAction("ChangesView.Diff").update(e);
186   }
187
188   private static class ShelvedChangeDiffRequestFactory implements PatchMergeRequestFactory {
189     public static final ShelvedChangeDiffRequestFactory INSTANCE = new ShelvedChangeDiffRequestFactory();
190
191     public MergeRequest createMergeRequest(final String leftText, final String rightText, final String originalContent, @NotNull final VirtualFile file,
192                                            final Project project) {
193       MergeRequest request = DiffRequestFactory.getInstance().create3WayDiffRequest(leftText, rightText, originalContent,
194                                                                                     project,
195                                                                                     null);
196       request.setVersionTitles(new String[] {
197         "Current Version",
198         "Base Version",
199         "Shelved Version"
200       });
201       request.setWindowTitle("Shelved Change Conflict for" + file.getPresentableUrl());
202       return request;
203     }
204   }
205 }