com.microsoft.alm.plugin.external.utils.CommandUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.alm.plugin.external.utils.CommandUtils.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.external.utils;

import com.intellij.openapi.project.Project;
import com.microsoft.alm.common.utils.ArgumentHelper;
import com.microsoft.alm.common.utils.SystemHelper;
import com.microsoft.alm.plugin.context.ServerContext;
import com.microsoft.alm.plugin.external.commands.AddCommand;
import com.microsoft.alm.plugin.external.commands.CheckinCommand;
import com.microsoft.alm.plugin.external.commands.Command;
import com.microsoft.alm.plugin.external.commands.CreateBranchCommand;
import com.microsoft.alm.plugin.external.commands.FindConflictsCommand;
import com.microsoft.alm.plugin.external.commands.FindWorkspaceCommand;
import com.microsoft.alm.plugin.external.commands.GetLocalPathCommand;
import com.microsoft.alm.plugin.external.commands.GetWorkspaceCommand;
import com.microsoft.alm.plugin.external.commands.HistoryCommand;
import com.microsoft.alm.plugin.external.commands.InfoCommand;
import com.microsoft.alm.plugin.external.commands.RenameCommand;
import com.microsoft.alm.plugin.external.commands.ResolveConflictsCommand;
import com.microsoft.alm.plugin.external.commands.StatusCommand;
import com.microsoft.alm.plugin.external.commands.SyncCommand;
import com.microsoft.alm.plugin.external.commands.UndoCommand;
import com.microsoft.alm.plugin.external.commands.UpdateWorkspaceCommand;
import com.microsoft.alm.plugin.external.commands.UpdateWorkspaceMappingCommand;
import com.microsoft.alm.plugin.external.models.ChangeSet;
import com.microsoft.alm.plugin.external.models.Conflict;
import com.microsoft.alm.plugin.external.models.ConflictResults;
import com.microsoft.alm.plugin.external.models.ItemInfo;
import com.microsoft.alm.plugin.external.models.PendingChange;
import com.microsoft.alm.plugin.external.models.RenameConflict;
import com.microsoft.alm.plugin.external.models.ServerStatusType;
import com.microsoft.alm.plugin.external.models.SyncResults;
import com.microsoft.alm.plugin.external.models.Workspace;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Helper for running commands
 */
public class CommandUtils {
    protected static final Logger logger = LoggerFactory.getLogger(CommandUtils.class);

    /**
     * This method will return just the workspace name or empty string (never null)
     *
     * @param context
     * @param project
     * @return
     */
    public static String getWorkspaceName(final ServerContext context, final Project project) {
        ArgumentHelper.checkNotNull(project, "project");
        final FindWorkspaceCommand command = new FindWorkspaceCommand(project.getBasePath());
        final Workspace workspace = command.runSynchronously();
        if (workspace != null) {
            return workspace.getName();
        }
        return StringUtils.EMPTY;
    }

    /**
     * This method determines the workspace name from the project and then calls getWorkspace with the name.
     *
     * @param context
     * @param project
     * @return
     */
    public static Workspace getWorkspace(final ServerContext context, final Project project) {
        final String workspaceName = getWorkspaceName(context, project);
        return getWorkspace(context, workspaceName);
    }

    /**
     * This method returns the fully filled out Workspace object.
     *
     * @param context
     * @param workspaceName
     * @return
     */
    public static Workspace getWorkspace(final ServerContext context, final String workspaceName) {
        final GetWorkspaceCommand command = new GetWorkspaceCommand(context, workspaceName);
        return command.runSynchronously();
    }

    public static String getLocalPathSynchronously(final ServerContext context, final String serverPath,
            final String workspace) {
        final Command<String> getLocalPathCommand = new GetLocalPathCommand(context, serverPath, workspace);
        return getLocalPathCommand.runSynchronously();
    }

    public static String tryGetLocalPath(final ServerContext context, final String serverPath,
            final String workspace) {
        final Command<String> getLocalPathCommand = new GetLocalPathCommand(context, serverPath, workspace);
        try {
            final String result = getLocalPathCommand.runSynchronously();
            if (StringUtils.startsWithIgnoreCase(result, "ERROR [main] Application - Unexpected exception:")) {
                return null;
            }
            return result;
        } catch (Throwable t) {
            logger.warn("Failed to find local path for server path " + serverPath, t);
            return null;
        }
    }

    public static List<ChangeSet> getHistoryCommand(final ServerContext context, final String itemPath,
            final String version, final int stopAfter, final boolean recursive, final String user) {
        return getHistoryCommand(context, itemPath, version, stopAfter, recursive, user, false);
    }

    public static List<ChangeSet> getHistoryCommand(final ServerContext context, final String itemPath,
            final String version, final int stopAfter, final boolean recursive, final String user,
            final boolean itemMode) {
        final Command<List<ChangeSet>> historyCommand = new HistoryCommand(context, itemPath, version, stopAfter,
                recursive, user, itemMode);
        return historyCommand.runSynchronously();
    }

