static bool isRootTypeElementPath(string path)
        {
            var root = ElementDefinitionNavigator.GetPathRoot(path);

            // Note: the API could provide a utility method to determine if a type name represents a root type (StructureDef.Base == null)
            return(root == "Resource" || root == "Element");
        }
        /// <summary>(Re-)generate the <see cref="ElementDefinition.Base"/> components.</summary>
        /// <param name="elements">A list of <see cref="ElementDefinition"/> instances.</param>
        /// <param name="baseProfileUrl">The canonical url of the base profile, as defined by the <see cref="StructureDefinition.Base"/> property.</param>
        /// <param name="force">If <c>true</c>, then always (re-)generate the Base component, even if it exists.</param>
        void ensureBaseComponents(IList <ElementDefinition> elements, string baseProfileUrl, bool force = false)
        {
            var nav = new ElementDefinitionNavigator(elements);

            if (nav.MoveToFirstChild() && !string.IsNullOrEmpty(baseProfileUrl))
            {
                var sd = _resolver.FindStructureDefinition(baseProfileUrl);
                if (ensureSnapshot(sd, baseProfileUrl))
                {
                    var baseNav = new ElementDefinitionNavigator(sd);
                    if (baseNav.MoveToFirstChild())
                    {
                        nav.Current.EnsureBaseComponent(baseNav.Current, force);

                        if (nav.MoveToFirstChild() && baseNav.MoveToFirstChild())
                        {
                            do
                            {
                                ensureBaseComponents(nav, baseNav, force);
                            } while (nav.MoveToNext());
                        }
                    }
                }
            }
        }
예제 #3
0
        public static IBucket CreateGroup(ElementDefinitionNavigator root, Validator validator, IBucket entryBucket, bool atRoot)
        {
            var childDiscriminators = root.Current.Slicing.Discriminator.ToArray();
            var slices = root.FindMemberSlices(atRoot);
            var bm     = root.Bookmark();
            var subs   = new List <IBucket>();

            foreach (var slice in slices)
            {
                root.ReturnToBookmark(slice);

                var subBucket = new SliceBucket(root, validator, childDiscriminators);

                if (root.Current.Slicing == null)
                {
                    subs.Add(subBucket);
                }
                else
                {
                    subs.Add(CreateGroup(root, validator, subBucket, atRoot: false));
                }
            }

            root.ReturnToBookmark(bm);

            return(new SliceGroupBucket(root.Current.Slicing, entryBucket, subs));
        }
예제 #4
0
        public static void DumpMatch(this ElementMatcher.MatchInfo match, ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav)
        {
            var sbm = snapNav.Bookmark();
            var dbm = diffNav.Bookmark();

            if (!snapNav.ReturnToBookmark(match.BaseBookmark) || !diffNav.ReturnToBookmark(match.DiffBookmark))
            {
                throw Error.InvalidOperation("Found unreachable bookmark in matches");
            }

            var bPos = snapNav.Path + $"[{snapNav.OrdinalPosition}]";
            var dPos = diffNav.Path + $"[{diffNav.OrdinalPosition}]";

            // [WMR 20160719] Add name, if not null
            if (snapNav.Current != null && snapNav.Current.Name != null)
            {
                bPos += $" '{snapNav.Current.Name}'";
            }
            if (diffNav.Current != null && diffNav.Current.Name != null)
            {
                dPos += $" '{diffNav.Current.Name}'";
            }

            Debug.WriteLine($"B:{bPos} <-- {match.Action.ToString()} --> D:{dPos}");

            snapNav.ReturnToBookmark(sbm);
            diffNav.ReturnToBookmark(dbm);
        }
