Ejemplo n.º 1
0
        // [WMR 20160720] NEW
        private void fixExtensionUrl(ElementNavigator nav)
        {
            var extElem = nav.Current;

            if (extElem.IsExtension() && nav.HasChildren)
            {
                // Resolve the canonical url of the extension definition from type[0]/profile[0]
                var primaryType = extElem.Type.FirstOrDefault();
                if (primaryType != null)
                {
                    var profile = primaryType.Profile.FirstOrDefault();
                    if (profile != null)
                    {
                        var snapExtPos = nav.Bookmark();
                        try
                        {
                            if (nav.MoveToChild("url"))
                            {
                                var urlElem = nav.Current;
                                if (urlElem != null && urlElem.Fixed == null)
                                {
                                    urlElem.Fixed = new FhirUri(profile);
                                }
                            }
                        }
                        finally
                        {
                            nav.ReturnToBookmark(snapExtPos);
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates matches between the elements pointed to by snapNav and diffNav. After returning, both
        /// navs will be located on the last element that was matched (e.g. in a slicing group)
        /// </summary>
        /// <param name="snapNav"></param>
        /// <param name="diffNav"></param>
        /// <returns></returns>
        private static List <MatchInfo> constructMatch(ElementNavigator snapNav, ElementNavigator diffNav)
        {
            // [WMR 20160802] snapNav and diffNav point to matching elements
            // Determine the associated action (Add, Merge, Slice)
            // If this represents a slice, then also process all the slice elements
            // Note that in case of a slice, both snapNav and diffNav will always point to the first element in the slice group

            var match = new MatchInfo()
            {
                BaseBookmark = snapNav.Bookmark(), DiffBookmark = diffNav.Bookmark()
            };

            bool baseIsSliced = snapNav.Current.Slicing != null;

            // [WMR 20160801] Only emit slicing entry for actual extension elements (with extension profile url)
            // Do not emit slicing entry for abstract Extension base element definition (inherited from external profiles)
            // bool diffIsExtension = diffNav.Current.IsExtension();
            bool diffIsExtension   = diffNav.Current.IsMappedExtension();
            var  nextDiffChildName = nextChildName(diffNav);
            bool diffIsSliced      = diffIsExtension || nextDiffChildName == diffNav.PathName;
            bool diffIsTypeSlice   = snapNav.Current.IsChoice() && snapNav.IsCandidateTypeSlice(diffNav.PathName);

            if (diffIsTypeSlice)
            {
                if (baseIsSliced)
                {
                    // TODO...?
                    throw Error.NotSupported("Cannot expand snapshot. Reslicing type slices is not yet supported (path = '{0}').", diffNav.Path);
                }

                // Only a single type slice? Then merge
                // e.g. base:value[x] <=> diff:valueString
                if (!snapNav.IsCandidateTypeSlice(nextDiffChildName))
                {
                    match.Action = MatchAction.Merge;
                    return(new List <MatchInfo>()
                    {
                        match
                    });
                }

                // Multiple type slices
                // e.g. base:value[x] <=> diff:valueString + diff:valueBoolean
                return(constructTypeSliceMatch(snapNav, diffNav));
            }
            else if (baseIsSliced || diffIsSliced)
            {
                // This is a slice match - process it separately
                return(constructSliceMatch(snapNav, diffNav));
            }
            else
            {
                // Easiest case - one to one match, without slicing involved
                match.Action = MatchAction.Merge;
                return(new List <MatchInfo>()
                {
                    match
                });
            }
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        private void makeSlice(ElementNavigator snap, ElementNavigator diff)
        {
            // diff is now located at the first repeat of a slice, which is normally the slice entry (Extension slices need
            // not have a slicing 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() && !snap.Current.IsChoice())
            {
                throw Error.InvalidOperation("The slicing entry in the differential at {0} indicates an slice, but the base element is not a repeating or choice element",
                                             diff.Current.Path);
            }

            ElementDefinition slicingEntry = diff.Current;

            //Mmmm....no slicing entry in the differential. This is only alloweable for extension slices, as a shorthand notation.
            if (slicingEntry.Slicing == null)
            {
                if (slicingEntry.IsExtension())
                {
                    // In this case we create a "prefab" extension slice (with just slincing info)
                    // that's simply merged with the original element in base
                    slicingEntry = createExtensionSlicingEntry();
                }
                else
                {
                    throw Error.InvalidOperation("The slice group at {0} does not start with a slice entry element", diff.Current.Path);
                }
            }

            (new ElementDefnMerger(_markChanges)).Merge(snap.Current, slicingEntry);

            ////TODO: update / check the slice entry's min/max property to match what we've found in the slice group
        }
Ejemplo n.º 5
0
        private void mergeElement(ElementNavigator snap, ElementNavigator diff)
        {
            (new ElementDefnMerger(_markChanges)).Merge(snap.Current, diff.Current);

            if (diff.HasChildren)
            {
                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

                    // Note that since we merged the parent, a (shorthand) typeslice will already
                    // have reduced the numer of types to 1. Still, if you don't do that, we cannot
                    // accept constraints on children, need to select a single type first...
                    if (snap.Current.Type.Count > 1)
                    {
                        throw new NotSupportedException("Differential has a constraint on a choice element {0}, but does so without using a type slice".FormatWith(diff.Path));
                    }

                    ExpandElement(snap, _resolver);

                    if (!snap.HasChildren)
                    {
                        // Snapshot's element turns out not to be expandable, so we can't move to the desired path
                        throw Error.InvalidOperation("Differential has nested constraints for node {0}, but this is a leaf node in base", diff.Path);
                    }
                }

                // Now, recursively merge the children
                merge(snap, diff);
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates a differential structure with all "skipped" parents filled in.
        /// </summary>
        /// <param name="differential"></param>
        /// <returns>The full tree structure representing the differential</returns>
        /// <remarks>This operation will not touch the source differential, but instead will return a new structure.</remarks>
        public Profile.ConstraintComponent MakeTree()
        {
            var diff = (Profile.ConstraintComponent)_source.DeepCopy();   // We're going to modify the differential

            if (diff.Element == null || diff.Element.Count == 0)
            {
                return(diff);                                                        // nothing to do
            }
            var index    = 0;
            var elements = diff.Element;

            while (index < elements.Count)
            {
                var thisPath = elements[index].Path;
                var prevPath = index > 0 ? elements[index - 1].Path : String.Empty;

                if (thisPath.IndexOf('.') == -1)
                {
                    // I am a root node, just one segment of path, I need to be the first element
                    if (index != 0)
                    {
                        throw Error.InvalidOperation("Differential has multiple roots");
                    }

                    // Else, I am fine, proceed
                    index++;
                }
                else if (ElementNavigator.IsSibling(thisPath, prevPath) || ElementNavigator.IsDirectChildPath(prevPath, thisPath))
                {
                    // The previous path is a sibling, or my direct parent, so everything is alright, proceed to next node
                    index++;
                }
                else
                {
                    var parentPath = ElementNavigator.GetParentPath(thisPath);

                    if (prevPath == String.Empty || !prevPath.StartsWith(parentPath + "."))
                    {
                        // We're missing a path part, insert an empty parent
                        var parentElement = new Profile.ElementComponent()
                        {
                            Path = parentPath
                        };
                        elements.Insert(index, parentElement);

                        // Now, we're not sure this parent has parents, so proceed by checking the parent we have just inserted
                        // so -> index is untouched
                    }
                    else
                    {
                        // So, my predecessor an I share ancestry, of which I am sure it has been inserted by this algorithm
                        // before because of my predecessor, so we're fine.
                        index++;
                    }
                }
            }

            return(diff);
        }
Ejemplo n.º 7
0
        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);
            }
        }
Ejemplo n.º 8
0
        public void MakeDifferentialTree()
        {
            var struc = new Profile.ProfileStructureComponent();

            struc.Differential         = new Profile.ConstraintComponent();
            struc.Differential.Element = new List <Profile.ElementComponent>();
            var e = struc.Differential.Element;

            e.Add(new Profile.ElementComponent()
            {
                Path = "A.B.C1"
            });
            e.Add(new Profile.ElementComponent()
            {
                Path = "A.B.C1"
            });
            e.Add(new Profile.ElementComponent()
            {
                Path = "A.B.C2"
            });
            e.Add(new Profile.ElementComponent()
            {
                Path = "A.B"
            });
            e.Add(new Profile.ElementComponent()
            {
                Path = "A.B.C1.D"
            });
            e.Add(new Profile.ElementComponent()
            {
                Path = "A.D.F"
            });

            var tree = new DifferentialTreeConstructor(struc.Differential).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"));
        }
Ejemplo n.º 9
0
        internal static bool ExpandElement(ElementNavigator nav, ArtifactResolver resolver)
        {
            if (resolver == null)
            {
                throw Error.ArgumentNull("source");
            }
            if (nav.Current == null)
            {
                throw Error.ArgumentNull("Navigator is not positioned on an element");
            }

            if (nav.HasChildren)
            {
                return(true);                     // already has children, we're not doing anything extra
            }
            var defn = nav.Current;

            if (!String.IsNullOrEmpty(defn.NameReference))
            {
                var sourceNav = new ElementNavigator(nav);
                var success   = sourceNav.JumpToNameReference(defn.NameReference);

                if (!success)
                {
                    throw Error.InvalidOperation("Trying to navigate down a node that has a nameReference of '{0}', which cannot be found in the StructureDefinition".FormatWith(defn.NameReference));
                }

                nav.CopyChildren(sourceNav);
            }
            else if (defn.Type != null && defn.Type.Count > 0)
            {
                if (defn.Type.Count > 1)
                {
                    throw new NotSupportedException("Element at path {0} has a choice of types, cannot expand".FormatWith(nav.Path));
                }
                else
                {
                    var coreType = resolver.GetStructureDefinitionForCoreType(defn.Type[0].Code.ToString());

                    if (coreType == null)
                    {
                        throw Error.NotSupported("Trying to navigate down a node that has a declared base type of '{0}', which is unknown".FormatWith(defn.Type[0].Code));
                    }
                    if (coreType.Snapshot == null)
                    {
                        throw Error.NotSupported("Found definition of base type '{0}', but is does not contain a snapshot representation".FormatWith(defn.Type[0].Code));
                    }

                    generateBaseElements(coreType.Snapshot.Element);
                    var sourceNav = new ElementNavigator(coreType.Snapshot.Element);
                    sourceNav.MoveToFirstChild();
                    nav.CopyChildren(sourceNav);
                }
            }

            return(true);
        }
Ejemplo n.º 10
0
        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 " + snapNav.Path + (snapNav.Current != null && snapNav.Current.Name != null ? " '" + snapNav.Current.Name + "'" : null));
                // 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)
                    {
                        // Add new slice after the last existing slice in base profile
                        snapNav.MoveToLastSlice();
                        var lastSlice = snapNav.Bookmark();

                        // Initialize slice by duplicating base slice entry
                        snapNav.ReturnToBookmark(match.BaseBookmark);
                        snapNav.DuplicateAfter(lastSlice);
                        // Important: explicitly clear the slicing node in the copy!
                        snapNav.Current.Slicing = null;

                        markChange(snapNav.Current);

                        // Merge differential
                        mergeElement(snapNav, diffNav);
                    }
                    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);
            }
        }
Ejemplo n.º 11
0
        // [WMR 20160801] NEW
        // Try to find matching slice element in base profile
        // Assume snapNav is positioned on slicing entry node
        // Assume diffNav is positioned on a resliced element node
        // Returns true when match is found, matchingSlice points to match in base (merge here)
        // Returns false otherwise, matchingSlice points to current node in base
        // Maintain snapNav current position
        private static bool FindBaseSlice(ElementNavigator snapNav, ElementNavigator diffNav, out Bookmark matchingSlice)
        {
            var slicing = snapNav.Current.Slicing;

            Debug.Assert(slicing != null);

            var slicingIntro = matchingSlice = snapNav.Bookmark();
            var result       = false;

            // url, type@profile, @type + @profile
            if (IsTypeProfileDiscriminator(slicing.Discriminator))
            {
                // [WMR 20160802] Handle complex extension constraints
                // e.g. sdc-questionnaire, Path = 'Questionnaire.group.question.extension.extension', name = 'question'
                // Type.Profile = 'http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen#question'
                // snapNav has already expanded target extension definition 'questionnaire-enableWhen'
                // => Match to base profile on child element with name 'question'

                var diffProfiles = diffNav.Current.Type.FirstOrDefault().Profile.ToArray();
                if (diffProfiles == null || diffProfiles.Length == 0)
                {
                    throw Error.InvalidOperation("Differential is reslicing on url, but resliced element has no type profile (path = '{0}').", diffNav.Path);
                }
                if (diffProfiles.Length > 1)
                {
                    throw Error.NotSupported("Cannot expand snapshot. Reslicing on complex discriminator is not supported (path = '{0}').", diffNav.Path);
                }

                var    diffProfile = diffProfiles.FirstOrDefault();
                string profileUrl, elementName;
                var    isComplex = SnapshotGenerator.IsComplexProfileReference(diffProfile, out profileUrl, out elementName);
                while (snapNav.MoveToNext(snapNav.PathName))
                {
                    var baseProfiles = snapNav.Current.Type.FirstOrDefault().Profile;
                    result = isComplex
                             // Match on element name
                        ? snapNav.Current.Name == elementName
                             // Match on profile(s)
                        : baseProfiles.SequenceEqual(diffProfiles);
                    if (result)
                    {
                        matchingSlice = snapNav.Bookmark();
                        break;
                    }
                }
            }
            // TODO: Support other discriminators
            else
            {
                throw Error.NotSupported("Cannot expand snapshot. Reslicing on discriminator '{0}' is not supported yet (path = '{1}').", string.Join("|", slicing.Discriminator), snapNav.Path);
            }

            snapNav.ReturnToBookmark(slicingIntro);

            return(result);
        }
Ejemplo n.º 12
0
        /// <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);
        }
