Exemplo n.º 1
0
            // [WMR 20170209] TODO: Merge global mapping components
#if false
            /// <summary>Merge two lists of global <see cref="StructureDefinition.MappingComponent"/> definitions.</summary>
            public static List<StructureDefinition.MappingComponent> Merge(SnapshotGenerator generator,
                List<StructureDefinition.MappingComponent> snap, List<StructureDefinition.MappingComponent> diff)
            {
                var merger = new ElementDefnMerger(generator);
                // Merge global mapping definitions having the same (unique) mapping id
                return merger.mergeCollection(snap, diff, (a, b) => a.Identity == b.Identity);
            }
Exemplo n.º 2
0
        // Match current snapshot and differential extension slice elements on extension type profile
        // Returns an initialized MatchInfo with action = Merge | Add
        static void matchExtensionSlice(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav, List <string> discriminators, MatchInfo match)
        {
            // [WMR 20170110] Accept missing slicing component, e.g. to close the extension slice: Extension.extension { max = 0 }
            // if (discriminators == null || discriminators.Count > 1 || discriminators.FirstOrDefault() != "url")
            if (discriminators != null && (discriminators.Count != 1 || discriminators.FirstOrDefault() != "url"))
            {
                // Invalid extension discriminator; generate issue and ignore
                Debug.WriteLine($"[{nameof(ElementMatcher)}.{nameof(matchExtensionSlice)}] Warning! Invalid discriminator for extension slice (path = '{diffNav.Path}') - must be 'url'.");

                match.Issue = SnapshotGenerator.CreateIssueInvalidExtensionSlicingDiscriminator(diffNav.Current);
            }

            // Ignore the specified discriminator, always match on url
            var snapExtensionUri = getExtensionProfileUri(snapNav.Current);
            var diffExtensionUri = getExtensionProfileUri(diffNav.Current);

            // if (snapExtensionUri == diffExtensionUri)
            if (StringComparer.Ordinal.Equals(snapExtensionUri, diffExtensionUri))
            {
                match.BaseBookmark = snapNav.Bookmark();
                match.Action       = MatchAction.Merge;
            }
            else
            {
                match.Action = MatchAction.Add;
            }
        }
Exemplo n.º 3
0
        // Match current snapshot and differential slice elements on @type = Element.Type.Code
        // Returns an initialized MatchInfo with action = Merge | Add
        // defaultBase represents the base element for newly introduced slices
        static MatchInfo matchSliceByTypeCode(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav, Bookmark defaultBase)
        {
            var match = new MatchInfo()
            {
                DiffBookmark = diffNav.Bookmark()
            };

            var diffTypeCodes = diffNav.Current.Type.Select(t => t.Code).ToList();

            if (diffTypeCodes.Count == 0)
            {
                Debug.Print($"[{nameof(ElementMatcher)}.{nameof(matchSliceByTypeCode)}] Error! Element '{diffNav.Path}' is part of a @type slice group, but the element itself has no type.");

                match.BaseBookmark = defaultBase;
                match.Action       = MatchAction.Invalid;
                match.Issue        = SnapshotGenerator.CreateIssueTypeSliceWithoutType(diffNav.Current);
                return(match);
            }

            var snapTypeCodes = snapNav.Current.Type.Select(t => t.Code);

            if (snapTypeCodes.SequenceEqual(diffTypeCodes))
            {
                match.BaseBookmark = snapNav.Bookmark();
                match.Action       = MatchAction.Merge;
                return(match);
            }

            match.BaseBookmark = defaultBase;
            match.Action       = MatchAction.Add;
            return(match);
        }
