Exemplo n.º 1
0
 /// <summary>
 /// Perform a three-way merge of documents.
 /// </summary>
 /// <param name="original">Original document.</param>
 /// <param name="left">Left hand modification of document.</param>
 /// <param name="right">Right hand modification of document.</param>
 /// <param name="maxsize">Maximum size of the difference response.</param>
 /// <param name="priority">Merge priority.</param>
 /// <param name="conflict">Output of conflict flag.</param>
 /// <returns>Merged document.</returns>
 public static XDoc Merge(XDoc original, XDoc left, XDoc right, int maxsize, ArrayMergeDiffPriority priority, out bool conflict)
 {
     if (original == null)
     {
         throw new ArgumentNullException("original");
     }
     if (left == null)
     {
         throw new ArgumentNullException("left");
     }
     if (right == null)
     {
         throw new ArgumentNullException("right");
     }
     return(Merge(Tokenize(original), Tokenize(left), Tokenize(right), maxsize, priority, out conflict));
 }
Exemplo n.º 2
0
        /// <summary>
        /// Merge two diffs.
        /// </summary>
        /// <typeparam name="T">Type of value items in the provided diffs.</typeparam>
        /// <param name="left">Left hand diff.</param>
        /// <param name="right">Right hand diff.</param>
        /// <param name="priority">Priority of resolving ambigious <see cref="ArrayDiffKind"/> values.</param>
        /// <param name="equal">Equality delegate for value comparison.</param>
        /// <param name="track">Tracking function for correlating related diff items, in case on of the related items is ambigious.</param>
        /// <param name="hasConflicts">Indicator whether any conflicts were found.</param>
        /// <returns>Diff result of merge.</returns>
        public static Tuple <ArrayDiffKind, T>[] MergeDiff <T>(Tuple <ArrayDiffKind, T>[] left, Tuple <ArrayDiffKind, T>[] right, ArrayMergeDiffPriority priority, Equality <T> equal, Func <T, object> track, out bool hasConflicts) where T : class
        {
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }

            // set a default equality test if none is provided
            if (equal == null)
            {
                equal = delegate(T lhs, T rhs) { return(((lhs == null) && (rhs == null)) || (((lhs != null) && (rhs != null)) && (lhs == rhs))); };
            }

            // loop over all entries and mark them as conflicted when appropriate
            var  result     = new List <Tuple <ArrayDiffKind, T> >(left.Length + right.Length);
            int  leftIndex  = 0;
            int  rightIndex = 0;
            bool ambiguous  = false;

            hasConflicts = false;
            while (true)
            {
                if ((leftIndex < left.Length) && (rightIndex < right.Length))
                {
                    if (left[leftIndex].Item1 == ArrayDiffKind.Same && right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        // NOTE: both sides agree to keep the current element

                        result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Same, left[leftIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                        ambiguous = false;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Same && right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        // NOTE: right-side removed element; this could be conflict

                        result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.RemovedRight, right[rightIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Same && right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: right-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, right[rightIndex].Item2));
                        }
                        ++rightIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed && right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        // NOTE: left-side removed element; this could be conflict

                        result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.RemovedLeft, left[leftIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed && right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        // NOTE: both sides removed the element; subsequent operations are ambiguous

                        result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Removed, left[leftIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                        ambiguous = true;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed && right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: right-side is attempting to add an item after an item that is deleted on the left-side

                        result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        ++rightIndex;
                        ambiguous = true;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added && right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        // NOTE: left-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, left[leftIndex].Item2));
                        }
                        ++leftIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added && right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        // NOTE: left-side is attempting to add an item after an item that is deleted on the right-side

                        result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                        ++leftIndex;
                        ambiguous = true;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added && right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: both sides are adding element; order is ambgiguous, unless it's the same item

                        if (equal(left[leftIndex].Item2, right[rightIndex].Item2))
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, left[leftIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        }
                        ++leftIndex;
                        ++rightIndex;
                        ambiguous = true;
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                }
                else if (leftIndex < left.Length)
                {
                    if (left[leftIndex].Item1 == ArrayDiffKind.Same)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: left-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, left[leftIndex].Item2));
                        }
                    }
                    ++leftIndex;
                }
                else if (rightIndex < right.Length)
                {
                    if (right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: right-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, right[rightIndex].Item2));
                        }
                    }
                    ++rightIndex;
                }
                else
                {
                    break;
                }
            }

            // convert 'RemovedLeft' and 'RemovedRight' if they aren't followed by 'AddedLeft' or 'AddedRight'
            ambiguous = false;
            result.Reverse();
            var modifiedResult = new List <Tuple <ArrayDiffKind, T> >(result.Count);

            foreach (var item in result)
            {
                switch (item.Item1)
                {
                case ArrayDiffKind.Same:
                case ArrayDiffKind.Removed:
                case ArrayDiffKind.Added:
                    ambiguous = false;
                    modifiedResult.Add(item);
                    break;

                case ArrayDiffKind.AddedLeft:
                case ArrayDiffKind.AddedRight:
                    ambiguous    = true;
                    hasConflicts = true;
                    modifiedResult.Add(item);
                    break;

                case ArrayDiffKind.RemovedLeft:
                case ArrayDiffKind.RemovedRight:
                    if (!ambiguous)
                    {
                        modifiedResult.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Removed, item.Item2));
                    }
                    else
                    {
                        hasConflicts = true;
                        modifiedResult.Add(item);
                    }
                    break;
                }
            }
            modifiedResult.Reverse();
            result = modifiedResult;

            // check if there is a merge priority to remove ambiguous changes
            if (priority != ArrayMergeDiffPriority.None)
            {
                // copy only accepted changes
                List <Tuple <ArrayDiffKind, T> > combined = result;
                result = new List <Tuple <ArrayDiffKind, T> >(combined.Count);
                for (int i = 0; i < combined.Count; ++i)
                {
                    switch (combined[i].Item1)
                    {
                    case ArrayDiffKind.Same:
                    case ArrayDiffKind.Removed:
                    case ArrayDiffKind.Added:
                        result.Add(combined[i]);
                        break;

                    case ArrayDiffKind.AddedLeft:
                        if (priority == ArrayMergeDiffPriority.Left)
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, combined[i].Item2));
                        }
                        break;

                    case ArrayDiffKind.RemovedLeft:
                        result.Add(new Tuple <ArrayDiffKind, T>(priority == ArrayMergeDiffPriority.Left ? ArrayDiffKind.Removed : ArrayDiffKind.Same, combined[i].Item2));
                        break;

                    case ArrayDiffKind.AddedRight:
                        if (priority == ArrayMergeDiffPriority.Right)
                        {
                            result.Add(new Tuple <ArrayDiffKind, T>(ArrayDiffKind.Added, combined[i].Item2));
                        }
                        break;

                    case ArrayDiffKind.RemovedRight:
                        result.Add(new Tuple <ArrayDiffKind, T>(priority == ArrayMergeDiffPriority.Right ? ArrayDiffKind.Removed : ArrayDiffKind.Same, combined[i].Item2));
                        break;
                    }
                }
            }
            return(result.ToArray());
        }