Ejemplo n.º 13
0
        private void expandBaseElement(ElementNavigator snap, ElementNavigator diff)
        {
            snap.ExpandElement(_resolver);

            if (!snap.HasChildren)
            {
                // Snapshot's element turns out not to be expandable, so we can't move to the desired path
                throw Error.InvalidOperation("Differential has nested constraints for node {0}, but this is a leaf node in base", diff.Path);
            }
        }
Ejemplo n.º 14
0
        public void MakeDifferentialTree()
        {
            var e = new List <ElementDefinition>();

            e.Add(new ElementDefinition()
            {
                Path = "A.B.C1"
            });
            e.Add(new ElementDefinition()
            {
                Path = "A.B.C1"
            });
            e.Add(new ElementDefinition()
            {
                Path = "A.B.C2"
            });
            e.Add(new ElementDefinition()
            {
                Path = "A.B"
            });
            e.Add(new ElementDefinition()
            {
                Path = "A.B.C1.D"
            });
            e.Add(new ElementDefinition()
            {
                Path = "A.D.F"
            });

            var tree = new DifferentialTreeConstructor(e).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"));
        }
Ejemplo n.º 15
0
        public void CopyChildTree()
        {
            var dest = createTestNav();

            var struc = new StructureDefinition();

            struc.Snapshot         = new StructureDefinition.SnapshotComponent();
            struc.Snapshot.Element = new List <ElementDefinition>();
            var e = struc.Snapshot.Element;

            e.Add(new ElementDefinition()
            {
                Path = "X"
            });
            e.Add(new ElementDefinition()
            {
                Path = "X.Y1"
            });
            e.Add(new ElementDefinition()
            {
                Path = "X.Y2"
            });
            e.Add(new ElementDefinition()
            {
                Path = "X.Y2.Z1"
            });
            e.Add(new ElementDefinition()
            {
                Path = "X.Y2.Z2"
            });
            var source = new ElementNavigator(e);

            Assert.IsTrue(dest.JumpToFirst("A.D"));
            var dstPos = dest.OrdinalPosition;

            source.MoveToFirstChild();
            var srcPos = source.OrdinalPosition;

            Assert.IsTrue(dest.CopyChildren(source));
            Assert.AreEqual(srcPos, source.OrdinalPosition, "source did not remain on original position");
            Assert.AreEqual(dstPos, dest.OrdinalPosition, "dest did not remain on original position");

            Assert.IsTrue(dest.MoveToFirstChild());
            Assert.AreEqual("Y1", dest.PathName);
            Assert.IsTrue(dest.MoveToNext());
            Assert.AreEqual("Y2", dest.PathName);
            Assert.IsFalse(dest.MoveToNext());
            Assert.IsTrue(dest.MoveToFirstChild());
            Assert.AreEqual("Z1", dest.PathName);
            Assert.IsTrue(dest.MoveToNext());
            Assert.AreEqual("Z2", dest.PathName);
            Assert.IsFalse(dest.MoveToNext());
        }
