Esempio n. 1
0
        /// <summary>
        /// Merges the children into the "ours" xmlnode, and uses the merger's listener to publish what happened
        /// </summary>
        public void Run()
        {
            // Pre-process three nodes to handle duplicates in each node, But only if finder is one of these:
            //		FindFirstElementWithSameName, FindByKeyAttribute, or FindByMultipleKeyAttributes.

            // Initialise lists of keepers to current ancestorChildren, ourChildren, theirChildren
            CopyChildrenToList(_ancestor, _childrenOfAncestorKeepers);
            CopyChildrenToList(_ours, _childrenOfOurKeepers);
            CopyChildrenToList(_theirs, _childrenOfTheirKeepers);

            // Deal with deletions.
            DoDeletions();

            // Allow the most promising parent's strategy to determine whether order is significant for children.
            // The default configuration of ElementStrategy uses an AskChildren strategy, which means individual
            // children determine whether their order is significant.
            var parentNode = _ours ?? _theirs ?? _ancestor;
            var parentStrategy = _merger.MergeStrategies.GetElementStrategy(parentNode);
            var parentOrder = parentStrategy == null ? ChildOrder.AskChildren : parentStrategy.ChildOrderPolicy.OrderSignificance(parentNode);
            // Determine if child order is important, we shouldn't create reordering conflicts if the order of children is unimportant
            var childOrderMatters = parentOrder == ChildOrder.Significant;
            if(parentOrder == ChildOrder.AskChildren && parentNode.HasChildNodes)
            {
                var childStrategy = _merger.MergeStrategies.GetElementStrategy(parentNode.FirstChild);
                if(childStrategy != null && childStrategy.OrderIsRelevant)
                {
                    childOrderMatters = true;
                }
            }

            ChildOrderer oursOrderer = new ChildOrderer(_childrenOfOurKeepers, _childrenOfTheirKeepers,
                MakeCorrespondences(_childrenOfOurKeepers, _childrenOfTheirKeepers, _theirs), _merger, parentOrder);

            ChildOrderer resultOrderer = oursOrderer; // default

            if (oursOrderer.OrderIsDifferent)
            {
                // The order of the two lists is not consistent. Compare with ancestor to see who changed.
                ChildOrderer ancestorOursOrderer = new ChildOrderer(_childrenOfAncestorKeepers, _childrenOfOurKeepers,
                    MakeCorrespondences(_childrenOfAncestorKeepers, _childrenOfOurKeepers, _ours), _merger, parentOrder);

                ChildOrderer ancestorTheirsOrderer = new ChildOrderer(_childrenOfAncestorKeepers, _childrenOfTheirKeepers,
                    MakeCorrespondences(_childrenOfAncestorKeepers, _childrenOfTheirKeepers, _theirs), _merger, parentOrder);

                if (ancestorTheirsOrderer.OrderIsDifferent)
                {
                    if (ancestorOursOrderer.OrderIsDifferent && childOrderMatters)
                    {
                        // stick with our orderer (we win), but report conflict.
                        // Route tested (XmlMergerTests).
                        _merger.ConflictOccurred(new BothReorderedElementConflict(_ours.Name, _ours,
                            _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                            _merger.MergeSituation.AlphaUserId));
                    }
                    else
                    {
                        // only they re-ordered; take their order as primary.
                        resultOrderer = new ChildOrderer(_childrenOfTheirKeepers, _childrenOfOurKeepers,
                            MakeCorrespondences(_childrenOfTheirKeepers, _childrenOfOurKeepers, _ours), _merger, parentOrder);
                    }
                }
                else if(!ancestorOursOrderer.OrderIsDifferent)
                {
                    // our order is different from theirs, but neither is different from the ancestor.
                    // the only way this can be true is if both inserted the same thing, but in
                    // different places. Stick with our orderer (we win), but report conflict.
                    // Route tested (XmlMergerTests).
                    _merger.ConflictOccurred(new BothInsertedAtDifferentPlaceConflict(_ours.Name, _ours,
                        _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                        _merger.MergeSituation.AlphaUserId));
                }
                // otherwise we re-ordered, but they didn't. That's not a problem, unless it resulted
                // in inconsistency or ambiguity in other stuff that someone added.
            }

            if (!resultOrderer.OrderIsConsistent ||
                (resultOrderer.OrderIsDifferent && resultOrderer.OrderIsAmbiguous))
            {
                // Route tested (XmlMergerTests[x2]).
                _merger.ConflictOccurred(new AmbiguousInsertReorderConflict(_ours.Name, _ours,
                    _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                    _merger.MergeSituation.AlphaUserId));
            }
            else if (resultOrderer.OrderIsAmbiguous)
            {
                // Route tested (MergeChildrenMethodTests, XmlMergerTests).
                _merger.ConflictOccurred(new AmbiguousInsertConflict(_ours.Name, _ours,
                    _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                    _merger.MergeSituation.AlphaUserId));
            }

            // Merge corresponding nodes.
            // 'resultsChildren' may contain nodes from either 'ours', 'theirs', or both,
            // as the 'resultsChildren' collection has been combined in the ordereing operation.
            List<XmlNode> resultsChildren = resultOrderer.GetResultList();
            for (int i = 0; i < resultsChildren.Count; i++)
            {
                XmlNode ourChild = resultsChildren[i];
                // The 'DoDeletions' method call, above, possibly called MergeTextNodesMethod.DoDeletions,
                // which may have added 'ourChild' to _skipInnerMergeFor,
                // as it did some fairly exotic work with full and partial deletions.
                // So, don't do those again here.
                if (_skipInnerMergeFor.Contains(ourChild))
                    continue;

                XmlNode theirChild;
                var ancestorChild = FindMatchingNode(ourChild, _ancestor, new HashSet<XmlNode>(_childrenOfAncestorKeepers));

                if (resultOrderer.Correspondences.TryGetValue(ourChild, out theirChild) && !ChildrenAreSame(ourChild, theirChild))
                {
                    // Both 'ourChild' and 'theirChild exist. 'ancestorChild' may, or, may not, exist.
                    // There's a corresponding node and it isn't the same as ours...
                    // Route tested: MergeChildrenMethod_DiffOnlyTests.
                    _merger.MergeInner(_ours, ref ourChild, theirChild, ancestorChild);
                    resultsChildren[i] = ourChild;
                }
                else
                {
                    // 'theirChild' may, or may not, exist. But if it does exist, it is the same as 'ourChild'.

                    //Review JohnT (jh): Is this the correct interpretation?
                    if (ancestorChild == null)
                    {
                        if (XmlUtilities.IsTextNodeContainer(ourChild) == TextNodeStatus.IsTextNodeContainer) // No, it hasn't. MergeTextNodesMethod has already added the addition report.
                        {
                            if (theirChild == null)
                            {
                                // Route tested: MergeChildrenMethod_DiffOnlyTests & XmlMergerTests.
                                _merger.EventListener.ChangeOccurred(new XmlTextAddedReport(_merger.MergeSituation.PathToFileInRepository, ourChild)); // Route tested (x2).
                            }
                            else
                            {
                                _merger.EventListener.ChangeOccurred(new XmlTextBothAddedReport(_merger.MergeSituation.PathToFileInRepository, ourChild)); // Route tested
                            }
                        }
                        else if (!(ourChild is XmlCharacterData))
                        {
                            if (theirChild == null)
                                // Route tested (MergeChildrenMethodTests, XmlMergerTests).
                                _merger.EventListener.ChangeOccurred(new XmlAdditionChangeReport(_merger.MergeSituation.PathToFileInRepository, ourChild));
                            else
                                _merger.EventListener.ChangeOccurred(new XmlBothAddedSameChangeReport(_merger.MergeSituation.PathToFileInRepository, ourChild));
                        }
                    }
                    else
                    {
                        // ancestorChild is not null.
                        if (XmlUtilities.IsTextNodeContainer(ourChild) == TextNodeStatus.IsTextNodeContainer)
                        {
                            if (theirChild != null && !XmlUtilities.AreXmlElementsEqual(ourChild, ancestorChild))
                                _merger.EventListener.ChangeOccurred(new XmlTextBothMadeSameChangeReport(_merger.MergeSituation.PathToFileInRepository, ourChild)); // Route tested
                        }
                        else
                        {
                            _merger.MergeInner(_ours, ref ourChild, theirChild, ancestorChild);
                            resultsChildren[i] = ourChild;
                        }
                    }
                }
            }

            // Plug results back into 'ours'
            for (int i = 0; i < resultsChildren.Count; i++)
            {
                XmlNode ourChild = resultsChildren[i];
                while (_ours.ChildNodes.Count > i && ourChild != _ours.ChildNodes[i])
                    _ours.RemoveChild(_ours.ChildNodes[i]);
                if (_ours.ChildNodes.Count > i)
                    continue; // we found the exact node already present, leave it alone.
                if (ourChild.ParentNode == _ours)
                    _ours.AppendChild(ourChild);
                else
                {
                    if (ourChild is XmlElement)
                        _ours.AppendChild(_ours.OwnerDocument.ImportNode(ourChild, true));
                    else if (ourChild is XmlText)
                        _ours.AppendChild(_ours.OwnerDocument.CreateTextNode(ourChild.OuterXml));
                    else
                        Debug.Fail("so far we only know how to copy elements and text nodes at this point");
                }
            }
            // Remove any leftovers.
            while (_ours.ChildNodes.Count > resultsChildren.Count)
                _ours.RemoveChild(_ours.ChildNodes[resultsChildren.Count]);
        }