    public static ChangeSet getLastHistoryEntryForAnyUser(final ServerContext context, final String localPath) {
        final List<ChangeSet> results = getHistoryCommand(context, localPath, null, 1, false, StringUtils.EMPTY);
        return results.isEmpty() ? null : results.get(0);
    }

    /**
     * Adds a workspace mapping to the workspace named
     *
     * @param serverContext
     * @param workspaceName
     * @param serverPath
     * @param localPath
     */
    public static String addWorkspaceMapping(final ServerContext serverContext, final String workspaceName,
            final String serverPath, final String localPath) {
        final UpdateWorkspaceMappingCommand updateMappingCommand = new UpdateWorkspaceMappingCommand(serverContext,
                workspaceName, new Workspace.Mapping(serverPath, localPath, false), false);
        return updateMappingCommand.runSynchronously();
    }

    /**
     * This command updates the properies of the workspace as well as the mappings.
     * There are many commands that go into the update, not just a single call.
     * If anything goes wrong, an exception will be thrown.
     * Note: this method does NOT sync the workspace.
     *
     * @param context
     * @param oldWorkspace
     * @param newWorkspace
     */
    public static String updateWorkspace(final ServerContext context, final Workspace oldWorkspace,
            final Workspace newWorkspace) {
        // No need to update the mappings if they are the same
        if (WorkspaceHelper.areMappingsDifferent(oldWorkspace, newWorkspace)) {
            // First remove the mappings that are no longer needed
            for (final Workspace.Mapping m : WorkspaceHelper.getMappingsToRemove(oldWorkspace, newWorkspace)) {
                final UpdateWorkspaceMappingCommand command = new UpdateWorkspaceMappingCommand(context,
                        oldWorkspace.getName(), m, true);
                command.runSynchronously();
            }

            // Now update the mappings to match the new workspace
            for (final Workspace.Mapping m : WorkspaceHelper.getMappingsToChange(oldWorkspace, newWorkspace)) {
                final UpdateWorkspaceMappingCommand command = new UpdateWorkspaceMappingCommand(context,
                        oldWorkspace.getName(), m, false);
                command.runSynchronously();
            }
        }

        // Finally update the properties of the workspace
        final UpdateWorkspaceCommand updateWorkspaceCommand = new UpdateWorkspaceCommand(context,
                oldWorkspace.getName(), newWorkspace.getName(), newWorkspace.getComment(), null, null);
        return updateWorkspaceCommand.runSynchronously();
    }

    /**
     * This method Syncs the workspace based on the root path recursively.
     * This is a synchronous call so it should only be called on a background thread.
     */
    public static SyncResults syncWorkspace(final ServerContext context, final String rootPath) {
        return syncWorkspace(context, Collections.singletonList(rootPath), true);
    }

    public static SyncResults syncWorkspace(final ServerContext context, final List<String> filesUpdatePaths,
            final boolean needRecursion) {
        final SyncCommand command = new SyncCommand(context, filesUpdatePaths, needRecursion);
        return command.runSynchronously();
    }

    /**
     * This method undoes the list of local files passed in.
     * This is a synchronous call so it should only be called on a background thread.
     */
    public static List<String> undoLocalFiles(final ServerContext context, final List<String> files) {
        final UndoCommand command = new UndoCommand(context, files);
        return command.runSynchronously();
    }

    /**
     * Get the status for a single file
     *
     * @param context
     * @param file
     * @return
     */
    public static PendingChange getStatusForFile(final ServerContext context, final String file) {
        final Command<List<PendingChange>> command = new StatusCommand(context, file);
        final List<PendingChange> results = command.runSynchronously();
        return results.isEmpty() ? null : results.get(0);
    }

    /**
     * Renames a file
     *
     * @param context
     * @param oldName
     * @param newName
     */
    public static void renameFile(final ServerContext context, final String oldName, final String newName) {
        final Command<String> command = new RenameCommand(context, oldName, newName);
        command.runSynchronously();
    }

    /**
     * Resolves conflicts with the given resolution type
     *
     * @param context
     * @param conflicts
     * @param type
     * @return
     */
    public static List<Conflict> resolveConflictsByPath(final ServerContext context, final List<String> conflicts,
            final ResolveConflictsCommand.AutoResolveType type) {
        final Command<List<Conflict>> conflictsCommand = new ResolveConflictsCommand(context, conflicts, type);
        return conflictsCommand.runSynchronously();
    }

    public static List<Conflict> resolveConflictsByConflict(final ServerContext context,
            final List<Conflict> conflicts, final ResolveConflictsCommand.AutoResolveType type) {
        final List<String> conflictFiles = new ArrayList<String>();
        for (final Conflict conflict : conflicts) {
            conflictFiles.add(conflict.getLocalPath());
        }

        return resolveConflictsByPath(context, conflictFiles, type);
    }

