/*******************************************************************************
* Copyright (C) 2008, 2014 Marek Zawirski <marek.zawirski@gmail.com> 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.push;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.egit.core.op.PushOperationResult;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.DecorationOverlayDescriptor;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.ui.model.WorkbenchAdapter;
/**
* Data class representing row (element) of table with push results.
* <p>
* Each row is associated with one ref update, while each column is associated
* with one URI (remote repository).
*
* @see PushOperationResult
* @see RefUpdateContentProvider
*/
class RefUpdateElement extends WorkbenchAdapter {
private final RemoteRefUpdate update;
private final PushOperationResult result;
private final URIish uri;
private final ObjectReader reader;
private final Repository repo;
private Object[] children;
private final boolean tag;
RefUpdateElement(final PushOperationResult result, RemoteRefUpdate update,
URIish uri, ObjectReader reader, Repository repo) {
this.result = result;
this.update = update;
this.uri = uri;
this.reader = reader;
this.repo = repo;
String remote = update.getRemoteName();
tag = remote != null && remote.startsWith(Constants.R_TAGS);
}
URIish getUri() {
return uri;
}
String getSrcRefName() {
return update.getSrcRef();
}
String getDstRefName() {
return update.getRemoteName();
}
boolean isDelete() {
// Assuming that we never use ObjectId.zeroId() in GUI.
// (no need to compare to it).
return getSrcRefName() == null;
}
boolean isAdd() {
return getAdvertisedRemoteRef() == null;
}
boolean isRejected() {
switch (getStatus()) {
case REJECTED_NODELETE:
case REJECTED_NONFASTFORWARD:
case REJECTED_OTHER_REASON:
case REJECTED_REMOTE_CHANGED:
return true;
default:
return false;
}
}
boolean isTag() {
return tag;
}
PushOperationResult getPushOperationResult() {
return result;
}
boolean isSuccessfulConnection() {
return result.isSuccessfulConnection(uri);
}
String getErrorMessage() {
return result.getErrorMessage(uri);
}
Status getStatus() {
return update.getStatus();
}
RemoteRefUpdate getRemoteRefUpdate() {
return update;
}
Ref getAdvertisedRemoteRef() {
return result.getPushResult(uri).getAdvertisedRef(getDstRefName());
}
@Override
public ImageDescriptor getImageDescriptor(Object object) {
switch (getStatus()) {
case OK:
if (isDelete())
return tag ? new DecorationOverlayDescriptor(UIIcons.TAG,
UIIcons.OVR_STAGED_REMOVE, IDecoration.TOP_RIGHT)
: new DecorationOverlayDescriptor(UIIcons.BRANCH,
UIIcons.OVR_STAGED_REMOVE,
IDecoration.TOP_RIGHT);
if (isAdd())
return tag ? UIIcons.CREATE_TAG : UIIcons.CREATE_BRANCH;
else
return tag ? UIIcons.TAG : UIIcons.BRANCH;
case UP_TO_DATE:
return tag ? UIIcons.TAG : UIIcons.BRANCH;
case REJECTED_NODELETE:
case REJECTED_NONFASTFORWARD:
case REJECTED_OTHER_REASON:
case REJECTED_REMOTE_CHANGED:
return tag ? new DecorationOverlayDescriptor(UIIcons.TAG,
UIIcons.OVR_ERROR, IDecoration.TOP_RIGHT)
: new DecorationOverlayDescriptor(UIIcons.BRANCH,
UIIcons.OVR_ERROR, IDecoration.TOP_RIGHT);
default:
return super.getImageDescriptor(object);
}
}
@Override
public String getLabel(Object object) {
return getStyledText(object).getString();
}
private RepositoryCommit[] getCommits(Ref end) {
try (final RevWalk walk = new RevWalk(reader)) {
walk.setRetainBody(true);
walk.markStart(walk.parseCommit(update.getNewObjectId()));
walk.markUninteresting(walk.parseCommit(end.getObjectId()));
List<RepositoryCommit> commits = new ArrayList<>();
for (RevCommit commit : walk)
commits.add(new RepositoryCommit(repo, commit));
return commits.toArray(new RepositoryCommit[commits.size()]);
} catch (IOException e) {
Activator.logError("Error parsing commits from push result", e); //$NON-NLS-1$
return new RepositoryCommit[0];
}
}
@Override
public Object[] getChildren(Object object) {
if (children != null)
return children;
switch (update.getStatus()) {
case OK:
if (!isDelete()) {
final Ref ref = getAdvertisedRemoteRef();
if (ref != null) {
children = getCommits(ref);
break;
}
}
//$FALL-THROUGH$
default:
children = super.getChildren(object);
}
return children;
}
/**
* Shorten ref name
*
* @param ref
* @return shortened ref name
*/
protected String shortenRef(final String ref) {
return NoteMap.shortenRefName(Repository.shortenRefName(ref));
}
/**
* Get styled text
*
* @param object
* @return styled string
*/
@Override
public StyledString getStyledText(Object object) {
StyledString styled = new StyledString();
final String remote = getDstRefName();
final String local = getSrcRefName();
if (!tag && local != null) {
styled.append(shortenRef(local));
styled.append(" \u2192 " /* → */); //$NON-NLS-1$
}
styled.append(shortenRef(remote));
styled.append(' ');
// Include uri if more than one
if (result.getURIs().size() > 1) {
styled.append(MessageFormat.format(
UIText.RefUpdateElement_UrisDecoration, uri.toString()),
StyledString.QUALIFIER_STYLER);
styled.append(' ');
}
switch (getStatus()) {
case OK:
if (update.isDelete())
styled.append(UIText.PushResultTable_statusOkDeleted,
StyledString.DECORATIONS_STYLER);
else {
final Ref oldRef = getAdvertisedRemoteRef();
if (oldRef == null) {
if (tag)
styled.append(UIText.PushResultTable_statusOkNewTag,
StyledString.DECORATIONS_STYLER);
else
styled.append(UIText.PushResultTable_statusOkNewBranch,
StyledString.DECORATIONS_STYLER);
} else {
String separator = update.isFastForward() ? ".." : "..."; //$NON-NLS-1$ //$NON-NLS-2$
ObjectId objectId = oldRef.getObjectId();
Object oldName = objectId != null
? objectId.abbreviate(7).name() : "?"; //$NON-NLS-1$
styled.append(MessageFormat.format(
UIText.RefUpdateElement_CommitRangeDecoration,
update.getNewObjectId().abbreviate(7).name(),
separator, oldName),
StyledString.DECORATIONS_STYLER);
styled.append(' ');
styled.append(MessageFormat.format(
UIText.RefUpdateElement_CommitCountDecoration,
Integer.valueOf(getChildren(this).length)),
StyledString.COUNTER_STYLER);
}
}
break;
case UP_TO_DATE:
styled.append(UIText.PushResultTable_statusUpToDate,
StyledString.DECORATIONS_STYLER);
break;
case NON_EXISTING:
styled.append(UIText.PushResultTable_statusNoMatch,
StyledString.DECORATIONS_STYLER);
break;
case REJECTED_NODELETE:
case REJECTED_REMOTE_CHANGED:
styled.append(UIText.PushResultTable_statusRejected,
StyledString.DECORATIONS_STYLER);
break;
case REJECTED_NONFASTFORWARD:
styled.append(UIText.RefUpdateElement_statusRejectedNonFastForward,
StyledString.DECORATIONS_STYLER);
break;
case REJECTED_OTHER_REASON:
styled.append(UIText.PushResultTable_statusRemoteRejected,
StyledString.DECORATIONS_STYLER);
break;
default:
break;
}
return styled;
}
}