Esempio n. 2
0
        /// <summary>
        /// Merges the children into the "ours" xmlnode, and uses the merger's listener to publish what happened
        /// </summary>
        public void Run()
        {
            // Pre-process three nodes to handle duplicates in each node, But only if finder is one of these:
            //		FindFirstElementWithSameName, FindByKeyAttribute, or FindByMultipleKeyAttributes.

            // Initialise lists of keepers to current ancestorChildren, ourChildren, theirChildren
            CopyChildrenToList(_ancestor, _childrenOfAncestorKeepers);
            CopyChildrenToList(_ours, _childrenOfOurKeepers);
            CopyChildrenToList(_theirs, _childrenOfTheirKeepers);

            // Deal with deletions.
            DoDeletions();

            // Allow the most promising parent's strategy to determine whether order is significant for children.
            // The default configuration of ElementStrategy uses an AskChildren strategy, which means individual
            // children determine whether their order is significant.
            var parentNode     = _ours ?? _theirs ?? _ancestor;
            var parentStrategy = _merger.MergeStrategies.GetElementStrategy(parentNode);
            var parentOrder    = parentStrategy == null ? ChildOrder.AskChildren : parentStrategy.ChildOrderPolicy.OrderSignificance(parentNode);
            // Determine if child order is important, we shouldn't create reordering conflicts if the order of children is unimportant
            var childOrderMatters = parentOrder == ChildOrder.Significant;

            if (parentOrder == ChildOrder.AskChildren && parentNode.HasChildNodes)
            {
                var childStrategy = _merger.MergeStrategies.GetElementStrategy(parentNode.FirstChild);
                if (childStrategy != null && childStrategy.OrderIsRelevant)
                {
                    childOrderMatters = true;
                }
            }

            ChildOrderer oursOrderer = new ChildOrderer(_childrenOfOurKeepers, _childrenOfTheirKeepers,
                                                        MakeCorrespondences(_childrenOfOurKeepers, _childrenOfTheirKeepers, _theirs), _merger, parentOrder);

            ChildOrderer resultOrderer = oursOrderer;             // default

            if (oursOrderer.OrderIsDifferent)
            {
                // The order of the two lists is not consistent. Compare with ancestor to see who changed.
                ChildOrderer ancestorOursOrderer = new ChildOrderer(_childrenOfAncestorKeepers, _childrenOfOurKeepers,
                                                                    MakeCorrespondences(_childrenOfAncestorKeepers, _childrenOfOurKeepers, _ours), _merger, parentOrder);

                ChildOrderer ancestorTheirsOrderer = new ChildOrderer(_childrenOfAncestorKeepers, _childrenOfTheirKeepers,
                                                                      MakeCorrespondences(_childrenOfAncestorKeepers, _childrenOfTheirKeepers, _theirs), _merger, parentOrder);

                if (ancestorTheirsOrderer.OrderIsDifferent)
                {
                    if (ancestorOursOrderer.OrderIsDifferent && childOrderMatters)
                    {
                        // stick with our orderer (we win), but report conflict.
                        // Route tested (XmlMergerTests).
                        _merger.ConflictOccurred(new BothReorderedElementConflict(_ours.Name, _ours,
                                                                                  _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                                                                                  _merger.MergeSituation.AlphaUserId));
                    }
                    else
                    {
                        // only they re-ordered; take their order as primary.
                        resultOrderer = new ChildOrderer(_childrenOfTheirKeepers, _childrenOfOurKeepers,
                                                         MakeCorrespondences(_childrenOfTheirKeepers, _childrenOfOurKeepers, _ours), _merger, parentOrder);
                    }
                }
                else if (!ancestorOursOrderer.OrderIsDifferent)
                {
                    // our order is different from theirs, but neither is different from the ancestor.
                    // the only way this can be true is if both inserted the same thing, but in
                    // different places. Stick with our orderer (we win), but report conflict.
                    // Route tested (XmlMergerTests).
                    _merger.ConflictOccurred(new BothInsertedAtDifferentPlaceConflict(_ours.Name, _ours,
                                                                                      _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                                                                                      _merger.MergeSituation.AlphaUserId));
                }
                // otherwise we re-ordered, but they didn't. That's not a problem, unless it resulted
                // in inconsistency or ambiguity in other stuff that someone added.
            }

            if (!resultOrderer.OrderIsConsistent ||
                (resultOrderer.OrderIsDifferent && resultOrderer.OrderIsAmbiguous))
            {
                // Route tested (XmlMergerTests[x2]).
                _merger.ConflictOccurred(new AmbiguousInsertReorderConflict(_ours.Name, _ours,
                                                                            _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                                                                            _merger.MergeSituation.AlphaUserId));
            }
            else if (resultOrderer.OrderIsAmbiguous)
            {
                // Route tested (MergeChildrenMethodTests, XmlMergerTests).
                _merger.ConflictOccurred(new AmbiguousInsertConflict(_ours.Name, _ours,
                                                                     _theirs, _ancestor, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(_ours),
                                                                     _merger.MergeSituation.AlphaUserId));
            }

            // Merge corresponding nodes.
            // 'resultsChildren' may contain nodes from either 'ours', 'theirs', or both,
            // as the 'resultsChildren' collection has been combined in the ordereing operation.
            List <XmlNode> resultsChildren = resultOrderer.GetResultList();

            for (int i = 0; i < resultsChildren.Count; i++)
            {
                XmlNode ourChild = resultsChildren[i];
                // The 'DoDeletions' method call, above, possibly called MergeTextNodesMethod.DoDeletions,
                // which may have added 'ourChild' to _skipInnerMergeFor,
                // as it did some fairly exotic work with full and partial deletions.
                // So, don't do those again here.
                if (_skipInnerMergeFor.Contains(ourChild))
                {
                    continue;
                }

                XmlNode theirChild;
                var     ancestorChild = FindMatchingNode(ourChild, _ancestor, new HashSet <XmlNode>(_childrenOfAncestorKeepers));

                if (resultOrderer.Correspondences.TryGetValue(ourChild, out theirChild) && !ChildrenAreSame(ourChild, theirChild))
                {
                    // Both 'ourChild' and 'theirChild exist. 'ancestorChild' may, or, may not, exist.
                    // There's a corresponding node and it isn't the same as ours...
                    // Route tested: MergeChildrenMethod_DiffOnlyTests.
                    _merger.MergeInner(ref ourChild, theirChild, ancestorChild);
                    resultsChildren[i] = ourChild;
                }
                else
                {
                    // 'theirChild' may, or may not, exist. But if it does exist, it is the same as 'ourChild'.

                    //Review JohnT (jh): Is this the correct interpretation?
                    if (ancestorChild == null)
                    {
                        if (XmlUtilities.IsTextNodeContainer(ourChild) == TextNodeStatus.IsTextNodeContainer)                         // No, it hasn't. MergeTextNodesMethod has already added the addition report.
                        {
                            if (theirChild == null)
                            {
                                // Route tested: MergeChildrenMethod_DiffOnlyTests & XmlMergerTests.
                                _merger.EventListener.ChangeOccurred(new XmlTextAddedReport(_merger.MergeSituation.PathToFileInRepository, ourChild));                                 // Route tested (x2).
                            }
                            else
                            {
                                _merger.EventListener.ChangeOccurred(new XmlTextBothAddedReport(_merger.MergeSituation.PathToFileInRepository, ourChild));                                 // Route tested
                            }
                        }
                        else if (!(ourChild is XmlCharacterData))
                        {
                            if (theirChild == null)
                            {
                                // Route tested (MergeChildrenMethodTests, XmlMergerTests).
                                _merger.EventListener.ChangeOccurred(new XmlAdditionChangeReport(_merger.MergeSituation.PathToFileInRepository, ourChild));
                            }
                            else
                            {
                                _merger.EventListener.ChangeOccurred(new XmlBothAddedSameChangeReport(_merger.MergeSituation.PathToFileInRepository, ourChild));
                            }
                        }
                    }
                    else
                    {
                        // ancestorChild is not null.
                        if (XmlUtilities.IsTextNodeContainer(ourChild) == TextNodeStatus.IsTextNodeContainer)
                        {
                            if (theirChild != null && !XmlUtilities.AreXmlElementsEqual(ourChild, ancestorChild))
                            {
                                _merger.EventListener.ChangeOccurred(new XmlTextBothMadeSameChangeReport(_merger.MergeSituation.PathToFileInRepository, ourChild));                                 // Route tested
                            }
                        }
                        else
                        {
                            _merger.MergeInner(ref ourChild, theirChild, ancestorChild);
                            resultsChildren[i] = ourChild;
                        }
                    }
                }
            }

            // Plug results back into 'ours'
            for (int i = 0; i < resultsChildren.Count; i++)
            {
                XmlNode ourChild = resultsChildren[i];
                while (_ours.ChildNodes.Count > i && ourChild != _ours.ChildNodes[i])
                {
                    _ours.RemoveChild(_ours.ChildNodes[i]);
                }
                if (_ours.ChildNodes.Count > i)
                {
                    continue;                     // we found the exact node already present, leave it alone.
                }
                if (ourChild.ParentNode == _ours)
                {
                    _ours.AppendChild(ourChild);
                }
                else
                {
                    if (ourChild is XmlElement)
                    {
                        _ours.AppendChild(_ours.OwnerDocument.ImportNode(ourChild, true));
                    }
                    else if (ourChild is XmlText)
                    {
                        _ours.AppendChild(_ours.OwnerDocument.CreateTextNode(ourChild.OuterXml));
                    }
                    else
                    {
                        Debug.Fail("so far we only know how to copy elements and text nodes at this point");
                    }
                }
            }
            // Remove any leftovers.
            while (_ours.ChildNodes.Count > resultsChildren.Count)
            {
                _ours.RemoveChild(_ours.ChildNodes[resultsChildren.Count]);
            }
        }