public void run()

in source/com.microsoft.tfs.client.clc/src/com/microsoft/tfs/client/clc/vc/commands/CommandResolve.java [76:390]


    public void run() throws ArgumentException, MalformedURLException, CLCException, LicenseException {
        final WorkspaceInfo cachedWorkspace = determineCachedWorkspace();

        final TFSTeamProjectCollection connection = createConnection();
        final VersionControlClient client = connection.getVersionControlClient();
        initializeClient(client);
        client.getEventEngine().addConflictResolvedListener(this);

        parseResolutionOption();

        final boolean preview = (findOptionType(OptionPreview.class) != null) || resolution == Resolution.NONE;

        if (resolution != Resolution.ACCEPT_MERGE && findOptionType(OptionForce.class) != null) {
            throw new InvalidOptionException(
                Messages.getString("CommandResolve.ForceOnlyValidWithAutoMergeOrExternal")); //$NON-NLS-1$
        }

        Option o = null;
        final ResolutionOptions options = new ResolutionOptions();

        /*
         * Detect if they want to override the encodings with a specific type
         * (which is incompatible with conversion).
         */
        if ((o = findOptionType(OptionOverrideType.class)) != null) {
            reportBadOptionCombinationIfPresent(OptionOverrideType.class, OptionConvertToType.class);

            options.setEncodingStrategy(
                ResolutionOptions.EncodingStrategy.OVERRIDE_EXPLICIT,
                ((OptionOverrideType) o).getValueAsEncoding());

            options.setAcceptMergeEncoding(((OptionOverrideType) o).getValueAsEncoding());
        }

        /*
         * Detect if they want to convert to another encoding.
         */
        if ((o = findOptionType(OptionConvertToType.class)) != null) {
            options.setEncodingStrategy(
                ResolutionOptions.EncodingStrategy.CONVERT_EXPLICIT,
                ((OptionConvertToType) o).getValueAsEncoding());

            options.setAcceptMergeEncoding(((OptionConvertToType) o).getValueAsEncoding());
        }

        options.setUseInternalEngine(useExternalTool == false);
        options.setAcceptMergeWithConflicts(findOptionType(OptionForce.class) != null);

        ExternalToolset mergeToolset = null;
        if (useExternalTool) {
            /*
             * Build a toolset with just one tool: the one the environment
             * variable describes.
             */
            mergeToolset = new ExternalToolset();
            mergeToolset.addAssociation(new ExternalToolAssociation(new String[] {
                ExternalToolset.WILDCARD_EXTENSION
            }, CLCTools.getMergeTool()));
        }

        /*
         * Calculate the path (if the user wants to override).
         */
        String newName = null;

        if ((o = findOptionType(OptionNewName.class)) != null) {
            newName = ((OptionNewName) o).getValue();
        }

        /*
         * Validate the new name against the resolution type and wildcards.
         */
        if (newName != null) {
            if (resolution != Resolution.ACCEPT_YOURS_RENAME_THEIRS && resolution != Resolution.ACCEPT_MERGE) {
                throw new InvalidOptionException(
                    //@formatter:off
                    Messages.getString("CommandResolve.NewnameOnlyValidWithAutoMergeOrExternalOrKeepYoursRenameTheirs")); //$NON-NLS-1$
                    //@formatter:on
            }

            if (getFreeArguments().length > 1 || LocalPath.isWildcard(newName)) {
                throw new InvalidFreeArgumentException(Messages.getString("CommandResolve.OnlyOneConflictWithNewname")); //$NON-NLS-1$
            }

            options.setNewPath(ItemPath.canonicalize(newName));
        } else if (resolution == Resolution.ACCEPT_YOURS_RENAME_THEIRS) {
            throw new InvalidFreeArgumentException(
                Messages.getString("CommandResolve.NewNameRequiredWithKeepYoursRenameTheirs")); //$NON-NLS-1$
        }

        /*
         * Query conflicts for the paths in the free arguments. If no free
         * arguments available, send null to the web service to query the whole
         * workspace.
         */

        String[] resolvePaths = null;
        if (getFreeArguments().length > 0) {
            resolvePaths = new String[getFreeArguments().length];
            for (int i = 0; i < resolvePaths.length; i++) {
                resolvePaths[i] = ItemPath.canonicalize(getFreeArguments()[i]);
            }
        }

        final Workspace workspace = realizeCachedWorkspace(cachedWorkspace, client);

        Conflict[] conflicts = workspace.queryConflicts(resolvePaths, findOptionType(OptionRecursive.class) != null);

        if (conflicts.length == 0) {
            getDisplay().printLine(Messages.getString("CommandResolve.ThereAreNoConflictsToResolve")); //$NON-NLS-1$
            return;
        }

        if (newName != null && conflicts.length != 1) {
            throw new InvalidOptionException(Messages.getString("CommandResolve.OnlyOneConflictWhenNewname")); //$NON-NLS-1$
        }

        if (preview) {
            for (int i = 0; i < conflicts.length; i++) {
                ConflictPrinter.printConflict(conflicts[i], getDisplay(), false);
            }

            setExitCode(ExitCode.PARTIAL_SUCCESS);
            return;
        }

        /*
         * Check symbolic links
         */
        boolean containsSymlink = false;
        for (final Conflict conflict : conflicts) {
            if (!StringUtil.isNullOrEmpty(conflict.getTargetLocalItem())
                && FileSystemUtils.getInstance().getAttributes(conflict.getTargetLocalItem()).isSymbolicLink()) {
                containsSymlink = true;
                break;
            }
        }

        if (containsSymlink
            && !(resolution.equals(Resolution.ACCEPT_THEIRS)
                || resolution.equals(Resolution.ACCEPT_YOURS)
                || resolution.equals(Resolution.OVERWRITE_LOCAL))) {
            getDisplay().printLine(Messages.getString("CommandResolve.SymlinksOnlyAcceptYoursOrTheirs")); //$NON-NLS-1$
            return;
        }

        /*
         * Resolve the conflicts.
         */

        /*
         * Collect the reported and resolved conflicts.
         */
        final Set<Integer> reportedConflictIDs = new HashSet<Integer>();
        final Set<Integer> resolvedConflictIDs = new HashSet<Integer>();

        /*
         * Contains server paths to directories that are conflicts, that were
         * already resolved in the resolution loop, so conflicts underneath
         * these directories can be skipped on that iteration.
         */
        final List<String> resolvedFolders = new ArrayList<String>();

        // Number of conflicts resolved per iteration in the loop.
        int numResolvedConflicts = 0;

        /*
         * We may have to do multiple passes (each outside loop iteration is a
         * pass) if the conflicts are not all resolved in the first pass.
         */
        boolean tryAgain = false;
        do {
            numResolvedConflicts = 0;
            resolvedFolders.clear();

            Conflict[] conflictsResolvedThisIteration = new Conflict[0];

            /*
             * Each pass, iterate over the conflicts starting with the specified
             * resolution and options.
             */
            for (final Conflict conflict : conflicts) {
                conflict.setResolution(resolution);
                conflict.setResolutionOptions(options);

                // There's no point in trying local conflicts without overwrite
                // being specified.
                if (resolution == Resolution.OVERWRITE_LOCAL && conflict.getType() != ConflictType.LOCAL) {
                    continue;
                }

                // If the conflict has already been processed then skip it.
                if (resolvedConflictIDs.contains(conflict.getConflictID())) {
                    continue;
                }

                /**
                 * If this conflict is a child of a folder such that the folder
                 * was both resolved in this pass (sorted top down) and the
                 * folder had a rename or undelete and the resolution is accept
                 * merge and we're likely to do a three-way merge on the child,
                 * we want to wait until we get the updated conflict with the
                 * updated disk location before resolving the conflict.
                 */
                if (conflict.getResolution() == Resolution.ACCEPT_MERGE
                    && conflict.getYourItemType() == ItemType.FILE
                    && conflict.getYourServerItemSource() != null
                    && conflict.canMergeContent()
                    && conflict.getYourEncoding() != FileEncoding.BINARY
                    && resolvedFolders.size() > 0) {
                    boolean affected = false;
                    for (final String directory : resolvedFolders) {
                        if (ServerPath.isChild(directory, conflict.getYourServerItemSource())) {
                            affected = true;
                            break;
                        }
                    }

                    if (affected) {
                        continue;
                    }
                }

                final ProcessFinishedHandler finishedHandler = new MergeToolFinishedHandler(getDisplay());

                /*
                 * Resolve!
                 */
                try {
                    final AtomicReference<Conflict[]> conflictsHolder = new AtomicReference<Conflict[]>();

                    workspace.resolveConflict(
                        conflict,
                        conflictsHolder,
                        null,
                        mergeToolset,
                        finishedHandler,
                        System.out,
                        System.err);

                    conflictsResolvedThisIteration = conflictsHolder.get();
                } catch (final ExternalToolException e) {
                    cleanupAfterFailedMerge(conflict);

                    /*
                     * Rethrow this, because it's fatal.
                     */
                    throw e;
                } catch (final VersionControlException e) {
                    cleanupAfterFailedMerge(conflict);

                    getDisplay().printErrorLine(e.getMessage());
                }

                /*
                 * Add to the lists so we can continue on other conflicts.
                 */
                if (conflict.isResolved()) {
                    /**
                     * Keep track of whether we have successfully resolved
                     * folders with accept merge where it was either rename or
                     * undelete. We don't want to work on the files under of
                     * those folders, because resolving the parent will change
                     * the on-disk location of the files.
                     */
                    if (conflict.getYourItemType() == ItemType.FOLDER
                        && conflict.getYourServerItemSource() != null
                        && conflict.getResolution() == Resolution.ACCEPT_MERGE
                        && (conflict.getBaseChangeType().contains(ChangeType.UNDELETE)
                            || conflict.getBaseChangeType().contains(ChangeType.RENAME))) {
                        resolvedFolders.add(conflict.getYourServerItemSource());
                    }

                    numResolvedConflicts++;
                    for (final Conflict c : conflictsResolvedThisIteration) {
                        resolvedConflictIDs.add(c.getConflictID());
                    }
                } else {
                    if (numResolvedConflicts == 0 && reportedConflictIDs.contains(conflict.getConflictID()) == false) {
                        /*
                         * If we couldn't resolve the conflict then say why.
                         */
                        displayConflictResolveError(conflict);

                        reportedConflictIDs.add(conflict.getConflictID());
                    }
                }
            }

            tryAgain = numResolvedConflicts != 0 && numResolvedConflicts != conflicts.length;

            if (tryAgain) {
                conflicts = workspace.queryConflicts(resolvePaths, findOptionType(OptionRecursive.class) != null);
            }
        } while (tryAgain && conflicts.length != 0);

        final boolean haveUnresolvedLeft = conflicts.length != 0 && conflicts.length != numResolvedConflicts;

        /*
         * Print any conflicts that were not resolved.
         */
        if (haveUnresolvedLeft) {
            for (int i = 0; i < conflicts.length; i++) {
                final Conflict conflict = conflicts[i];

                if (conflict.isResolved() == false && reportedConflictIDs.contains(conflict.getConflictID()) == false) {
                    displayConflictResolveError(conflict);
                }
            }
        }

        if (haveUnresolvedLeft) {
            setExitCode(ExitCode.PARTIAL_SUCCESS);
        }
    }