/******************************************************************************
* Copyright (c) 2011, 2013 GitHub Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Kevin Sawicki (GitHub Inc.) - initial API and implementation
*****************************************************************************/
package org.eclipse.egit.ui.internal.blame;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.egit.core.internal.CompareCoreUtils;
import org.eclipse.jface.text.revisions.Revision;
import org.eclipse.jface.text.source.LineRange;
import org.eclipse.jgit.diff.DiffAlgorithm;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.EditList;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.swt.graphics.RGB;
/**
* Annotation revision
*/
public class BlameRevision extends Revision {
private int start;
private int lines = 1;
private RevCommit commit;
private Repository repository;
private String sourcePath;
private Map<Integer, Integer> sourceLines = new HashMap<>();
private Map<RevCommit, Diff> diffToParentCommit = new HashMap<>();
@Override
public Object getHoverInfo() {
return this;
}
@Override
public RGB getColor() {
return AuthorColors.getDefault().getCommitterRGB(getAuthor());
}
@Override
public String getId() {
return commit.abbreviate(7).name();
}
@Override
public Date getDate() {
PersonIdent person = commit.getAuthorIdent();
if( person == null)
person = commit.getCommitterIdent();
return person != null ? person.getWhen() : new Date(0);
}
/**
* Register revision
*
* @return this revision
*/
public BlameRevision register() {
addRange(new LineRange(start, lines));
return this;
}
/**
* Increment line count
*
* @return this revision
*/
public BlameRevision addLine() {
lines++;
return this;
}
/**
* Reset revision
*
* @param number
* @return this revision
*/
public BlameRevision reset(int number) {
start = number;
lines = 1;
return this;
}
/**
* Set revision
*
* @param commit
* @return this
*/
public BlameRevision setCommit(RevCommit commit) {
this.commit = commit;
return this;
}
/**
* Get revision
*
* @return revision
*/
public RevCommit getCommit() {
return this.commit;
}
/**
* Set repository
*
* @param repository
* @return this
*/
public BlameRevision setRepository(Repository repository) {
this.repository = repository;
return this;
}
/**
* Get repository
*
* @return repository
*/
public Repository getRepository() {
return this.repository;
}
@Override
public String getAuthor() {
return commit.getAuthorIdent().getName();
}
/**
* @return repository-relative path of file
*/
public String getSourcePath() {
return sourcePath;
}
/**
* @param sourcePath
*/
public void setSourcePath(String sourcePath) {
this.sourcePath = sourcePath;
}
/**
* Get the line number the content had in the source of this blame
* information.
*
* @param currentLine
* 0-based line number
* @return 0-based source line or null
*/
public Integer getSourceLine(int currentLine) {
return sourceLines.get(Integer.valueOf(currentLine));
}
/**
* @param currentLine
* 0-based line number
* @param sourceLine
* 0-based line number
*/
public void addSourceLine(int currentLine, int sourceLine) {
sourceLines.put(Integer.valueOf(currentLine),
Integer.valueOf(sourceLine));
}
/**
* @param parentCommit
* @return the diff or null if none could be calculated
*/
public Diff getDiffToParent(RevCommit parentCommit) {
if (diffToParentCommit.containsKey(parentCommit))
return diffToParentCommit.get(parentCommit);
Diff diff = calculateDiffToParent(parentCommit);
diffToParentCommit.put(parentCommit, diff);
return diff;
}
private Diff calculateDiffToParent(RevCommit parentCommit) {
try (ObjectReader reader = repository.newObjectReader()) {
DiffEntry diffEntry = CompareCoreUtils.getChangeDiffEntry(
repository, sourcePath, commit, parentCommit, reader);
if (diffEntry == null)
return null;
RawText oldText = readText(diffEntry.getOldId(), reader);
RawText newText = readText(diffEntry.getNewId(), reader);
StoredConfig config = repository.getConfig();
DiffAlgorithm diffAlgorithm = DiffAlgorithm.getAlgorithm(config
.getEnum(ConfigConstants.CONFIG_DIFF_SECTION, null,
ConfigConstants.CONFIG_KEY_ALGORITHM,
SupportedAlgorithm.HISTOGRAM));
EditList editList = diffAlgorithm.diff(RawTextComparator.DEFAULT,
oldText, newText);
return new Diff(diffEntry.getOldPath(), oldText, newText, editList);
} catch (IOException e) {
return null;
}
}
private static RawText readText(AbbreviatedObjectId blobId,
ObjectReader reader) throws IOException {
ObjectLoader oldLoader = reader.open(blobId.toObjectId(),
Constants.OBJ_BLOB);
return new RawText(oldLoader.getCachedBytes());
}
/**
* Information about the diff to a parent commit of the blamed revision.
*/
public static class Diff {
private final String oldPath;
private final RawText oldText;
private final RawText newText;
private final EditList editList;
private Diff(String oldPath, RawText oldText, RawText newText,
EditList editList) {
this.oldPath = oldPath;
this.oldText = oldText;
this.newText = newText;
this.editList = editList;
}
/**
* @return old path of file in diff
*/
public String getOldPath() {
return oldPath;
}
/**
* @return old text of diff
*/
public RawText getOldText() {
return oldText;
}
/**
* @return new text of diff
*/
public RawText getNewText() {
return newText;
}
/**
* @return edit list of diff
*/
public EditList getEditList() {
return editList;
}
}
}