Ejemplo n.º 16
0
        private static string nextChildName(ElementNavigator nav)
        {
            string result = null;

            if (nav.MoveToNext())
            {
                result = nav.PathName;
                nav.MoveToPrevious();
            }

            return(result);
        }
Ejemplo n.º 17
0
        private void merge(ElementNavigator snap, ElementNavigator diff)
        {
            (new ElementDefnMerger(_markChanges)).Merge(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

                    if (snap.Current.Type.Count > 1)
                    {
                        throw new NotSupportedException("Differential has a constraint on a choice element {0}, but does so without using a type slice".FormatWith(diff.Path));
                    }

                    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.MoveTo(diff.PathName)))  // HACK: I don't think it should be allowed for a diff to list constraints in the wrong order...
                    {
                        throw Error.InvalidOperation("Differential has a constraint for path '{0}', which does not exist in its base", diff.Path);
                    }
                    firstEntry = false;

                    // Child found in both, merge them
                    if (countChildNameRepeats(diff) > 1 || 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();
            }
        }
        public XElement generateStructureTable(Profile.ProfileStructureComponent structure, bool diff, Profile profile)
        {
            HierarchicalTableGenerator gen = new HierarchicalTableGenerator(_pkp);
            var model = TableModel.CreateNormalTable();

            // List<Profile.ElementComponent> list = diff ? structure.getDifferential().getElement() : structure.getSnapshot().getElement();   DSTU2
            var list = structure.Element;
            var nav  = new ElementNavigator(structure);

            nav.MoveToFirstChild();

            genElement(gen, model.Rows, nav, profile, true);
            return(gen.generate(model));
        }
