/*******************************************************************************
* Copyright (c) 2011, 2015 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.orion.server.git.objects;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.orion.server.core.ProtocolConstants;
import org.eclipse.orion.server.core.resources.Property;
import org.eclipse.orion.server.core.resources.ResourceShape;
import org.eclipse.orion.server.core.resources.annotations.PropertyDescription;
import org.eclipse.orion.server.core.resources.annotations.ResourceDescription;
import org.eclipse.orion.server.git.BaseToCommitConverter;
import org.eclipse.orion.server.git.GitConstants;
import org.eclipse.orion.server.git.servlets.GitServlet;
import org.eclipse.orion.server.git.servlets.GitUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ResourceDescription(type = Branch.TYPE)
public class Branch extends GitObject {
public static final String RESOURCE = "branch"; //$NON-NLS-1$
public static final String TYPE = "Branch"; //$NON-NLS-1$
public static final Comparator<Branch> COMPARATOR = new Comparator<Branch>() {
@Override
public int compare(Branch o1, Branch o2) {
try {
if (o1.isCurrent()) {
return -1;
} else if (o2.isCurrent()) {
return 1;
}
} catch (IOException e) {
}
return o1.getTime() < o2.getTime() ? 1 : (o1.getTime() > o2.getTime() ? -1 : o2.getName(true, false).compareTo(o1.getName(true, false)));
}
};
private static final ResourceShape DEFAULT_RESOURCE_SHAPE = new ResourceShape();
{
Property[] defaultProperties = new Property[] { //
new Property(ProtocolConstants.KEY_LOCATION), // super
new Property(GitConstants.KEY_CLONE), // super
new Property(ProtocolConstants.KEY_NAME), //
new Property(ProtocolConstants.KEY_LOCATION), //
new Property(ProtocolConstants.KEY_FULL_NAME), //
new Property(GitConstants.KEY_HEAD_SHA), //
new Property(GitConstants.KEY_COMMIT), //
new Property(GitConstants.KEY_TREE), //
new Property(GitConstants.KEY_DIFF), //
new Property(GitConstants.KEY_REMOTE), //
new Property(GitConstants.KEY_HEAD), //
new Property(GitConstants.KEY_BRANCH_CURRENT), //
new Property(GitConstants.KEY_BRANCH_DETACHED), //
new Property(ProtocolConstants.KEY_LOCAL_TIMESTAMP) };
DEFAULT_RESOURCE_SHAPE.setProperties(defaultProperties);
}
private Ref ref;
public Branch(URI cloneLocation, Repository db, Ref ref) {
super(cloneLocation, db);
this.ref = ref;
}
/**
* Returns a JSON representation of this local branch.
*/
@Override
public JSONObject toJSON() throws JSONException, URISyntaxException, IOException, CoreException {
return jsonSerializer.serialize(this, DEFAULT_RESOURCE_SHAPE);
}
@PropertyDescription(name = GitConstants.KEY_BRANCH_CURRENT)
public boolean isCurrent() throws IOException {
return getName(false, false).equals(db.getBranch())||(isDetached()&&this.getHeadSHA().equals(db.getBranch()));
}
@PropertyDescription(name = GitConstants.KEY_BRANCH_DETACHED)
public boolean isDetached() throws IOException {
return !ref.getName().startsWith(Constants.R_HEADS);
}
@Override
protected URI getLocation() throws URISyntaxException {
return createLocation(Branch.RESOURCE);
}
private URI createLocation(String resource) throws URISyntaxException {
String shortName = getName(false, true);
IPath basePath = new Path(cloneLocation.getPath());
IPath newPath = new Path(GitServlet.GIT_URI).append(resource).append(shortName).append(basePath.removeFirstSegments(2));
return new URI(cloneLocation.getScheme(), cloneLocation.getUserInfo(), cloneLocation.getHost(), cloneLocation.getPort(), newPath.toString(),
cloneLocation.getQuery(), cloneLocation.getFragment());
}
// TODO: expandable
public JSONObject toJSON(JSONObject log) throws JSONException, URISyntaxException, IOException, CoreException {
JSONObject result = this.toJSON();
result.put(GitConstants.KEY_TAG_COMMIT, log);
return result;
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_REMOTE)
private JSONArray getRemotes() throws URISyntaxException, JSONException, IOException, CoreException {
String branchName = Repository.shortenRefName(ref.getName());
String remoteName = getConfig().getString(ConfigConstants.CONFIG_BRANCH_SECTION, branchName, ConfigConstants.CONFIG_KEY_REMOTE);
List<RemoteConfig> remoteConfigs = RemoteConfig.getAllRemoteConfigs(getConfig());
ArrayList<JSONObject> remotes = new ArrayList<JSONObject>();
for (RemoteConfig remoteConfig : remoteConfigs) {
if (!remoteConfig.getFetchRefSpecs().isEmpty()) {
Remote r = new Remote(cloneLocation, db, remoteConfig.getName());
r.setNewBranch(branchName);
// Ensure that the remote tracking branch is the first in the list.
// Insert at the beginning of the list as well when the remote tracking branch is not set but the branch has been pushed to the remote
if (remoteConfig.getName().equals(remoteName) || (remoteName == null && db.resolve(Constants.R_REMOTES + remoteConfig.getName() + "/" + branchName) != null)) {
remotes.add(0, r.toJSON());
} else {
remotes.add(r.toJSON());
}
}
}
JSONArray result = new JSONArray();
for (JSONObject remote : remotes) {
result.put(remote);
}
return result;
}
@PropertyDescription(name = ProtocolConstants.KEY_NAME)
private String getName() {
return getName(false, false);
}
@PropertyDescription(name = ProtocolConstants.KEY_FULL_NAME)
private String getFullName() {
return getName(true, false);
}
@PropertyDescription(name = GitConstants.KEY_HEAD_SHA)
public String getHeadSHA() {
return ref.getObjectId().getName();
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_COMMIT)
private URI getCommitLocation() throws IOException, URISyntaxException {
return BaseToCommitConverter.getCommitLocation(cloneLocation, getName(true, true), BaseToCommitConverter.REMOVE_FIRST_2);
}
@PropertyDescription(name = GitConstants.KEY_TREE)
private URI getTreeLocation() throws URISyntaxException {
return createTreeLocation(null);
}
@PropertyDescription(name = GitConstants.KEY_DIFF)
private URI getDiffLocation() throws IOException, URISyntaxException {
return createLocation(Diff.RESOURCE);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_HEAD)
private URI getHeadLocation() throws IOException, URISyntaxException {
return BaseToCommitConverter.getCommitLocation(cloneLocation, Constants.HEAD, BaseToCommitConverter.REMOVE_FIRST_2);
}
public String getName(boolean fullName, boolean encode) {
String name = ref.getName();
if (!fullName)
name = Repository.shortenRefName(ref.getName());
if (encode)
name = GitUtils.encode(name);
return name;
}
@PropertyDescription(name = ProtocolConstants.KEY_LOCAL_TIMESTAMP)
private long getLocalTimestamp() {
return (long) getTime() * 1000;
}
public int getTime() {
RevCommit c = parseCommit();
if (c != null)
return c.getCommitTime();
return 0;
}
private URI createTreeLocation(String path) throws URISyntaxException {
// remove /gitapi/clone from the start of path
IPath clonePath = new Path(cloneLocation.getPath()).removeFirstSegments(2);
IPath result = new Path(GitServlet.GIT_URI).append(Tree.RESOURCE).append(clonePath).append(GitUtils.encode(this.getName()));
if (path != null) {
result.append(path);
}
return new URI(cloneLocation.getScheme(), cloneLocation.getUserInfo(), cloneLocation.getHost(), cloneLocation.getPort(), result.makeAbsolute()
.toString(), cloneLocation.getQuery(), cloneLocation.getFragment());
}
private RevCommit parseCommit() {
ObjectId oid = ref.getObjectId();
if (oid == null)
return null;
RevWalk walk = new RevWalk(db);
try {
return walk.parseCommit(oid);
} catch (IOException e) {
// ignore and return null
} finally {
walk.close();
}
return null;
}
@Override
public String toString() {
return "Branch [ref=" + ref + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
}