Exemplo n.º 4
0
        // Match current snapshot and differential slice elements
        // Returns an initialized MatchInfo with action = Merge | Add
        // defaultBase represents the base element for newly introduced slices
        static MatchInfo matchSlice(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav, List <string> discriminators, Bookmark defaultBase)
        {
            Debug.Assert(diffNav.Current.Slicing == null); // Caller must handle reslicing

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

            // 1. If the diff slice has a name, than match base slice by name
            var diffSliceName = diffNav.Current.Name;

            if (!string.IsNullOrEmpty(diffSliceName))
            {
                // if (snapNav.PathName == diffSliceName)
                if (StringComparer.Ordinal.Equals(snapNav.Current.Name, diffSliceName))
                {
                    match.BaseBookmark = snapNav.Bookmark();
                    match.Action       = MatchAction.Merge;
                    return(match);
                }
                else
                {
                    match.BaseBookmark = defaultBase;
                    match.Action       = MatchAction.Add;
                }
                return(match);
            }

            // Slice has no name
            // Allowed for:
            // - Extensions => discriminator = url
            // - type slices => discriminator = @type / @profile

            if (diffNav.Current.IsExtension())
            {
                // Discriminator = url => match on ElementDefinition.Type[0].Profile
                return(matchExtensionSlice(snapNav, diffNav, discriminators, defaultBase));
            }

            else if (discriminators.Count == 1 && isTypeDiscriminator(discriminators[0]))
            {
                // Discriminator = @type => match on ElementDefinition.Type[0].Code
                return(matchSliceByTypeCode(snapNav, diffNav, defaultBase));
            }

            else if (isTypeProfileDiscriminator(discriminators))
            {
                // Discriminator = type@profile, { @type, @profile }
                return(matchSliceByTypeProfile(snapNav, diffNav, defaultBase));
            }

            // Error! Unsupported discriminator => slices must be named
            match.BaseBookmark = defaultBase;
            match.Action       = MatchAction.Invalid;
            match.Issue        = SnapshotGenerator.CreateIssueSliceWithoutName(diffNav.Current);
            return(match);
        }
Exemplo n.º 5
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);
        }
Exemplo n.º 6
0
        private static void generateSnapshotAndCompare(StructureDefinition original, ArtifactResolver source)
        {
            var generator = new SnapshotGenerator(source, markChanges: false);        

            var expanded = (StructureDefinition)original.DeepCopy();
            Assert.IsTrue(original.IsExactly(expanded));

            generator.Generate(expanded);
           
            var areEqual = original.IsExactly(expanded);

            if (!areEqual)
            {
                File.WriteAllText("c:\\temp\\snapshotgen-source.xml", FhirSerializer.SerializeResourceToXml(original));
                File.WriteAllText("c:\\temp\\snapshotgen-dest.xml", FhirSerializer.SerializeResourceToXml(expanded));
            }

            Assert.IsTrue(areEqual);
        }
 ElementDefnMerger(SnapshotGenerator generator)
 {
     _generator = generator;
 }
            /// <summary>Merge two <see cref="ElementDefinition"/> instances. Existing diff properties override associated snap properties.</summary>
            public static void Merge(SnapshotGenerator generator, ElementDefinition snap, ElementDefinition diff, bool mergeElementId)
            {
                var merger = new ElementDefnMerger(generator);

                merger.merge(snap, diff, mergeElementId);
            }
Exemplo n.º 9
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>
        static List <MatchInfo> constructMatch(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator 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

            // [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)
            // [WMR 20160906] WRONG! Also need to handle complex extensions
            var diffIsExtension = diffNav.Current.IsExtension() &&
                                  (
                diffNav.Current.PrimaryTypeProfile() != null ||                             // Extension element in a profile
                ElementDefinitionNavigator.GetPathRoot(diffNav.Path) == "Extension"         // Complex extension child element
                                  );
            bool baseIsSliced = snapNav.Current.Slicing != null;
            bool diffIsSliced = diffIsExtension || diffNav.Current.Slicing != null;

            if (baseIsSliced || diffIsSliced)
            {
                // This is a slice match - process it separately
                return(constructSliceMatch(snapNav, diffNav));
            }

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

            // Verify type slice constraints (e.g. value[x] => valueString)
            // - base element has a type choice (value[x])
            // - Single derived element is constrained to single type and renamed (valueString)
            // - This is NOT a type slice => derived profile cannot contain multiple renamed elements!
            var result = new List <MatchInfo>()
            {
                match
            };

            if (snapNav.Current.IsChoice())
            {
                var bm = diffNav.Bookmark();

                // [WMR 20170308] NEW - Clone slice base element
                var sliceBase = initSliceBase(snapNav);

                while (diffNav.MoveToNext())
                {
                    if (snapNav.IsRenamedChoiceTypeElement(diffNav.PathName))
                    {
                        match = new MatchInfo()
                        {
#if MULTIPLE_RENAMED_CHOICE_TYPES
                            Action = MatchAction.Add,
#else
                            Action = MatchAction.Invalid,
#endif
                            BaseBookmark = snapNav.Bookmark(),
                            DiffBookmark = diffNav.Bookmark(),

                            // [WMR 20170308] NEW - Slice base element
                            SliceBase = sliceBase,

                            Issue = SnapshotGenerator.CreateIssueInvalidChoiceConstraint(diffNav.Current)
                        };
                        result.Add(match);
                        bm = diffNav.Bookmark();
                    }
                    else
                    {
                        diffNav.ReturnToBookmark(bm);
                        break;
                    }
                }
            }
            return(result);
        }