Exemplo n.º 3
0
        /// <summary>
        /// Perform a three-way merge between token sets resulting in a document.
        /// </summary>
        /// <param name="original">Original Token set.</param>
        /// <param name="left">Left hand token set.</param>
        /// <param name="right">Right hand token set.</param>
        /// <param name="maxsize">Maximum size of the difference response.</param>
        /// <param name="priority">Merge priority.</param>
        /// <param name="conflict">Output of conflict flag.</param>
        /// <returns>Merged document.</returns>
        public static XDoc Merge(Token[] original, Token[] left, Token[] right, int maxsize, ArrayMergeDiffPriority priority, out bool conflict)
        {
            if(original == null) {
                throw new ArgumentNullException("original");
            }
            if(left == null) {
                throw new ArgumentNullException("left");
            }
            if(right == null) {
                throw new ArgumentNullException("right");
            }

            // create left diff
            Tuplet<ArrayDiffKind, Token>[] leftDiff = Diff(original, left, maxsize);
            if(leftDiff == null) {
                conflict = false;
                return null;
            }

            // create right diff
            Tuplet<ArrayDiffKind, Token>[] rightDiff = Diff(original, right, maxsize);
            if(rightDiff == null) {
                conflict = false;
                return null;
            }

            // merge changes
            Tuplet<ArrayDiffKind, Token>[] mergeDiff = ArrayUtil.MergeDiff(leftDiff, rightDiff, priority, Token.Equal, x => x.Key, out conflict);
            return Detokenize(mergeDiff);
        }