예제 #5
0
        // Match current snapshot and differential slice elements on @type|@profile = Element.Type.Code and Element.Type.Profile
        // Returns an initialized MatchInfo with action = Merge | Add
        static void matchSliceByTypeProfile(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav, MatchInfo match)
        {
            matchSliceByTypeCode(snapNav, diffNav, match);
            if (match.Action == MatchAction.Merge)
            {
                // We have a match on type code(s); match type profiles
                var diffProfiles = diffNav.Current.PrimaryTypeProfiles().ToList();
                var snapProfiles = snapNav.Current.PrimaryTypeProfiles().ToList();

                // Handle Chris Grenz example http://example.com/fhir/SD/patient-research-auth-reslice
                if (diffProfiles.IsNullOrEmpty() && snapProfiles.IsNullOrEmpty())
                {
                    return;
                }

                var diffProfile = diffProfiles.FirstOrDefault();
                var profileRef  = ProfileReference.Parse(diffProfile);
                var result      = profileRef.IsComplex
                                  // Match on element name (for complex extension elements)
                    ? StringComparer.Ordinal.Equals(snapNav.Current.Name, profileRef.ElementName)
                                  // Match on type profile(s)
                    : snapProfiles.SequenceEqual(diffProfiles);

                if (!result)
                {
                    match.Action = MatchAction.Add;
                }
            }
        }
        public void TestIsSiblingSliceOf()
        {
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1", null));
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf(null, "1"));
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1", ""));
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("", "1"));

            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1", "2"));
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("2", "1"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1", "1"));

            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1/2", "1/1"));
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1/1", "1/2"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1", "1/1"));

            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1/1/1", "1/1/2"));
            Assert.IsTrue(ElementDefinitionNavigator.IsSiblingSliceOf("1/1/2", "1/1/1"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1/1", "1/1/1"));

            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf(null, null));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("", ""));

            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf(null, "1/1"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1", null));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("", "1/1"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1", ""));

            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1", "1/1"));
            //Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/", "1/1")); // Invalid
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1", "1/1"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1/", "1/1")); // Invalid
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/2/", "1/1")); // Invalid
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/1/1", "1/1"));
            Assert.IsFalse(ElementDefinitionNavigator.IsSiblingSliceOf("1/2/1", "1/1"));
        }
예제 #7
0
        // Generate an ElementId for the specified element and parent element
        static string generateElementId(ElementDefinition element, ElementDefinition parent)
        {
            if (element == null)
            {
                throw new ArgumentNullException(nameof(element));
            }
            // Add element name (last path component)
            // parent is null for the resource root element
            var pathPart = ElementDefinitionNavigator.GetLastPathComponent(element.Path);
            var id       = addIdComponent(parent?.ElementId, ".", pathPart);

            // Add element slice name (user defined), if present
            // [WMR 20160922] Except for root element, e.g. SimpleQuantity
            if (parent != null && !string.IsNullOrEmpty(element.Name))
            {
                // Chris Grenz generates compound names, e.g. [slice-name].[child-element-name]
                // Only use the last part, and only if it differs from the xml element name
                var localName = ElementDefinitionNavigator.GetLastPathComponent(element.Name);
                if (localName != pathPart)
                {
                    id = addIdComponent(id, ":", localName);
                }
            }
            return(id);
        }
예제 #8
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;
            }
        }
        private static List <ElementDefinitionNavigator> harvestDefinitionNames(ElementDefinitionNavigator nav)
        {
            Bookmark bm = nav.Bookmark();
            var      definitionElements = new List <ElementDefinitionNavigator>();

            try
            {
                if (nav.MoveToFirstChild())
                {
                    do
                    {
                        // If a name appears twice, it's a slice child, and we can skip it: we will just
                        // match an instance element to a single definition element, which is the slicing entry
                        // if we're dealing with a slice
                        if (!definitionElements.Any() || definitionElements.Last().PathName != nav.PathName)
                        {
                            definitionElements.Add(nav.ShallowCopy());
                        }
                    } while (nav.MoveToNext());
                }
            }
            finally
            {
                nav.ReturnToBookmark(bm);
            }

            return(definitionElements);
        }