Ejemplo n.º 19
0
        private static List <MatchInfo> constructSliceMatch(ElementNavigator snapNav, ElementNavigator diffNav)
        {
            var result = new List <MatchInfo>();

            var  bm           = snapNav.Bookmark();
            var  diffName     = diffNav.PathName;
            bool baseIsSliced = snapNav.Current.Slicing != null;
            bool diffIsSliced = diffNav.Current.IsExtension() || nextChildName(diffNav) == diffNav.PathName;

            if (baseIsSliced)
            {
                throw Error.NotSupported("Cannot yet handle re-slicing found at diff {0}".FormatWith(diffNav.Path));
            }

            // For the first entries with explicit slicing information (or implicit if this is an extension),
            // generate a match between the base's unsliced element and the first entry in the diff
            if (diffNav.Current.Slicing != null || diffNav.Current.IsExtension())
            {
                // Differential has information for the slicing entry
                result.Add(new MatchInfo()
                {
                    BaseBookmark = bm,
                    DiffBookmark = diffNav.Bookmark(),
                    Action       = MatchAction.Slice
                });

                if (!diffNav.Current.IsExtension())
                {
                    if (!diffNav.MoveToNext())
                    {
                        throw Error.InvalidOperation("Differential has a slicing entry {0}, but no first actual slice", diffNav.Path);
                    }
                }
            }

            // Then, generate a match between the base's unsliced element and the slicing entries in the diff
            // Note that the first entry may serve a double role and have to result matches (one for the constraints, one as a slicing entry)
            do
            {
                result.Add(new MatchInfo()
                {
                    BaseBookmark = bm,
                    DiffBookmark = diffNav.Bookmark(),
                    Action       = MatchAction.Add
                });
            } while (nextChildName(diffNav) == diffName && diffNav.MoveToNext());  // Warning: Subtle use of short-cut evaluation

            return(result);
        }
