private string FindOrAddDataTypeTemplate(string dataTypeName) { string templateName = "dataType_" + dataTypeName; if (this.dataTypeTemplates.ContainsKey(dataTypeName)) { return(templateName); } SimpleSchema.SchemaObject simpleComplexType = this.simpleSchema.ComplexTypes.SingleOrDefault(y => y.Name == dataTypeName); bool dataTypeDefined = this.tdb.ImplementationGuideTypeDataTypes.Count(y => y.ImplementationGuideTypeId == this.rootTemplate.ImplementationGuideTypeId && y.DataTypeName == dataTypeName) > 0; if (simpleComplexType == null || !dataTypeDefined) { return(string.Empty); } XmlElement dataTypeTemplate = TransformHelper.CreateXslTemplate(this.transformDoc, templateName, string.Empty); dataTypeTemplate.AppendChild( TransformHelper.CreateXsltTemplateParam(this.transformDoc, "instance")); CompleteDataTypeTemplate(dataTypeTemplate, simpleComplexType, "$instance"); this.dataTypeTemplates.Add(dataTypeName, dataTypeTemplate); return(templateName); }
public string GetConstraintDataType(DB.TemplateConstraint aConstraint, string aConstraintXpath) { var templateType = aConstraint.Template.TemplateType; string context = !string.IsNullOrEmpty(aConstraint.Template.PrimaryContextType) ? aConstraint.Template.PrimaryContextType : templateType.RootContextType; var schema = SimplifiedSchemaContext.GetSimplifiedSchema(HttpContext.Current.Application, templateType.ImplementationGuideType); schema = schema.GetSchemaFromContext(context); SimpleSchema.SchemaObject lSchemaObject = schema.FindFromPath(aConstraintXpath); return(lSchemaObject.DataType); }
public void FindFromTypeTest() { string type = "IVL_TS"; SimpleSchema.SchemaObject actual = this.cdaSchema.FindFromType(type); Assert.IsTrue(actual.Children.Exists(y => y.Name == "nullFlavor" && y.IsAttribute), "Could not find @nullFlavor attribute on IVL_TS"); Assert.IsTrue(actual.Children.Exists(y => y.Name == "value" && y.IsAttribute), "Could not find @value attribute on IVL_TS"); SimpleSchema.SchemaObject lowSchemaObject = actual.Children.SingleOrDefault(y => y.Name == "low" && !y.IsAttribute); Assert.IsNotNull(lowSchemaObject, "Could not find <low> within IVL_TS"); Assert.IsTrue(lowSchemaObject.Children.Exists(y => y.Name == "nullFlavor" && y.IsAttribute), "Could now find low/@nullFlavor within IVL_TS"); Assert.IsTrue(lowSchemaObject.Children.Exists(y => y.Name == "value" && y.IsAttribute), "Could now find low/@value within IVL_TS"); }
public void FindFromPathTest() { SimpleSchema.SchemaObject documentObj = this.cdaSchema.FindFromPath("ClinicalDocument[ClinicalDocument]"); Assert.IsNotNull(documentObj, "FindFromPath should have returned a ClinicalDocument schema object."); Assert.AreNotEqual(0, documentObj.Children.Count, "Expected the ClinicalDocument schema object to have children."); SimpleSchema.SchemaObject idObj = this.cdaSchema.FindFromPath("ClinicalDocument[ClinicalDocument]/id[II]"); Assert.IsNotNull(idObj, "FindFromPath should have returned an id schema object."); Assert.IsFalse(idObj.IsAttribute, "id schema object should not be an attribute."); // Should be able to find without specifying the types for each level SimpleSchema.SchemaObject rootObj = this.cdaSchema.FindFromPath("ClinicalDocument/id/@root"); Assert.IsNotNull(rootObj, "FindFromPath should have returned a root schemaobject."); Assert.IsTrue(rootObj.IsAttribute, "Expected FindFromPath to return an attribute for @root."); // Should be able to find WITH specifying the types for each level rootObj = this.cdaSchema.FindFromPath("ClinicalDocument[ClinicalDocument]/id[II]/@root[uid]"); Assert.IsNotNull(rootObj); Assert.IsTrue(rootObj.IsAttribute, "Expected FindFromPath to return an attribute for @root."); SimpleSchema.SchemaObject invalidObj = this.cdaSchema.FindFromPath("ClinicalDocument[ClinicalDocument]/test[test]"); Assert.IsNull(invalidObj, "Expected not to find an element."); }
public void FindFromTypeTest() { string type = "IVL_TS"; SimpleSchema.SchemaObject actual = this.cdaSchema.FindFromType(type); Assert.IsTrue(actual.Children.Exists(y => y.Name == "nullFlavor" && y.IsAttribute), "Could not find @nullFlavor attribute on IVL_TS"); Assert.IsTrue(actual.Children.Exists(y => y.Name == "value" && y.IsAttribute), "Could not find @value attribute on IVL_TS"); /* TODO: Schema Choice support temporarily removed from non-FHIR schemas * var choiceSchemaObject = actual.Children.SingleOrDefault(y => y.Name == "choice" && y.IsChoice); * Assert.IsNotNull(choiceSchemaObject, "Expected to find a choice"); * * SimpleSchema.SchemaObject lowSchemaObject = choiceSchemaObject.Children.SingleOrDefault(y => y.Name == "low" && !y.IsAttribute); * Assert.IsNotNull(lowSchemaObject, "Could not find <low> within IVL_TS"); * Assert.IsTrue(lowSchemaObject.Children.Exists(y => y.Name == "nullFlavor" && y.IsAttribute), "Could now find low/@nullFlavor within IVL_TS"); * Assert.IsTrue(lowSchemaObject.Children.Exists(y => y.Name == "value" && y.IsAttribute), "Could now find low/@value within IVL_TS"); */ SimpleSchema.SchemaObject lowSchemaObject = actual.Children.SingleOrDefault(y => y.Name == "low" && !y.IsAttribute); Assert.IsNotNull(lowSchemaObject, "Could not find <low> within IVL_TS"); Assert.IsTrue(lowSchemaObject.Children.Exists(y => y.Name == "nullFlavor" && y.IsAttribute), "Could now find low/@nullFlavor within IVL_TS"); Assert.IsTrue(lowSchemaObject.Children.Exists(y => y.Name == "value" && y.IsAttribute), "Could now find low/@value within IVL_TS"); }
private void CompleteDataTypeTemplate(XmlElement parent, SimpleSchema.SchemaObject simpleObject, string currentXpath) { foreach (SimpleSchema.SchemaObject cChildObject in simpleObject.Children) { if (cChildObject.Type == SimpleSchema.ObjectTypes.Attribute) { string cAttributeName = Helper.NormalizeName(cChildObject.Name); string cAttributeXpath = !string.IsNullOrEmpty(currentXpath) ? currentXpath + "/@" + cAttributeName : "@" + cAttributeName; XmlElement attributeIfEle = TransformHelper.CreateXsltIf(this.transformDoc, cAttributeXpath); attributeIfEle.AppendChild( TransformHelper.CreateXsltAttributeWithValueOf(this.transformDoc, cAttributeName, cAttributeXpath)); parent.AppendChild(attributeIfEle); } else if (cChildObject.Type == SimpleSchema.ObjectTypes.Element) { string cChildXpath = !string.IsNullOrEmpty(currentXpath) ? currentXpath + "/" + cChildObject.Name : cChildObject.Name; XmlElement newForeachEle = TransformHelper.CreateXsltForEach(this.transformDoc, cChildXpath); XmlElement newChildElement = TransformHelper.CreateXsltElement(this.transformDoc, cChildObject.Name); CompleteDataTypeTemplate(newChildElement, cChildObject, string.Empty); newForeachEle.AppendChild(newChildElement); parent.AppendChild(newForeachEle); } } if (simpleObject.Mixed) { string textXpath = !string.IsNullOrEmpty(currentXpath) ? currentXpath + "/text()" : "text()"; XmlElement textValue = TransformHelper.CreateXsltValueOf(this.transformDoc, textXpath); parent.AppendChild(textValue); } }
public StructureDefinition Convert(Template template, SimpleSchema schema, SummaryType?summaryType = null) { var fhirStructureDef = new fhir_stu3.Hl7.Fhir.Model.StructureDefinition() { Id = template.FhirId(), Name = template.Name, Description = template.Description != null ? new Markdown(template.Description.RemoveInvalidUtf8Characters()) : null, Kind = template.PrimaryContextType == "Extension" ? StructureDefinition.StructureDefinitionKind.ComplexType : StructureDefinition.StructureDefinitionKind.Resource, Url = template.FhirUrl(), Type = template.TemplateType.RootContextType, Context = new List <string> { template.PrimaryContextType }, ContextType = StructureDefinition.ExtensionContext.Resource, Abstract = false, Derivation = StructureDefinition.TypeDerivationRule.Constraint }; // If this is an extension, determine what uses the extension and list them in the // "context" field so that the extension knows where it can be used. foreach (var extension in template.Extensions) { var fhirExtension = Convert(extension); if (fhirExtension != null) { fhirStructureDef.Extension.Add(fhirExtension); } } // Status if (template.Status == null || template.Status.IsDraft || template.Status.IsBallot) { fhirStructureDef.Status = PublicationStatus.Draft; } else if (template.Status.IsPublished) { fhirStructureDef.Status = PublicationStatus.Active; } else if (template.Status.IsDraft) { fhirStructureDef.Status = PublicationStatus.Retired; } // Publisher and Contact if (template.Author != null) { if (!string.IsNullOrEmpty(template.Author.ExternalOrganizationName)) { fhirStructureDef.Publisher = template.Author.ExternalOrganizationName; } var newContact = new ContactDetail(); newContact.Name = string.Format("{0} {1}", template.Author.FirstName, template.Author.LastName); newContact.Telecom.Add(new ContactPoint() { Value = template.Author.Phone, Use = ContactPoint.ContactPointUse.Work, System = ContactPoint.ContactPointSystem.Phone }); newContact.Telecom.Add(new ContactPoint() { Value = template.Author.Email, Use = ContactPoint.ContactPointUse.Work, System = ContactPoint.ContactPointSystem.Email }); fhirStructureDef.Contact.Add(newContact); } // Base profile if (template.ImpliedTemplate != null) { fhirStructureDef.BaseDefinitionElement = new FhirUri(template.ImpliedTemplate.FhirUrl()); } else { fhirStructureDef.BaseDefinitionElement = new FhirUri(string.Format("http://hl7.org/fhir/StructureDefinition/{0}", template.TemplateType.RootContextType)); } // Constraints if (summaryType == null || summaryType == SummaryType.Data) { var differential = new StructureDefinition.DifferentialComponent(); fhirStructureDef.Differential = differential; // Add base element for resource var rootElement = new ElementDefinition(); rootElement.Path = template.PrimaryContextType; rootElement.ElementId = template.PrimaryContextType; differential.Element.Add(rootElement); var rootConstraints = template.ChildConstraints.Where(y => y.ParentConstraint == null).OrderBy(y => y.Order); foreach (var constraint in rootConstraints) { SimpleSchema.SchemaObject schemaObject = null; if (schema != null) { schemaObject = schema.Children.SingleOrDefault(y => y.Name == constraint.Context); } CreateElementDefinition(fhirStructureDef, constraint, schemaObject); } // Slices var slices = template.ChildConstraints.Where(y => y.IsBranch); var sliceGroups = slices.GroupBy(y => y.GetElementPath(template.TemplateType.RootContextType)); int currentSliceGroupCount = 2; // Adds an element that contains "slicing" information for the branch(es) foreach (var sliceGroup in sliceGroups) { ElementDefinition newElementDef = new ElementDefinition(); //newElementDef.ElementId = string.Format("{0}-{1}", template.Id, currentSliceGroupCount.ToString("00")); newElementDef.Path = sliceGroup.Key; newElementDef.ElementId = sliceGroup.First().GetElementPath(template.PrimaryContextType); foreach (var branchConstraint in sliceGroup) { var igSettings = GetIGSettings(branchConstraint); var constraintFormatter = FormattedConstraintFactory.NewFormattedConstraint(this.tdb, igSettings, this.igTypePlugin, branchConstraint); var branchIdentifiers = branchConstraint.ChildConstraints.Where(y => y.IsBranchIdentifier); newElementDef.Definition = constraintFormatter.GetPlainText(false, false, false); newElementDef.Slicing = new ElementDefinition.SlicingComponent() { Rules = template.IsOpen ? ElementDefinition.SlicingRules.Open : ElementDefinition.SlicingRules.Closed }; if (branchIdentifiers.Count() > 0) { newElementDef.Slicing.Discriminator = (from bi in branchIdentifiers select new ElementDefinition.DiscriminatorComponent() { Type = ElementDefinition.DiscriminatorType.Value, Path = bi.GetElementPath(null, branchConstraint) }).ToList(); } else if (branchConstraint.Context == "extension") { newElementDef.Slicing.Discriminator.Add(new ElementDefinition.DiscriminatorComponent() { Type = ElementDefinition.DiscriminatorType.Value, Path = "url" }); } else // If no discriminators are specified, assume the child SHALL constraints are discriminators { var discriminatorConstraints = branchConstraint.ChildConstraints.Where(y => y.Conformance == "SHALL"); var singleValueDiscriminators = discriminatorConstraints.Where(y => !string.IsNullOrEmpty(y.Value)); // If there are constraints that have specific single-value bindings, prefer those if (singleValueDiscriminators.Count() > 0 && singleValueDiscriminators.Count() != discriminatorConstraints.Count()) { discriminatorConstraints = singleValueDiscriminators; } newElementDef.Slicing.Discriminator = (from d in discriminatorConstraints select new ElementDefinition.DiscriminatorComponent() { Type = ElementDefinition.DiscriminatorType.Value, Path = d.GetElementPath(template.PrimaryContextType, branchConstraint) }).ToList(); } } // Find where to insert the slice in the element list var firstElement = fhirStructureDef.Differential.Element.First(y => y.Path == sliceGroup.Key); var firstElementIndex = fhirStructureDef.Differential.Element.IndexOf(firstElement); differential.Element.Insert(firstElementIndex, newElementDef); currentSliceGroupCount++; } } return(fhirStructureDef); }
public void CreateElementDefinition( StructureDefinition strucDef, TemplateConstraint constraint, SimpleSchema.SchemaObject schemaObject) { if (constraint.IsPrimitive) // Skip primitives (for now, at least) { return; } string sliceName = constraint.GetSliceName(); string elementPath = constraint.GetElementPath(strucDef.Type != null ? strucDef.Type.ToString() : null); var igSettings = GetIGSettings(constraint); var constraintFormatter = FormattedConstraintFactory.NewFormattedConstraint(this.tdb, igSettings, this.igTypePlugin, constraint); string definition = constraintFormatter.GetPlainText(false, false, false); if (definition == null) { definition = string.Empty; } ElementDefinition newElementDef = new ElementDefinition() { Short = !string.IsNullOrEmpty(constraint.Label) ? constraint.Label : constraint.Context, Label = !string.IsNullOrEmpty(constraint.Label) ? constraint.Label : null, Comment = !string.IsNullOrEmpty(constraint.Notes) ? constraint.Notes : null, Path = elementPath, SliceName = sliceName, Definition = definition, ElementId = constraint.GetElementId() }; if (constraint.IsChoice) { newElementDef.Slicing = new ElementDefinition.SlicingComponent(); newElementDef.Slicing.Discriminator.Add(new ElementDefinition.DiscriminatorComponent() { Type = ElementDefinition.DiscriminatorType.Type, Path = "$this" }); newElementDef.Slicing.Rules = ElementDefinition.SlicingRules.Open; } else if (constraint.Parent != null && constraint.Parent.IsChoice) { newElementDef.SliceName = constraint.Context; } // Cardinality if (!string.IsNullOrEmpty(constraint.Cardinality)) { newElementDef.Min = constraint.CardinalityType.Left; newElementDef.Max = constraint.CardinalityType.Right == Cardinality.MANY ? "*" : constraint.CardinalityType.Right.ToString(); } // Binding string valueConformance = string.IsNullOrEmpty(constraint.ValueConformance) ? constraint.Conformance : constraint.ValueConformance; bool hasBinding = constraint.References.Any(y => y.ReferenceType == ConstraintReferenceTypes.Template); if (constraint.ValueSet != null && valueConformance.IndexOf("NOT") < 0) { hasBinding = true; newElementDef.Binding = new ElementDefinition.ElementDefinitionBindingComponent() { ValueSet = new ResourceReference() { Reference = constraint.ValueSet.GetIdentifier(ValueSetIdentifierTypes.HTTP), Display = constraint.ValueSet.Name } }; if (valueConformance == "SHALL") { newElementDef.Binding.Strength = BindingStrength.Required; } else if (valueConformance == "SHOULD") { newElementDef.Binding.Strength = BindingStrength.Preferred; } else if (valueConformance == "MAY") { newElementDef.Binding.Strength = BindingStrength.Example; } } // Single-Value Binding if (schemaObject != null && (!string.IsNullOrEmpty(constraint.Value) || !string.IsNullOrEmpty(constraint.DisplayName))) { Element elementBinding = null; hasBinding = true; switch (schemaObject.DataType) { case "CodeableConcept": var codableConceptBinding = new CodeableConcept(); var coding = new Coding(); codableConceptBinding.Coding.Add(coding); if (!string.IsNullOrEmpty(constraint.Value)) { coding.Code = constraint.Value; } if (constraint.CodeSystem != null) { coding.System = constraint.CodeSystem.Oid; } if (!string.IsNullOrEmpty(constraint.DisplayName)) { coding.Display = constraint.DisplayName; } elementBinding = codableConceptBinding; break; case "Coding": var codingBinding = new Coding(); if (!string.IsNullOrEmpty(constraint.Value)) { codingBinding.Code = constraint.Value; } if (constraint.CodeSystem != null) { codingBinding.System = constraint.CodeSystem.Oid; } if (!string.IsNullOrEmpty(constraint.DisplayName)) { codingBinding.Display = constraint.DisplayName; } elementBinding = codingBinding; break; case "code": var codeBinding = new Code(); codeBinding.Value = !string.IsNullOrEmpty(constraint.Value) ? constraint.Value : constraint.DisplayName; elementBinding = codeBinding; break; default: var stringBinding = new FhirString(); stringBinding.Value = !string.IsNullOrEmpty(constraint.Value) ? constraint.Value : constraint.DisplayName; elementBinding = stringBinding; break; } if (constraint.IsFixed) { newElementDef.Fixed = elementBinding; } else { newElementDef.Pattern = elementBinding; } } // Add the type of the element when bound to a value set if (hasBinding && schemaObject != null && !string.IsNullOrEmpty(schemaObject.DataType)) { StructureDefinition profile = GetBaseProfile(constraint.Template); newElementDef.Type = GetProfileDataTypes(profile, constraint); var containedTemplates = (from tcr in constraint.References join t in this.tdb.Templates on tcr.ReferenceIdentifier equals t.Oid where tcr.ReferenceType == ConstraintReferenceTypes.Template select new { Identifier = t.Oid, t.PrimaryContextType }); // If there is a contained template/profile, make sure it supports a "Reference" type, and then output the profile identifier in the type if (containedTemplates.Count() > 0 && newElementDef.Type.Exists(y => y.Code == "Reference" || y.Code == "Extension")) { if (!string.IsNullOrEmpty(newElementDef.SliceName)) { var foundMatchingElement = strucDef.Differential.Element.SingleOrDefault(y => y.Path == newElementDef.Path && y.SliceName == newElementDef.SliceName); if (foundMatchingElement != null) { foundMatchingElement.Definition += " " + definition; newElementDef = foundMatchingElement; } } var containedTypes = new List <ElementDefinition.TypeRefComponent>(); foreach (var containedTemplate in containedTemplates) { bool isExtension = containedTemplate.PrimaryContextType == "Extension" && newElementDef.Type.Exists(y => y.Code == "Extension"); containedTypes.Add(new ElementDefinition.TypeRefComponent() { Code = isExtension ? "Extension" : "Reference", Profile = isExtension ? containedTemplate.Identifier : null, TargetProfile = !isExtension ? containedTemplate.Identifier : null }); } newElementDef.Type = containedTypes; } } // Add the element to the list if it's new if (!strucDef.Differential.Element.Contains(newElementDef)) { strucDef.Differential.Element.Add(newElementDef); } // Children foreach (var childConstraint in constraint.ChildConstraints.OrderBy(y => y.Order)) { var childSchemaObject = schemaObject != null?schemaObject.Children.SingleOrDefault(y => y.Name == childConstraint.Context) : null; CreateElementDefinition(strucDef, childConstraint, childSchemaObject); } }
private void AddTemplateTableConstraint(Template template, IEnumerable <ConstraintReference> templateReferences, Table table, TemplateConstraint constraint, int level, bool includeCategoryHeader, SimpleSchema.SchemaObject schemaObject) { // Skip the child constraints if this is a choice there is only one child constraint. This constraint // adopts the constraint narrative of the child constraint when there is only one option. if (constraint.IsChoice && constraint.ChildConstraints.Count == 1) { constraint = constraint.ChildConstraints.First(); } if (constraint.IsPrimitive != true && !string.IsNullOrEmpty(constraint.Context)) { string xpath = constraint.Context; string cardinality = constraint.Cardinality; string conformance = constraint.Conformance; string dataType = constraint.DataType; string fixedValue = string.Empty; string fixedValueLink = string.Empty; string levelSpacing = string.Empty; string confNumber = template.OwningImplementationGuideId + "-" + constraint.Number.ToString(); var isFhir = constraint.Template.ImplementationGuideType.SchemaURI == ImplementationGuideType.FHIR_NS; // Check if we're dealing with a FHIR constraint if (isFhir && schemaObject != null) { dataType = schemaObject.DataType; } if (constraint.ValueSet != null) { string valueSetIdentifier = constraint.ValueSet.GetIdentifier(igTypePlugin); fixedValue = string.Format("{0} ({1})", valueSetIdentifier, constraint.ValueSet.Name); } else if (constraint.CodeSystem != null) { fixedValue = string.Format("{0} ({1})", constraint.CodeSystem.Oid, constraint.CodeSystem.Name); } var constraintReferences = templateReferences.Where(y => y.TemplateConstraintId == constraint.Id); if (!string.IsNullOrEmpty(constraint.Value)) { if (!string.IsNullOrEmpty(fixedValue)) { fixedValue += " = " + constraint.Value; } else { fixedValue = constraint.Value; } } else if (constraintReferences.Count() > 0) { var containedTemplateValues = constraintReferences.Select(y => string.Format("{0} (identifier: {1}", y.Name, y.Identifier)); fixedValue = string.Join(" or ", containedTemplateValues); var firstConstraintReference = constraintReferences.First(); if (constraintReferences.Count() == 1 && firstConstraintReference.IncludedInIG) { fixedValueLink = firstConstraintReference.Bookmark; } } for (int i = 1; i <= (level); i++) // One tab for each level { levelSpacing += "\t"; } TableRow entryRow = new TableRow(); if (includeCategoryHeader) { AppendTextCell(entryRow, constraint.Category); } AppendTextCell(entryRow, levelSpacing + xpath); AppendTextCell(entryRow, cardinality); AppendTextCell(entryRow, conformance); AppendTextCell(entryRow, dataType); AppendHyperlinkCell(entryRow, confNumber, "C_" + confNumber); if (!string.IsNullOrEmpty(fixedValueLink)) { AppendHyperlinkCell(entryRow, fixedValue, fixedValueLink); } else { AppendTextCell(entryRow, fixedValue); } table.AppendChild(entryRow); } // Recursively handle child constraints var childConstraints = template.ChildConstraints .Where(y => y.ParentConstraintId == constraint.Id) .OrderBy(y => y.Order); foreach (TemplateConstraint cConstraint in childConstraints) { if (this.HasSelectedCategories && !string.IsNullOrEmpty(cConstraint.Category) && !this.selectedCategories.Contains(cConstraint.Category)) { continue; } var nextSchemaObject = schemaObject != null? schemaObject.Children.SingleOrDefault(y => y.Name == cConstraint.Context) : null; this.AddTemplateTableConstraint(template, templateReferences, table, cConstraint, level + 1, includeCategoryHeader, nextSchemaObject); } }
public void CreateElementDefinition( StructureDefinition strucDef, TemplateConstraint constraint, SimpleSchema.SchemaObject schemaObject, string sliceName = null) { if (constraint.IsPrimitive) // Skip primitives (for now, at least) { return; } string newSliceName = null; if (constraint.IsBranch) { newSliceName = string.Format("{0}_slice_pos{1}", constraint.Context, constraint.Order); } var igSettings = GetIGSettings(constraint); var constraintFormatter = FormattedConstraintFactory.NewFormattedConstraint(this.tdb, igSettings, this.igTypePlugin, constraint); ElementDefinition newElementDef = new ElementDefinition() { Short = !string.IsNullOrEmpty(constraint.Label) ? constraint.Label : constraint.Context, Label = !string.IsNullOrEmpty(constraint.Label) ? constraint.Label : null, Comments = !string.IsNullOrEmpty(constraint.Notes) ? constraint.Notes : null, Path = constraint.GetElementPath(strucDef.ConstrainedType), Name = constraint.IsBranch ? newSliceName : sliceName, Definition = constraintFormatter.GetPlainText(false, false, false) }; // Cardinality if (!string.IsNullOrEmpty(constraint.Cardinality)) { newElementDef.Min = constraint.CardinalityType.Left; newElementDef.Max = constraint.CardinalityType.Right == Cardinality.MANY ? "*" : constraint.CardinalityType.Right.ToString(); } // Binding string valueConformance = string.IsNullOrEmpty(constraint.ValueConformance) ? constraint.Conformance : constraint.ValueConformance; bool hasBinding = constraint.References.Any(y => y.ReferenceType == ConstraintReferenceTypes.Template); if (constraint.ValueSet != null && valueConformance.IndexOf("NOT") < 0) { hasBinding = true; newElementDef.Binding = new ElementDefinition.ElementDefinitionBindingComponent() { ValueSet = new ResourceReference() { Reference = string.Format("ValueSet/{0}", constraint.ValueSet.Id), Display = constraint.ValueSet.Name } }; if (valueConformance == "SHALL") { newElementDef.Binding.Strength = BindingStrength.Required; } else if (valueConformance == "SHOULD") { newElementDef.Binding.Strength = BindingStrength.Preferred; } else if (valueConformance == "MAY") { newElementDef.Binding.Strength = BindingStrength.Example; } } // Single-Value Binding if (schemaObject != null && (!string.IsNullOrEmpty(constraint.Value) || !string.IsNullOrEmpty(constraint.DisplayName))) { hasBinding = true; switch (schemaObject.DataType) { case "CodeableConcept": var fixedCodeableConcept = new CodeableConcept(); var coding = new Coding(); fixedCodeableConcept.Coding.Add(coding); if (!string.IsNullOrEmpty(constraint.Value)) { coding.Code = constraint.Value; } if (constraint.CodeSystem != null) { coding.System = constraint.CodeSystem.Oid; } if (!string.IsNullOrEmpty(constraint.DisplayName)) { coding.Display = constraint.DisplayName; } newElementDef.Fixed = fixedCodeableConcept; break; case "Coding": var fixedCoding = new Coding(); if (!string.IsNullOrEmpty(constraint.Value)) { fixedCoding.Code = constraint.Value; } if (constraint.CodeSystem != null) { fixedCoding.System = constraint.CodeSystem.Oid; } if (!string.IsNullOrEmpty(constraint.DisplayName)) { fixedCoding.Display = constraint.DisplayName; } newElementDef.Fixed = fixedCoding; break; case "code": var fixedCode = new Code(); fixedCode.Value = !string.IsNullOrEmpty(constraint.Value) ? constraint.Value : constraint.DisplayName; newElementDef.Fixed = fixedCode; break; default: var fixedString = new FhirString(); fixedString.Value = !string.IsNullOrEmpty(constraint.Value) ? constraint.Value : constraint.DisplayName; newElementDef.Fixed = fixedString; break; } } // Add the type of the element when bound to a value set if (hasBinding && schemaObject != null && !string.IsNullOrEmpty(schemaObject.DataType)) { StructureDefinition profile = GetBaseProfile(constraint.Template); newElementDef.Type = GetProfileDataTypes(profile, constraint); var containedTemplates = (from tcr in constraint.References join t in this.tdb.Templates on tcr.ReferenceIdentifier equals t.Oid where tcr.ReferenceType == ConstraintReferenceTypes.Template select new { Identifier = t.Oid, t.PrimaryContextType }); // If there is a contained template/profile, make sure it supports a "Reference" type, and then output the profile identifier in the type if (containedTemplates.Count() > 0 && newElementDef.Type.Exists(y => y.Code == "Reference" || y.Code == "Extension")) { var containedTypes = new List <ElementDefinition.TypeRefComponent>(); foreach (var containedTemplate in containedTemplates) { bool isExtension = containedTemplate.PrimaryContextType == "Extension" && newElementDef.Type.Exists(y => y.Code == "Extension"); containedTypes.Add(new ElementDefinition.TypeRefComponent() { Code = isExtension ? "Extension" : "Reference", Profile = new List <string>(new string[] { containedTemplate.Identifier }) }); } newElementDef.Type = containedTypes; } } // Add the element to the list strucDef.Differential.Element.Add(newElementDef); // Children foreach (var childConstraint in constraint.ChildConstraints.OrderBy(y => y.Order)) { var childSchemaObject = schemaObject != null?schemaObject.Children.SingleOrDefault(y => y.Name == childConstraint.Context) : null; CreateElementDefinition(strucDef, childConstraint, childSchemaObject, newSliceName); } }
/// <summary> /// Perform validations on a single constraint. /// </summary> /// <param name="results">The list of errors that should be added to when a constraint has an error matching the schema</param> /// <param name="schemaObjects">The list of sibling-level schema objects that the constraint should be compared to</param> /// <param name="currentConstraint">The current constraint to match against the schema</param> public void ValidateTemplateConstraint( Template template, XPathNavigator xpathNavigator, List <ValidationResult> results, SimpleSchema schema, List <SimpleSchema.SchemaObject> schemaObjects, TemplateConstraint currentConstraint, IEnumerable <Template> allContainedTemplates) { List <TemplateConstraint> childConstraints = currentConstraint.ChildConstraints.ToList(); if (!string.IsNullOrEmpty(currentConstraint.Schematron)) { try { XPathExpression expr = xpathNavigator.Compile(currentConstraint.Schematron); } catch (XPathException ex) { results.Add(ValidationResult.CreateResult(template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Error, "Custom schematron is not valid: " + ex.Message)); } } if (currentConstraint.IsPrimitive && string.IsNullOrEmpty(currentConstraint.PrimitiveText)) { results.Add(ValidationResult.CreateResult(template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Error, "Primitive does not have any narrative text.")); return; } else if (!string.IsNullOrEmpty(currentConstraint.Context)) { string context = currentConstraint.Context; bool isAttribute = context.StartsWith("@"); // If it is an attribute, then we need to remove the @ from the context if (isAttribute) { context = context.Substring(1); } SimpleSchema.SchemaObject foundSchemaObject = schemaObjects.SingleOrDefault(y => y.Name.ToLower() == context.ToLower() && y.IsAttribute == isAttribute); // Verify that the template (if specified) matches the datatype of the constraints if (foundSchemaObject != null) { var templates = allContainedTemplates != null ? allContainedTemplates : this.tdb.Templates.AsEnumerable(); var containedTemplates = (from tcr in currentConstraint.References join t in templates on tcr.ReferenceIdentifier equals t.Oid where tcr.ReferenceType == ConstraintReferenceTypes.Template select t); string constraintDataType = !string.IsNullOrEmpty(currentConstraint.DataType) ? currentConstraint.DataType : foundSchemaObject.DataType; foreach (var containedTemplate in containedTemplates) { string containedTemplateDataType = containedTemplate.PrimaryContextType; bool isFhirResourceReference = schema.Schema.TargetNamespace == "http://hl7.org/fhir" && (constraintDataType == "ResourceReference" || constraintDataType == "Reference"); if (!isFhirResourceReference && containedTemplateDataType.ToLower() != constraintDataType.ToLower()) { results.Add(ValidationResult.CreateResult( template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Error, "Contained template \"{0}\" has a type of \"{1}\" which does not match the containing element \"{2}\"", containedTemplate.Oid, containedTemplateDataType, constraintDataType)); } } } // Verify that branched elements have at least one identifier if (currentConstraint.IsBranch && childConstraints.Count(y => y.IsBranchIdentifier) == 0) { results.Add(ValidationResult.CreateResult(template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Warning, "Branched constraint \"{0}\" does not have any identifiers associated with it.", currentConstraint.GetXpath())); } // Verify that a schema-object can be matched to this constraint if (foundSchemaObject == null) { results.Add(ValidationResult.CreateResult(template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Error, "Constraint has context of \"{0}\" which is not found in the schema.", currentConstraint.GetXpath())); return; // Do not process child constraints when the parent is not matched to the schema } else if (foundSchemaObject.Cardinality != null && currentConstraint.Cardinality != null) { var siblings = currentConstraint.ParentConstraint != null ? currentConstraint.ParentConstraint.ChildConstraints : template.ChildConstraints.Where(y => y.ParentConstraint == null); var isDuplicate = siblings.Count(y => y.Context == currentConstraint.Context) > 1; // Warn when a constraint that is associated with the schema that has multiple cardinality but is not branched if (template.IsOpen && !foundSchemaObject.Cardinality.EndsWith("..1") && !foundSchemaObject.Cardinality.EndsWith("..0") && !currentConstraint.IsBranch && isDuplicate) { var error = ValidationResult.CreateResult(template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Warning, "Schema allows multiple for \"{0}\" but the constraint is not branched. Consider branching this constraint.", currentConstraint.GetXpath()); results.Add(error); } if (foundSchemaObject.Cardinality.EndsWith("..1") && !(currentConstraint.Cardinality.EndsWith("..0") || currentConstraint.Cardinality.EndsWith("..1"))) { var error = ValidationResult.CreateResult(template.Id, template.Name, currentConstraint.Number.Value, ValidationLevels.Error, "The cardinality for the element is loosened in the constraint from the underlying schema."); results.Add(error); } } List <SimpleSchema.SchemaObject> childSchemaObjects = foundSchemaObject.Children; // If a data-type is specified, then we should find the data-type within the schema, since it is likely different than what // is specified by default. if (!string.IsNullOrEmpty(currentConstraint.DataType)) { SimpleSchema.SchemaObject foundSchemaTypeObject = schema.FindFromType(currentConstraint.DataType); if (foundSchemaTypeObject != null) { childSchemaObjects = foundSchemaTypeObject.Children; } } childConstraints.ForEach(y => ValidateTemplateConstraint(template, xpathNavigator, results, schema, childSchemaObjects, y, allContainedTemplates)); } }
private void AddTemplateTableConstraint(Template template, Table table, TemplateConstraint constraint, int level, bool includeCategoryHeader, SimpleSchema.SchemaObject schemaObject) { if (constraint.IsPrimitive != true && !string.IsNullOrEmpty(constraint.Context)) { string xpath = constraint.Context; string cardinality = constraint.Cardinality; string conformance = constraint.Conformance; string dataType = constraint.DataType; string fixedValue = string.Empty; string fixedValueLink = string.Empty; string levelSpacing = string.Empty; string confNumber = constraint.GetFormattedNumber(this.igSettings.PublishDate); var isFhir = constraint.Template.ImplementationGuideType.SchemaURI == ImplementationGuideType.FHIR_NS; // Check if we're dealing with a FHIR constraint if (isFhir && schemaObject != null) { dataType = schemaObject.DataType; } if (constraint.ValueSet != null) { fixedValue = string.Format("{0} ({1})", constraint.ValueSet.Oid, constraint.ValueSet.Name); } else if (constraint.CodeSystem != null) { fixedValue = string.Format("{0} ({1})", constraint.CodeSystem.Oid, constraint.CodeSystem.Name); } if (!string.IsNullOrEmpty(constraint.Value)) { if (!string.IsNullOrEmpty(fixedValue)) { fixedValue += " = " + constraint.Value; } else { fixedValue = constraint.Value; } } else if (constraint.ContainedTemplate != null) { fixedValue = string.Format("{0} (identifier: {1}", constraint.ContainedTemplate.Name, constraint.ContainedTemplate.Oid); if (this.templates.Contains(constraint.ContainedTemplate)) { fixedValueLink = constraint.ContainedTemplate.Bookmark; } } for (int i = 1; i <= (level); i++) // One tab for each level { levelSpacing += "\t"; } TableRow entryRow = new TableRow(); if (includeCategoryHeader) { AppendTextCell(entryRow, constraint.Category); } AppendTextCell(entryRow, levelSpacing + xpath); AppendTextCell(entryRow, cardinality); AppendTextCell(entryRow, conformance); AppendTextCell(entryRow, dataType); AppendHyperlinkCell(entryRow, confNumber, "C_" + confNumber); if (!string.IsNullOrEmpty(fixedValueLink)) { AppendHyperlinkCell(entryRow, fixedValue, fixedValueLink); } else { AppendTextCell(entryRow, fixedValue); } table.AppendChild(entryRow); } // Recursively handle child constraints var childConstraints = template.ChildConstraints .Where(y => y.ParentConstraintId == constraint.Id) .OrderBy(y => y.Order); foreach (TemplateConstraint cConstraint in childConstraints) { if (this.HasSelectedCategories && !string.IsNullOrEmpty(cConstraint.Category) && !this.selectedCategories.Contains(cConstraint.Category)) { continue; } var nextSchemaObject = schemaObject != null? schemaObject.Children.SingleOrDefault(y => y.Name == cConstraint.Context) : null; this.AddTemplateTableConstraint(template, table, cConstraint, level + 1, includeCategoryHeader, nextSchemaObject); } }
public void TestSimplifiedSchema_CDA() { SimpleSchema cdaSchema = SimpleSchema.CreateSimpleSchema( Trifolia.Shared.Helper.GetIGSimplifiedSchemaLocation( new ImplementationGuideType() { Name = Constants.IGTypeNames.CDA, SchemaLocation = Constants.IGTypeSchemaLocations.CDA })); Assert.IsNotNull(cdaSchema.ComplexTypes); Assert.AreEqual(232, cdaSchema.ComplexTypes.Count); // This number may change if the schema changes Assert.IsNotNull(cdaSchema.Children); Assert.AreEqual(1, cdaSchema.Children.Count); // Test root document level SimpleSchema.SchemaObject clinicalDocument = cdaSchema.Children.SingleOrDefault(y => y.Name == "ClinicalDocument"); Assert.IsNotNull(clinicalDocument); Assert.AreEqual("ClinicalDocument", clinicalDocument.DataType); Assert.AreEqual(false, clinicalDocument.Mixed); Assert.AreEqual(31, clinicalDocument.Children.Count); var clinicalDocumentInvalidChildNames = clinicalDocument.Children.Where(y => string.IsNullOrEmpty(y.Name) || y.Name.Length <= 1); Assert.IsTrue(clinicalDocumentInvalidChildNames.Count() == 0); Assert.AreEqual(SimpleSchema.ObjectTypes.Element, clinicalDocument.Type); // Test datatype SimpleSchema.SchemaObject typeId = clinicalDocument.Children.SingleOrDefault(y => y.Name == "typeId"); Assert.IsNotNull(typeId); Assert.AreEqual("typeId[typeId]", typeId.ToString()); Assert.AreEqual("typeId", typeId.DataType); Assert.AreEqual("1..1", typeId.Cardinality); Assert.AreEqual("SHALL", typeId.Conformance); Assert.AreEqual(false, typeId.Mixed); Assert.AreEqual(5, typeId.Children.Count); var typeIdInvalidNames = typeId.Children.Where(y => string.IsNullOrEmpty(y.Name) || y.Name.Length <= 1); Assert.IsTrue(typeIdInvalidNames.Count() == 0); Assert.AreEqual(SimpleSchema.ObjectTypes.Element, typeId.Type); // Test attribute SimpleSchema.SchemaObject typeIdRoot = typeId.Children.SingleOrDefault(y => y.Name == "root"); Assert.IsNotNull(typeId); Assert.AreEqual("uid", typeIdRoot.DataType); Assert.AreEqual("1..1", typeIdRoot.Cardinality); Assert.AreEqual("SHALL", typeIdRoot.Conformance); Assert.AreEqual(false, typeIdRoot.Mixed); Assert.AreEqual(0, typeIdRoot.Children.Count); Assert.AreEqual(SimpleSchema.ObjectTypes.Attribute, typeIdRoot.Type); // Test many cardinality SimpleSchema.SchemaObject recordTarget = clinicalDocument.Children.SingleOrDefault(y => y.Name == "recordTarget"); Assert.IsNotNull(recordTarget); Assert.AreEqual("RecordTarget", recordTarget.DataType); Assert.AreEqual("1..*", recordTarget.Cardinality); Assert.AreEqual("SHALL", recordTarget.Conformance); Assert.AreEqual(false, recordTarget.Mixed); Assert.AreEqual(7, recordTarget.Children.Count); var recordTargetInvalidNames = recordTarget.Children.Where(y => string.IsNullOrEmpty(y.Name) || y.Name.Length <= 1); Assert.IsTrue(recordTargetInvalidNames.Count() == 0); Assert.AreEqual(SimpleSchema.ObjectTypes.Element, recordTarget.Type); // Test patientRole SimpleSchema.SchemaObject patientRole = recordTarget.Children.SingleOrDefault(y => y.Name == "patientRole"); Assert.IsNotNull(patientRole); Assert.AreEqual("PatientRole", patientRole.DataType); Assert.AreEqual("1..1", patientRole.Cardinality); Assert.AreEqual("SHALL", patientRole.Conformance); Assert.AreEqual(false, patientRole.Mixed); Assert.AreEqual(10, patientRole.Children.Count); Assert.AreEqual(SimpleSchema.ObjectTypes.Element, patientRole.Type); // Test conformance SimpleSchema.SchemaObject patient = patientRole.Children.SingleOrDefault(y => y.Name == "patient"); Assert.IsNotNull(patient); Assert.AreEqual("Patient", patient.DataType); Assert.AreEqual("0..1", patient.Cardinality); Assert.AreEqual("MAY", patient.Conformance); Assert.AreEqual(false, patient.Mixed); Assert.AreEqual(24, patient.Children.Count); Assert.IsNotNull(patient.Children.SingleOrDefault(y => y.Name == "sdtc:raceCode")); var patientInvalidNames = patient.Children.Where(y => string.IsNullOrEmpty(y.Name) || y.Name.Length <= 1); Assert.IsTrue(patientInvalidNames.Count() == 0); Assert.AreEqual(SimpleSchema.ObjectTypes.Element, patientRole.Type); SimpleSchema.SchemaObject sdtcRaceCode = patient.Children.Single(y => y.Name == "sdtc:raceCode"); var sdtcRaceCodeInvalidNames = sdtcRaceCode.Children.Where(y => string.IsNullOrEmpty(y.Name) || y.Name.Length <= 1); Assert.IsTrue(sdtcRaceCodeInvalidNames.Count() == 0); // Test mixed SimpleSchema.SchemaObject patientName = patient.Children.SingleOrDefault(y => y.Name == "name"); Assert.IsNotNull(patientName); Assert.AreEqual("PN", patientName.DataType); Assert.AreEqual("0..*", patientName.Cardinality); Assert.AreEqual("MAY", patientName.Conformance); Assert.AreEqual(true, patientName.Mixed); Assert.AreEqual(8, patientName.Children.Count, "Expected patient to have 8 children"); // TODO: Schema Choice support temporarily removed from non-FHIR schemas Assert.IsTrue(patientName.Children.Count(y => string.IsNullOrEmpty(y.Name) || y.Name.Length <= 1) == 0); Assert.AreEqual(SimpleSchema.ObjectTypes.Element, patientRole.Type); }
private void AddConstraint(XmlNode aParent, List <SimpleSchema.SchemaObject> siblingSchemaObjects, TemplateConstraint aConstraint) { // If the constraint is a primitive, add a comment to the sample if (aConstraint.IsPrimitive) { XmlComment comment = _currentDocument.CreateComment("PRIMITIVE: " + aConstraint.PrimitiveText); XmlNode nonAttributeParent = GetNonAttributeParent(aParent); if (nonAttributeParent != null) { nonAttributeParent.AppendChild(comment); } } // If the constraint is not primitive and does not have a context, add an error comment to the sample else if (!aConstraint.IsPrimitive && string.IsNullOrEmpty(aConstraint.Context)) { XmlComment comment = _currentDocument.CreateComment("ERROR: Constraint " + aConstraint.Id.ToString() + " does not have a context."); aParent.AppendChild(comment); } else { // Splitting on / in case legacy data is exported that makes use of xpath in constraint contexts string[] lContextSplit = aConstraint.Context.Split('/'); XmlNode lCurrentNode = aParent; SimpleSchema.SchemaObject lSchemaObject = null; foreach (string lCurrentContextSplit in lContextSplit) { bool lIsAttribute = lCurrentContextSplit.StartsWith("@"); string lStrippedContext = lCurrentContextSplit.Replace("@", ""); // Add an error comment if an attribute is a child of another attribute (ex: "@root/@extension") if (lIsAttribute && lCurrentNode is XmlAttribute) { XmlComment comment = _currentDocument.CreateComment( "ERROR: An attribute cannot be a child of another attribute for constraint " + aConstraint.Id.ToString()); lCurrentNode.AppendChild(comment); break; } // Find the schema object associated with this constraint if (siblingSchemaObjects != null) { lSchemaObject = siblingSchemaObjects.SingleOrDefault( y => y.Name == lStrippedContext && y.IsAttribute == lIsAttribute); } // If the constraint is an attribute, add an attribute to the parent if (lIsAttribute) { XmlAttribute attribute = lCurrentNode.Attributes[lStrippedContext]; if (attribute == null) { attribute = _currentDocument.CreateAttribute(lStrippedContext); lCurrentNode.Attributes.Append(attribute); } if (!string.IsNullOrEmpty(aConstraint.Value)) { attribute.Value = aConstraint.Value; } else { attribute.Value = "XXX"; } lCurrentNode = attribute; } // Otherwise, create an element and add it to the parent else { XmlElement lNewContextElement = _currentDocument.CreateElement(lCurrentContextSplit); if (lSchemaObject != null) { foreach (SimpleSchema.SchemaObject lCurrentSchemaAttribute in lSchemaObject.Children.Where(y => y.IsAttribute)) { if (!lCurrentSchemaAttribute.Cardinality.StartsWith("1..")) { continue; } XmlAttribute newAttribute = _currentDocument.CreateAttribute(lCurrentSchemaAttribute.Name); lNewContextElement.Attributes.Append(newAttribute); if (!string.IsNullOrEmpty(lCurrentSchemaAttribute.FixedValue)) { newAttribute.Value = lCurrentSchemaAttribute.FixedValue; } else if (!string.IsNullOrEmpty(lCurrentSchemaAttribute.Value)) { newAttribute.Value = lCurrentSchemaAttribute.Value; } else { newAttribute.Value = "YYY"; } } if (!string.IsNullOrEmpty(aConstraint.DataType) && lSchemaObject.DataType != aConstraint.DataType) { XmlAttribute xsiTypeAttribute = _currentDocument.CreateAttribute("xsi", "type", "http://www.w3.org/2001/XMLSchema-instance"); xsiTypeAttribute.Value = aConstraint.DataType; lNewContextElement.Attributes.Append(xsiTypeAttribute); } } // Do not add elements to attributes if (!(lCurrentNode is XmlAttribute)) { lCurrentNode.AppendChild(lNewContextElement); lCurrentNode = lNewContextElement; } else { // Add a comment to the generated sample that indicates the problem string commentText = string.Format( "Error: Attribute contains an element. (CONF #: {0}, context: '{1}')", aConstraint.Id, !string.IsNullOrEmpty(aConstraint.Context) ? aConstraint.Context : "N/A"); XmlComment comment = this._currentDocument.CreateComment(commentText); if (lCurrentNode.ParentNode != null) { lCurrentNode.ParentNode.AppendChild(comment); } else { this._currentDocument.DocumentElement.AppendChild(comment); } } } if (lSchemaObject == null) { this.isSchemaValid = false; } } List <TemplateConstraint> childConstraints = aConstraint.ChildConstraints .OrderBy(y => y.Order) .ToList(); childConstraints.ForEach(y => AddConstraint(lCurrentNode, lSchemaObject != null ? lSchemaObject.Children : null, y)); if (lSchemaObject != null) { this.AddMissingRequiredData(lCurrentNode, lSchemaObject.Children); } if (lCurrentNode is XmlElement) { foreach (XmlNode childNode in lCurrentNode.ChildNodes) { if (childNode is XmlElement) { this.igTypePlugin.FillSampleData((XmlElement)childNode); } } } } }
private void CreateConstraints(IGSettingsManager igManager, IIGTypePlugin igTypePlugin, IEnumerable <TemplateConstraint> constraints, List <ViewDataModel.Constraint> parentList, SimpleSchema templateSchema, SimpleSchema.SchemaObject schemaObject = null) { foreach (var constraint in constraints.OrderBy(y => y.Order)) { var theConstraint = constraint; // TODO: Possible bug? Should schemaObject always be re-set? Remove schemaObject == null from if() if (templateSchema != null && schemaObject == null) { schemaObject = templateSchema.Children.SingleOrDefault(y => y.Name == constraint.Context); } IFormattedConstraint fc = FormattedConstraintFactory.NewFormattedConstraint(this.tdb, igManager, igTypePlugin, theConstraint, this.constraintReferences.Cast <ConstraintReference>().ToList(), "#/volume2/", "#/valuesets/#", true, true, true, false); var newConstraintModel = new ViewDataModel.Constraint() { Number = string.Format("{0}-{1}", theConstraint.Template.OwningImplementationGuideId, theConstraint.Number), Narrative = fc.GetHtml(string.Empty, 1, true), Conformance = theConstraint.Conformance, Cardinality = theConstraint.Cardinality, Context = theConstraint.Context, DataType = theConstraint.DataType, Value = theConstraint.Value, ValueSetIdentifier = theConstraint.ValueSet != null?theConstraint.ValueSet.GetIdentifier(igTypePlugin) : null, ValueSetDate = theConstraint.ValueSetDate != null?theConstraint.ValueSetDate.Value.ToShortDateString() : null, IsChoice = theConstraint.IsChoice }; newConstraintModel.ContainedTemplates = (from cr in this.constraintReferences where cr.TemplateConstraintId == theConstraint.Id select new ViewDataModel.TemplateReference() { Identifier = cr.Identifier, Bookmark = cr.Bookmark, ImplementationGuide = cr.ImplementationGuide, PublishDate = cr.PublishDate, Name = cr.Name }).ToList(); var isFhir = constraint.Template.ImplementationGuideType.SchemaURI == ImplementationGuideType.FHIR_NS; // Check if we're dealing with a FHIR constraint if (isFhir && schemaObject != null) { newConstraintModel.DataType = schemaObject.DataType; } parentList.Add(newConstraintModel); var nextSchemaObject = schemaObject != null? schemaObject.Children.SingleOrDefault(y => y.Name == constraint.Context) : null; // Recursively add child constraints CreateConstraints(igManager, igTypePlugin, theConstraint.ChildConstraints, newConstraintModel.Constraints, null, nextSchemaObject); } }