/// <summary>Harvest specific summary information from a <see cref="StructureDefinition"/> resource.</summary> /// <returns><c>true</c> if the current target represents a <see cref="StructureDefinition"/> resource, or <c>false</c> otherwise.</returns> /// <remarks>The <see cref="ArtifactSummaryGenerator"/> calls this method from a <see cref="ArtifactSummaryHarvester"/> delegate.</remarks> public static bool Harvest(ISourceNode nav, ArtifactSummaryPropertyBag properties) { if (IsStructureDefinitionSummary(properties)) { // [WMR 20171218] Harvest global core extensions, e.g. MaturityLevel nav.HarvestExtensions(properties, harvestExtension); // Explicit extractor chaining if (ConformanceSummaryProperties.Harvest(nav, properties)) { nav.HarvestValue(properties, FhirVersionKey, "fhirVersion"); nav.HarvestValue(properties, KindKey, "kind"); nav.HarvestValue(properties, ConstrainedTypeKey, "constrainedType"); nav.HarvestValue(properties, ContextTypeKey, "contextType"); // [WMR 20180919] NEW: Extension Context nav.HarvestValues(properties, ContextKey, "context"); nav.HarvestValue(properties, BaseKey, "base"); // [WMR 20180725] Also harvest definition property from (first) root element in snapshot/differential // HL7 FHIR website displays this text as introduction on top of each resource/datatype page var elementNode = nav.Children("snapshot").FirstOrDefault() ?? nav.Children("differential").FirstOrDefault(); if (elementNode != null) { var childNode = elementNode.Children("element").FirstOrDefault(); if (childNode != null && Navigation.ElementDefinitionNavigator.IsRootPath(childNode.Name)) { childNode.HarvestValue(properties, RootDefinitionKey, "definition"); } } } return(true); } return(false); }
public static void ElementNavPerformance(ISourceNode nav) { // run extraction once to allow for caching extract(); //System.Threading.Thread.Sleep(20000); var sw = new Stopwatch(); sw.Start(); for (var i = 0; i < 5_000; i++) { extract(); } sw.Stop(); Debug.WriteLine($"Navigating took {sw.ElapsedMilliseconds / 5 } micros"); void extract() { var usual = nav.Children("identifier").First().Children("use").First().Text; Assert.IsNotNull(usual); var phone = nav.Children("telecom").First().Children("system").First().Text; Assert.IsNotNull(usual); var prefs = nav.Children("communication").Where(c => c.Children("preferred").Any(pr => pr.Text == "true")).Count(); Assert.AreNotEqual(0, prefs); var link = nav.Children("link").Children("other").Children("reference"); Assert.IsNotNull(link); } }
internal Base Deserialize(ClassMapping mapping, Base existing = null) { if (mapping == null) { throw Error.ArgumentNull(nameof(mapping)); } if (existing == null) { var fac = new DefaultModelFactory(); existing = (Base)fac.Create(mapping.NativeType); } else { if (mapping.NativeType != existing.GetType()) { throw Error.Argument(nameof(existing), "Existing instance is of type {0}, but data indicates resource is a {1}".FormatWith(existing.GetType().Name, mapping.NativeType.Name)); } } var members = _current.Text != null ? new[] { SourceNode.Valued("value", _current.Text) }.Union(_current.Children()) : _current.Children(); read(mapping, members, existing); return(existing); }
private IEnumerable <TypedElementNode> enumerateElements(ElementDefinitionSummaryCache dis, ISourceNode parent, string name) { IEnumerable <ISourceNode> childSet = null; // no name filter: work on all the parent's children if (name == null) { childSet = parent.Children(); } else { var hit = dis.TryGetBySuffixedName(name, out var info); childSet = hit && info.IsChoiceElement ? parent.Children(name + "*") : parent.Children(name); } string lastName = null; int _nameIndex = 0; foreach (var scan in childSet) { var hit = dis.TryGetBySuffixedName(scan.Name, out var info); NavigatorPosition match = info == null ? new NavigatorPosition(scan, null, scan.Name, null) : deriveInstanceType(scan, info); // If we found a type, but we don't know about the specific child, complain if (dis != ElementDefinitionSummaryCache.Empty && !match.IsTracking) { raiseTypeError($"Encountered unknown element '{scan.Name}' while parsing", this, warning: _settings.ErrorMode != TypedElementSettings.TypeErrorMode.Report); // don't include member, unless we are explicitly told to let it pass if (_settings.ErrorMode != TypedElementSettings.TypeErrorMode.Passthrough) { continue; } } if (lastName == scan.Name) { _nameIndex += 1; } else { _nameIndex = 0; lastName = scan.Name; } var prettyPath = hit && !info.IsCollection ? $"{ShortPath}.{match.Name}" : $"{ShortPath}.{match.Name}[{_nameIndex}]"; yield return(new TypedElementNode(this, match, prettyPath)); } }
private IEnumerable <TypedElementOnSourceNode> enumerateElements(Dictionary <string, IElementDefinitionSummary> dis, ISourceNode parent, string name) { IEnumerable <ISourceNode> childSet; // no name filter: work on all the parent's children if (name == null) { childSet = parent.Children(); } else { var hit = tryGetBySuffixedName(dis, name, out var info); childSet = hit && info.IsChoiceElement ? parent.Children(name + "*") : parent.Children(name); } string lastName = null; int _nameIndex = 0; foreach (var scan in childSet) { var hit = tryGetBySuffixedName(dis, scan.Name, out var info); string instanceType = info == null ? null : deriveInstanceType(scan, info); // If we have definitions for the children, but we didn't find definitions for this // child in the instance, complain if (dis.Any() && info == null) { raiseTypeError($"Encountered unknown element '{scan.Name}' at location '{scan.Location}' while parsing", this, warning: _settings.ErrorMode != TypedElementSettings.TypeErrorMode.Report); // don't include member, unless we are explicitly told to let it pass if (_settings.ErrorMode != TypedElementSettings.TypeErrorMode.Passthrough) { continue; } } if (lastName == scan.Name) { _nameIndex += 1; } else { _nameIndex = 0; lastName = scan.Name; } var prettyPath = hit && !info.IsCollection ? $"{ShortPath}.{info.ElementName}" : $"{ShortPath}.{scan.Name}[{_nameIndex}]"; yield return(new TypedElementOnSourceNode(this, scan, info, instanceType, prettyPath)); } }
public static void ProducesCorrectUntypedLocations(ISourceNode patient) { Assert.AreEqual("Patient", patient.Location); Assert.AreEqual("Patient.id[0]", patient.Children().First().Location); var identifiers = patient.Children("identifier").ToList(); Assert.AreEqual("Patient.identifier[0]", identifiers[0].Location); Assert.AreEqual("Patient.identifier[0].use[0]", identifiers[0].Children().First().Location); Assert.AreEqual("Patient.identifier[1]", identifiers[1].Location); Assert.AreEqual("Patient.identifier[1].use[0]", identifiers[1].Children().First().Location); Assert.AreEqual("Patient.deceasedBoolean[0]", patient.Children("deceasedBoolean").Single().Location); Assert.AreEqual("Patient.contained[0].name[1].use[0]", patient.Children("contained").First().Children("name").Skip(1).First().Children("use").Single().Location); }
/// <summary>Harvest the value of a child element into a property bag.</summary> /// <param name="nav">An <see cref="ISourceNode"/> instance.</param> /// <param name="properties">A property bag to store harvested summary information.</param> /// <param name="key">A property key.</param> /// <param name="element">An element name.</param> /// <param name="childElement">A child element name.</param> public static bool HarvestValue(this ISourceNode nav, IDictionary <string, object> properties, string key, string element, string childElement) { var elementNode = nav.Children(element).FirstOrDefault(); var childNode = elementNode?.Children(childElement).FirstOrDefault(); return(childNode != null && HarvestValue(childNode, properties, key)); }
private static SourceNode buildNode(ISourceNode node, bool recursive, IEnumerable <Type> annotationsToCopy) { var me = new SourceNode(node.Name, node.Text); var rts = node.Annotation <IResourceTypeSupplier>(); if (rts != null) { me.ResourceType = rts.ResourceType; } foreach (var t in annotationsToCopy ?? Enumerable.Empty <Type>()) { foreach (var ann in node.Annotations(t)) { me.AddAnnotation(ann); } } if (recursive) { me.AddRange(node.Children().Select(c => buildNode(c, recursive: true, annotationsToCopy: annotationsToCopy))); } return(me); }
private void WriteNode(ISourceNode node, TextWriter writer) { bool isFlat = node.GetType().GetCustomAttribute <FlatAttribute>() != null; foreach (var line in node.Prefix()) { writer.Write(styleStack.Peek()); writer.WriteLine(line); } if (!isFlat) { styleStack.Push(styleStack.Peek() + indent); } foreach (var child in node.Children()) { WriteNode(child, writer); } if (!isFlat) { styleStack.Pop(); } foreach (var line in node.Suffix()) { writer.Write(styleStack.Peek()); writer.WriteLine(line); } }
private static SourceNode buildNode(ISourceNode node) { var me = new SourceNode(node.Name, node.Text); me.AddRange(node.Children().Select(c => buildNode(c))); return(me); }
private static void visit(this ISourceNode navigator, Action <int, ISourceNode> visitor, int depth = 0) { visitor(depth, navigator); foreach (var child in navigator.Children()) { visit(child, visitor, depth + 1); } }
private void _DumpNode(ISourceNode node) { Console.WriteLine($"{node.Location,70} {node.Name,20} {node.Text}"); foreach (ISourceNode child in node.Children()) { _DumpNode(child); } }
// Generate summary for a single artifact static ArtifactSummary generate( ArtifactSummaryPropertyBag props, ISourceNode nav, ArtifactSummaryHarvester[] harvesters) { Exception error = null; // [WMR 20180419] Support empty harvester list (harvest only default props, no custom props) if (harvesters != null && harvesters.Length > 0) { try { // Harvest summary information via specified harvesters // Top-level harvesters receive navigator positioned on the first child element level // Catch individual exceptions inside loop, return as AggregateException var errors = new List <Exception>(); if (nav.Children().Any()) { foreach (var harvester in harvesters) { try { if (harvester != null && harvester.Invoke(nav, props)) { break; } } // TODO Catch specific exceptions // catch (FormatException) catch (Exception ex) { errors.Add(ex); } } } // Combine all errors into single AggregateException error = errors.Count > 0 ? new AggregateException(errors) : null; } // TODO Catch specific exceptions // catch (FormatException) // catch (NotSupportedException) catch (Exception ex) { // Error in summary factory? // Make sure we always return a valid summary error = ex; } } // Create final summary from harvested properties and optional error return(new ArtifactSummary(props, error)); }
// Callback for HarvestExtensions, called for each individual extension entry static void harvestExtension(ISourceNode nav, IDictionary <string, object> properties, string url) { if (StringComparer.Ordinal.Equals(FmmExtensionUrl, url)) { var child = nav.Children("valueInteger").FirstOrDefault(); if (child != null) { properties[MaturityLevelKey] = child.Text; } } }
public static void HasLineNumbers <T>(ISourceNode nav) where T : class, IPositionInfo { nav = nav.Children().FirstOrDefault(); Assert.IsNotNull(nav); var posInfo = (nav as IAnnotated)?.Annotation <T>(); Assert.IsNotNull(posInfo); Assert.AreNotEqual(-1, posInfo.LineNumber); Assert.AreNotEqual(-1, posInfo.LinePosition); Assert.AreNotEqual(0, posInfo.LineNumber); Assert.AreNotEqual(0, posInfo.LinePosition); }
/// <summary>Harvest extension values into a property bag.</summary> /// <param name="nav">An <see cref="ISourceNode"/> instance.</param> /// <param name="properties">A property bag to store harvested summary information.</param> /// <param name="extensionValueHarvester">Callback function called for each individual extension entry.</param> public static void HarvestExtensions(this ISourceNode nav, IDictionary <string, object> properties, Action <ISourceNode, IDictionary <string, object>, string> extensionValueHarvester) { const string extension = "extension"; foreach (var child in nav.Children(extension)) { var url = child.Children("url").FirstOrDefault(); if (url != null) { extensionValueHarvester(child, properties, url.Text); } } }
/// <summary>Harvest an array of element values into a property bag.</summary> /// <param name="nav">An <see cref="ISourceNode"/> instance.</param> /// <param name="properties">A property bag to store harvested summary information.</param> /// <param name="key">A property key.</param> /// <param name="element">An element name.</param> public static bool HarvestValues(this ISourceNode nav, IDictionary <string, object> properties, string key, string element) { var values = new List <string>(); foreach (var elementNode in nav.Children(element)) { HarvestValue(elementNode, values); } if (values.Count > 0) { properties[key] = values.ToArray(); return(true); } return(false); }
/// <summary> /// Returns all descendants of a node. /// </summary> /// <param name="node">A node.</param> /// <returns>The descendants (children and by recursion all children of the children) of the node passed into /// <paramref name="node"/></returns> public static IEnumerable <ISourceNode> Descendants(this ISourceNode node) { if (node == null) { throw Error.ArgumentNull(nameof(node)); } foreach (var child in node.Children()) { yield return(child); foreach (var grandchild in child.Descendants()) { yield return(grandchild); } } }
private void AddLocations(ISourceNode node, ref IPositionInfo lastNode, ref int lastCharPos, char[] chars) { int location = 0; IPositionInfo posInfo = (node as IAnnotated)?.Annotation <Hl7.Fhir.Serialization.XmlSerializationDetails>(); if (posInfo == null) { posInfo = (node as IAnnotated)?.Annotation <Hl7.Fhir.Serialization.JsonSerializationDetails>(); } if (posInfo != null) { if (lastNode == null) { location = 0; } else { var linesToSkip = posInfo.LineNumber - lastNode.LineNumber; var colsToSkip = posInfo.LinePosition; if (linesToSkip == 0) { colsToSkip -= lastNode.LinePosition; } while (linesToSkip > 0 && lastCharPos < chars.Length) { lastCharPos++; if (chars[lastCharPos] == '\r') { linesToSkip--; } } lastCharPos += colsToSkip; location = lastCharPos; // need to patch this } lastNode = posInfo; _locations.Add(location, node.Location); // System.Diagnostics.Trace.WriteLine($"{location}: {node.Location}"); } lastCharPos = location; foreach (var child in node.Children()) { AddLocations(child, ref lastNode, ref lastCharPos, chars); } }
public static TreeComparisonResult IsEqualTo(this ISourceNode expected, ISourceNode actual) { if (expected.Name != actual.Name) { return(TreeComparisonResult.Fail(actual.Location, $"name: was '{actual.Name}', expected '{expected.Name}'")); } if (expected.Text != actual.Text) { return(TreeComparisonResult.Fail(actual.Location, $"value: was '{actual.Text}', expected '{expected.Text}'")); } if (expected.Location != actual.Location) { TreeComparisonResult.Fail(actual.Location, $"Path: was '{actual.Location}', expected '{expected.Location}'"); } // Ignore ordering (only relevant to xml) var childrenExp = expected.Children().OrderBy(e => e.Name); var childrenActual = actual.Children().OrderBy(e => e.Name).GetEnumerator(); // Don't compare lengths, as this would require complete enumeration of both collections // just enumerate through the lists, comparing each item as they go. // first fail (or list end) will drop out. foreach (var exp in childrenExp) { if (!childrenActual.MoveNext()) { TreeComparisonResult.Fail(actual.Location, $"number of children was different"); } var result = exp.IsEqualTo(childrenActual.Current); if (!result.Success) { return(result); } } if (childrenActual.MoveNext()) { TreeComparisonResult.Fail(actual.Location, $"number of children was different"); } return(TreeComparisonResult.OK); }
private string deriveInstanceType(ISourceNode current, IElementDefinitionSummary info) { var resourceTypeIndicator = current.GetResourceTypeIndicator(); // First, handle the case where this (appears to be) a resource. if (info.IsResource) { if (resourceTypeIndicator == null) { raiseTypeError($"Element '{current.Name}' should contain a resource, but does not actually contain one", current, location: current.Location); } return(resourceTypeIndicator); } else if (resourceTypeIndicator != null) { raiseTypeError($"Element '{current.Name}' is not a contained resource, but seems to contain a resource of type '{resourceTypeIndicator}'.", current, location: current.Location); return(resourceTypeIndicator); } // Now, it's a data element. But is a choice, so we need to take // a look at the suffix on the element's name to figure out what we // are dealing with. if (info.IsChoiceElement) { var suffix = current.Name.Substring(info.ElementName.Length); if (string.IsNullOrEmpty(suffix)) { raiseTypeError($"Choice element '{current.Name}' is not suffixed with a type.", current, location: current.Location); return(null); } suffix = normalizeSuffix(suffix); // If any of the types is the abstract 'DataType' type, we'll just // accept whatever type is mentioned, we have no real list to go on => // An example of where this happens is Extension.value[x], which is // a POCO that is common to all (FHIR) datamodels, and since value[x] is an // "open" types, we have no idea which types are allowed. if (info.Type.Any(t => t.GetTypeName() == "DataType")) { return(suffix); } else { var isListed = info.Type .OfType <IStructureDefinitionReference>() .Select(t => t.ReferredType) .Any(t => t == suffix); if (!isListed) { raiseTypeError($"Choice element '{current.Name}' is suffixed with unexpected type '{suffix}'", current, location: current.Location); } return(suffix); } string normalizeSuffix(string s) { // Unfortunately, we decided to nicely capitalize the suffix, even for // primitives - luckily then, there's just a single list of primitives, // so we can "correct" them return(_suffixMap.TryGetValue(s, out var corrected) ? corrected : s); }; } else if (info.Representation == XmlRepresentation.TypeAttr) // May be used by models other then FHIR, e.g. CCDA represented by a StructureDefinition { if (info.Type.Count() == 1) { return(info.Type.Single().GetTypeName()); } else { var typeName = current.Children("type").FirstOrDefault()?.Text; return(typeName != null ? info.Type.Where(type => typeFromLogicalModelCanonical(type).Equals(typeName)).FirstOrDefault()?.GetTypeName() : info.DefaultTypeName); } } var tp = info.Type.Single(); return(tp.GetTypeName()); }
private string deriveInstanceType(ISourceNode current, IElementDefinitionSummary info) { string instanceType = null; var resourceTypeIndicator = current.GetResourceTypeIndicator(); if (info.IsResource) { instanceType = resourceTypeIndicator; if (instanceType == null) { raiseTypeError($"Element '{current.Name}' should contain a resource, but does not actually contain one", current, location: current.Location); } } else if (!info.IsResource && resourceTypeIndicator != null) { raiseTypeError($"Element '{current.Name}' is not a contained resource, but seems to contain a resource of type '{resourceTypeIndicator}'.", current, location: current.Location); instanceType = resourceTypeIndicator; } else if (info.IsChoiceElement) { var suffix = current.Name.Substring(info.ElementName.Length); if (string.IsNullOrEmpty(suffix)) { raiseTypeError($"Choice element '{current.Name}' is not suffixed with a type.", current, location: current.Location); instanceType = null; } else { instanceType = info.Type .OfType <IStructureDefinitionReference>() .Select(t => t.ReferredType) .FirstOrDefault(t => string.Compare(t, suffix, System.StringComparison.OrdinalIgnoreCase) == 0); if (string.IsNullOrEmpty(instanceType)) { raiseTypeError($"Choice element '{current.Name}' is suffixed with unexpected type '{suffix}'", current, location: current.Location); } } } else if (info.Representation == XmlRepresentation.TypeAttr) // May be used by models other then FHIR, e.g. CCDA represented by a StructureDefinition { if (info.Type.Count() == 1) { instanceType = info.Type.Single().GetTypeName(); } else { var typeName = current.Children("type").FirstOrDefault()?.Text; if (typeName != null) { instanceType = info.Type.Where(type => typeFromLogicalModelCanonical(type).Equals(typeName)).FirstOrDefault()?.GetTypeName(); } else { instanceType = info.DefaultTypeName; } } } else { var tp = info.Type.Single(); instanceType = tp.GetTypeName(); } return(instanceType); }
private IEnumerable <TypedElementOnSourceNode> enumerateElements(Dictionary <string, IElementDefinitionSummary> dis, ISourceNode parent, string name) { IEnumerable <ISourceNode> childSet; // no name filter: work on all the parent's children if (name == null) { childSet = parent.Children(); } else { var hit = tryGetBySuffixedName(dis, name, out var info); childSet = hit && info.IsChoiceElement ? parent.Children(name + "*") : parent.Children(name); } string lastName = null; int _nameIndex = 0; foreach (var scan in childSet) { var hit = tryGetBySuffixedName(dis, scan.Name, out var info); string instanceType = info == null ? null : deriveInstanceType(scan, info); // If we have definitions for the children, but we didn't find definitions for this // child in the instance, complain if (dis.Any() && info == null) { raiseTypeError($"Encountered unknown element '{scan.Name}' at location '{scan.Location}' while parsing", this, warning: _settings.ErrorMode != TypedElementSettings.TypeErrorMode.Report); // don't include member, unless we are explicitly told to let it pass if (_settings.ErrorMode != TypedElementSettings.TypeErrorMode.Passthrough) { continue; } } if (lastName == scan.Name) { _nameIndex += 1; } else { _nameIndex = 0; lastName = scan.Name; } var prettyPath = hit && !info.IsCollection ? $"{ShortPath}.{info.ElementName}" : $"{ShortPath}.{scan.Name}[{_nameIndex}]"; // Special condition for ccda. // If we encounter a xhtml node in a ccda document we will flatten all childnodes // and use their content to build up the xml. // The xml will be put in this node and children will be ignored. if (instanceType == XHTML_INSTANCETYPE && info.Representation == XmlRepresentation.CdaText) { #pragma warning disable CS0618 // Type or member is obsolete var xmls = scan.Children().Select(c => c.Annotation <ICdaInfoSupplier>()?.XHtmlText); #pragma warning restore CS0618 // Type or member is obsolete var source = SourceNode.Valued(scan.Name, string.Join(string.Empty, xmls)); yield return(new TypedElementOnSourceNode(this, source, info, instanceType, prettyPath)); continue; } yield return(new TypedElementOnSourceNode(this, scan, info, instanceType, prettyPath)); } }
public IEnumerable <ITypedElement> Children(string name) => Current.Children(name).Select(c => new SourceNodeToTypedElementAdapter(this, c));