Ejemplo n.º 20
0
        public void TestExpandChild()
        {
            var loader     = new StructureLoader(ArtifactResolver.CreateDefault());
            var profStruct = loader.Locate(new Uri("http://hl7.org/fhir/Profile/Profile"), new Code("Profile"));

            var nav = new ElementNavigator(profStruct);

            nav.JumpToFirst("Profile.telecom");
            Assert.IsTrue(nav.ExpandElement(loader));
            Assert.IsTrue(nav.MoveToChild("period"));

            nav.JumpToFirst("Profile.extensionDefn.definition");
            Assert.IsTrue(nav.ExpandElement(loader));
            Assert.IsTrue(nav.MoveToChild("max"));
        }
Ejemplo n.º 21
0
        public void TestExpandChild()
        {
            var loader     = new StructureLoader(ArtifactResolver.CreateDefault());
            var profStruct = loader.LocateStructure(new Uri("http://hl7.org/fhir/Profile/Profile"), new Code("Profile"));

            var nav = new ElementNavigator(profStruct.Snapshot);

            nav.JumpToFirst("Profile.telecom");
            Assert.IsTrue(nav.ExpandElement(loader));
            Assert.IsTrue(nav.MoveToChild("period"));

            nav.JumpToFirst("Profile.structure.differential");
            Assert.IsTrue(nav.ExpandElement(loader));
            Assert.IsTrue(nav.MoveToChild("element"));
        }
Ejemplo n.º 22
0
        public XElement generateStructureTable(String defFile, Profile.ProfileStructureComponent structure, bool diff, String imageFolder,
                                               bool inlineGraphics, Profile profile, string profileUrl, String profileBaseFileName)
        {
            HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics);
            TableModel model = gen.initNormalTable();

            // List<Profile.ElementComponent> list = diff ? structure.getDifferential().getElement() : structure.getSnapshot().getElement();   DSTU2
            var list = structure.Element;
            var nav  = new ElementNavigator(structure);

            nav.MoveToFirstChild();

            genElement(defFile == null ? null : defFile + "#" + structure.Name + ".", gen, model.getRows(), nav, profile, diff, profileUrl, profileBaseFileName);
            return(gen.generate(model));
        }