Exemplo n.º 4
0
 /// <summary>
 /// Perform a three-way merge of documents.
 /// </summary>
 /// <param name="original">Original document.</param>
 /// <param name="left">Left hand modification of document.</param>
 /// <param name="right">Right hand modification of document.</param>
 /// <param name="maxsize">Maximum size of the difference response.</param>
 /// <param name="priority">Merge priority.</param>
 /// <param name="conflict">Output of conflict flag.</param>
 /// <returns>Merged document.</returns>
 public static XDoc Merge(XDoc original, XDoc left, XDoc right, int maxsize, ArrayMergeDiffPriority priority, out bool conflict)
 {
     if(original == null) {
         throw new ArgumentNullException("original");
     }
     if(left == null) {
         throw new ArgumentNullException("left");
     }
     if(right == null) {
         throw new ArgumentNullException("right");
     }
     return Merge(Tokenize(original), Tokenize(left), Tokenize(right), maxsize, priority, out conflict);
 }
Exemplo n.º 5
0
        /// <summary>
        /// Merge two diffs.
        /// </summary>
        /// <typeparam name="T">Type of value items in the provided diffs.</typeparam>
        /// <param name="left">Left hand diff.</param>
        /// <param name="right">Right hand diff.</param>
        /// <param name="priority">Priority of resolving ambigious <see cref="ArrayDiffKind"/> values.</param>
        /// <param name="equal">Equality delegate for value comparison.</param>
        /// <param name="track">Tracking function for correlating related diff items, in case on of the related items is ambigious.</param>
        /// <param name="hasConflicts">Indicator whether any conflicts were found.</param>
        /// <returns>Diff result of merge.</returns>
        public static Tuplet <ArrayDiffKind, T>[] MergeDiff <T>(Tuplet <ArrayDiffKind, T>[] left, Tuplet <ArrayDiffKind, T>[] right, ArrayMergeDiffPriority priority, Equality <T> equal, Func <T, object> track, out bool hasConflicts) where T : class
        {
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }

            // set a default equality test if none is provided
            if (equal == null)
            {
                equal = delegate(T lhs, T rhs) { return(((lhs == null) && (rhs == null)) || (((lhs != null) && (rhs != null)) && (lhs == rhs))); };
            }

            // loop over all entries and mark them as conflicted when appropriate
            List <Tuplet <ArrayDiffKind, T> > result = new List <Tuplet <ArrayDiffKind, T> >(left.Length + right.Length);
            int  leftIndex  = 0;
            int  rightIndex = 0;
            bool ambiguous  = false;

            hasConflicts = false;
            while (true)
            {
                if ((leftIndex < left.Length) && (rightIndex < right.Length))
                {
                    if (left[leftIndex].Item1 == ArrayDiffKind.Same && right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        // NOTE: both sides agree to keep the current element

                        result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Same, left[leftIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                        ambiguous = false;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Same && right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        // NOTE: right-side removed element; this could be conflict

                        result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.RemovedRight, right[rightIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Same && right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: right-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Added, right[rightIndex].Item2));
                        }
                        ++rightIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed && right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        // NOTE: left-side removed element; this could be conflict

                        result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.RemovedLeft, left[leftIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed && right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        // NOTE: both sides removed the element; subsequent operations are ambiguous

                        result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Removed, left[leftIndex].Item2));
                        ++leftIndex;
                        ++rightIndex;
                        ambiguous = true;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed && right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: right-side is attempting to add an item after an item that is deleted on the left-side

                        result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        ++rightIndex;
                        ambiguous = true;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added && right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        // NOTE: left-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Added, left[leftIndex].Item2));
                        }
                        ++leftIndex;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added && right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        // NOTE: left-side is attempting to add an item after an item that is deleted on the right-side

                        result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                        ++leftIndex;
                        ambiguous = true;
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added && right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: both sides are adding element; order is ambgiguous, unless it's the same item

                        if (equal(left[leftIndex].Item2, right[rightIndex].Item2))
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Added, left[leftIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        }
                        ++leftIndex;
                        ++rightIndex;
                        ambiguous = true;
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                }
                else if (leftIndex < left.Length)
                {
                    if (left[leftIndex].Item1 == ArrayDiffKind.Same)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (left[leftIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: left-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedLeft, left[leftIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Added, left[leftIndex].Item2));
                        }
                    }
                    ++leftIndex;
                }
                else if (rightIndex < right.Length)
                {
                    if (right[rightIndex].Item1 == ArrayDiffKind.Same)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (right[rightIndex].Item1 == ArrayDiffKind.Removed)
                    {
                        throw new InvalidOperationException();
                    }
                    else if (right[rightIndex].Item1 == ArrayDiffKind.Added)
                    {
                        // NOTE: right-side added an element; conflict depends on ambiguity of current position

                        if (ambiguous)
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.AddedRight, right[rightIndex].Item2));
                        }
                        else
                        {
                            result.Add(new Tuplet <ArrayDiffKind, T>(ArrayDiffKind.Added, right[rightIndex].Item2));
                        }
                    }
                    ++rightIndex;
                }
                else
                {
                    break;
                }
            }

            // convert 'RemovedLeft' and 'RemovedRight' if they aren't followed by 'AddedLeft' or 'AddedRight'
            ambiguous = false;
            for (int i = result.Count - 1; i >= 0; --i)
            {
                switch (result[i].Item1)
                {
                case ArrayDiffKind.Same:
                case ArrayDiffKind.Removed:
                case ArrayDiffKind.Added:
                    ambiguous = false;
                    break;

                case ArrayDiffKind.AddedLeft:
                case ArrayDiffKind.AddedRight:
                    ambiguous    = true;
                    hasConflicts = true;
                    break;

                case ArrayDiffKind.RemovedLeft:
                case ArrayDiffKind.RemovedRight:
                    if (!ambiguous)
                    {
                        result[i].Item1 = ArrayDiffKind.Removed;
                    }
                    else
                    {
                        hasConflicts = true;
                    }
                    break;
                }
            }


            // check if we need to track dependencies between changes
            if (track != null)
            {
                Dictionary <object, List <Tuplet <ArrayDiffKind, T> > > dependencies   = new Dictionary <object, List <Tuplet <ArrayDiffKind, T> > >();
                Dictionary <object, List <Tuplet <ArrayDiffKind, T> > > conflictsLeft  = new Dictionary <object, List <Tuplet <ArrayDiffKind, T> > >();
                Dictionary <object, List <Tuplet <ArrayDiffKind, T> > > conflictsRight = new Dictionary <object, List <Tuplet <ArrayDiffKind, T> > >();

                // detect changes that are conflicting
                for (int i = 0; i < result.Count; ++i)
                {
                    object key = track(result[i].Item2);
                    if (key != null)
                    {
                        List <Tuplet <ArrayDiffKind, T> > entry;

                        // add change to list
                        if (!dependencies.TryGetValue(key, out entry))
                        {
                            entry = new List <Tuplet <ArrayDiffKind, T> >();
                            dependencies.Add(key, entry);
                        }
                        entry.Add(result[i]);

                        // if change represents a conflict, add it to the conflict list
                        switch (result[i].Item1)
                        {
                        case ArrayDiffKind.AddedLeft:
                        case ArrayDiffKind.RemovedLeft:
                            conflictsLeft[key] = entry;
                            break;

                        case ArrayDiffKind.AddedRight:
                        case ArrayDiffKind.RemovedRight:
                            conflictsRight[key] = entry;
                            break;
                        }
                    }
                }

                // visit all left conflicts and elevate any 'Added' or 'Removed' items to 'AddedLeft' or 'RemovedLeft'
                foreach (List <Tuplet <ArrayDiffKind, T> > entry in conflictsLeft.Values)
                {
                    foreach (Tuplet <ArrayDiffKind, T> item in entry)
                    {
                        if (item.Item1 == ArrayDiffKind.Added)
                        {
                            item.Item1 = ArrayDiffKind.AddedLeft;
                        }
                        else if (item.Item1 == ArrayDiffKind.Removed)
                        {
                            item.Item1 = ArrayDiffKind.RemovedLeft;
                        }
                    }
                }

                // visit all right conflicts and elevate any 'Added' or 'Removed' items to 'AddedRight' or 'RemovedRight'
                foreach (List <Tuplet <ArrayDiffKind, T> > entry in conflictsRight.Values)
                {
                    foreach (Tuplet <ArrayDiffKind, T> item in entry)
                    {
                        if (item.Item1 == ArrayDiffKind.Added)
                        {
                            item.Item1 = ArrayDiffKind.AddedRight;
                        }
                        else if (item.Item1 == ArrayDiffKind.Removed)
                        {
                            item.Item1 = ArrayDiffKind.RemovedRight;
                        }
                    }
                }
            }

            // check if there is a merge priority to remove ambiguous changes
            if (priority != ArrayMergeDiffPriority.None)
            {
                // copy only accepted changes
                List <Tuplet <ArrayDiffKind, T> > combined = result;
                result = new List <Tuplet <ArrayDiffKind, T> >(combined.Count);
                for (int i = 0; i < combined.Count; ++i)
                {
                    switch (combined[i].Item1)
                    {
                    case ArrayDiffKind.Same:
                    case ArrayDiffKind.Removed:
                    case ArrayDiffKind.Added:
                        result.Add(combined[i]);
                        break;

                    case ArrayDiffKind.AddedLeft:
                        if (priority == ArrayMergeDiffPriority.Left)
                        {
                            combined[i].Item1 = ArrayDiffKind.Added;
                            result.Add(combined[i]);
                        }
                        break;

                    case ArrayDiffKind.RemovedLeft:
                        if (priority == ArrayMergeDiffPriority.Left)
                        {
                            combined[i].Item1 = ArrayDiffKind.Removed;
                        }
                        else
                        {
                            combined[i].Item1 = ArrayDiffKind.Same;
                        }
                        result.Add(combined[i]);
                        break;

                    case ArrayDiffKind.AddedRight:
                        if (priority == ArrayMergeDiffPriority.Right)
                        {
                            combined[i].Item1 = ArrayDiffKind.Added;
                            result.Add(combined[i]);
                        }
                        break;

                    case ArrayDiffKind.RemovedRight:
                        if (priority == ArrayMergeDiffPriority.Right)
                        {
                            combined[i].Item1 = ArrayDiffKind.Removed;
                        }
                        else
                        {
                            combined[i].Item1 = ArrayDiffKind.Same;
                        }
                        result.Add(combined[i]);
                        break;
                    }
                }
            }
            return(result.ToArray());
        }
