/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsManager.java

https://bitbucket.org/nbargnesi/idea · Java · 365 lines · 297 code · 45 blank · 23 comment · 36 complexity · bf97c08290f9c2ea77c43b7fa249bcfc MD5 · raw file

  1. /*
  2. * Copyright 2000-2011 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;
  17. import com.intellij.openapi.Disposable;
  18. import com.intellij.openapi.components.ServiceManager;
  19. import com.intellij.openapi.diff.DiffBundle;
  20. import com.intellij.openapi.diff.DiffContent;
  21. import com.intellij.openapi.diff.DiffManager;
  22. import com.intellij.openapi.diff.DiffPanel;
  23. import com.intellij.openapi.diff.ex.DiffPanelEx;
  24. import com.intellij.openapi.diff.ex.DiffPanelOptions;
  25. import com.intellij.openapi.diff.impl.DiffPanelImpl;
  26. import com.intellij.openapi.extensions.Extensions;
  27. import com.intellij.openapi.progress.BackgroundTaskQueue;
  28. import com.intellij.openapi.project.Project;
  29. import com.intellij.openapi.util.Disposer;
  30. import com.intellij.openapi.util.Pair;
  31. import com.intellij.openapi.vcs.FilePath;
  32. import com.intellij.openapi.vcs.VcsException;
  33. import com.intellij.openapi.vcs.VcsKey;
  34. import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
  35. import com.intellij.openapi.vcs.history.ShortVcsRevisionNumber;
  36. import com.intellij.openapi.vcs.history.VcsRevisionNumber;
  37. import com.intellij.util.BeforeAfter;
  38. import com.intellij.util.containers.HashMap;
  39. import com.intellij.vcsUtil.UIVcsUtil;
  40. import org.jetbrains.annotations.Nullable;
  41. import javax.swing.*;
  42. import java.awt.*;
  43. import java.util.ArrayList;
  44. import java.util.Arrays;
  45. import java.util.List;
  46. import java.util.Map;
  47. /**
  48. * todo extract interface and create stub for dummy project
  49. * @author irengrig
  50. * Date: 6/15/11
  51. * Time: 5:36 PM
  52. */
  53. public class VcsChangeDetailsManager {
  54. private final List<VcsChangeDetailsProvider> myProviders = new ArrayList<VcsChangeDetailsProvider>();
  55. private final List<VcsChangeDetailsProvider> myDedicatedList;
  56. private final Project myProject;
  57. private final BackgroundTaskQueue myQueue;
  58. public VcsChangeDetailsManager(final Project project) {
  59. myProject = project;
  60. myQueue = new BackgroundTaskQueue(myProject, "Loading change details");
  61. myDedicatedList = new ArrayList<VcsChangeDetailsProvider>();
  62. myDedicatedList.add(new BinaryDetailsProviderNew(project));
  63. myDedicatedList.add(new FragmentedDiffDetailsProvider(myProject));
  64. VcsChangeDetailsProvider[] extensions = Extensions.getExtensions(VcsChangeDetailsProvider.EP_NAME, myProject);
  65. myProviders.addAll(Arrays.asList(extensions));
  66. Disposer.register(project, new Disposable() {
  67. @Override
  68. public void dispose() {
  69. myQueue.clear();
  70. }
  71. });
  72. }
  73. public boolean canComment(final Change change) {
  74. for (VcsChangeDetailsProvider provider : myDedicatedList) {
  75. if (provider.canComment(change)) return true;
  76. }
  77. for (VcsChangeDetailsProvider provider : myProviders) {
  78. if (provider.canComment(change)) return true;
  79. }
  80. return false;
  81. }
  82. @Nullable
  83. public RefreshablePanel getPanel(final Change change, JComponent parent) {
  84. final List<Pair<String, RefreshablePanel>> panels = new ArrayList<Pair<String, RefreshablePanel>>();
  85. for (VcsChangeDetailsProvider convertor : myDedicatedList) {
  86. if (! convertor.canComment(change)) continue;
  87. RefreshablePanel panel = convertor.comment(change, parent, myQueue);
  88. if (panel != null) {
  89. panels.add(new Pair<String, RefreshablePanel>("Diff", panel));
  90. break; // only one of dedicated for now
  91. }
  92. }
  93. for (VcsChangeDetailsProvider provider : myProviders) {
  94. if (provider.canComment(change)) {
  95. RefreshablePanel panel = provider.comment(change, parent, myQueue);
  96. if (panel != null) {
  97. panels.add(new Pair<String, RefreshablePanel>(provider.getName(), panel));
  98. }
  99. }
  100. }
  101. if (! panels.isEmpty()) {
  102. if (panels.size() == 1) {
  103. return panels.get(0).getSecond();
  104. } else {
  105. TabbedRefreshablePanel tabbedRefreshablePanel = new TabbedRefreshablePanel();
  106. for (Pair<String, RefreshablePanel> panel : panels) {
  107. tabbedRefreshablePanel.addTab(panel.getFirst(), panel.getSecond());
  108. }
  109. return tabbedRefreshablePanel;
  110. }
  111. }
  112. return null;
  113. }
  114. public static VcsChangeDetailsManager getInstance(Project project) {
  115. return ServiceManager.getService(project, VcsChangeDetailsManager.class);
  116. }
  117. private static class BinaryDetailsProviderNew implements VcsChangeDetailsProvider {
  118. private final Project myProject;
  119. private BinaryDetailsProviderNew(Project project) {
  120. myProject = project;
  121. }
  122. @Override
  123. public String getName() {
  124. return "Contents Diff";
  125. }
  126. @Override
  127. public boolean canComment(Change change) {
  128. return canBeShownInBinaryDiff(change, myProject);
  129. }
  130. @Override
  131. public RefreshablePanel comment(Change change, JComponent parent, BackgroundTaskQueue queue) {
  132. return new BinaryDiffDetailsPanel(myProject, queue, change);
  133. }
  134. }
  135. private static boolean canBeShownInBinaryDiff(Change change, final Project project) {
  136. FilePath path = ChangesUtil.getFilePath(change);
  137. if (path != null && path.isDirectory()) return false;
  138. if (change.isTreeConflict() || change.isPhantom()) return false;
  139. return ShowDiffAction.isBinaryChangeAndCanShow(project, change);
  140. }
  141. private static class BinaryDiffDetailsPanel extends AbstractRefreshablePanel<ValueWithVcsException<List<BeforeAfter<DiffContent>>>> {
  142. private final BinaryDiffRequestFromChange myRequestFromChange;
  143. private final Project myProject;
  144. private final ChangeListManager myChangeListManager;
  145. private final FilePath myFilePath;
  146. private Change myChange;
  147. private final DiffPanel myPanel;
  148. private BinaryDiffDetailsPanel(Project project, BackgroundTaskQueue queue, final Change change) {
  149. super(project, "Loading change content", queue);
  150. myProject = project;
  151. myFilePath = ChangesUtil.getFilePath(change);
  152. myRequestFromChange = new BinaryDiffRequestFromChange(myProject);
  153. myChangeListManager = ChangeListManager.getInstance(myProject);
  154. myPanel = DiffManager.getInstance().createDiffPanel(null, myProject,this);
  155. myPanel.enableToolbar(false);
  156. myPanel.removeStatusBar();
  157. DiffPanelOptions o = ((DiffPanelEx)myPanel).getOptions();
  158. o.setRequestFocusOnNewContent(false);
  159. }
  160. @Override
  161. public boolean isStillValid(Change data) {
  162. return canBeShownInBinaryDiff(data, myProject);
  163. }
  164. @Override
  165. protected void refreshPresentation() {
  166. }
  167. @Override
  168. protected ValueWithVcsException<List<BeforeAfter<DiffContent>>> loadImpl() throws VcsException {
  169. myChange = myChangeListManager.getChange(myFilePath);
  170. if (myChange == null) {
  171. return null;
  172. }
  173. return new ValueWithVcsException<List<BeforeAfter<DiffContent>>>() {
  174. @Override
  175. protected List<BeforeAfter<DiffContent>> computeImpl() throws VcsException {
  176. return myRequestFromChange.createRequestForChange(myChange, 0);
  177. }
  178. };
  179. }
  180. @Override
  181. protected JPanel dataToPresentation(ValueWithVcsException<List<BeforeAfter<DiffContent>>> value) {
  182. if (value == null) return noDifferences();
  183. final List<BeforeAfter<DiffContent>> contents;
  184. try {
  185. contents = value.get();
  186. if (contents == null) throw new VcsException("Can not load content");
  187. }
  188. catch (VcsException e) {
  189. return UIVcsUtil.errorPanel(e.getMessage(), true);
  190. }
  191. if (contents.isEmpty()) return noDifferences();
  192. assert contents.size() == 1;
  193. myPanel.setContents(contents.get(0).getBefore(), contents.get(0).getAfter());
  194. ((DiffPanelImpl)myPanel).getOptions().setRequestFocusOnNewContent(false);
  195. final JPanel wholeWrapper = new JPanel(new BorderLayout());
  196. final JPanel topPanel = new JPanel(new BorderLayout());
  197. final JPanel wrapper = new JPanel();
  198. final BoxLayout boxLayout = new BoxLayout(wrapper, BoxLayout.X_AXIS);
  199. wrapper.setLayout(boxLayout);
  200. final JLabel label = new JLabel(changeDescription(myChange));
  201. label.setBorder(BorderFactory.createEmptyBorder(1,2,0,0));
  202. wrapper.add(label);
  203. topPanel.add(wrapper, BorderLayout.CENTER);
  204. wholeWrapper.add(topPanel, BorderLayout.NORTH);
  205. //wholeWrapper.add(new JBScrollPane(panel.getComponent()), BorderLayout.CENTER);
  206. wholeWrapper.add(myPanel.getComponent(), BorderLayout.CENTER);
  207. return wholeWrapper;
  208. }
  209. @Override
  210. protected void disposeImpl() {
  211. }
  212. @Override
  213. public void away() {
  214. //
  215. }
  216. }
  217. private static JPanel noDifferences() {
  218. return UIVcsUtil.errorPanel(DiffBundle.message("diff.contents.have.differences.only.in.line.separators.message.text"), false);
  219. }
  220. private static class FragmentedDiffDetailsProvider implements VcsChangeDetailsProvider {
  221. private final Project myProject;
  222. private FragmentedDiffDetailsProvider(Project project) {
  223. myProject = project;
  224. }
  225. @Override
  226. public String getName() {
  227. return "Contents Diff";
  228. }
  229. @Override
  230. public boolean canComment(Change change) {
  231. return FragmentedDiffRequestFromChange.canCreateRequest(change);
  232. }
  233. @Override
  234. public RefreshablePanel comment(Change change, JComponent parent, BackgroundTaskQueue queue) {
  235. return new FragmentedDiffDetailsPanel(myProject, queue, change, parent);
  236. }
  237. }
  238. private static class FragmentedDiffDetailsPanel extends AbstractRefreshablePanel<ValueWithVcsException<PreparedFragmentedContent>> {
  239. private final FragmentedDiffRequestFromChange myRequestFromChange;
  240. private final FilePath myFilePath;
  241. private final ChangeListManager myChangeListManager;
  242. private final ChangesFragmentedDiffPanel myDiffPanel;
  243. private final Project myProject;
  244. private FragmentedDiffDetailsPanel(Project project, BackgroundTaskQueue queue, final Change change, JComponent parent) {
  245. super(project, "Loading change content", queue);
  246. myProject = project;
  247. myFilePath = ChangesUtil.getFilePath(change);
  248. myRequestFromChange = new FragmentedDiffRequestFromChange(project);
  249. myChangeListManager = ChangeListManager.getInstance(project);
  250. myDiffPanel = new ChangesFragmentedDiffPanel(project, changeDescription(change), parent);
  251. myDiffPanel.buildUi();
  252. }
  253. @Override
  254. protected void refreshPresentation() {
  255. myDiffPanel.refreshPresentation();
  256. }
  257. @Override
  258. public boolean isStillValid(Change data) {
  259. return FragmentedDiffRequestFromChange.canCreateRequest(data);
  260. }
  261. @Override
  262. protected ValueWithVcsException<PreparedFragmentedContent> loadImpl() throws VcsException {
  263. return new ValueWithVcsException<PreparedFragmentedContent>() {
  264. @Override
  265. protected PreparedFragmentedContent computeImpl() throws VcsException {
  266. final Change change = myChangeListManager.getChange(myFilePath);
  267. if (change == null) {
  268. return null;
  269. }
  270. myDiffPanel.setTitle(changeDescription(change));
  271. return myRequestFromChange.getRanges(change);
  272. }
  273. };
  274. }
  275. @Override
  276. protected JPanel dataToPresentation(ValueWithVcsException<PreparedFragmentedContent> value) {
  277. final PreparedFragmentedContent requestForChange;
  278. try {
  279. requestForChange = value.get();
  280. if (requestForChange == null) return noDifferences();
  281. if (requestForChange.isEmpty()) {
  282. return noDifferences();
  283. }
  284. }
  285. catch (VcsException e) {
  286. return UIVcsUtil.errorPanel(e.getMessage(), true);
  287. }
  288. myDiffPanel.refreshData(requestForChange);
  289. return myDiffPanel.getPanel();
  290. }
  291. @Override
  292. protected void disposeImpl() {
  293. Disposer.dispose(myDiffPanel);
  294. }
  295. @Override
  296. public boolean refreshDataSynch() {
  297. try {
  298. myTicket.increment();
  299. ValueWithVcsException<PreparedFragmentedContent> exception = loadImpl();
  300. dataToPresentation(exception);
  301. }
  302. catch (VcsException e) {
  303. return false;
  304. }
  305. return true;
  306. }
  307. @Override
  308. public void away() {
  309. myDiffPanel.away();
  310. }
  311. }
  312. private static String changeDescription(Change o) {
  313. return ChangesUtil.getFilePath(o).getName() + " (" + (o.getBeforeRevision() == null ? "New" : beforeRevisionText(o)) + ")";
  314. }
  315. private static String beforeRevisionText(Change o) {
  316. VcsRevisionNumber revisionNumber = o.getBeforeRevision().getRevisionNumber();
  317. return revisionNumber instanceof ShortVcsRevisionNumber ? ((ShortVcsRevisionNumber) revisionNumber).toShortString() :
  318. revisionNumber.asString();
  319. }
  320. }