Ejemplo n.º 23
0
        public void TestModificationResilience()
        {
            var nav = createTestNav();

            Assert.IsTrue(nav.JumpToFirst("A.D"));

            var nav2 = new ElementNavigator(nav);

            // Delete A.D in nav
            Assert.IsTrue(nav.Delete());

            // Should still be there in nav2
            Assert.IsFalse(nav.JumpToFirst("A.D"));
            Assert.IsTrue(nav2.JumpToFirst("A.D"));
        }
Ejemplo n.º 24
0
        private void mergeElement(ElementNavigator snap, ElementNavigator diff)
        {
            if (_settings.ExpandTypeProfiles)
            {
                ExpandTypeProfiles(snap, diff);
            }

            // [WMR 20160720] Changed, use SnapshotGeneratorSettings
            // (new ElementDefnMerger(_markChanges)).Merge(snap.Current, diff.Current);
            (new ElementDefnMerger(_settings.MarkChanges)).Merge(snap.Current, diff.Current);

            if (diff.HasChildren)
            {
                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

                    // Note that since we merged the parent, a (shorthand) typeslice will already
                    // have reduced the numer of types to 1. Still, if you don't do that, we cannot
                    // accept constraints on children, need to select a single type first...
                    if (snap.Current.Type.Count > 1)
                    {
                        throw Error.InvalidOperation("Differential has a constraint on a choice element '{0}', but does so without using a type slice", diff.Path);
                    }

                    expandElement(snap, _resolver, _settings);

                    if (!snap.HasChildren)
                    {
                        // Snapshot's element turns out not to be expandable, so we can't move to the desired path
                        throw Error.InvalidOperation("Differential has nested constraints for node '{0}', but this is a leaf node in base", diff.Path);
                    }
                }

                // Now, recursively merge the children
                merge(snap, diff);

                // [WMR 20160720] NEW
                // generate [...]extension.url/fixedUri if missing
                // Ewout: [...]extension.url may be missing from differential
                // Information is redundant (same value as [...]extension/type/profile)
                // => snapshot generator should add this
                fixExtensionUrl(snap);
            }
        }
Ejemplo n.º 25
0
        public void Generate(StructureDefinition structure)
        {
            if (structure.Differential == null)
            {
                throw Error.Argument("structure", "structure does not contain a differential specification");
            }

            // [WMR 20160718] Also accept extension definitions (IsConstraint == false)
            if (!structure.IsConstraint && !structure.IsExtension)
            {
                throw Error.Argument("structure", "structure is not a constraint or extension");
            }

            if (structure.Base == null)
            {
                throw Error.Argument("structure", "structure is a constraint, but no base has been specified");
            }

            var differential = structure.Differential;

            var baseStructure = _resolver.GetStructureDefinition(structure.Base);

            if (baseStructure == null)
            {
                throw Error.InvalidOperation("Could not locate the base StructureDefinition for url " + structure.Base);
            }
            if (baseStructure.Snapshot == null)
            {
                throw Error.InvalidOperation("Snapshot generator required the base at '{0}' to have a snapshot representation", structure.Base);
            }

            var snapshot = (StructureDefinition.SnapshotComponent)baseStructure.Snapshot.DeepCopy();

            generateBaseElements(snapshot.Element);
            var snapNav = new ElementNavigator(snapshot.Element);

            // Fill out the gaps (mostly missing parents) in the differential representation
            var fullDifferential = new DifferentialTreeConstructor(differential.Element).MakeTree();
            var diffNav          = new ElementNavigator(fullDifferential);

            merge(snapNav, diffNav);

            structure.Snapshot = new StructureDefinition.SnapshotComponent()
            {
                Element = snapNav.ToListOfElements()
            };
        }
        //private static void mergeStructure(Profile.ConstraintComponent snapshot, Profile.ConstraintComponent differential)
        //{
        //    if (differential.Name != null) snapshot.Name = differential.Name;
        //    if (differential.Publish != null) snapshot.Publish = differential.Publish;
        //    if (differential.Purpose != null) snapshot.Purpose = differential.Purpose;
        //}


        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 (countChildNameRepeats(diff) > 1 || 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();
            }
        }
Ejemplo n.º 27
0
        /// <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);
        }