예제 #10
0
        /// <summary>Move snapNav to matching base element for diffNav, if it exists. Otherwise diffNav introduces a new element.</summary>
        /// <returns><c>true</c> if snapNav is positioned on macthing base element, or <c>false</c> if diffNav introduces a new element.</returns>
        static bool matchBase(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav, List <string> choiceNames)
        {
            // First, match directly -> try to find the child in base with the same name as the path in the diff
            if (StringComparer.Ordinal.Equals(snapNav.PathName, diffNav.PathName) || snapNav.MoveToNext(diffNav.PathName))
            {
                return(true);
            }

            // 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(xName => ElementDefinitionNavigator.IsRenamedChoiceTypeElement(xName, typeSliceShorthand));

            if (matchingChoice != null)
            {
                // [WMR 20170406] Match on current element? Then don't advance to next!
                // return snapNav.MoveToNext(matchingChoice);
                return(snapNav.PathName == matchingChoice || snapNav.MoveToNext(matchingChoice));
            }

            // No match; consider this to be a new element definition
            // This is allowed for core resource & datatype definitions
            // Note that the SnapshotGenerator does not verify correctness; that is the responsibility of the Validator!
            // SnapshotGenerator should never throw, unless there is faulty logic
            // Instead, emit a list of OperationDefinitions to describe issues (TODO)
            // Ewout: also annotate ElementDefinitions with associated OperationDefinitions
            // Validator is responsible for verifying correctness
            return(false);
        }
예제 #11
0
        public void PrimitiveChildMatching()
        {
            var boolean    = _source.FindStructureDefinitionForCoreType(FHIRDefinedType.Boolean);
            var boolDefNav = ElementDefinitionNavigator.ForSnapshot(boolean);

            boolDefNav.MoveToFirstChild();

            var data = SourceNode.Valued("active", "true",
                                         SourceNode.Node("extension",
                                                         SourceNode.Valued("value", "4")),
                                         SourceNode.Node("nonExistant")
                                         ).ToTypedElement(new PocoStructureDefinitionSummaryProvider(), type: "boolean", settings: new TypedElementSettings {
                ErrorMode = TypedElementSettings.TypeErrorMode.Passthrough
            });

            var matches = ChildNameMatcher.Match(boolDefNav, new ScopedNode(data));

            Assert.Single(matches.UnmatchedInstanceElements);
            Assert.Equal(3, matches.Matches.Count());           // id, extension, value
            Assert.Empty(matches.Matches[0].InstanceElements);  // id
            Assert.Single(matches.Matches[1].InstanceElements); // extension
            Assert.Single(matches.Matches[2].InstanceElements); // value

            Assert.Equal("extension", matches.Matches[1].InstanceElements.First().Name);
            Assert.Equal("extension", matches.Matches[1].Definition.PathName);
            Assert.Equal("active", matches.Matches[2].InstanceElements.First().Name);
            Assert.Equal("value", matches.Matches[2].Definition.PathName);
        }
        internal static IEnumerable <IElementDefinitionSummary> getElements(ElementDefinitionNavigator nav)
        {
            string lastName = "";

            var bookmark = nav.Bookmark();

            try
            {
                if (!nav.MoveToFirstChild())
                {
                    yield break;
                }

                do
                {
                    if (nav.PathName == lastName)
                    {
                        continue;                              // ignore slices
                    }
                    if (isPrimitiveValueConstraint(nav.Current))
                    {
                        continue;                                               // ignore value attribute
                    }
                    lastName = nav.PathName;
                    yield return(new ElementDefinitionSerializationInfo(nav.ShallowCopy()));
                }while (nav.MoveToNext());
            }
            finally
            {
                nav.ReturnToBookmark(bookmark);
            }
        }
