/*
*
* * Copyright 2013-2014 Urs Wolfer
* *
* * 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.urswolfer.intellij.plugin.gerrit.ui.changesbrowser;
import com.google.common.base.*;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.SimpleContentRevision;
import git4idea.history.browser.GitCommit;
import java.util.List;
import java.util.Map;
/**
* This class helps to get a list of {@link com.intellij.openapi.vcs.changes.Change}s between two
* {@link git4idea.history.browser.GitCommit}s.
*
* @author Thomas Forrer
*/
public class CommitDiffBuilder {
private static final Function<Change, String> GET_CHANGED_FILE_PATH = new Function<Change, String>() {
@Override
public String apply(Change change) {
ContentRevision afterRevision = change.getAfterRevision();
if (afterRevision != null) {
return afterRevision.getFile().getPath();
}
ContentRevision beforeRevision = change.getBeforeRevision();
if (beforeRevision != null) {
return beforeRevision.getFile().getPath();
}
throw new IllegalStateException("Change should have at least one ContentRevision set.");
}
};
private static final Predicate<Change> CONTAINS_NO_CHANGE = new Predicate<Change>() {
@Override
public boolean apply(Change change) {
ContentRevision base = change.getBeforeRevision();
ContentRevision contentRevision = change.getAfterRevision();
if (base == null && contentRevision == null) {
return true;
}
if (base == null) return false;
if (contentRevision == null) return false;
try {
String baseContent = Strings.nullToEmpty(base.getContent());
return baseContent.equals(Strings.nullToEmpty(contentRevision.getContent()));
} catch (VcsException e) {
throw Throwables.propagate(e);
}
}
};
private final String baseHash;
private final String hash;
private final GitCommit base;
private final GitCommit commit;
private Map<String, Change> baseChanges;
private Map<String, Change> changes;
private final List<Change> diff = Lists.newArrayList();
private ChangesProvider changesProvider = new SimpleChangesProvider();
public CommitDiffBuilder(GitCommit base, GitCommit commit) {
this.base = base;
this.commit = commit;
baseHash = base.getHash().getValue();
hash = commit.getHash().getValue();
}
public CommitDiffBuilder withChangesProvider(ChangesProvider changesProvider) {
this.changesProvider = changesProvider;
return this;
}
public List<Change> getDiff() throws VcsException {
baseChanges = Maps.uniqueIndex(changesProvider.provide(base), GET_CHANGED_FILE_PATH);
changes = Maps.uniqueIndex(changesProvider.provide(commit), GET_CHANGED_FILE_PATH);
addedFiles();
changedFiles();
removedFiles();
return Lists.newArrayList(Iterables.filter(diff, Predicates.not(CONTAINS_NO_CHANGE)));
}
private void addedFiles() throws VcsException {
Sets.SetView<String> addedFiles = Sets.difference(changes.keySet(), baseChanges.keySet());
for (String addedFile : addedFiles) {
Change change = changes.get(addedFile);
ContentRevision beforeRevision = null;
if (change.getType().equals(Change.Type.MODIFICATION)) {
ContentRevision changeBeforeRevision = change.getBeforeRevision();
assert changeBeforeRevision != null;
beforeRevision = new SimpleContentRevision(
changeBeforeRevision.getContent(),
changeBeforeRevision.getFile(),
baseHash);
}
diff.add(new Change(beforeRevision, change.getAfterRevision()));
}
}
private void changedFiles() {
Sets.SetView<String> changedFiles = Sets.intersection(baseChanges.keySet(), changes.keySet());
for (String changedFile : changedFiles) {
Change baseChange = baseChanges.get(changedFile);
ContentRevision baseRevision = baseChange.getAfterRevision();
Change change = changes.get(changedFile);
ContentRevision revision = change.getAfterRevision();
if (baseRevision != null || revision != null) {
diff.add(new Change(baseRevision, revision));
}
}
}
private void removedFiles() throws VcsException {
Sets.SetView<String> removedFiles = Sets.difference(baseChanges.keySet(), changes.keySet());
for (String removedFile : removedFiles) {
Change baseChange = baseChanges.get(removedFile);
ContentRevision afterRevision = null;
if (baseChange.getType().equals(Change.Type.MODIFICATION)) {
ContentRevision baseChangeBeforeRevision = baseChange.getBeforeRevision();
assert baseChangeBeforeRevision != null;
afterRevision = new SimpleContentRevision(
baseChangeBeforeRevision.getContent(),
baseChangeBeforeRevision.getFile(),
hash
);
}
diff.add(new Change(baseChange.getAfterRevision(), afterRevision));
}
}
public static interface ChangesProvider {
List<Change> provide(GitCommit gitCommit);
}
private static final class SimpleChangesProvider implements ChangesProvider {
@Override
public List<Change> provide(GitCommit gitCommit) {
return gitCommit.getChanges();
}
}
}