Ejemplo n.º 28
0
        public void TestExpandChild()
        {
            var qStructDef = _source.GetStructureDefinition("http://hl7.org/fhir/StructureDefinition/Questionnaire");

            Assert.IsNotNull(qStructDef);
            Assert.IsNotNull(qStructDef.Snapshot);

            var nav = new ElementNavigator(qStructDef.Snapshot.Element);

            nav.JumpToFirst("Questionnaire.telecom");
            Assert.IsTrue(nav.ExpandElement(_source));
            Assert.IsTrue(nav.MoveToChild("period"), "Did not move into complex datatype ContactPoint");

            nav.JumpToFirst("Questionnaire.group");
            Assert.IsTrue(nav.ExpandElement(_source));
            Assert.IsTrue(nav.MoveToChild("title"), "Did not move into internally defined backbone element Group");
        }
        public void Expand(Profile.ProfileStructureComponent structure)
        {
            if (structure.Differential == null)
            {
                throw Error.Argument("structure", "structure does not contain a differential specification");
            }
            var differential = structure.Differential;

            var baseStructure = _loader.LocateBaseStructure(structure.TypeElement);

            if (baseStructure == null)
            {
                throw Error.InvalidOperation("Could not locate the base profile for type {0}", structure.TypeElement.ToString());
            }
            if (baseStructure.Snapshot == null)
            {
                throw Error.InvalidOperation("Base definition to use for expansion lacks a snapshot representation");
            }

            //   var baseUri = StructureLoader.BuildBaseStructureUri(structure.TypeElement).ToString();

            var snapshot = (Profile.ConstraintComponent)baseStructure.Snapshot.DeepCopy();

            //DSTU1
            //snapshot.SetStructureForm(StructureForm.Snapshot);
            //snapshot.SetStructureBaseUri(baseUri.ToString());
            //mergeStructure(snapshot, differential);

            var fullDifferential = new DifferentialTreeConstructor(differential).MakeTree();

            var snapNav = new ElementNavigator(snapshot);

            snapNav.MoveToFirstChild();

            var diffNav = new ElementNavigator(fullDifferential);

            diffNav.MoveToFirstChild();

            merge(snapNav, diffNav);

            //TODO: Merge search params?

            snapNav.CommitChanges();
            structure.Snapshot = snapNav.Elements;
        }
Ejemplo n.º 30
0
        /// <summary>
        /// Creates matches between the elements pointed to by snapNav and diffNav. After returning, both
        /// navs will be located on the last element that was matched (e.g. in a slicing group)
        /// </summary>
        /// <param name="snapNav"></param>
        /// <param name="diffNav"></param>
        /// <returns></returns>
        private static List <MatchInfo> constructMatch(ElementNavigator snapNav, ElementNavigator diffNav)
        {
            var match = new MatchInfo()
            {
                BaseBookmark = snapNav.Bookmark(), DiffBookmark = diffNav.Bookmark()
            };

            bool baseIsSliced = snapNav.Current.Slicing != null;
            bool diffIsSliced = diffNav.Current.IsExtension() || nextChildName(diffNav) == diffNav.PathName;

            // Easiest case - one to one match, without slicing involved
            if (!baseIsSliced && !diffIsSliced)
            {
                match.Action = MatchAction.Merge;

                return(new List <MatchInfo>()
                {
                    match
                });
            }

            // Check whether this is a type-slice shorthand - only the most common usecase
            // is supported for this case, otherwise us a normal type-slice
            // See also gForge issues #8974 and #8973
            if (isPossibleTypeSlice(snapNav.PathName, diffNav.PathName))
            {
                if (baseIsSliced)
                {
                    throw Error.NotSupported("Using a slicing shorthand ({0}) is not supported for re-slicing".FormatWith(diffNav.Path));
                }
                if (diffIsSliced)
                {
                    throw Error.NotSupported("Using a slicing shorthand ({0}) can only be used to introduce a single slice".FormatWith(diffNav.Path));
                }

                match.Action = MatchAction.Merge;
                return(new List <MatchInfo>()
                {
                    match
                });
            }

            // Else, this is a slice match - process it separately
            return(constructSliceMatch(snapNav, diffNav));
        }
        public void MakeDifferentialTree()
        {
            var struc = new Profile.ProfileStructureComponent();
            struc.Differential = new Profile.ConstraintComponent();
            struc.Differential.Element = new List<Profile.ElementComponent>();
            var e = struc.Differential.Element;

            e.Add(new Profile.ElementComponent() { Path = "A.B.C1" });
            e.Add(new Profile.ElementComponent() { Path = "A.B.C1" });
            e.Add(new Profile.ElementComponent() { Path = "A.B.C2" });
            e.Add(new Profile.ElementComponent() { Path = "A.B" });
            e.Add(new Profile.ElementComponent() { Path = "A.B.C1.D" });
            e.Add(new Profile.ElementComponent() { Path = "A.D.F" });

            var tree = new DifferentialTreeConstructor(struc.Differential).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"));
        }
        public void CopyChildTree()
        {
            var dest = createTestNav();

            var struc = new StructureDefinition();
            struc.Snapshot = new StructureDefinition.StructureDefinitionSnapshotComponent();
            struc.Snapshot.Element = new List<ElementDefinition>();
            var e = struc.Snapshot.Element;

            e.Add(new ElementDefinition() { Path = "X" });
            e.Add(new ElementDefinition() { Path = "X.Y1" });
            e.Add(new ElementDefinition() { Path = "X.Y2" });
            e.Add(new ElementDefinition() { Path = "X.Y2.Z1" });
            e.Add(new ElementDefinition() { Path = "X.Y2.Z2" });
            var source = new ElementNavigator(struc);

            Assert.IsTrue(dest.JumpToFirst("A.D"));
            var dstPos = dest.OrdinalPosition;

            source.MoveToFirstChild();
            var srcPos = source.OrdinalPosition;

            Assert.IsTrue(dest.CopyChildren(source));
            Assert.AreEqual(srcPos, source.OrdinalPosition, "source did not remain on original position");
            Assert.AreEqual(dstPos, dest.OrdinalPosition, "dest did not remain on original position");

            Assert.IsTrue(dest.MoveToFirstChild());
            Assert.AreEqual("Y1", dest.PathName);
            Assert.IsTrue(dest.MoveToNext());
            Assert.AreEqual("Y2", dest.PathName);
            Assert.IsFalse(dest.MoveToNext());
            Assert.IsTrue(dest.MoveToFirstChild());
            Assert.AreEqual("Z1", dest.PathName);
            Assert.IsTrue(dest.MoveToNext());
            Assert.AreEqual("Z2", dest.PathName);
            Assert.IsFalse(dest.MoveToNext());
        }
        public void TestModificationResilience()
        {
            var nav = createTestNav();

            Assert.IsTrue(nav.JumpToFirst("A.D"));

            var nav2 = new ElementNavigator(nav);

            // Delete A.D in nav
            Assert.IsTrue(nav.Delete()); 

            // Should still be there in nav2
            Assert.IsFalse(nav.JumpToFirst("A.D"));
            Assert.IsTrue(nav2.JumpToFirst("A.D"));
        }