예제 #13
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);
        }
        public void PrimitiveChildMatching()
        {
            var boolean    = _source.FindStructureDefinitionForCoreType(FHIRDefinedType.Boolean);
            var boolDefNav = ElementDefinitionNavigator.ForSnapshot(boolean);

            boolDefNav.MoveToFirstChild();

            var data = ElementNode.Valued("active", true, FHIRDefinedType.Boolean.GetLiteral(),
                                          ElementNode.Node("extension",
                                                           ElementNode.Valued("value", 4, "integer")),
                                          ElementNode.Node("nonExistant")
                                          ).ToNavigator();

            var matches = ChildNameMatcher.Match(boolDefNav, data);

            Assert.AreEqual(1, matches.UnmatchedInstanceElements.Count);
            Assert.AreEqual(3, matches.Matches.Count());                     // id, extension, value
            Assert.AreEqual(0, matches.Matches[0].InstanceElements.Count()); // id
            Assert.AreEqual(1, matches.Matches[1].InstanceElements.Count()); // extension
            Assert.AreEqual(1, matches.Matches[2].InstanceElements.Count()); // value

            Assert.AreEqual("extension", matches.Matches[1].InstanceElements.First().Name);
            Assert.AreEqual("extension", matches.Matches[1].Definition.PathName);
            Assert.AreEqual("active", matches.Matches[2].InstanceElements.First().Name);
            Assert.AreEqual("value", matches.Matches[2].Definition.PathName);
        }
예제 #15
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);
        }
예제 #16
0
        public SliceBucket(ElementDefinitionNavigator root, Validator validator, string[] discriminator = null) : base(root.Current)
        {
            // TODO: Should check whether the discriminator is a valid child path of root. Wait until we have the
            // definition walker, which would walk across references if necessary.

            Root          = root.ShallowCopy();
            Validator     = validator;
            Discriminator = discriminator;
        }
        void ensureBaseComponents(ElementDefinitionNavigator nav, ElementDefinitionNavigator baseNav, bool force = false)
        {
            // Debug.Print($"[nameof(generateElementBase)}] Path = '{nav.Path}'  Base = '{baseNav.Path}'");
            var elem = nav.Current;

            Debug.Assert(elem != null);

            // Determine if the current element matches the current base element
            if (baseNav.PathName == nav.PathName || ElementDefinitionNavigator.IsRenamedChoiceTypeElement(baseNav.PathName, nav.PathName))
            {
                // Match!

                // Initialize Base component
                elem.EnsureBaseComponent(baseNav.Current, force);

                // Recurse child elements
                var navBm     = nav.Bookmark();
                var baseNavBm = baseNav.Bookmark();
                if (nav.MoveToFirstChild() && baseNav.MoveToFirstChild())
                {
                    do
                    {
                        ensureBaseComponents(nav, baseNav, force);
                    } while (nav.MoveToNext());

                    nav.ReturnToBookmark(navBm);
                    baseNav.ReturnToBookmark(baseNavBm);
                }

                // Consume the matched base element
                baseNav.MoveToNext();

                return;
            }
            else
            {
                // Drill down base profile
                var baseUrl = baseNav.StructureDefinition.Base;
                if (baseUrl != null)
                {
                    var baseDef = _resolver.FindStructureDefinition(baseUrl);
                    if (ensureSnapshot(baseDef, baseUrl, elem.Path))
                    {
                        baseNav = new ElementDefinitionNavigator(baseDef);
                        if (baseNav.MoveToFirstChild())
                        {
                            ensureBaseComponents(nav, baseNav, force);
                            return;
                        }
                    }
                }
            }

            // No match... try base profile
            // Debug.Print($"[nameof(generateElementBase)}] Path = '{nav.Path}'  (no base)");
        }
        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 ElementDefinitionNavigator(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());
        }
예제 #19
0
        public static string QualifiedDefinitionPath(this ElementDefinitionNavigator nav)
        {
            string path = "";

            if (nav.StructureDefinition != null && nav.StructureDefinition.Url != null)
            {
                path = "{" + nav.StructureDefinition.Url + "}";
            }

            path += nav.Path;

            return(path);
        }
