3fc16f5bcf71f7a096e836602e2d3c440d30ab8f
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / update / ShowUpdatedDiffAction.java
1 /*
2  * Copyright 2000-2014 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.update;
17
18 import com.intellij.diff.DiffDialogHints;
19 import com.intellij.history.ByteContent;
20 import com.intellij.history.Label;
21 import com.intellij.openapi.actionSystem.*;
22 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
23 import com.intellij.openapi.project.DumbAware;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Condition;
26 import com.intellij.openapi.util.Pair;
27 import com.intellij.openapi.util.io.FileUtil;
28 import com.intellij.openapi.vcs.FilePath;
29 import com.intellij.openapi.vcs.FileStatus;
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.ContentRevision;
34 import com.intellij.openapi.vcs.changes.actions.diff.ShowDiffAction;
35 import com.intellij.openapi.vcs.changes.actions.diff.ShowDiffContext;
36 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
39 import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
40 import com.intellij.vcsUtil.VcsUtil;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import java.io.File;
45 import java.lang.ref.SoftReference;
46 import java.util.Iterator;
47
48 public class ShowUpdatedDiffAction extends AnAction implements DumbAware {
49   @Override
50   public void update(AnActionEvent e) {
51     final DataContext dc = e.getDataContext();
52
53     final Presentation presentation = e.getPresentation();
54
55     //presentation.setVisible(isVisible(dc));
56     presentation.setEnabled(isVisible(dc) && isEnabled(dc));
57   }
58
59   private boolean isVisible(final DataContext dc) {
60     final Project project = CommonDataKeys.PROJECT.getData(dc);
61     return (project != null) && (VcsDataKeys.LABEL_BEFORE.getData(dc) != null) && (VcsDataKeys.LABEL_AFTER.getData(dc) != null);
62   }
63
64   private boolean isEnabled(final DataContext dc) {
65     final Iterable<Pair<VirtualFilePointer,FileStatus>> iterable = VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getData(dc);
66     return iterable != null;
67   }
68
69   public void actionPerformed(AnActionEvent e) {
70     final DataContext dc = e.getDataContext();
71     if ((! isVisible(dc)) || (! isEnabled(dc))) return;
72
73     final Project project = CommonDataKeys.PROJECT.getData(dc);
74     final Iterable<Pair<VirtualFilePointer, FileStatus>> iterable = VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getData(dc);
75     final Label before = (Label) VcsDataKeys.LABEL_BEFORE.getData(dc);
76     final Label after = (Label) VcsDataKeys.LABEL_AFTER.getData(dc);
77
78     final String selectedUrl = VcsDataKeys.UPDATE_VIEW_SELECTED_PATH.getData(dc);
79
80     Iterable<Change> changes = new MyIterableWrapper(iterable.iterator(), before, after, project);
81     Condition<Change> selection = new MySelectionMarker(selectedUrl);
82     ShowDiffAction.showDiffForChange(project, changes, selection, new ShowDiffContext(DiffDialogHints.FRAME));
83   }
84
85   private static class MySelectionMarker implements Condition<Change> {
86     private final String mySelectedPath;
87     private boolean myFirstSelected;
88
89     public MySelectionMarker(String selectedPath) {
90       mySelectedPath = selectedPath;
91     }
92
93     public boolean value(Change change) {
94       if (mySelectedPath == null) {
95         if (myFirstSelected) {
96           myFirstSelected = true;
97           return true;
98         }
99         return false;
100       }
101       final MyCheckpointContentRevision revision = (MyCheckpointContentRevision)(change.getBeforeRevision() == null ? change.getAfterRevision() : change.getBeforeRevision());
102       final String url = revision.getUrl();
103       return mySelectedPath.equals(url);
104     }
105   }
106
107   private static class MyIterableWrapper implements Iterable<Change> {
108     private final Iterator<Pair<VirtualFilePointer, FileStatus>> myVfIterator;
109     private final Label myBefore;
110     private final Label myAfter;
111     @NotNull private final Project myProject;
112
113     private MyIterableWrapper(Iterator<Pair<VirtualFilePointer, FileStatus>> vfIterator,
114                               final Label before,
115                               final Label after,
116                               @NotNull Project project) {
117       myVfIterator = vfIterator;
118       myBefore = before;
119       myAfter = after;
120       myProject = project;
121     }
122
123     public Iterator<Change> iterator() {
124       return new MyIteratorWrapper(myVfIterator, myBefore, myAfter, myProject);
125     }
126   }
127
128   private static class MyLoader {
129     private final Label myLabel;
130
131     private MyLoader(Label label) {
132       myLabel = label;
133     }
134
135     @Nullable
136     public String convert(final VirtualFilePointer pointer, @NotNull Project project) {
137       if (pointer == null) return null;
138       final String path = pointer.getPresentableUrl();
139       final ByteContent byteContent = myLabel.getByteContent(FileUtil.toSystemIndependentName(path));
140       if (byteContent == null || byteContent.isDirectory() || byteContent.getBytes() == null) {
141         return null;
142       }
143       final VirtualFile vf = pointer.getFile();
144       if (vf == null) {
145         return LoadTextUtil.getTextByBinaryPresentation(byteContent.getBytes(), EncodingProjectManager.getInstance(project).getDefaultCharset()).toString();
146       }
147       else {
148         return LoadTextUtil.getTextByBinaryPresentation(byteContent.getBytes(), vf).toString();
149       }
150     }
151   }
152
153   private static class MyCheckpointContentRevision implements ContentRevision {
154     private SoftReference<String> myContent;
155     private final MyLoader myLoader;
156     private final VirtualFilePointer myPointer;
157     private final boolean myBefore;
158     @NotNull private final Project myProject;
159
160     private MyCheckpointContentRevision(final VirtualFilePointer pointer, final MyLoader loader, final boolean before, @NotNull Project project) {
161       myLoader = loader;
162       myPointer = pointer;
163       myBefore = before;
164       myProject = project;
165     }
166
167     public String getContent() throws VcsException {
168       final String s = com.intellij.reference.SoftReference.dereference(myContent);
169       if (s != null) {
170         return s;
171       }
172
173       final String loaded = myLoader.convert(myPointer, myProject);
174       myContent = new SoftReference<String>(loaded);
175
176       return loaded;
177     }
178
179     public String getUrl() {
180       return myPointer.getUrl();
181     }
182
183     @NotNull
184     public FilePath getFile() {
185       return VcsUtil.getFilePath(myPointer.getPresentableUrl(), false);
186     }
187
188     @NotNull
189     public VcsRevisionNumber getRevisionNumber() {
190       return new VcsRevisionNumber() {
191         public String asString() {
192           return myBefore ? "Before update" : "After update";
193         }
194
195         public int compareTo(VcsRevisionNumber o) {
196           return myBefore ? -1 : 1;
197         }
198       };
199     }
200   }
201
202   private static class MyIteratorWrapper implements Iterator<Change> {
203     private final MyLoader myBeforeLoader;
204     private final MyLoader myAfterLoader;
205     private final Iterator<Pair<VirtualFilePointer, FileStatus>> myVfIterator;
206     @NotNull private final Project myProject;
207
208     public MyIteratorWrapper(final Iterator<Pair<VirtualFilePointer, FileStatus>> vfIterator,
209                              final Label before,
210                              final Label after,
211                              @NotNull Project project) {
212       myVfIterator = vfIterator;
213       myProject = project;
214       myBeforeLoader = new MyLoader(before);
215       myAfterLoader = new MyLoader(after);
216     }
217
218     public boolean hasNext() {
219       return myVfIterator.hasNext();
220     }
221
222     public Change next() {
223       final Pair<VirtualFilePointer, FileStatus> pair = myVfIterator.next();
224       final VirtualFilePointer pointer = pair.getFirst();
225
226       MyCheckpointContentRevision before = new MyCheckpointContentRevision(pointer, myBeforeLoader, true, myProject);
227       MyCheckpointContentRevision after = new MyCheckpointContentRevision(pointer, myAfterLoader, false, myProject);
228       if (FileStatus.ADDED.equals(pair.getSecond())) {
229         before = null;
230       } else if (FileStatus.DELETED.equals(pair.getSecond())) {
231         after = null;
232       }
233       return new Change(before, after, pair.getSecond());
234     }
235
236     public void remove() {
237       throw new UnsupportedOperationException();
238     }
239   }
240 }