Ejemplo n.º 34
0
        public void MakeDifferentialTree()
        {
            var e = new List<ElementDefinition>();

            e.Add(new ElementDefinition() { Path = "A.B.C1" });
            e.Add(new ElementDefinition() { Path = "A.B.C1" });
            e.Add(new ElementDefinition() { Path = "A.B.C2" });
            e.Add(new ElementDefinition() { Path = "A.B" });
            e.Add(new ElementDefinition() { Path = "A.B.C1.D" });
            e.Add(new ElementDefinition() { Path = "A.D.F" });

            var tree = new DifferentialTreeConstructor(e).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"));
        }
        public void TestExpandChild()
        {
            var loader = new StructureLoader(ArtifactResolver.CreateDefault());
            var profStruct = loader.LocateStructure(new Uri("http://hl7.org/fhir/Profile/Profile"), new Code("Profile"));

            var nav = new ElementNavigator(profStruct.Snapshot);
            
            nav.JumpToFirst("Profile.telecom");
            Assert.IsTrue(nav.ExpandElement(loader));
            Assert.IsTrue(nav.MoveToChild("period"));

            nav.JumpToFirst("Profile.structure.differential");
            Assert.IsTrue(nav.ExpandElement(loader));
            Assert.IsTrue(nav.MoveToChild("element"));
        }
Ejemplo n.º 36
0
        public void TestExpandChild()
        {            
            var qStructDef = _source.GetStructureDefinition("http://hl7.org/fhir/StructureDefinition/Questionnaire");
            Assert.IsNotNull(qStructDef);
            Assert.IsNotNull(qStructDef.Snapshot);

            var nav = new ElementNavigator(qStructDef.Snapshot.Element);
            
            nav.JumpToFirst("Questionnaire.telecom");
            Assert.IsTrue(nav.ExpandElement(_source));
            Assert.IsTrue(nav.MoveToChild("period"), "Did not move into complex datatype ContactPoint");

            nav.JumpToFirst("Questionnaire.group");
            Assert.IsTrue(nav.ExpandElement(_source));
            Assert.IsTrue(nav.MoveToChild("title"), "Did not move into internally defined backbone element Group");
        }