예제 #20
0
        // [WMR 20170308] The snapshot generator initializes snapNav with base profile elements, then merges diff constraints on top of that.
        // So after processing an element in snapNav, the original base element is no longer available.
        // However for sliced elements, we need to initialize the slice entry and all following named slices from the same base element.
        // Therefore we first clone the original, unmerged base element and it's children (recursively).
        // Now each slice match return a reference to the associated original base element, unaffected by further processing.
        static ElementDefinitionNavigator initSliceBase(ElementDefinitionNavigator snapNav)
        {
            var sliceBase = snapNav.CloneSubtree();

            // Named slices never inherit a slicing component
            sliceBase.Current.Slicing = null;

            // Special rule for named slices: always reset minimum cardinality to 0 (don't inherit from base)
            // Because even though slice entry may be required (min=1), a profile can still define optional named slices (min = 0); must specify at least one
            sliceBase.Current.Min = 0;

            return(sliceBase);
        }
 public void TestGetBaseSliceName()
 {
     Assert.AreEqual(null, ElementDefinitionNavigator.GetBaseSliceName(null));
     Assert.AreEqual(null, ElementDefinitionNavigator.GetBaseSliceName(""));
     Assert.AreEqual(null, ElementDefinitionNavigator.GetBaseSliceName("1"));
     Assert.AreEqual("1", ElementDefinitionNavigator.GetBaseSliceName("1/"));
     Assert.AreEqual("1", ElementDefinitionNavigator.GetBaseSliceName("1/1"));
     Assert.AreEqual("1", ElementDefinitionNavigator.GetBaseSliceName("1/2"));
     Assert.AreEqual("1/1", ElementDefinitionNavigator.GetBaseSliceName("1/1/")); // Invalid
     Assert.AreEqual("1/1", ElementDefinitionNavigator.GetBaseSliceName("1/1/1"));
     Assert.AreEqual("1/1", ElementDefinitionNavigator.GetBaseSliceName("1/1/2"));
     Assert.AreEqual("1/2", ElementDefinitionNavigator.GetBaseSliceName("1/2/1"));
     Assert.AreEqual("2/1", ElementDefinitionNavigator.GetBaseSliceName("2/1/1"));
 }
예제 #22
0
        static string previousElementName(ElementDefinitionNavigator nav)
        {
            string result = null;

            var bm = nav.Bookmark();

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

            return(result);
        }
