/*******************************************************************************
* 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.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jgit.api.SubmoduleStatusCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.submodule.SubmoduleStatus;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.orion.internal.server.servlets.Activator;
import org.eclipse.orion.internal.server.servlets.file.NewFileServlet;
import org.eclipse.orion.server.core.ProtocolConstants;
import org.eclipse.orion.server.core.resources.JSONSerializer;
import org.eclipse.orion.server.core.resources.Property;
import org.eclipse.orion.server.core.resources.ResourceShape;
import org.eclipse.orion.server.core.resources.Serializer;
import org.eclipse.orion.server.core.resources.annotations.PropertyDescription;
import org.eclipse.orion.server.core.resources.annotations.ResourceDescription;
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;
/**
* A git clone created in Orion.
*/
@ResourceDescription(type = Clone.TYPE)
public class Clone {
public static final String RESOURCE = "clone"; //$NON-NLS-1$
public static final String TYPE = "Clone"; //$NON-NLS-1$
private String id;
private URI contentLocation;
private URIish uriish;
private String name;
private URI baseLocation;
private String cloneUrl;
private IPath path;
private JSONArray parents;
private static final ResourceShape DEFAULT_RESOURCE_SHAPE = new ResourceShape();
{
Property[] defaultProperties = new Property[] { //
new Property(ProtocolConstants.KEY_NAME), //
new Property(ProtocolConstants.KEY_LOCATION), //
new Property(ProtocolConstants.KEY_CONTENT_LOCATION), //
new Property(GitConstants.KEY_REMOTE), //
new Property(GitConstants.KEY_CONFIG), //
new Property(GitConstants.KEY_HEAD), //
new Property(GitConstants.KEY_COMMIT), //
new Property(GitConstants.KEY_BRANCH), //
new Property(GitConstants.KEY_TAG), //
new Property(GitConstants.KEY_INDEX), //
new Property(GitConstants.KEY_STASH), //
new Property(GitConstants.KEY_PULL_REQUEST_LOCATION), //
new Property(GitConstants.KEY_STATUS), //
new Property(GitConstants.KEY_DIFF), //
new Property(GitConstants.KEY_URL), //
new Property(ProtocolConstants.KEY_CHILDREN),//
new Property(ProtocolConstants.KEY_PARENTS),//
new Property(GitConstants.KEY_SUBMODULE)};
DEFAULT_RESOURCE_SHAPE.setProperties(defaultProperties);
}
protected Serializer<JSONObject> jsonSerializer = new JSONSerializer();
/**
* Sets the clone id. The clone id is the HTTP resource URI of the file resource. This id is of the form /file/{workspaceId}/{projectName}[/{folderPath}]
*
* @param id
*/
public void setId(String id) {
this.id = id;
}
private String getId() {
return this.id;
}
/**
* Sets the location of the contents of this clone. The location is an absolute URI referencing to the filesystem.
*/
public void setContentLocation(URI contentURI) {
this.contentLocation = contentURI;
}
/**
* Returns the location of the contents of this clone. The result is an absolute URI in the filesystem.
*
* @return The location of the contents of this clone
*/
public URI getContentLocation() {
return this.contentLocation;
}
/**
* Sets the git URL of this clone.
*/
public void setUrl(URIish gitURI) {
this.uriish = gitURI;
}
/**
* Returns the URL of the git repository.
* <p>
* This method never returns null.
* </p>
*
* @return The URL of the git repository
*/
public String getUrl() {
return this.uriish.toString();
}
public void setName(String name) {
this.name = name;
}
@PropertyDescription(name = ProtocolConstants.KEY_NAME)
public String getName() {
return this.name;
}
public void setBaseLocation(URI baseLocation) {
this.baseLocation = baseLocation;
}
/**
* Returns a JSON representation of this clone.
*
* @return
* @throws JSONException
* @throws URISyntaxException
*/
public JSONObject toJSON() throws URISyntaxException {
return jsonSerializer.serialize(this, DEFAULT_RESOURCE_SHAPE);
}
@PropertyDescription(name = ProtocolConstants.KEY_LOCATION)
private URI getLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Clone.RESOURCE).append(getId());
return createUriWithPath(np);
}
@PropertyDescription(name = ProtocolConstants.KEY_CONTENT_LOCATION)
private URI getContentLocation2() throws URISyntaxException { // TODO duplicated method
IPath np = new Path(getId()).makeAbsolute();
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_REMOTE)
private URI getRemoteLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Remote.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_CONFIG)
private URI getConfigLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(ConfigOption.RESOURCE).append(Clone.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable?
@PropertyDescription(name = GitConstants.KEY_HEAD)
private URI getHeadLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Commit.RESOURCE).append(Constants.HEAD).append(getId());
return createUriWithPath(np);
}
// TODO: expandable?
@PropertyDescription(name = GitConstants.KEY_SUBMODULE)
private URI getSubmoduleLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Submodule.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable?
@PropertyDescription(name = GitConstants.KEY_COMMIT)
private URI getCommitLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Commit.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable?
@PropertyDescription(name = GitConstants.KEY_PULL_REQUEST_LOCATION)
private URI getPullRequestLocation() throws URISyntaxException {
String url = this.cloneUrl;
if(url!=null && GitUtils.isInGithub(url)){
IPath np = new Path(GitServlet.GIT_URI).append(PullRequest.RESOURCE).append(getId());
return createUriWithPath(np);
}
return null;
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_BRANCH)
private URI getBranchLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Branch.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_TAG)
private URI getTagLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Tag.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_INDEX)
private URI getIndexLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Index.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_STASH)
private URI getIgnoreLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Stash.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_STATUS)
private URI getStatusLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Status.RESOURCE).append(getId());
return createUriWithPath(np);
}
// TODO: expandable
@PropertyDescription(name = GitConstants.KEY_DIFF)
private URI getDiffLocation() throws URISyntaxException {
IPath np = new Path(GitServlet.GIT_URI).append(Diff.RESOURCE).append(GitConstants.KEY_DIFF_DEFAULT).append(getId());
return createUriWithPath(np);
}
@PropertyDescription(name = ProtocolConstants.KEY_CHILDREN)
private JSONArray getChildren() throws URISyntaxException, IOException, CoreException, JSONException, GitAPIException {
if (path == null)
return null;
IFileStore fileStore = NewFileServlet.getFileStore(null, path);
if (fileStore == null)
return null;
File localFile = fileStore.toLocalFile(EFS.NONE, null);
File gitModule = new File(localFile, ".gitmodules");
JSONArray submodules = null;
if (gitModule.exists() && !gitModule.isDirectory()) {
submodules = new JSONArray();
Repository parentRepository = null;
try {
parentRepository = FileRepositoryBuilder.create(GitUtils.resolveGitDir(localFile));
SubmoduleWalk walk = SubmoduleWalk.forIndex(parentRepository);
while (walk.next()) {
String cloneUrl;
if (walk.getRepository() != null) {
cloneUrl = GitUtils.getCloneUrl(walk.getRepository());
} else {
cloneUrl = walk.getRemoteUrl();
}
JSONArray newParents = (this.parents == null ? new JSONArray() : new JSONArray(this.parents.toString()));
newParents.put(getLocation().getPath());
JSONObject submoduleCloneJSON = new Clone().toJSON(path.append(walk.getPath()).addTrailingSeparator(), baseLocation, cloneUrl, newParents);
SubmoduleStatus ss = new SubmoduleStatusCommand(parentRepository).addPath(walk.getModulesPath()).call().values().iterator().next();
if(ss != null){
JSONObject submoduleStatus = new JSONObject();
submoduleStatus.put(ProtocolConstants.KEY_TYPE, ss.getType().toString());
submoduleStatus.put(ProtocolConstants.KEY_PATH, ss.getPath());
if(ss.getHeadId()!=null){
submoduleStatus.put(GitConstants.KEY_HEAD_SHA, ss.getHeadId().getName());
}
submoduleCloneJSON.put(GitConstants.KEY_SUBMODULE_STATUS, submoduleStatus);
}
submodules.put(submoduleCloneJSON);
}
walk.close();
submodules = submodules.length() > 0 ? submodules : null;
} catch (ConfigInvalidException e) {
} catch (IOException e) {
// ignore and skip Git URL
} finally {
if (parentRepository != null) {
parentRepository.close();
}
}
}
return submodules;
}
@PropertyDescription(name = ProtocolConstants.KEY_PARENTS)
private JSONArray getParents() throws URISyntaxException, IOException, CoreException {
return this.parents;
}
@PropertyDescription(name = GitConstants.KEY_URL)
private String getCloneUrl() {
return cloneUrl;
}
private URI createUriWithPath(final IPath path) throws URISyntaxException {
return new URI(baseLocation.getScheme(), baseLocation.getUserInfo(), baseLocation.getHost(), baseLocation.getPort(), path.toString(),
baseLocation.getQuery(), baseLocation.getFragment());
}
public JSONObject toJSON(IPath path, URI baseLocation, String cloneUrl) throws IOException, URISyntaxException {
return toJSON(path, baseLocation, cloneUrl, null);
}
public JSONObject toJSON(IPath path, URI baseLocation, String cloneUrl, JSONArray parents) throws IOException, URISyntaxException {
id = Activator.LOCATION_FILE_SERVLET + '/' + path.toString();
name = path.lastSegment();
this.path = path;
this.cloneUrl = cloneUrl;
this.baseLocation = baseLocation;
this.parents = parents;
return toJSON();
}
}