public void MakeDifferentialTree() { var struc = new Profile.ProfileStructureComponent(); struc.Element = new List<Profile.ElementComponent>(); struc.Element.Add(new Profile.ElementComponent() { Path = "A.B.C1" }); struc.Element.Add(new Profile.ElementComponent() { Path = "A.B.C1" }); struc.Element.Add(new Profile.ElementComponent() { Path = "A.B.C2" }); struc.Element.Add(new Profile.ElementComponent() { Path = "A.B" }); struc.Element.Add(new Profile.ElementComponent() { Path = "A.B.C1.D" }); struc.Element.Add(new Profile.ElementComponent() { Path = "A.D.F" }); var tree = new DifferentialTreeConstructor(struc).MakeTree(); Assert.IsNotNull(tree); var nav = new ElementNavigator(tree); Assert.AreEqual(10, nav.Count); Assert.IsTrue(nav.MoveToChild("A")); Assert.IsTrue(nav.MoveToChild("B")); Assert.IsTrue(nav.MoveToChild("C1")); Assert.IsTrue(nav.MoveToNext("C1")); Assert.IsTrue(nav.MoveToNext("C2")); Assert.IsTrue(nav.MoveToParent()); // 1st A.B Assert.IsTrue(nav.MoveToNext() && nav.Path == "A.B"); // (now) 2nd A.B Assert.IsTrue(nav.MoveToChild("C1")); Assert.IsTrue(nav.MoveToChild("D")); Assert.IsTrue(nav.MoveToParent()); // A.B.C1 Assert.IsTrue(nav.MoveToParent()); // A.B (2nd) Assert.IsTrue(nav.MoveToNext() && nav.Path == "A.D"); Assert.IsTrue(nav.MoveToChild("F")); }
private static bool childNameRepeats(ElementNavigator diff) { var isSliced = false; var currentPath = diff.PathName; if (diff.MoveToNext()) { // check whether the next sibling in the differential has the same name, // that means we're looking at a slice isSliced = diff.PathName == currentPath; diff.MoveToPrevious(); } return(isSliced); }
private void merge(ElementNavigator snap, ElementNavigator diff) { mergeElementAttributes(snap.Current, diff.Current); // If there are children, move into them, and recursively merge them if (diff.MoveToFirstChild()) { if (!snap.HasChildren) { // The differential moves into an element that has no children in the base. // This is allowable if the base's element has a nameReference or a TypeRef, // in which case needs to be expanded before we can move to the path indicated // by the differential expandBaseElement(snap, diff); } // Due to how MoveToFirstChild() works, we have to move to the first matching *child* // when entering the loop for the first time, after that we can look for the next // matching *sibling*. bool firstEntry = true; do { if ((firstEntry && !snap.MoveToChild(diff.PathName)) || (!firstEntry && !snap.MoveToNext(diff.PathName))) { throw Error.InvalidOperation("Differential has a constraint for path {0}, which does not exist in its base", diff.PathName); } firstEntry = false; // Child found in both, merge them if (childNameRepeats(diff) || diff.Current.IsExtension()) { // The child in the diff repeats or we recognize it as an extension slice -> we're on the first element of a slice! mergeSlice(snap, diff); } else { merge(snap, diff); } }while (diff.MoveToNext()); // After the merge, return the diff and snapho back to their original position diff.MoveToParent(); snap.MoveToParent(); } }
/// <summary> /// Insert the children of the current source node under the node pointed to by the destination. /// </summary> /// <param name="dest"></param> /// <param name="source"></param> /// <returns></returns> public static bool CopyChildren(this BaseElementNavigator dest, ElementNavigator source) { if (dest.HasChildren) { return(false); // Protect children from being overwritten } if (!source.MoveToFirstChild()) { return(true); // Nothing to copy, but successful anyway } bool firstChild = true; do { var copiedChild = (Profile.ElementComponent)source.Current.DeepCopy(); if (firstChild) { // The first time, create a new child in the destination dest.InsertFirstChild(copiedChild); firstChild = false; } else { // Then insert other childs after that dest.InsertAfter(copiedChild); } // If there are nested children in the source, insert them under // the newly inserted node in the destination if (source.HasChildren) { dest.CopyChildren(source); } }while (source.MoveToNext()); // Bring both source & destination back one step to the original parents source.MoveToParent(); dest.MoveToParent(); return(true); }
/// <summary> /// Insert the children of the current source node under the node pointed to by the destination. /// </summary> /// <param name="dest"></param> /// <param name="source"></param> /// <returns></returns> public static bool CopyChildren(this BaseElementNavigator dest, ElementNavigator source) { if (dest.HasChildren) return false; // Protect children from being overwritten if (!source.MoveToFirstChild()) return true; // Nothing to copy, but successful anyway bool firstChild = true; do { var copiedChild = (Profile.ElementComponent)source.Current.DeepCopy(); if (firstChild) { // The first time, create a new child in the destination dest.InsertFirstChild(copiedChild); firstChild = false; } else // Then insert other childs after that dest.InsertAfter(copiedChild); // If there are nested children in the source, insert them under // the newly inserted node in the destination if (source.HasChildren) dest.CopyChildren(source); } while (source.MoveToNext()); // Bring both source & destination back one step to the original parents source.MoveToParent(); dest.MoveToParent(); return true; }
private void merge(ElementNavigator snap, ElementNavigator diff) { mergeElementAttributes(snap.Current, diff.Current); // If there are children, move into them, and recursively merge them if (diff.MoveToFirstChild()) { if (!snap.HasChildren) { // The differential moves into an element that has no children in the base. // This is allowable if the base's element has a nameReference or a TypeRef, // in which case needs to be expanded before we can move to the path indicated // by the differential expandBaseElement(snap, diff); } // Due to how MoveToFirstChild() works, we have to move to the first matching *child* // when entering the loop for the first time, after that we can look for the next // matching *sibling*. bool firstEntry = true; do { if( (firstEntry && !snap.MoveToChild(diff.PathName)) || (!firstEntry && !snap.MoveToNext(diff.PathName)) ) throw Error.InvalidOperation("Differential has a constraint for path {0}, which does not exist in its base", diff.PathName); firstEntry = false; // Child found in both, merge them if (childNameRepeats(diff) || diff.Current.IsExtension()) { // The child in the diff repeats or we recognize it as an extension slice -> we're on the first element of a slice! mergeSlice(snap, diff); } else merge(snap, diff); } while (diff.MoveToNext()); // After the merge, return the diff and snapho back to their original position diff.MoveToParent(); snap.MoveToParent(); } }
private static bool childNameRepeats(ElementNavigator diff) { var isSliced = false; var currentPath = diff.PathName; if (diff.MoveToNext()) { // check whether the next sibling in the differential has the same name, // that means we're looking at a slice isSliced = diff.PathName == currentPath; diff.MoveToPrevious(); } return isSliced; }
private void mergeSlice(ElementNavigator snap, ElementNavigator diff) { // diff is now located at the first repeat of a slice, which is (possibly) the slice entry // snap is located at the base definition of the element that will become sliced. But snap is not yet sliced. // Before we start, is the base element sliceable? if (!snap.Current.IsRepeating() && !isSlicedToOne(diff.Current)) throw Error.InvalidOperation("The slicing entry in the differential at {0} indicates an unbounded slice, but the base element is not a repeating element", diff.Current.Path); Profile.ElementComponent slicingEntry; // Yes, so, first, add the slicing entry to the snapshot. if (diff.Current.Slicing != null) { slicingEntry = createSliceEntry(snap.Current, diff.Current); snap.InsertBefore(slicingEntry); if (!diff.MoveToNext(diff.PathName)) throw Error.InvalidOperation("Slicing has no elements beyond the slicing entry"); // currently impossible to happen } else { // Mmmm....no slicing entry in the differential. This is only alloweable for extension slices, as a shorthand notation. if (!snap.Current.IsExtension()) throw Error.InvalidOperation("The slice group at {0} does not start with a slice entry element", diff.Current.Path); // In this case we insert a "prefab" extension slice. slicingEntry = createExtensionSlicingEntry(snap.Path); snap.InsertBefore(slicingEntry); } snap.MoveToNext(); // The differential and the snapshot are now both positioned on the first "real" slicing content element // Start by getting an unaltered copy of the current base definition, we need to re-insert a fresh copy // of it every time we encounter a new slice in the differential var slicingTemplate = (Profile.ElementComponent)snap.Current.DeepCopy(); var slicingName = snap.PathName; var first = true; do { if(first) { // The first time, we still have the original base definition available to slice first = false; } else { snap.InsertAfter((Profile.ElementComponent)slicingTemplate.DeepCopy()); //snap.MoveToNext(); } merge(snap, diff); } while (diff.MoveToNext(slicingName)); if (slicingEntry.Slicing.Rules != Profile.SlicingRules.Closed) { // Slices that are open in some form need to repeat the original "base" definition, // so that the open slices have a place to "fit in" snap.InsertAfter((Profile.ElementComponent)slicingTemplate.DeepCopy()); } //TODO: update/check the slice entry's min/max property to match what we've found in the slice group }
private void mergeSlice(ElementNavigator snap, ElementNavigator diff) { // diff is now located at the first repeat of a slice, which is (possibly) the slice entry // snap is located at the base definition of the element that will become sliced. But snap is not yet sliced. // Before we start, is the base element sliceable? if (!snap.Current.IsRepeating() && !isSlicedToOne(diff.Current)) { throw Error.InvalidOperation("The slicing entry in the differential at {0} indicates an unbounded slice, but the base element is not a repeating element", diff.Current.Path); } Profile.ElementComponent slicingEntry; // Yes, so, first, add the slicing entry to the snapshot. if (diff.Current.Slicing != null) { slicingEntry = createSliceEntry(snap.Current, diff.Current); snap.InsertBefore(slicingEntry); if (!diff.MoveToNext(diff.PathName)) { throw Error.InvalidOperation("Slicing has no elements beyond the slicing entry"); // currently impossible to happen } } else { // Mmmm....no slicing entry in the differential. This is only alloweable for extension slices, as a shorthand notation. if (!snap.Current.IsExtension()) { throw Error.InvalidOperation("The slice group at {0} does not start with a slice entry element", diff.Current.Path); } // In this case we insert a "prefab" extension slice. slicingEntry = createExtensionSlicingEntry(snap.Path); snap.InsertBefore(slicingEntry); } snap.MoveToNext(); // The differential and the snapshot are now both positioned on the first "real" slicing content element // Start by getting an unaltered copy of the current base definition, we need to re-insert a fresh copy // of it every time we encounter a new slice in the differential var slicingTemplate = (Profile.ElementComponent)snap.Current.DeepCopy(); var slicingName = snap.PathName; var first = true; do { if (first) { // The first time, we still have the original base definition available to slice first = false; } else { snap.InsertAfter((Profile.ElementComponent)slicingTemplate.DeepCopy()); //snap.MoveToNext(); } merge(snap, diff); }while (diff.MoveToNext(slicingName)); if (slicingEntry.Slicing.Rules != Profile.SlicingRules.Closed) { // Slices that are open in some form need to repeat the original "base" definition, // so that the open slices have a place to "fit in" snap.InsertAfter((Profile.ElementComponent)slicingTemplate.DeepCopy()); } //TODO: update/check the slice entry's min/max property to match what we've found in the slice group }