예제 #23
0
        public static IBucket CreateRoot(ElementDefinitionNavigator root, Validator validator)
        {
            // Create a single bucket
            var entryBucket = new ElementBucket(root, validator);

            if (root.Current.Slicing == null)
            {
                return(entryBucket);
            }
            else
            {
                return(CreateGroup(root, validator, entryBucket, atRoot: true));
            }
        }
        public void TestModificationResilience()
        {
            var nav = createTestNav();

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

            var nav2 = new ElementDefinitionNavigator(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"));
        }
 public static ParsedSliceName Parse(ElementDefinition element)
 {
     var name = element.Name;
     if (!string.IsNullOrEmpty(name))
     {
         var last = ElementDefinitionNavigator.GetLastPathComponent(element.Path);
         if (name.EndsWith("." + last))
         {
             return new ParsedSliceName(
                 name.Substring(0, name.Length - last.Length - 1),
                 name.Substring(name.Length - last.Length)
             );
         }
     }
     return new ParsedSliceName(name, null);
 }
예제 #26
0
        /// <summary>List all names of nodes in the current navigator that are choice ('[x]') elements.</summary>
        static List <string> listChoiceElements(ElementDefinitionNavigator snapNav)
        {
            var bm     = snapNav.Bookmark();
            var result = new List <string>();

            do
            {
                if (snapNav.Current != null && snapNav.Current.IsChoice())
                {
                    result.Add(snapNav.PathName);
                }
            } while (snapNav.MoveToNext());

            snapNav.ReturnToBookmark(bm);

            return(result);
        }
예제 #27
0
        /// <summary>Match the children of the current element in diffNav to the children of the current element in snapNav.</summary>
        /// <param name="snapNav">A navigator for the user profile differential.</param>
        /// <param name="diffNav">A navigator for the base profile snapshot.</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 choice 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 static List <MatchInfo> Match(ElementDefinitionNavigator snapNav, ElementDefinitionNavigator diffNav)
        {
            // if (!snapNav.HasChildren) { throw Error.Argument(nameof(snapNav), $"Cannot match base to diff: element '{snapNav.Path}' in snap has no children"); }
            // if (!diffNav.HasChildren) { throw Error.Argument(nameof(diffNav), $"Cannot match base to diff: element '{diffNav.Path}' in diff has no children"); }
            if (!diffNav.HasChildren)
            {
                return(new List <MatchInfo>());
            }                                                           // [WMR 20161208] Gracefully handle missing differential

            // 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
                {
                    var match = matchBase(snapNav, diffNav, choiceNames);
                    if (match)
                    {
                        result.AddRange(constructMatch(snapNav, diffNav));
                    }
                    else
                    {
                        // No matching base element; this is a new element (core resource definitions)
                        // Note: this loop consumes all new diffNav elements when processing the first element from snapNav
                        // When Match is called for remaining snapNav (base) elements, all new diffNav elements will already have been merged
                        result.Add(constructNew(snapNav, diffNav));
                    }
                }while (diffNav.MoveToNext());
            }
            finally
            {
                snapNav.ReturnToBookmark(baseStartBM);
                diffNav.ReturnToBookmark(diffStartBM);
            }

            return(result);
        }
        public static MatchResult Match(ElementDefinitionNavigator definitionParent, ScopedNavigator instanceParent)
        {
            var definitionElements = harvestDefinitionNames(definitionParent);
            var elementsToMatch    = instanceParent.Children().Cast <ScopedNavigator>().ToList();

            List <Match> matches = new List <Match>();

            foreach (var definitionElement in definitionElements)
            {
                var match = new Match()
                {
                    Definition = definitionElement, InstanceElements = new List <ScopedNavigator>()
                };

                // Special case is the .value of a primitive fhir type, this is represented
                // as the "Value" of the IValueProvider interface, not as a real child
                if (definitionElement.Current.IsPrimitiveValueConstraint())
                {
                    if (instanceParent.Value != null)
                    {
                        match.InstanceElements.Add(instanceParent);
                    }
                }
                else
                {
                    var definitionPath = ProfileNavigationExtensions.GetNameFromPath(definitionElement.Current?.Base?.Path ?? definitionElement.Path);
                    var found          = elementsToMatch.Where(ie => NameMatches(definitionPath, ie)).ToList();

                    match.InstanceElements.AddRange(found);
                    elementsToMatch.RemoveAll(e => found.Contains(e));
                }

                matches.Add(match);
            }

            MatchResult result = new MatchResult();

            result.Matches = matches;
            result.UnmatchedInstanceElements = elementsToMatch;

            return(result);
        }
예제 #29
0
        // (Re-)generate ElementId values for all children of the current element
        static void generateChildElementsId(ElementDefinitionNavigator nav, bool force = false)
        {
            var parent = nav.Current;
            // Debug.Print($"[{nameof(generateChildElementsId)}] '{(parent != null ? parent.Path : "[root]")}'");
            var bm = nav.Bookmark();

            if (nav.MoveToFirstChild())
            {
                do
                {
                    var elem = nav.Current;
                    if (force || string.IsNullOrEmpty(elem.ElementId))
                    {
                        elem.ElementId = generateElementId(elem, parent);
                    }
                    generateChildElementsId(nav, force);
                } while (nav.MoveToNext());
                nav.ReturnToBookmark(bm);
            }
        }
예제 #30
0
        public SliceBucket(ElementDefinitionNavigator root, Validator validator, string[] discriminator = null) : base(root.Current)
        {
            // TODO: Should check whether the discriminator is a valid child path of root. Wait until we have the
            // definition walker, which would walk across references if necessary.
            foreach (var d in discriminator)
            {
                if (d.EndsWith("@type"))
                {
                    throw Error.NotImplemented($"Slicing with an '@type' discriminator is not yet supported by this validator.");
                }
                else if (d.EndsWith("@profile"))
                {
                    throw Error.NotImplemented($"Slicing with an '@profile' discriminator is not yet supported by this validator.");
                }
            }

            Root          = root.ShallowCopy();
            Validator     = validator;
            Discriminator = discriminator;
        }