org.eclipse.orion.server.git.objects.Commit.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.orion.server.git.objects.Commit.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2012 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.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.*;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.*;
import org.eclipse.orion.internal.server.servlets.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.core.users.UserUtilities;
import org.eclipse.orion.server.git.BaseToCommitConverter;
import org.eclipse.orion.server.git.GitConstants;
import org.eclipse.orion.server.git.servlets.GitServlet;
import org.json.*;

@ResourceDescription(type = Commit.TYPE)
public class Commit extends GitObject {

    public static final String RESOURCE = "commit"; //$NON-NLS-1$
    public static final String TYPE = "Commit"; //$NON-NLS-1$

    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_CONTENT_LOCATION), //
                new Property(GitConstants.KEY_DIFF), //
                new Property(ProtocolConstants.KEY_NAME), //
                new Property(GitConstants.KEY_AUTHOR_NAME), //
                new Property(GitConstants.KEY_AUTHOR_EMAIL), //
                new Property(GitConstants.KEY_AUTHOR_IMAGE), //
                new Property(GitConstants.KEY_COMMITTER_NAME), //
                new Property(GitConstants.KEY_COMMITTER_EMAIL), //
                new Property(GitConstants.KEY_COMMIT_TIME), //
                new Property(GitConstants.KEY_COMMIT_MESSAGE), //
                new Property(GitConstants.KEY_TAGS), //
                new Property(GitConstants.KEY_BRANCHES), //
                new Property(ProtocolConstants.KEY_PARENTS), //
                new Property(GitConstants.KEY_COMMIT_DIFFS) };
        DEFAULT_RESOURCE_SHAPE.setProperties(defaultProperties);
    }

    private RevCommit revCommit;
    private String pattern;
    private TreeFilter filter;
    /**
     * Whether this is a commit at the root of the repository, or only a
     * particular path (git commit -o {path}).
     */
    private boolean isRoot = true;
    private Map<ObjectId, JSONArray> commitToBranchMap;

    public Commit(URI cloneLocation, Repository db, RevCommit revCommit, String pattern) {
        super(cloneLocation, db);
        this.revCommit = revCommit;
        this.pattern = pattern;
        if (pattern != null && !pattern.isEmpty()) {
            filter = AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(pattern)),
                    TreeFilter.ANY_DIFF);
            isRoot = false;
        }
    }

    public void setCommitToBranchMap(Map<ObjectId, JSONArray> map) {
        this.commitToBranchMap = map;
    }

    public Map<ObjectId, JSONArray> getCommitToBranchMap()
            throws GitAPIException, JSONException, URISyntaxException, IOException, CoreException {
        if (commitToBranchMap == null)
            commitToBranchMap = Log.getCommitToBranchMap(cloneLocation, db);
        return commitToBranchMap;
    }

    /**
     * Return body of the commit
     *
     * @return body of the commit as an Object Stream
     * @throws IOException when reading the object failed
     */
    public ObjectStream toObjectStream() throws IOException {
        final TreeWalk w = TreeWalk.forPath(db, pattern, revCommit.getTree());
        if (w == null) {
            return null;
        }
        ObjectId blobId = w.getObjectId(0);
        return db.open(blobId, Constants.OBJ_BLOB).openStream();
    }

    @Override
    public JSONObject toJSON() throws JSONException, URISyntaxException, IOException, CoreException {
        return jsonSerializer.serialize(this, DEFAULT_RESOURCE_SHAPE);
    }

    @PropertyDescription(name = ProtocolConstants.KEY_CONTENT_LOCATION)
    private URI getContentLocation() throws URISyntaxException {
        if (!isRoot) // linking to body makes only sense for files
            return BaseToCommitConverter.getCommitLocation(cloneLocation, revCommit.getName(), pattern,
                    BaseToCommitConverter.REMOVE_FIRST_2.setQuery("parts=body")); //$NON-NLS-1$      
        return null; // in the eventuality of null, the property won't be added
    }

    // TODO: expandable
    @PropertyDescription(name = GitConstants.KEY_DIFF)
    private URI getDiffLocation() throws URISyntaxException {
        return createDiffLocation(revCommit.getName(), null, pattern);
    }

    @PropertyDescription(name = ProtocolConstants.KEY_NAME)
    private String getName() {
        return revCommit.getName();
    }

    @PropertyDescription(name = GitConstants.KEY_AUTHOR_NAME)
    private String getAuthorName() {
        PersonIdent author = revCommit.getAuthorIdent();
        return author.getName();
    }

    @PropertyDescription(name = GitConstants.KEY_AUTHOR_EMAIL)
    private String getAuthorEmail() {
        PersonIdent author = revCommit.getAuthorIdent();
        return author.getEmailAddress();
    }

    @PropertyDescription(name = GitConstants.KEY_AUTHOR_IMAGE)
    private String getAuthorImage() {
        PersonIdent author = revCommit.getAuthorIdent();
        return UserUtilities.getImageLink(author.getEmailAddress()); // can be null
    }

    @PropertyDescription(name = GitConstants.KEY_COMMITTER_NAME)
    private String getCommitterName() {
        PersonIdent committer = revCommit.getCommitterIdent();
        return committer.getName();
    }

    @PropertyDescription(name = GitConstants.KEY_COMMITTER_EMAIL)
    private String getCommitterEmail() {
        PersonIdent committer = revCommit.getCommitterIdent();
        return committer.getEmailAddress();
    }

    @PropertyDescription(name = GitConstants.KEY_COMMIT_TIME)
    private long getCommitTime() {
        return ((long) revCommit.getCommitTime()) * 1000 /* time in milliseconds */;
    }

    @PropertyDescription(name = GitConstants.KEY_COMMIT_MESSAGE)
    private String getCommitMessiage() {
        return revCommit.getFullMessage();
    }

    // TODO: expandable
    @PropertyDescription(name = GitConstants.KEY_TAGS)
    private JSONArray getTags()
            throws MissingObjectException, JSONException, URISyntaxException, CoreException, IOException {
        return toJSON(getTagsForCommit());
    }

    // TODO: expandable
    @PropertyDescription(name = GitConstants.KEY_BRANCHES)
    private JSONArray getBranches()
            throws JSONException, GitAPIException, URISyntaxException, IOException, CoreException {
        return getCommitToBranchMap().get(revCommit.getId());
    }

    // TODO: expandable?
    @PropertyDescription(name = ProtocolConstants.KEY_PARENTS)
    private JSONArray getParents() throws JSONException, URISyntaxException, IOException, CoreException {
        return parentsToJSON(revCommit.getParents());
    }

    // TODO: expandable
    @PropertyDescription(name = GitConstants.KEY_COMMIT_DIFFS)
    private JSONArray getDiffs() throws JSONException, URISyntaxException, MissingObjectException,
            IncorrectObjectTypeException, IOException {
        if (revCommit.getParentCount() > 0) {
            JSONArray diffs = new JSONArray();

            final TreeWalk tw = new TreeWalk(db);
            final RevWalk rw = new RevWalk(db);
            RevCommit parent = rw.parseCommit(revCommit.getParent(0));
            tw.reset(parent.getTree(), revCommit.getTree());
            tw.setRecursive(true);

            if (filter != null)
                tw.setFilter(filter);
            else
                tw.setFilter(TreeFilter.ANY_DIFF);

            List<DiffEntry> l = DiffEntry.scan(tw);
            for (DiffEntry entr : l) {
                JSONObject diff = new JSONObject();
                diff.put(ProtocolConstants.KEY_TYPE, org.eclipse.orion.server.git.objects.Diff.TYPE);
                diff.put(GitConstants.KEY_COMMIT_DIFF_NEWPATH, entr.getNewPath());
                diff.put(GitConstants.KEY_COMMIT_DIFF_OLDPATH, entr.getOldPath());
                diff.put(GitConstants.KEY_COMMIT_DIFF_CHANGETYPE, entr.getChangeType().toString());

                // add diff location for the commit
                String path = entr.getChangeType() != ChangeType.DELETE ? entr.getNewPath() : entr.getOldPath();
                diff.put(GitConstants.KEY_DIFF,
                        createDiffLocation(revCommit.getName(), revCommit.getParent(0).getName(), path));
                diff.put(ProtocolConstants.KEY_CONTENT_LOCATION, createContentLocation(entr, path));

                diffs.put(diff);
            }
            tw.release();

            return diffs;
        }
        return null;
    }

    private JSONArray toJSON(Map<String, Ref> revTags)
            throws JSONException, URISyntaxException, CoreException, IOException {
        JSONArray children = new JSONArray();
        for (Entry<String, Ref> revTag : revTags.entrySet()) {
            Tag tag = new Tag(cloneLocation, db, revTag.getValue());
            children.put(tag.toJSON());
        }
        return children;
    }

    @Override
    protected URI getLocation() throws URISyntaxException {
        return BaseToCommitConverter.getCommitLocation(cloneLocation, revCommit.getName(), pattern,
                BaseToCommitConverter.REMOVE_FIRST_2);
    }

    private Map<String, Ref> getTagsForCommit() throws MissingObjectException, IOException {
        final Map<String, Ref> tags = new HashMap<String, Ref>();
        for (final Entry<String, Ref> tag : db.getTags().entrySet()) {
            Ref ref = db.peel(tag.getValue());
            ObjectId refId = ref.getPeeledObjectId();
            if (refId == null)
                refId = ref.getObjectId();
            if (!AnyObjectId.equals(refId, revCommit))
                continue;
            tags.put(tag.getKey(), tag.getValue());
        }
        return tags;
    }

    private URI createDiffLocation(String toRefId, String fromRefId, String path) throws URISyntaxException {
        IPath diffPath = new Path(GitServlet.GIT_URI).append(Diff.RESOURCE);

        //diff range format is [fromRef..]toRef
        String diffRange = ""; //$NON-NLS-1$
        if (fromRefId != null)
            diffRange = fromRefId + ".."; //$NON-NLS-1$
        diffRange += toRefId;
        diffPath = diffPath.append(diffRange);

        //clone location is of the form /gitapi/clone/file/{workspaceId}/{projectName}[/{path}]
        IPath clonePath = new Path(cloneLocation.getPath()).removeFirstSegments(2);
        if (path == null) {
            diffPath = diffPath.append(clonePath);
        } else if (isRoot) {
            diffPath = diffPath.append(clonePath).append(path);
        } else {
            //need to start from the project root
            //project path is of the form /file/{workspaceId}/{projectName}
            IPath projectRoot = clonePath.uptoSegment(3);
            diffPath = diffPath.append(projectRoot).append(path);
        }

        return new URI(cloneLocation.getScheme(), cloneLocation.getAuthority(), diffPath.toString(), null, null);
    }

    private URI createContentLocation(final DiffEntry entr, String path) throws URISyntaxException {
        //remove /gitapi/clone from the start of path
        IPath clonePath = new Path(cloneLocation.getPath()).removeFirstSegments(2);
        IPath result;
        if (path == null) {
            result = clonePath;
        } else if (isRoot) {
            result = clonePath.append(path);
        } else {
            //need to start from the project root
            //project path is of the form /file/{workspaceId}/{projectName}
            result = clonePath.uptoSegment(3).append(path);
        }
        return new URI(cloneLocation.getScheme(), cloneLocation.getUserInfo(), cloneLocation.getHost(),
                cloneLocation.getPort(), result.makeAbsolute().toString(), cloneLocation.getQuery(),
                cloneLocation.getFragment());
    }

    private JSONArray parentsToJSON(RevCommit[] revCommits) throws JSONException, IOException, URISyntaxException {
        JSONArray parents = new JSONArray();
        for (RevCommit revCommit : revCommits) {
            JSONObject parent = new JSONObject();
            parent.put(ProtocolConstants.KEY_NAME, revCommit.getName());
            parent.put(ProtocolConstants.KEY_LOCATION, BaseToCommitConverter.getCommitLocation(cloneLocation,
                    revCommit.getName(), pattern, BaseToCommitConverter.REMOVE_FIRST_2));
            parents.put(parent);
        }
        return parents;
    }
}