summaryrefslogtreecommitdiff
path: root/platform/vcs-impl/src/com/intellij/openapi/vcs/history/BaseDiffFromHistoryHandler.java
blob: 8a16ba6a481f5382deabb22c71a4c754560af5a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
 * Copyright 2000-2014 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 com.intellij.openapi.vcs.history;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

/**
 * @author Konstantin Kolosovsky.
 */
public abstract class BaseDiffFromHistoryHandler<T extends VcsFileRevision> implements DiffFromHistoryHandler {

  private static final Logger LOG = Logger.getInstance(BaseDiffFromHistoryHandler.class);

  @NotNull protected final Project myProject;

  protected BaseDiffFromHistoryHandler(@NotNull Project project) {
    myProject = project;
  }

  @Override
  public void showDiffForOne(@NotNull AnActionEvent e,
                             @NotNull FilePath filePath,
                             @NotNull VcsFileRevision previousRevision,
                             @NotNull VcsFileRevision revision) {
    doShowDiff(filePath, previousRevision, revision, false);
  }

  @Override
  public void showDiffForTwo(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2) {
    doShowDiff(filePath, revision1, revision2, true);
  }

  @SuppressWarnings("unchecked")
  protected void doShowDiff(@NotNull FilePath filePath,
                            @NotNull VcsFileRevision revision1,
                            @NotNull VcsFileRevision revision2,
                            boolean autoSort) {
    if (!filePath.isDirectory()) {
      VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort);
    }
    else if (revision1.equals(VcsFileRevision.NULL)) {
      T right = (T)revision2;
      showAffectedChanges(filePath, right);
    }
    else if (revision2 instanceof CurrentRevision) {
      T left = (T)revision1;
      showChangesBetweenRevisions(filePath, left, null);
    }
    else {
      T left = (T)revision1;
      T right = (T)revision2;
      if (autoSort) {
        Couple<VcsFileRevision> pair = VcsHistoryUtil.sortRevisions(revision1, revision2);
        left = (T)pair.first;
        right = (T)pair.second;
      }
      showChangesBetweenRevisions(filePath, left, right);
    }
  }

  protected void showChangesBetweenRevisions(@NotNull final FilePath path, @NotNull final T rev1, @Nullable final T rev2) {
    new CollectChangesTask("Comparing revisions...") {

      @NotNull
      @Override
      public List<Change> getChanges() throws VcsException {
        return getChangesBetweenRevisions(path, rev1, rev2);
      }

      @NotNull
      @Override
      public String getDialogTitle() {
        return getChangesBetweenRevisionsDialogTitle(path, rev1, rev2);
      }
    }.queue();
  }

  protected void showAffectedChanges(@NotNull final FilePath path, @NotNull final T rev) {
    new CollectChangesTask("Collecting affected changes...") {

      @NotNull
      @Override
      public List<Change> getChanges() throws VcsException {
        return getAffectedChanges(path, rev);
      }

      @NotNull
      @Override
      public String getDialogTitle() {
        return getAffectedChangesDialogTitle(path, rev);
      }
    }.queue();
  }

  // rev2 == null -> compare rev1 with local
  // rev2 != null -> compare rev1 with rev2
  @NotNull
  protected abstract List<Change> getChangesBetweenRevisions(@NotNull final FilePath path, @NotNull final T rev1, @Nullable final T rev2)
    throws VcsException;

  @NotNull
  protected abstract List<Change> getAffectedChanges(@NotNull final FilePath path, @NotNull final T rev) throws VcsException;

  @NotNull
  protected abstract String getPresentableName(@NotNull T revision);

  protected void showChangesDialog(@NotNull String title, @NotNull List<Change> changes) {
    DialogBuilder dialogBuilder = new DialogBuilder(myProject);

    dialogBuilder.setTitle(title);
    dialogBuilder.setActionDescriptors(new DialogBuilder.ActionDescriptor[]{new DialogBuilder.CloseDialogAction()});
    final ChangesBrowser changesBrowser =
      new ChangesBrowser(myProject, null, changes, null, false, true, null, ChangesBrowser.MyUseCase.COMMITTED_CHANGES, null);
    changesBrowser.setChangesToDisplay(changes);
    dialogBuilder.setCenterPanel(changesBrowser);
    dialogBuilder.showNotModal();
  }

  protected void showError(@NotNull VcsException e, @NotNull String logMessage) {
    LOG.info(logMessage, e);
    VcsBalloonProblemNotifier.showOverVersionControlView(myProject, e.getMessage(), MessageType.ERROR);
  }

  @NotNull
  protected String getChangesBetweenRevisionsDialogTitle(@NotNull final FilePath path, @NotNull final T rev1, @Nullable final T rev2) {
    String rev1Title = getPresentableName(rev1);

    return rev2 != null
           ? String.format("Difference between %s and %s in %s", rev1Title, getPresentableName(rev2), path.getName())
           : String.format("Difference between %s and local version in %s", rev1Title, path.getName());
  }

  @NotNull
  protected String getAffectedChangesDialogTitle(@NotNull final FilePath path, @NotNull final T rev) {
    return String.format("Initial commit %s in %s", getPresentableName(rev), path.getName());
  }

  protected abstract class CollectChangesTask extends Task.Backgroundable {

    private List<Change> myChanges;

    public CollectChangesTask(@NotNull String title) {
      super(BaseDiffFromHistoryHandler.this.myProject, title);
    }

    @Override
    public void run(@NotNull ProgressIndicator indicator) {
      try {
        myChanges = getChanges();
      }
      catch (VcsException e) {
        showError(e, "Error during task: " + getDialogTitle());
      }
    }

    @NotNull
    public abstract List<Change> getChanges() throws VcsException;

    @NotNull
    public abstract String getDialogTitle();

    @Override
    public void onSuccess() {
      showChangesDialog(getDialogTitle(), ContainerUtil.notNullize(myChanges));
    }
  }
}