/// <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); }
// [WMR 20160720] Merge custom element type profiles, e.g. Patient.name with type.profile = "MyHumanName" // Also for extensions, i.e. an extension element in a profile inherits constraints from the extension definition // Specifically, the profile extension element inherits the cardinality from the extension definition root element (unless overridden in differential) // // Controversial - see GForge #9791 // // How to merge elements with a custom type profile constraint? // // Example 1: Patient.Address with type.profile = AddressNL // A. Merge constraints from base profile element Patient.Address // B. Merge constraints from external profile AddressNL // // Example 2: slice value[x] + valueQuantity(Age) // A. Merge constraints from value[x] into valueQuantity // B. Merge constraints from Age profile into valueQuantity // // Ewout: no clear answer, valid use cases exist for both options // // Following logic is configurable // By default, use strategy (A): ignore custom type profile, merge from base // If ExpandTypeProfiles is enabled, then first merge custom type profile before merging base private void ExpandTypeProfiles(ElementNavigator snap, ElementNavigator diff) { // [WMR 20160721] Note that we also try to resolve and expand extension definitions! var primaryDiffType = diff.Current.Type.FirstOrDefault(); if (primaryDiffType != null && primaryDiffType.Code != FHIRDefinedType.Reference) { var primaryDiffTypeProfile = primaryDiffType.Profile.FirstOrDefault(); var primarySnapType = snap.Current.Type.FirstOrDefault(); var primarySnapTypeProfile = primarySnapType != null?primarySnapType.Profile.FirstOrDefault() : null; if (!string.IsNullOrEmpty(primaryDiffTypeProfile) && primaryDiffTypeProfile != primarySnapTypeProfile) { // Debug.Print("Path = '{0}' - Merge custom type profile '{1}'".FormatWith(diff.Path, primaryTypeProfile)); // cf. ExpandElement // [WMR 20160721] NEW: Handle type profiles with name references // e.g. profile "http://hl7.org/fhir/StructureDefinition/qicore-adverseevent" // Extension element "cause" => "http://hl7.org/fhir/StructureDefinition/qicore-adverseevent-cause" // Constraint on extension child element "certainty" => "http://hl7.org/fhir/StructureDefinition/qicore-adverseevent-cause#certainty" // This means: // - First inherit child element constraints from extension definition, element with name "certainty" // - Then override inherited constraints by explicit element constraints in profile differential string profileUrl, elementName; var isComplex = IsComplexProfileReference(primaryDiffTypeProfile, out profileUrl, out elementName); if (isComplex) { primaryDiffTypeProfile = profileUrl; } var baseType = _resolver.GetStructureDefinition(primaryDiffTypeProfile); if (baseType != null) { if (baseType.Snapshot != null) { // Clone and rebase baseType = (StructureDefinition)baseType.DeepCopy(); var rebasePath = diff.Path; if (isComplex) { rebasePath = ElementNavigator.GetParentPath(rebasePath); } baseType.Snapshot.Rebase(rebasePath); generateBaseElements(baseType.Snapshot.Element); var baseNav = new ElementNavigator(baseType.Snapshot.Element); if (elementName == null) { baseNav.MoveToFirstChild(); } else { if (!baseNav.JumpToNameReference(elementName)) { throw Error.InvalidOperation("Found type profile with invalid name reference '{0}' - the base profile does not contain an element with name '{1}'".FormatWith(primaryDiffTypeProfile, elementName)); } } // Merge the external type profile if (diff.HasChildren) { // Recursively merge the full profile, then merge overriding constraints from differential mergeElement(snap, baseNav); } else { // Only merge the profile root element; no need to expand children (new ElementDefnMerger(_settings.MarkChanges)).Merge(snap.Current, baseNav.Current); } } else if (!_settings.IgnoreMissingTypeProfiles) { throw Error.NotSupported("Found definition of type profile '{0}', but is does not contain a snapshot representation.".FormatWith(primaryDiffTypeProfile)); } } else { Debug.Print("Warning! Unresolved external type profile reference: '{0}' - Ignore, skip expansion...".FormatWith(primaryDiffTypeProfile)); if (!_settings.IgnoreMissingTypeProfiles) { // throw Error.NotSupported("Trying to navigate down a node that has a declared type profile of '{0}', which is unknown".FormatWith(primaryDiffTypeProfile)); throw Error.ResourceReferenceNotFoundException( primaryDiffTypeProfile, "The profile contains an unresolved reference to an external type profile with url '{0}'".FormatWith(primaryDiffTypeProfile) ); } } } } }