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()); } } } } }
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)); }
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); }
// 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")); }
// 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); }
// 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); }
/// <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); }
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); } }
// 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); }
// 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); }
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()); }
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); }
// [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")); }
static string previousElementName(ElementDefinitionNavigator nav) { string result = null; var bm = nav.Bookmark(); if (nav.MoveToPrevious()) { result = nav.PathName; nav.ReturnToBookmark(bm); } return(result); }
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); }
/// <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); }
/// <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); }
// (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); } }
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; }