/*******************************************************************************
* Copyright (C) 2010, Dariusz Luksza <dariusz@luksza.org>
*
* 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.synchronize.model;
import static org.eclipse.compare.structuremergeviewer.Differencer.RIGHT;
import static org.eclipse.egit.ui.internal.synchronize.compare.GitCompareInput.getFileRevisionLabel;
import static org.eclipse.jgit.lib.ObjectId.zeroId;
import java.io.IOException;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.IResourceProvider;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
import org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.synchronize.GitCommitsModelCache.Change;
import org.eclipse.egit.ui.internal.synchronize.compare.ComparisonDataSource;
import org.eclipse.egit.ui.internal.synchronize.compare.GitCompareInput;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.swt.graphics.Image;
import org.eclipse.team.ui.mapping.ISynchronizationCompareInput;
import org.eclipse.team.ui.mapping.SaveableComparison;
/**
* Git blob object representation in Git ChangeSet
*/
public class GitModelBlob extends GitModelObject implements
ISynchronizationCompareInput, IResourceProvider {
private static final GitModelObject[] empty = new GitModelObject[0];
private final Change change;
private ITypedElement ancestorElement;
private ITypedElement leftElement;
private ITypedElement rightElement;
/**
* Absolute path to changed object
*/
protected final IPath path;
/**
* {@link Repository} associated with this object
*/
protected final Repository repo;
/**
* @param parent
* parent object
* @param repo
* repository associated with this object
* @param change
* change associated with this object
* @param path
* absolute path of change
*/
public GitModelBlob(GitModelObjectContainer parent, Repository repo,
Change change, IPath path) {
super(parent);
this.repo = repo;
this.path = path;
this.change = change;
}
@Override
public GitModelObject[] getChildren() {
return empty;
}
@Override
public String getName() {
return path.lastSegment();
}
@Override
public IPath getLocation() {
return path;
}
@Override
public boolean isContainer() {
return false;
}
@Override
public int getKind() {
return change.getKind();
}
/**
* @return abbreviated object id of base commit
*/
public AbbreviatedObjectId getBaseCommitId() {
return change.getCommitId();
}
/**
* @return abbreviated object id of remote commit
*/
public AbbreviatedObjectId getRemoteCommitId() {
return change.getRemoteCommitId();
}
@Override
public int repositoryHashCode() {
return repo.getWorkTree().hashCode();
}
@Override
public void dispose() {
// there is nothing to dispose
}
@Override
public String toString() {
return "ModelBlob[objectId=" + change.getObjectId() + ", location=" + getLocation() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
@Override
public Image getImage() {
// not used
return null;
}
@Override
public ITypedElement getAncestor() {
prepareTypedElements();
return ancestorElement;
}
@Override
public ITypedElement getLeft() {
prepareTypedElements();
return leftElement;
}
@Override
public ITypedElement getRight() {
prepareTypedElements();
return rightElement;
}
@Override
public void addCompareInputChangeListener(
ICompareInputChangeListener listener) {
// data in commit will never change, therefore change listeners are
// useless
}
@Override
public void removeCompareInputChangeListener(
ICompareInputChangeListener listener) {
// data in commit will never change, therefore change listeners are
// useless
}
@Override
public void copy(boolean leftToRight) {
// do nothing, we should disallow coping content between commits
}
@Override
public SaveableComparison getSaveable() {
// unused
return null;
}
@Override
public void prepareInput(CompareConfiguration configuration,
IProgressMonitor monitor) throws CoreException {
configuration.setLeftLabel(getFileRevisionLabel(getLeft()));
configuration.setRightLabel(getFileRevisionLabel(getRight()));
}
@Override
public String getFullPath() {
return path.toOSString();
}
@Override
public boolean isCompareInputFor(Object object) {
// not used
return false;
}
@Override
public int hashCode() {
int baseHash = 1;
if (change != null)
baseHash = change.getObjectId() != null ? change.getObjectId()
.hashCode() : 31;
int remoteHash = 11;
if (change != null)
remoteHash = change.getRemoteObjectId() != null ? change
.getRemoteObjectId().hashCode() : 41;
return baseHash ^ remoteHash ^ path.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GitModelBlob other = (GitModelBlob) obj;
if (change == null) {
if (other.change != null)
return false;
} else if (!change.equals(other.change))
return false;
if (path == null) {
if (other.path != null)
return false;
} else if (!path.equals(other.path))
return false;
return true;
}
private void prepareTypedElements() {
if (ancestorElement != null) // other elements should also not be null
return;
ComparisonDataSource baseData;
ComparisonDataSource remoteData;
RevCommit baseCommit = null;
RevCommit remoteCommit = null;
try (RevWalk rw = new RevWalk(repo)) {
rw.setRetainBody(true);
if (change.getCommitId() != null)
baseCommit = rw.parseCommit(change.getCommitId().toObjectId());
if (change.getRemoteCommitId() != null)
remoteCommit = rw.parseCommit(change.getRemoteCommitId()
.toObjectId());
} catch (IOException e) {
Activator.logError(e.getMessage(), e);
}
if (baseCommit == null && remoteCommit != null)
baseCommit = remoteCommit; // prevent from NPE for deleted files
ObjectId localId = extractObjectId(change.getObjectId());
ObjectId remoteId = extractObjectId(change.getRemoteObjectId());
if ((getKind() & RIGHT) == RIGHT) {
baseData = new ComparisonDataSource(baseCommit, localId);
remoteData = new ComparisonDataSource(remoteCommit, remoteId);
} else /* getKind() == LEFT */{
baseData = new ComparisonDataSource(remoteCommit, remoteId);
remoteData = new ComparisonDataSource(baseCommit, localId);
}
GitCompareInput compareInput = getCompareInput(baseData, remoteData, remoteData);
ancestorElement = compareInput.getAncestor();
leftElement = compareInput.getLeft();
rightElement = compareInput.getRight();
}
@Override
public IResource getResource() {
IFile file = ResourcesPlugin.getWorkspace().getRoot()
.getFileForLocation(path);
return file;
}
/**
* Returns specific instance of {@link GitCompareInput} for particular
* compare input.
*
* @param baseData
* @param remoteData
* @param ancestorData
* @return Git specific {@link ICompareInput}
*/
protected GitCompareInput getCompareInput(ComparisonDataSource baseData,
ComparisonDataSource remoteData, ComparisonDataSource ancestorData) {
String gitPath = Repository.stripWorkDir(repo.getWorkTree(), path.toFile());
return new GitCompareInput(repo, ancestorData, baseData, remoteData,
gitPath);
}
private ObjectId extractObjectId(AbbreviatedObjectId objectId) {
if (objectId != null)
return objectId.toObjectId();
else
return zeroId();
}
}