/// <summary> /// Uses forward algorithm to go from this constraint forward through the tree /// </summary> /// <param name="aTemplate"></param> /// <param name="aTemplateConstraint"></param> /// <returns></returns> public static string CreateFullBranchedParentContext(string aPrefix, Template aTemplate, TemplateConstraint aTemplateConstraint) { TemplateConstraint current = aTemplateConstraint; var igTypePlugin = aTemplate.ImplementationGuideType.GetPlugin(); string templateContext = TemplateContextBuilder.BuildContextString( aPrefix, igTypePlugin.TemplateIdentifierXpath, igTypePlugin.TemplateVersionIdentifierXpath, aTemplate) + "/"; return(templateContext + CreateFullBranchedParentContext(aPrefix, aTemplateConstraint, isTarget: true)); }
/// <summary> /// Adds the constraints for the closed template to ensure that no template id's other than those explicitly named are used in the xml. /// </summary> /// <param name="aClosedTemplate">Closed template to generate the constraint for</param> /// <param name="aPhase">The error phase for the schematron</param> /// <returns> /// Pattern that was added to the schematron /// </returns> public Pattern AddClosedTemplateConstraints(Template aClosedTemplate, Phase aPhase) { if (aClosedTemplate.IsOpen) { return(null); } Pattern newPattern = null; string test = GenerateClosedTemplateConstraints(aClosedTemplate); if (!string.IsNullOrEmpty(test)) { string templateContext = TemplateContextBuilder.BuildContextString( this.ig.ImplementationGuideType.SchemaPrefix, this.igTypePlugin.TemplateIdentifierXpath, this.igTypePlugin.TemplateVersionIdentifierXpath, aClosedTemplate); newPattern = new Pattern() { ID = string.Format("p-{0}-CLOSEDTEMPLATE", aClosedTemplate.Oid), Name = string.Format("p-{0}-CLOSEDTEMPLATE", aClosedTemplate.Oid) }; Rule newRule = new Rule() { Id = string.Format("r-{0}-errors-CL", aClosedTemplate.Oid), Context = templateContext }; Assertion newAssertion = new Assertion() { Id = string.Format("{0}-{1}", aClosedTemplate.OwningImplementationGuideId, aClosedTemplate.Id), IdPostFix = "-CL", AssertionMessage = string.Format("'{0}' is a closed template, only defined templates are allowed.", aClosedTemplate.Oid), Test = test }; newRule.Assertions.Add(newAssertion); newPattern.Rules.Add(newRule); if (newPattern.Rules.Count > 0) { aPhase.ActivePatterns.Add(newPattern); } } return(newPattern); }
internal void GenerateBranchedPattern(Conformance MinimumConformanceLevel, Pattern aPattern, IEnumerable <TemplateConstraint> aConstraints, IEnumerable <TemplateConstraint> aTemplateConstraints, Template aTemplate, Phase aPhase) { string rulePostFix = MinimumConformanceLevel == Conformance.SHALL ? "-errors" : "-warnings"; string errorId = aTemplate.Oid.Trim() + "-{0}"; string patternErrorId = "p-" + errorId; string ruleErrorId = "r-" + errorId + "-branch-{0}" + rulePostFix; List <int> AlreadyGeneratedRoots = new List <int>(); foreach (var branch in aConstraints) { if (!branch.CategoryIsMatch(this.categories)) { continue; } TemplateConstraint root = GetBranch(branch); //get all constraints that have this root as their parent if (!AlreadyGeneratedRoots.Contains(root.Id)) { AlreadyGeneratedRoots.Add(root.Id); string context = TemplateContextBuilder.CreateFullBranchedParentContext(this.ig.ImplementationGuideType.SchemaPrefix, aTemplate, root); var rule = new Rule(); rule.Id = string.Format(ruleErrorId, root.Number); rule.Context = context; // No branch identifiers unless there is custom schematron var branchedConstraints = aTemplateConstraints.Where(c => (c.Id != root.Id) && (root == GetNearestBranch(c))) .Where(y => !y.IsBranchIdentifier || !string.IsNullOrEmpty(y.Schematron)); foreach (var constraint in branchedConstraints) { if (!constraint.CategoryIsMatch(this.categories)) { continue; } try { Conformance leastConformanceForGraph = GetMinimumConformanceForConstraint(constraint); //exclude MAYs (we never want a constraint whose conformance is MAY, we will take graph MAYs but not directly on the constraint) if (constraint.BusinessConformanceType == Conformance.MAY || constraint.BusinessConformanceType == Conformance.MAY_NOT || ShouldSkipConstraint(constraint) || !MeetsConformance(MinimumConformanceLevel, leastConformanceForGraph)) { continue; } //skip direct SHOULD on SHALL if (constraint.BusinessConformanceType == Conformance.SHOULD && MinimumConformanceLevel == Conformance.SHALL) { continue; } //skip direct SHALL on SHOULD if (constraint.BusinessConformanceType == Conformance.SHALL && MinimumConformanceLevel == Conformance.SHOULD) { continue; } // Skip for constraints that are rooted, but only when there is either custom schematron or it is a primitive if (constraint.IsSchRooted && (!string.IsNullOrEmpty(constraint.Schematron) || constraint.IsPrimitive)) { continue; } rule.Assertions.Add(GetAssertion(constraint, string.Format("-branch-{0}", root.Number))); } catch (Exception ex) { Log.For(this).Error("Error getting assertion for constraint {0} on template {1}.", ex, constraint.Id, constraint.TemplateId); throw ex; } } if (rule.Assertions.Count > 0) { aPattern.Rules.Add(rule); } } } }
internal void AddTemplate(Template template, Phase errorPhase, Phase warningPhase, bool isImplied = false) { string errorId = template.Oid.Trim() + "-errors"; string warningId = template.Oid.Trim() + "-warnings"; string patternErrorId = "p-" + errorId; string patternWarningId = "p-" + warningId; string ruleErrorId = "r-" + errorId; string ruleWarningId = "r-" + warningId; string impliedPatternErrorId = null; string impliedPatternWarningId = null; string impliedRuleErrorId = null; string impliedRuleWarningId = null; // Add the implied template first if (template.ImpliedTemplate != null && template.ImpliedTemplateId != template.Id && this.templates.Contains(template.ImpliedTemplate)) { string impliedErrorId = template.ImpliedTemplate.Oid.Trim() + "-errors"; string impliedWarningId = template.ImpliedTemplate.Oid.Trim() + "-warnings"; impliedPatternErrorId = "p-" + impliedErrorId; impliedPatternWarningId = "p-" + impliedWarningId; impliedRuleErrorId = "r-" + impliedErrorId; impliedRuleWarningId = "r-" + impliedWarningId; // Make sure we haven't already added the implied template and that this is not an endless recursive loop if (!errorPhase.ActivePatterns.Exists(y => y.ID == impliedPatternErrorId) && !warningPhase.ActivePatterns.Exists(y => y.ID == impliedPatternWarningId)) { AddTemplate(template.ImpliedTemplate, errorPhase, warningPhase, true); } } bool alreadyGeneratedErrorPattern = errorPhase.ActivePatterns.Exists(y => y.ID == patternErrorId); bool alreadyGeneratedWarningPattern = warningPhase.ActivePatterns.Exists(y => y.ID == patternWarningId); if (alreadyGeneratedErrorPattern && alreadyGeneratedWarningPattern) { return; } //realize the list so that all of them are pulled from database var constraints = template.ChildConstraints.ToList(); //NOTE: there are also child templates on the template class. If GetRecursiveTemplates does not flatten the heirarchy, then we'll need to walk the tree. We could do this in the linq query, but I' not certain //whether the context is set explicitly in the child templates, or whether it's implied that the implementer (me) would have to discover it. //get only SHALLS that aren't part of a branch and don't have optional parents var requiredConformance = (from c in constraints where (!string.IsNullOrEmpty(c.Conformance) && (c.BusinessConformanceType == Conformance.SHALL || c.BusinessConformanceType == Conformance.SHALL_NOT)) && (!c.IsBranchIdentifier || (c.IsBranchIdentifier && !string.IsNullOrEmpty(c.Schematron))) select c).OrderBy(c => c.Id); //get all SHOULDs and only SHALLs having optional parents but that aren't part of a branch var optionalConformance = (from c in constraints where ((!string.IsNullOrEmpty(c.Conformance) && (c.BusinessConformanceType == Conformance.SHOULD || c.BusinessConformanceType == Conformance.SHOULD_NOT))) && !(c.IsBranchIdentifier == true) select c).OrderBy(c => c.Id).ToList(); //add in optional value conformance on required constraints AddOptionalValueConformanceOnRequiredElement(optionalConformance, constraints); //get only SHALLs that are leaf level of a branch and don't have optional parents var branchedRequiredConformance = (from c in constraints where (!string.IsNullOrEmpty(c.Conformance) && c.BusinessConformanceType == Conformance.SHALL || c.BusinessConformanceType == Conformance.SHALL_NOT) && c.HasParentBranch() select c).OrderBy(c => c.Id); //get all SHOULDs and only SHALLs having optional parents but that are leaf level part of a branch var branchedOptionalConformance = (from c in constraints where ((!string.IsNullOrEmpty(c.Conformance) && (c.BusinessConformanceType == Conformance.SHOULD || c.BusinessConformanceType == Conformance.SHOULD_NOT)) || (IsOptionalParent(c) && HasRequiredElement(c))) select c).Where(c => IsBranchDescendent(c)).OrderBy(c => c.Id); string templateContext = string.Empty; templateContext = TemplateContextBuilder.BuildContextString( this.ig.ImplementationGuideType.SchemaPrefix, this.igTypePlugin.TemplateIdentifierXpath, this.igTypePlugin.TemplateVersionIdentifierXpath, template); Pattern ErrorPattern = null; ErrorPattern = GeneratePattern(Conformance.SHALL, requiredConformance, errorPhase, patternErrorId, ruleErrorId, templateContext, impliedPatternErrorId, impliedRuleErrorId, isImplied); Pattern WarningPattern = null; WarningPattern = GeneratePattern(Conformance.SHOULD, optionalConformance, warningPhase, patternWarningId, ruleWarningId, templateContext, impliedPatternWarningId, impliedRuleWarningId, isImplied); if (branchedRequiredConformance.Count() > 0) { GenerateBranchedPattern(Conformance.SHALL, ErrorPattern, branchedRequiredConformance, constraints, template, errorPhase); } if (branchedOptionalConformance.Count() > 0) { GenerateBranchedPattern(Conformance.SHOULD, WarningPattern, branchedOptionalConformance, constraints, template, warningPhase); } }