Example #1
0
        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);
            }
        }
Example #2
0
        /// <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}\" ({1}) but the constraint is not branched. Consider branching this constraint.", currentConstraint.GetXpath(), currentConstraint.GetFormattedNumber());
                        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));
            }
        }