com.urswolfer.intellij.plugin.gerrit.extension.GerritCheckoutProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.urswolfer.intellij.plugin.gerrit.extension.GerritCheckoutProvider.java

Source

/*
 * Copyright 2000-2011 JetBrains s.r.o.
 * Copyright 2013 Urs Wolfer
 *
 * 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.urswolfer.intellij.plugin.gerrit.extension;

import com.google.common.io.Files;
import com.google.inject.Inject;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CheckoutProvider;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.urswolfer.intellij.plugin.gerrit.GerritModule;
import com.urswolfer.intellij.plugin.gerrit.GerritSettings;
import com.urswolfer.intellij.plugin.gerrit.rest.GerritApiUtil;
import com.urswolfer.intellij.plugin.gerrit.rest.GerritUtil;
import com.urswolfer.intellij.plugin.gerrit.rest.HttpStatusException;
import com.urswolfer.intellij.plugin.gerrit.rest.bean.ProjectInfo;
import com.urswolfer.intellij.plugin.gerrit.util.NotificationBuilder;
import com.urswolfer.intellij.plugin.gerrit.util.NotificationService;
import git4idea.actions.BasicAction;
import git4idea.checkout.GitCheckoutProvider;
import git4idea.checkout.GitCloneDialog;
import git4idea.commands.Git;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Parts based on org.jetbrains.plugins.github.GithubCheckoutProvider
 *
 * @author oleg
 * @author Urs Wolfer
 */
public class GerritCheckoutProvider implements CheckoutProvider {

    @Inject
    private LocalFileSystem localFileSystem;
    @Inject
    private GerritUtil gerritUtil;
    @Inject
    private GerritSettings gerritSettings;
    @Inject
    private GerritApiUtil gerritApiUtil;
    @Inject
    private Logger log;
    @Inject
    private NotificationService notificationService;

    @Override
    public void doCheckout(@NotNull final Project project, @Nullable final Listener listener) {
        if (!gerritUtil.testGitExecutable(project)) {
            return;
        }
        BasicAction.saveAll();
        List<ProjectInfo> availableProjects = null;
        try {
            availableProjects = gerritUtil.getAvailableProjects(project);
        } catch (Exception e) {
            log.info(e);
            NotificationBuilder notification = new NotificationBuilder(project,
                    "Couldn't get the list of Gerrit repositories", gerritUtil.getErrorTextFromException(e));
            notificationService.notifyError(notification);
        }
        if (availableProjects == null) {
            return;
        }
        Collections.sort(availableProjects, new Comparator<ProjectInfo>() {
            @Override
            public int compare(final ProjectInfo p1, final ProjectInfo p2) {
                return p1.getId().compareTo(p2.getId());
            }
        });

        final GitCloneDialog dialog = new GitCloneDialog(project);
        // Add predefined repositories to history
        for (int i = availableProjects.size() - 1; i >= 0; i--) {
            dialog.prependToHistory(gerritSettings.getHost() + '/' + availableProjects.get(i).getDecodedId());
        }
        dialog.show();
        if (!dialog.isOK()) {
            return;
        }
        dialog.rememberSettings();
        final VirtualFile destinationParent = localFileSystem
                .findFileByIoFile(new File(dialog.getParentDirectory()));
        if (destinationParent == null) {
            return;
        }
        final String sourceRepositoryURL = dialog.getSourceRepositoryURL();
        final String directoryName = dialog.getDirectoryName();
        final String parentDirectory = dialog.getParentDirectory();

        Git git = ServiceManager.getService(Git.class);

        Listener listenerWrapper = addCommitMsgHookListener(listener, directoryName, parentDirectory, project);

        GitCheckoutProvider.clone(project, git, listenerWrapper, destinationParent, sourceRepositoryURL,
                directoryName, parentDirectory);
    }

    @Override
    public String getVcsName() {
        return "Gerrit";
    }

    /*
     * Since this is a listener which needs to be executed in any case, it cannot be a normal checkout-listener.
     * Checkout-listeners only get executed when "previous" listener got not executed (returns false).
     * Example: If user decides to setup a new project from newly created checkout, our listener does not get executed.
     */
    private Listener addCommitMsgHookListener(final Listener listener, final String directoryName,
            final String parentDirectory, final Project project) {
        return new Listener() {
            @Override
            public void directoryCheckedOut(File directory, VcsKey vcs) {
                setupCommitMsgHook(parentDirectory, directoryName, project);

                if (listener != null)
                    listener.directoryCheckedOut(directory, vcs);
            }

            @Override
            public void checkoutCompleted() {
                if (listener != null)
                    listener.checkoutCompleted();
            }
        };
    }

    private void setupCommitMsgHook(String parentDirectory, String directoryName, Project project) {
        try {
            HttpResponse response = gerritApiUtil.doREST("/tools/hooks/commit-msg", null,
                    Collections.<Header>emptyList(), GerritApiUtil.HttpVerb.GET);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new HttpStatusException(response.getStatusLine().getStatusCode(),
                        response.getStatusLine().getReasonPhrase(), response.getStatusLine().getReasonPhrase());
            }
            File targetFile = new File(parentDirectory + '/' + directoryName + "/.git/hooks/commit-msg");
            Files.write(EntityUtils.toString(response.getEntity(), Consts.UTF_8).getBytes(), targetFile);
            targetFile.setExecutable(true);
        } catch (Exception e) {
            log.info(e);
            NotificationBuilder notification = new NotificationBuilder(project,
                    "Couldn't set up Gerrit Commit-Message Hook. Please do it manually.",
                    gerritUtil.getErrorTextFromException(e));
            notificationService.notifyError(notification);
        }
    }

    public static final class Proxy implements CheckoutProvider {
        private final CheckoutProvider delegate;

        public Proxy() {
            delegate = GerritModule.getInstance(GerritCheckoutProvider.class);
        }

        @Override
        public void doCheckout(@NotNull Project project, @Nullable Listener listener) {
            delegate.doCheckout(project, listener);
        }

        @Override
        @NonNls
        public String getVcsName() {
            return delegate.getVcsName();
        }
    }
}