private XmlNode FindMatchingNode(XmlNode node, XmlNode otherParent, HashSet <XmlNode> acceptableTargets) { IFindNodeToMerge finder = _merger.MergeStrategies.GetMergePartnerFinder(node); var result = finder.GetNodeToMerge(node, otherParent, acceptableTargets); // This check allows implementations where there is only one possible match to ignore acceptableTargets // and just give us the one match from otherParent. If we don't want it, we discard it. if (!acceptableTargets.Contains(result)) { return(null); } return(result); }
public OptionalKeyAttrFinder(string key, IFindNodeToMerge backupFinder) { _key = key; _backup = backupFinder; }
private static void AddKeyedElementType(IDictionary<string, ElementStrategy> sharedElementStrategies, string elementName, IFindNodeToMerge findBykeyAttribute, bool orderOfTheseIsRelevant, bool isAtomic) { var strategy = new ElementStrategy(orderOfTheseIsRelevant) { MergePartnerFinder = findBykeyAttribute, ContextDescriptorGenerator = ContextGen, IsAtomic = isAtomic }; sharedElementStrategies.Add(elementName, strategy); }
/// <summary> /// Determine possible correspondences for the given range of currently unmatched items. /// </summary> /// <param name="startPrimaryRange"></param> /// <param name="endPrimaryRange"></param> private void MakePossibleCorrespondences(int startPrimaryRange, int endPrimaryRange) { if (startPrimaryRange == -1) { return; // empty range } int limPrimaryRange = endPrimaryRange + 1; int startOtherRange = 0; int limOtherRange = _positions.Length; if (startPrimaryRange > 0) { startOtherRange = _indexInPositions[_correspondences[_primary[startPrimaryRange - 1]]] + 1; } if (endPrimaryRange < _primary.Count - 1) { limOtherRange = _indexInPositions[_correspondences[_primary[limPrimaryRange]]]; } if (limOtherRange - startOtherRange <= 0) { return; // no objects that might correspond } List <XmlNode> possibleMatches = new List <XmlNode>(limOtherRange - startOtherRange); for (int i = startOtherRange; i < limOtherRange; i++) { possibleMatches.Add(_positions[i].Other); } for (int iPrimary = startPrimaryRange; iPrimary < limPrimaryRange; iPrimary++) { XmlNode ourChild = _primary[iPrimary]; IFindNodeToMerge finder = _merger.MergeStrategies.GetMergePartnerFinder(ourChild); IFindPossibleNodeToMerge possibleFinder = finder as IFindPossibleNodeToMerge; if (possibleFinder == null) { continue; } XmlNode otherChild = possibleFinder.GetPossibleNodeToMerge(ourChild, possibleMatches); if (otherChild == null) { continue; } // We don't want to reorder things here, so future searches are limited to subsequent // possible matches. possibleMatches.RemoveRange(0, possibleMatches.IndexOf(otherChild) + 1); _correspondences[ourChild] = otherChild; // Take advantage of the new match to reduce ambiguity. int iPositionChanged = _indexInPositions[otherChild]; _positions[iPositionChanged].exactPosition = iPrimary; for (int ipos = iPositionChanged + 1; ipos < limOtherRange; ipos++) { _positions[ipos].indexofPrecedingPrimaryNode = iPrimary; } for (int ipos = iPositionChanged - 1; ipos >= startOtherRange && _positions[ipos].exactPosition == -1; ipos--) { _positions[ipos].indexOfFollowingPrimaryNode = iPrimary; } } }
/// <summary> /// Remove from ancestorKeepers any node that does not correspond to anything (both deleted) /// Remove from ancestorKeepers and theirKeepers any pair that correspond to each other but not to anything in ours. Report conflict (delete/edit) if pair not identical. /// Remove from ancestorKeepers and ourKeepers any pair that correspond to each other and are identical, but don't correspond to anything in theirs (they deleted) /// Report conflict (edit/delete) on any pair that correspond in ours and ancestor, but nothing in theirs, and that are NOT identical. (but keep them...we win) /// </summary> private void DoDeletions() { // loop over a copy of the list, since we may modify ancestorKeepers. List <XmlNode> loopSource = new List <XmlNode>(_childrenOfAncestorKeepers); var ourChildSet = new HashSet <XmlNode>(_ours == null ? new XmlNode[0] : _ours.ChildNodes.Cast <XmlNode>()); var theirChildSet = new HashSet <XmlNode>(_theirs == null ? new XmlNode[0] : _theirs.ChildNodes.Cast <XmlNode>()); foreach (XmlNode ancestorChild in loopSource) { ElementStrategy mergeStrategy = _merger.MergeStrategies.GetElementStrategy(ancestorChild); IFindNodeToMerge finder = mergeStrategy.MergePartnerFinder; XmlNode ourChild = finder.GetNodeToMerge(ancestorChild, _ours, ourChildSet); XmlNode theirChild = finder.GetNodeToMerge(ancestorChild, _theirs, theirChildSet); var extantNode = ancestorChild ?? ourChild ?? theirChild; if (extantNode is XmlCharacterData) { return; // Already done. } if (XmlUtilities.IsTextLevel(ourChild, theirChild, ancestorChild)) { new MergeTextNodesMethod(_merger, mergeStrategy, _skipInnerMergeFor, ref ourChild, _childrenOfOurKeepers, theirChild, _childrenOfTheirKeepers, ancestorChild, _childrenOfAncestorKeepers).DoDeletions(); } else if (ourChild == null) { // We deleted it. if (theirChild == null) { // We both deleted it. Forget it ever existed. // Route tested: MergeChildrenMethodTests. _merger.EventListener.ChangeOccurred(new XmlBothDeletionChangeReport(_merger.MergeSituation.PathToFileInRepository, ancestorChild)); _childrenOfAncestorKeepers.Remove(ancestorChild); } else { if (!XmlUtilities.AreXmlElementsEqual(ancestorChild, theirChild)) { // We deleted, they modified, report conflict. if (theirChild.NodeType == XmlNodeType.Element) { // Route tested (XmlMergerTests). _merger.ConflictOccurred( new RemovedVsEditedElementConflict(theirChild.Name, null, theirChild, ancestorChild, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(theirChild), _merger.MergeSituation.BetaUserId), theirChild); _skipInnerMergeFor.Add(theirChild); } else { // Never used. But then, there isn't plain text in an xml file. _merger.ConflictOccurred( new RemovedVsEditedTextConflict(null, theirChild, ancestorChild, _merger.MergeSituation, _merger.MergeSituation.BetaUserId)); _skipInnerMergeFor.Add(theirChild); } _childrenOfAncestorKeepers.Remove(ancestorChild); //review hatton added dec 2009, wanting whoever edited it to win (previously "we" always won) } else { //We deleted it, they didn't edit it. So just make it go away. // Route tested in TextElementMergeTests, MergeChildrenMethod_DiffOnlyTests, XmlMergerTests _merger.EventListener.ChangeOccurred(new XmlDeletionChangeReport(_merger.MergeSituation.PathToFileInRepository, ancestorChild, theirChild)); _childrenOfAncestorKeepers.Remove(ancestorChild); _childrenOfTheirKeepers.Remove(theirChild); } } } else if (theirChild == null) { // they deleted it (and we didn't) if (XmlUtilities.AreXmlElementsEqual(ancestorChild, ourChild)) { // We didn't touch it, allow their deletion to go forward, forget it existed. // Route tested (XmlMergerTests). _merger.EventListener.ChangeOccurred(new XmlDeletionChangeReport(_merger.MergeSituation.PathToFileInRepository, ancestorChild, ourChild)); _childrenOfAncestorKeepers.Remove(ancestorChild); _childrenOfOurKeepers.Remove(ourChild); } else { // We changed it, ignore their deletion and report conflict. if (ourChild.NodeType == XmlNodeType.Element) { // Route tested (XmlMergerTests). _merger.ConflictOccurred( new EditedVsRemovedElementConflict(ourChild.Name, ourChild, null, ancestorChild, _merger.MergeSituation, _merger.MergeStrategies.GetElementStrategy(ourChild), _merger.MergeSituation.AlphaUserId), ourChild); _skipInnerMergeFor.Add(ourChild); } else { // Never used. But then, there isn't plain text in an xml file. _merger.ConflictOccurred( new EditedVsRemovedTextConflict(ourChild, null, ancestorChild, _merger.MergeSituation, _merger.MergeSituation.AlphaUserId)); _skipInnerMergeFor.Add(ourChild); } } } } }
private static void AddKeyedElementType(IDictionary <string, ElementStrategy> sharedElementStrategies, string elementName, IFindNodeToMerge findBykeyAttribute, bool orderOfTheseIsRelevant, bool isAtomic) { var strategy = new ElementStrategy(orderOfTheseIsRelevant) { MergePartnerFinder = findBykeyAttribute, ContextDescriptorGenerator = ContextGen, IsAtomic = isAtomic }; sharedElementStrategies.Add(elementName, strategy); }