Exemplo n.º 6
0
        /// <summary>
        /// Perform a three-way merge between token sets resulting in a document.
        /// </summary>
        /// <param name="original">Original Token set.</param>
        /// <param name="left">Left hand token set.</param>
        /// <param name="right">Right hand token set.</param>
        /// <param name="maxsize">Maximum size of the difference response.</param>
        /// <param name="priority">Merge priority.</param>
        /// <param name="conflict">Output of conflict flag.</param>
        /// <returns>Merged document.</returns>
        public static XDoc Merge(Token[] original, Token[] left, Token[] right, int maxsize, ArrayMergeDiffPriority priority, out bool conflict)
        {
            if (original == null)
            {
                throw new ArgumentNullException("original");
            }
            if (left == null)
            {
                throw new ArgumentNullException("left");
            }
            if (right == null)
            {
                throw new ArgumentNullException("right");
            }

            // create left diff
            var leftDiff = Diff(original, left, maxsize);

            if (leftDiff == null)
            {
                conflict = false;
                return(null);
            }

            // create right diff
            var rightDiff = Diff(original, right, maxsize);

            if (rightDiff == null)
            {
                conflict = false;
                return(null);
            }

            // merge changes
            var mergeDiff = ArrayUtil.MergeDiff(leftDiff, rightDiff, priority, Token.Equal, x => x.Key, out conflict);

            return(Detokenize(mergeDiff));
        }