com.microsoft.alm.plugin.idea.common.actions.OpenFileInBrowserAction.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.alm.plugin.idea.common.actions.OpenFileInBrowserAction.java

Source

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.plugin.idea.common.actions;

import com.intellij.ide.BrowserUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.microsoft.alm.common.utils.UrlHelper;
import com.microsoft.alm.plugin.idea.common.resources.Icons;
import com.microsoft.alm.plugin.idea.common.resources.TfPluginBundle;
import com.microsoft.alm.plugin.idea.git.utils.TfGitHelper;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;

public class OpenFileInBrowserAction extends InstrumentedAction {

    private static final Logger logger = LoggerFactory.getLogger(OpenFileInBrowserAction.class);

    protected OpenFileInBrowserAction() {
        super(TfPluginBundle.message(TfPluginBundle.KEY_ACTIONS_OPEN_BROWSER),
                TfPluginBundle.message(TfPluginBundle.KEY_ACTIONS_OPEN_BROWSER_MSG), Icons.VSLogoSmall,
                false /*uses git.exe*/);
    }

    @Override
    public void doUpdate(@NotNull final AnActionEvent anActionEvent) {
        final Presentation presentation = anActionEvent.getPresentation();
        final Project project = anActionEvent.getProject();
        if (project == null || project.isDisposed()) {
            presentation.setEnabledAndVisible(false);
            return;
        }

        final VirtualFile[] vFiles = anActionEvent.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY);
        if (vFiles == null || vFiles.length == 0 || vFiles[0] == null) {
            // quick exit if no valid file selected
            presentation.setEnabledAndVisible(false);
            return;
        } else if (vFiles.length > 1) {
            // only supporting one file for now, so disable the action
            presentation.setEnabled(false);

            // however we do want to leave a breadcrumb if any of the files selected individually are valid
            final GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
            for (VirtualFile vFile : vFiles) {
                final GitRepository repository = manager.getRepositoryForFile(vFile);
                if (repository != null && TfGitHelper.isTfGitRepository(repository)) {
                    // show the action if any of the files are TF
                    presentation.setVisible(true);
                    return;
                }
            }

            // no valid selection, hide the action.
            presentation.setVisible(false);
            return;
        }

        final VirtualFile vFile = vFiles[0];

        final GitRepositoryManager manager = GitUtil.getRepositoryManager(project);

        final GitRepository repository = manager.getRepositoryForFile(vFile);

        if (repository == null || !TfGitHelper.isTfGitRepository(repository)) {
            presentation.setEnabledAndVisible(false);
            return;
        }

        final ChangeListManager changeListManager = ChangeListManager.getInstance(project);

        // ignored via .gitignore
        if (changeListManager.isIgnoredFile(vFile)) {
            presentation.setEnabledAndVisible(false);
            return;
        }

        // OK show the action
        presentation.setVisible(true);

        // Now check if should be enabled
        presentation.setEnabled(isEnabled(changeListManager, project, vFile));
    }

    /**
     * Returns true if the action should be enabled, false otherwise
     *
     * @param project, vFile
     * @return
     */
    private boolean isEnabled(final ChangeListManager changeListManager, final Project project,
            final VirtualFile vFile) {
        if (vFile.isDirectory()) {
            /* Empty directories are not yet supported by Git.  Recursive scanning works, but could be error prone
               (e.g.) symbolic links.  Approach here is to always show it. */
            // TODO we may want to revisit this approach
            return true;
        }

        final GitVcs vcs = GitVcs.getInstance(project);

        if (!ProjectLevelVcsManager.getInstance(project).checkAllFilesAreUnder(vcs, new VirtualFile[] { vFile })) {
            return false;
        }

        if (changeListManager.isUnversioned(vFile)) {
            return false;
        }

        final Change change = changeListManager.getChange(vFile);
        if (change != null && change.getType() == Change.Type.NEW) {
            // a new file that has not yet been checked in
            return false;
        }

        return true;
    }

    @Override
    public void doActionPerformed(final AnActionEvent anActionEvent) {
        final Project project = anActionEvent.getRequiredData(CommonDataKeys.PROJECT);
        final VirtualFile virtualFile = anActionEvent.getRequiredData(CommonDataKeys.VIRTUAL_FILE);

        final GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
        final GitRepository gitRepository = manager.getRepositoryForFile(virtualFile);
        final GitRemote gitRemote = TfGitHelper.getTfGitRemote(gitRepository);

        // guard for null so findbugs doesn't complain
        if (gitRemote == null || gitRepository == null || gitRepository.getRoot() == null) {
            return;
        }

        final String rootPath = gitRepository.getRoot().getPath();
        final String path = virtualFile.getPath();
        final String relativePath = path.substring(rootPath.length());

        String gitRemoteBranchName = StringUtils.EMPTY;
        final GitLocalBranch gitLocalBranch = gitRepository.getCurrentBranch();
        if (gitLocalBranch != null) {
            final GitRemoteBranch gitRemoteBranch = gitLocalBranch.findTrackedBranch(gitRepository);
            if (gitRemoteBranch != null) {
                gitRemoteBranchName = gitRemoteBranch.getNameForRemoteOperations();
            }
        }

        final URI urlToBrowseTo = UrlHelper.getFileURI(gitRemote.getFirstUrl(), encodeVirtualFilePath(relativePath),
                gitRemoteBranchName);
        logger.info("Browsing to url " + urlToBrowseTo.getPath());
        BrowserUtil.browse(urlToBrowseTo);
    }

    /**
     * Returns a UTF-8 encoded version of the specified virtual file path.
     */
    private String encodeVirtualFilePath(final String virtualFilePath) {
        final String UTF_8 = "UTF-8";

        // ensures we are dealing with '/' separators (they should be used already)
        final String path = FileUtil.toSystemIndependentName(virtualFilePath);
        try {
            return URLEncoder.encode(path, UTF_8);
        } catch (UnsupportedEncodingException e) {
            // eat it; UTF-8 is a required charset and is always present
            logger.warn(virtualFilePath, e);
        }
        return path;
    }

}