public static void DumpMatches(this IEnumerable<ElementMatcher.MatchInfo> matches, ElementNavigator snapNav, ElementNavigator diffNav) { var sbm = snapNav.Bookmark(); var dbm = diffNav.Bookmark(); foreach(var match in matches) { if (!snapNav.ReturnToBookmark(match.BaseBookmark) || !diffNav.ReturnToBookmark(match.DiffBookmark)) throw Error.InvalidOperation("Found unreachable bookmark in matches"); var bPos = snapNav.Path + "[{0}]".FormatWith(snapNav.OrdinalPosition); var dPos = diffNav.Path + "[{0}]".FormatWith(diffNav.OrdinalPosition); Debug.WriteLine("B:{0} <--{1}--> D:{2}".FormatWith(bPos, match.Action.ToString(), dPos)); } snapNav.ReturnToBookmark(sbm); diffNav.ReturnToBookmark(dbm); }
private void merge(ElementNavigator snapNav, ElementNavigator diffNav) { var snapPos = snapNav.Bookmark(); var diffPos = diffNav.Bookmark(); try { var matches = (new ElementMatcher()).Match(snapNav, diffNav); //Debug.WriteLine("Matches for children of {0}".FormatWith(snapNav.Path)); //matches.DumpMatches(snapNav, diffNav); foreach (var match in matches) { if (!snapNav.ReturnToBookmark(match.BaseBookmark)) throw Error.InvalidOperation("Internal merging error: bookmark {0} in snap is no longer available", match.BaseBookmark); if (!diffNav.ReturnToBookmark(match.DiffBookmark)) throw Error.InvalidOperation("Internal merging error: bookmark {0} in diff is no longer available", match.DiffBookmark); if (match.Action == ElementMatcher.MatchAction.Add) { // TODO: move this logic to matcher, the Add should point to the last slice where // the new slice will be added after. // Find last entry in slice to add to the end var current = snapNav.Path; while (snapNav.Current.Path == current && snapNav.MoveToNext()) ; snapNav.MoveToPrevious(); // take one step back... var dest = snapNav.Bookmark(); snapNav.ReturnToBookmark(match.BaseBookmark); snapNav.DuplicateAfter(dest); markChange(snapNav.Current); mergeElement(snapNav, diffNav); snapNav.Current.Slicing = null; // Probably not good enough... } else if (match.Action == ElementMatcher.MatchAction.Merge) { mergeElement(snapNav, diffNav); } else if (match.Action == ElementMatcher.MatchAction.Slice) { makeSlice(snapNav, diffNav); } } } finally { snapNav.ReturnToBookmark(snapPos); diffNav.ReturnToBookmark(diffPos); } }
private static int countChildNameRepeats(ElementNavigator diff) { var repeats = 1; var currentPath = diff.PathName; var bm = diff.Bookmark(); while(diff.MoveToNext()) { // check whether the next sibling in the differential has the same name, // that means we're looking at a slice if (diff.PathName == currentPath) repeats++; else break; } diff.ReturnToBookmark(bm); return repeats; }
/// <summary> /// Will match up the children of the current element in diffNav to the children of the element in snapNav. /// </summary> /// <param name="snapNav"></param> /// <param name="diffNav"></param> /// <returns>Returns a list of Bookmark combinations, the first bookmark pointing to an element in the base, /// the second a bookmark in the diff that matches the bookmark in the base.</returns> /// <remarks>Will match slices to base elements, re-sliced slices to slices and type-slice shorthands to choie elements. /// Note that this function may expand snapNav when it encounters paths in the differential that move into the complex types /// of one of snap's elements. (NO NEED, it just has to match direct children, not deeper) /// This function assumes the differential is not sparse: it must have parent nodes for all child constraint paths. /// </remarks> public List<MatchInfo> Match(ElementNavigator snapNav, ElementNavigator diffNav) { if (!snapNav.HasChildren) throw Error.Argument("snapNav", "Cannot match base to diff: element '{0}' in snap has no children".FormatWith(snapNav.PathName)); if (!diffNav.HasChildren) throw Error.Argument("diffNav", "Cannot match base to diff: element '{0}' in diff has no children".FormatWith(diffNav.PathName)); // These bookmarks are used only in the finally {} to make sure we don't alter the position of the navs when leaving the merger var baseStartBM = snapNav.Bookmark(); var diffStartBM = diffNav.Bookmark(); snapNav.MoveToFirstChild(); diffNav.MoveToFirstChild(); var choiceNames = listChoiceElements(snapNav); var result = new List<MatchInfo>(); try { do { // First, match directly -> try to find the child in base with the same name as the path in the diff if (snapNav.PathName != diffNav.PathName && !snapNav.MoveToNext(diffNav.PathName)) { // Not found, maybe this is a type slice shorthand, look if we have a matching choice prefix in snap var typeSliceShorthand = diffNav.PathName; // Try to match nameXXXXX to name[x] var matchingChoice = choiceNames.SingleOrDefault(prefix => isPossibleTypeSlice(prefix, typeSliceShorthand)); if (matchingChoice != null) snapNav.MoveToNext(matchingChoice); else throw Error.InvalidOperation("Differential has a constraint for path '{0}', which does not exist in its base".FormatWith(diffNav.Path)); } result.AddRange(constructMatch(snapNav, diffNav)); } while (diffNav.MoveToNext()); } finally { snapNav.ReturnToBookmark(baseStartBM); diffNav.ReturnToBookmark(diffStartBM); } return result; }
/// <summary> /// List all names of nodes in the current navigator that are choice ('[x]') elements /// </summary> /// <param name="snapNav"></param> /// <returns></returns> private List<string> listChoiceElements(ElementNavigator snapNav) { var bm = snapNav.Bookmark(); var result = new List<string>(); do { if (snapNav.Current.IsChoice()) result.Add(snapNav.PathName); } while (snapNav.MoveToNext()); snapNav.ReturnToBookmark(bm); return result; }
private static int countChildNameRepeats(ElementNavigator diff) { //TODO: We use this function to determine whether an element is sliced...doing this by counting repeats of elements //in the diff. However, when reslicing, the diff doesn't need to have repeating elements, and you have to derive from the //base (snapshot) that the element is sliced. var repeats = 1; var currentPath = diff.PathName; var bm = diff.Bookmark(); while (diff.MoveToNext()) { // check whether the next sibling in the differential has the same name, // that means we're looking at a slice if (diff.PathName == currentPath) repeats++; else break; } diff.ReturnToBookmark(bm); return repeats; }