    /**
     * Finds the conflicts under a given directory
     *
     * @param context
     * @param root
     * @return
     */
    public static List<Conflict> getConflicts(final ServerContext context, final String root) {
        final List<Conflict> conflicts = new ArrayList<Conflict>();

        final Command<ConflictResults> conflictsCommand = new FindConflictsCommand(context, root);
        final ConflictResults conflictResults = conflictsCommand.runSynchronously();

        for (final String contentConflict : conflictResults.getContentConflicts()) {
            conflicts.add(new Conflict(contentConflict, Conflict.ConflictType.CONTENT));
        }

        for (final String renameConflict : conflictResults.getRenameConflicts()) {
            final RenameConflict rename = findLocalRename(context, renameConflict, root,
                    Conflict.ConflictType.RENAME);
            if (rename != null) {
                conflicts.add(rename);
            }
        }

        for (final String bothConflict : conflictResults.getBothConflicts()) {
            final RenameConflict rename = findLocalRename(context, bothConflict, root, Conflict.ConflictType.BOTH);
            if (rename != null) {
                conflicts.add(rename);
            }
        }

        return conflicts;
    }

    /**
     * For rename conflicts, find the old name and local name of the file by looking for the last rename entry in the
     * history. Look at the last 50 history entries first and if not found there look at all the history
     *
     * @param context
     * @param serverName
     * @param root
     * @param type
     * @return
     */
    private static RenameConflict findLocalRename(final ServerContext context, final String serverName,
            final String root, final Conflict.ConflictType type) {
        final RenameConflict conflict = searchChangeSetForRename(context, serverName, root, type, 50);

        // return conflict if found, else do a search on all of the history (-1 will not add a stopAfter parameter to cmd)
        return conflict != null ? conflict : searchChangeSetForRename(context, serverName, root, type, -1);
    }

    private static RenameConflict searchChangeSetForRename(final ServerContext context, final String serverName,
            final String root, final Conflict.ConflictType type, final int stopAfter) {
        final List<ChangeSet> changeSets = CommandUtils.getHistoryCommand(context, serverName, StringUtils.EMPTY,
                stopAfter, false, StringUtils.EMPTY, true);

        // step through most current changesets to find the one that did the rename
        for (int index = 0; index < changeSets.size(); index++) {
            final ChangeSet changeSet = changeSets.get(index);
            if (doesChangeSetHaveChanges(changeSets, index)
                    && changeSet.getChanges().get(0).getChangeTypes().contains(ServerStatusType.RENAME)) {
                // the entry after the rename contains the old name of the file
                if (doesChangeSetHaveChanges(changeSets, index + 1)) {
                    final String oldName = changeSets.get(index + 1).getChanges().get(0).getServerItem();

                    // parse local changes for the old file name to get the new local name
                    final Command<List<PendingChange>> command = new StatusCommand(context, root);
                    final List<PendingChange> results = command.runSynchronously();

                    for (final PendingChange change : results) {
                        if (SystemHelper.areFilePathsSame(change.getSourceItem(), oldName)) {
                            return new RenameConflict(change.getLocalItem(), serverName, oldName, type);
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Checks that a changeset in the list contains a change
     *
     * @param changeSets
     * @param index
     * @return
     */
    private static boolean doesChangeSetHaveChanges(final List<ChangeSet> changeSets, final int index) {
        if (changeSets == null || index >= changeSets.size() || changeSets.get(index).getChanges() == null
                || changeSets.get(index).getChanges().isEmpty()) {
            return false;
        }

        return true;
    }

    /**
     * Adds the given files to the repo
     *
     * @param context
     * @param filesToAddPaths
     * @return
     */
    public static List<String> addFiles(final ServerContext context, final List<String> filesToAddPaths) {
        final Command<List<String>> addCommand = new AddCommand(context, filesToAddPaths);
        return addCommand.runSynchronously();
    }

    /**
     * Checks in the list of files
     *
     * @param context
     * @param files
     * @param preparedComment
     * @return
     */
    public static String checkinFiles(final ServerContext context, final List<String> files,
            final String preparedComment, final List<Integer> workItemsToAssociate) {
        final Command<String> checkinCommand = new CheckinCommand(context, files, preparedComment,
                workItemsToAssociate);
        return checkinCommand.runSynchronously();
    }

    /**
     * Returns the item info for a single item.
     *
     * @param context
     * @param itemPath
     * @return
     */
    public static ItemInfo getItemInfo(final ServerContext context, final String itemPath) {
        final Command<List<ItemInfo>> infoCommand = new InfoCommand(context, Collections.singletonList(itemPath));
        List<ItemInfo> items = infoCommand.runSynchronously();
        if (items != null && items.size() > 0) {
            return items.get(0);
        }

        throw new RuntimeException("No items match " + itemPath);
    }

    public static String createBranch(final ServerContext context, final String workingFolder,
            final boolean recursive, final String comment, final String author, final String existingItem,
            final String newBranchedItem) {
        final CreateBranchCommand createBranchCommand = new CreateBranchCommand(context, workingFolder, recursive,
                comment, author, existingItem, newBranchedItem);
        return createBranchCommand.runSynchronously();
    }
}