com.google.gerrit.server.change.GetPatch.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.server.change.GetPatch.java

Source

// Copyright (C) 2013 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.server.change;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;

import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Option;

import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class GetPatch implements RestReadView<RevisionResource> {
    private final GitRepositoryManager repoManager;

    @Option(name = "--zip")
    private boolean zip;

    @Option(name = "--download")
    private boolean download;

    @Inject
    GetPatch(GitRepositoryManager repoManager) {
        this.repoManager = repoManager;
    }

    @Override
    public BinaryResult apply(RevisionResource rsrc) throws ResourceConflictException, IOException {
        Project.NameKey project = rsrc.getControl().getProject().getNameKey();
        final Repository repo = repoManager.openRepository(project);
        boolean close = true;
        try {
            final RevWalk rw = new RevWalk(repo);
            try {
                final RevCommit commit = rw
                        .parseCommit(ObjectId.fromString(rsrc.getPatchSet().getRevision().get()));
                RevCommit[] parents = commit.getParents();
                if (parents.length > 1) {
                    throw new ResourceConflictException("Revision has more than 1 parent.");
                } else if (parents.length == 0) {
                    throw new ResourceConflictException("Revision has no parent.");
                }
                final RevCommit base = parents[0];
                rw.parseBody(base);

                BinaryResult bin = new BinaryResult() {
                    @Override
                    public void writeTo(OutputStream out) throws IOException {
                        if (zip) {
                            ZipOutputStream zos = new ZipOutputStream(out);
                            ZipEntry e = new ZipEntry(fileName(rw, commit));
                            e.setTime(commit.getCommitTime() * 1000L);
                            zos.putNextEntry(e);
                            format(zos);
                            zos.closeEntry();
                            zos.finish();
                        } else {
                            format(out);
                        }
                    }

                    private void format(OutputStream out) throws IOException {
                        out.write(formatEmailHeader(commit).getBytes(UTF_8));
                        try (DiffFormatter fmt = new DiffFormatter(out)) {
                            fmt.setRepository(repo);
                            fmt.format(base.getTree(), commit.getTree());
                            fmt.flush();
                        }
                    }

                    @Override
                    public void close() throws IOException {
                        rw.close();
                        repo.close();
                    }
                };

                if (zip) {
                    bin.disableGzip().setContentType("application/zip")
                            .setAttachmentName(fileName(rw, commit) + ".zip");
                } else {
                    bin.base64().setContentType("application/mbox")
                            .setAttachmentName(download ? fileName(rw, commit) + ".base64" : null);
                }

                close = false;
                return bin;
            } finally {
                if (close) {
                    rw.close();
                }
            }
        } finally {
            if (close) {
                repo.close();
            }
        }
    }

    private static String formatEmailHeader(RevCommit commit) {
        StringBuilder b = new StringBuilder();
        PersonIdent author = commit.getAuthorIdent();
        String subject = commit.getShortMessage();
        String msg = commit.getFullMessage().substring(subject.length());
        if (msg.startsWith("\n\n")) {
            msg = msg.substring(2);
        }
        b.append("From ").append(commit.getName()).append(' ').append("Mon Sep 17 00:00:00 2001\n") // Fixed timestamp to match output of C Git's format-patch
                .append("From: ").append(author.getName()).append(" <").append(author.getEmailAddress())
                .append(">\n").append("Date: ").append(formatDate(author)).append('\n').append("Subject: [PATCH] ")
                .append(subject).append('\n').append('\n').append(msg);
        if (!msg.endsWith("\n")) {
            b.append('\n');
        }
        return b.append("---\n\n").toString();
    }

    private static String formatDate(PersonIdent author) {
        SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
        df.setCalendar(Calendar.getInstance(author.getTimeZone(), Locale.US));
        return df.format(author.getWhen());
    }

    private static String fileName(RevWalk rw, RevCommit commit) throws IOException {
        AbbreviatedObjectId id = rw.getObjectReader().abbreviate(commit, 8);
        return id.name() + ".diff";
    }
}