internal void AddReference(ScopedNode node) { if (References == null || !References.Contains(node)) { (References ?? (References = new List <ScopedNode>())).Add(node); } }
internal static OperationOutcome ValidateChildConstraints(this Validator validator, ElementDefinitionNavigator definition, ScopedNode instance, bool allowAdditionalChildren) { var outcome = new OperationOutcome(); if (!definition.HasChildren) { return(outcome); } validator.Trace(outcome, "Start validation of inlined child constraints for '{0}'".FormatWith(definition.Path), Issue.PROCESSING_PROGRESS, instance); var matchResult = ChildNameMatcher.Match(definition, instance); if (matchResult.UnmatchedInstanceElements.Any() && !allowAdditionalChildren) { var elementList = String.Join(",", matchResult.UnmatchedInstanceElements.Select(e => "'" + e.Name + "'")); validator.Trace(outcome, $"Encountered unknown child elements {elementList} for definition '{definition.Path}'", Issue.CONTENT_ELEMENT_HAS_UNKNOWN_CHILDREN, instance); } //TODO: Give warnings for out-of order children. Really? That's an xml artifact, no such thing in Json! //(with the serializationrepresentationnav we could determine the source is xml and make order matter....) // Recursively validate my children foreach (var match in matchResult.Matches) { outcome.Add(validator.ValidateMatch(match, instance)); } return(outcome); }
public IList <Declaration> FindMembers(object result, int line, int col) { List <Declaration> members = new List <Declaration>(); if (_node != null) { ScopedNode found = FindNode(_node.TreeNode, line, col); if (found != null) { if (found.TreeFuncs != null) { foreach (Method func in found.TreeFuncs) { members.Add(new Declaration(func.Description, func.Name, 207, func.Name)); } } if (found.ScopeVars != null) { foreach (Field field in found.ScopeVars) { members.Add(new Declaration(field.Description, field.Name, 208, field.Name)); } } } } return(members); }
private ScopedNode FindNode(ParseTreeNode rootnode, int line, int col) { ScopedNode found = null; foreach (ParseTreeNode childnode in rootnode.ChildNodes) { ScopedNode node = childnode.AstNode as ScopedNode; if (node != null) { int endline, endcol; try { _source.GetLineIndexOfPosition(node.Span.EndPosition, out endline, out endcol); if ((node.Location.Line < line && endline > line) || (node.Location.Line == line && node.Location.Column >= col && (endline > line || endcol >= col))) { found = FindNode(node.TreeNode, line, col) ?? node as ScopedNode; } } catch (ArgumentException) { } } } return(found); }
internal Field(string name, UoToken uotypetoken, ScopedNode locationnode, ParsingContext context, bool isconst = false) { Name = name; Description = string.Empty; Node = locationnode; if (Node != null) { AddReference(Node); } if (uotypetoken != null) { UoTypeToken = uotypetoken; Type = uotypetoken.Value; } else { Type = null; UoTypeToken = null; } Value = null; DefFilename = context.CurrentParseTree.FileName; isConst = isconst; }
protected static ITypedElement MakeElementStack(Base instance, SummaryType summary) { if (summary == SummaryType.False) { return(instance.ToTypedElement()); } var patchedInstance = (Base)instance.DeepCopy(); MetaSubsettedAdder.AddSubsetted(patchedInstance, atRoot: true); var baseNav = new ScopedNode(patchedInstance.ToTypedElement()); switch (summary) { case SummaryType.True: return(MaskingNode.ForSummary(baseNav)); case SummaryType.Text: return(MaskingNode.ForText(baseNav)); case SummaryType.Data: return(MaskingNode.ForData(baseNav)); case SummaryType.Count: return(MaskingNode.ForCount(baseNav)); default: return(baseNav); } }
internal void AddReference(ScopedNode node) { // if (node is DeclarationNode) node = ((DeclarationNode)Node).DeclNode; if (References == null || !References.Contains(node)) { (References ?? (References = new List <ScopedNode>())).Add(node); } }
private static ITypedElement wrapInScopedNode(ITypedElement input) { if (input is not ScopedNode) { input = new ScopedNode(input); } return(input); }
public void SetupSource() { var bundleXml = File.ReadAllText(Path.Combine("TestData", "bundle-contained-references.xml")); var bundle = (new FhirXmlParser()).Parse <Bundle>(bundleXml); Assert.IsNotNull(bundle); _bundleNode = new ScopedNode(bundle.ToTypedElement()); }
private void EmitScopedNode(ScopedNode node) { using (var model = emitter.DeclareLocal(node.ModelToScope.ResultType)) { EvaluateExpression(node.ModelToScope); emitter.StoreLocal(model); AddModelScope(x => x.LoadLocal(model)); EmitNode(node.Node); RemoveModelScope(); } }
public void SummaryData() { var tpXml = File.ReadAllText(Path.Combine("TestData", "mask-text.xml")); var typeinfo = new PocoStructureDefinitionSummaryProvider().Provide("ValueSet"); var nav = new ScopedNode(getXmlNode(tpXml)); var masker = MaskingNode.ForData(nav); var output = masker.ToXml(); var maskedChildren = masker.Descendants().Count(); Assert.AreEqual(nav.Descendants().Count() - 3, maskedChildren); }
public void CreateFuncsNode(ParsingContext context, ParseTreeNode parseNode) { ScopedNode node = new ScopedNode(); node.Init(context, parseNode); parseNode.AstNode = node; foreach (ParseTreeNode cnode in parseNode.ChildNodes) { ((ScopedNode)cnode.AstNode).Parent = node; node.ChildNodes.Add((ScopedNode)cnode.AstNode); } }
public void TestBucketAssignment() { var s = createSliceDefs() as SliceGroupBucket; var p = new Patient(); p.Telecom.Add(new ContactPoint { System = ContactPoint.ContactPointSystem.Phone, Use = ContactPoint.ContactPointUse.Home, Value = "+31-6-39015765" }); p.Telecom.Add(new ContactPoint { System = ContactPoint.ContactPointSystem.Email, Use = ContactPoint.ContactPointUse.Work, Value = "*****@*****.**" }); p.Telecom.Add(new ContactPoint { System = ContactPoint.ContactPointSystem.Other, Use = ContactPoint.ContactPointUse.Temp, Value = "skype://crap" }); p.Telecom.Add(new ContactPoint { System = ContactPoint.ContactPointSystem.Other, Use = ContactPoint.ContactPointUse.Home, Value = "http://nu.nl" }); p.Telecom.Add(new ContactPoint { System = ContactPoint.ContactPointSystem.Fax, Use = ContactPoint.ContactPointUse.Work, Value = "+31-20-6707070" }); var pnode = new ScopedNode(p.ToTypedElement()); var telecoms = pnode.Children("telecom").Cast <ScopedNode>(); foreach (var telecom in telecoms) { Assert.True(s.Add(telecom)); } var outcome = s.Validate(_validator, pnode); Assert.True(outcome.Success); Assert.Equal(0, outcome.Warnings); Assert.Equal("+31-6-39015765", s.ChildSlices[0].Members.Single().Children("value").Single().Value); var emailBucket = s.ChildSlices[1] as SliceGroupBucket; Assert.Equal("*****@*****.**", emailBucket.Members.Single().Children("value").Single().Value); Assert.False(emailBucket.ChildSlices[0].Members.Any()); Assert.Equal("*****@*****.**", emailBucket.ChildSlices[1].Members.Single().Children("value").Single().Value); var otherBucket = s.ChildSlices[2] as SliceGroupBucket; Assert.Equal("http://nu.nl", otherBucket.ChildSlices[0].Members.Single().Children("value").Single().Value); Assert.False(otherBucket.ChildSlices[1].Members.Any()); Assert.Equal("skype://crap", otherBucket.Members.First().Children("value").Single().Value); // in the open slice - find it on other bucket, not child Assert.Equal("+31-20-6707070", s.Members.Last().Children("value").Single().Value); // in the open-at-end slice }
public void SummaryCountUsingStructureDefinitionSummaryProvider() { var tpXml = File.ReadAllText(Path.Combine("TestData", "mask-text.xml")); var nav = new ScopedNode(getXmlNodeSDSP(tpXml)); var masker = MaskingNode.ForCount(nav); var maskedChildren = masker.Descendants().Count(); Assert.AreEqual(maskedChildren, 2); ITypedElement getXmlNodeSDSP(string xml, FhirXmlParsingSettings s = null) => XmlParsingHelpers.ParseToTypedElement(xml, new StructureDefinitionSummaryProvider(ZipSource.CreateValidationSource()), s); }
public void Summary() { var tpXml = File.ReadAllText(Path.Combine("TestData", "fp-test-patient.xml")); var typeinfo = new PocoStructureDefinitionSummaryProvider().Provide("Patient"); var inSummary = typeinfo.GetElements().Where(e => e.InSummary).ToList(); var nav = new ScopedNode(getXmlNode(tpXml)); var masker = MaskingNode.ForSummary(nav); var output = masker.ToXml(); var maskedChildren = masker.Children().ToList(); Assert.IsTrue(maskedChildren.Count < inSummary.Count); Assert.IsTrue(maskedChildren.Select(c => c.Name).All(c => inSummary.Any(s => s.ElementName == c))); }
private Expression HandleScopedNode(ScopedNode node) { var scopedModel = ParseExpression(node.ModelToScope); var storedModel = Expression.Variable(scopedModel.Type); PushScope(storedModel); var body = HandleNode(node.Node); PopScope(); return(Expression.Block( new[] { storedModel }, Expression.Assign(storedModel, scopedModel), body )); }
public void ScriptNodeCreator(ParsingContext context, ParseTreeNode parseNode) { ScopedNode node = new ScopedNode(); node.Init(context, parseNode); parseNode.AstNode = node; foreach (ParseTreeNode cnode in parseNode.ChildNodes) { if (cnode.Term.Name == "Declarations") { node.ChildNodes.Add((ScopedNode)cnode.AstNode); ((ScopedNode)cnode.AstNode).Parent = node; node.m_LocalVars = GetMembers(context); } } // Initialize a check of types and scope on the script tree node.CheckTree(context); }
protected static ITypedElement MakeElementStack(Base instance, SummaryType summary, string[] elements) { if (summary == SummaryType.False && elements == null) { return(instance.ToTypedElement()); } if (elements != null && summary != SummaryType.False) { throw Error.Argument("elements", "Elements parameter is supported only when summary is SummaryType.False or summary is not specified at all."); } var patchedInstance = (Base)instance.DeepCopy(); MetaSubsettedAdder.AddSubsetted(patchedInstance, atRoot: true); var baseNav = new ScopedNode(patchedInstance.ToTypedElement()); switch (summary) { case SummaryType.True: return(MaskingNode.ForSummary(baseNav)); case SummaryType.Text: return(MaskingNode.ForText(baseNav)); case SummaryType.Data: return(MaskingNode.ForData(baseNav)); case SummaryType.Count: return(MaskingNode.ForCount(baseNav)); case SummaryType.False: return(MaskingNode.ForElements(baseNav, elements)); default: return(baseNav); } }
private static ITypedElement resolveReference(this Validator validator, ScopedNode instance, string reference, out ElementDefinition.AggregationMode?referenceKind, OperationOutcome outcome) { var identity = new ResourceIdentity(reference); if (identity.Form == ResourceIdentityForm.Undetermined) { if (!Uri.IsWellFormedUriString(Uri.EscapeDataString(reference), UriKind.RelativeOrAbsolute)) { validator.Trace(outcome, $"Encountered an unparseable reference ({reference})", Issue.CONTENT_UNPARSEABLE_REFERENCE, instance); referenceKind = null; return(null); } } var result = instance.Resolve(reference); if (identity.Form == ResourceIdentityForm.Local) { referenceKind = ElementDefinition.AggregationMode.Contained; if (result == null) { validator.Trace(outcome, $"Contained reference ({reference}) is not resolvable", Issue.CONTENT_CONTAINED_REFERENCE_NOT_RESOLVABLE, instance); } } else { if (result != null) { referenceKind = ElementDefinition.AggregationMode.Bundled; } else { referenceKind = ElementDefinition.AggregationMode.Referenced; } } return(result); }
private Func <OperationOutcome> createValidator(ElementDefinitionNavigator nav, ScopedNode instance) { return(() => validateElement(nav, instance)); }
internal void LoadFile(string path, ScopedNode node, ParsingContext context, IDictionary <string, string> refDepends = null) { string curExt = context.CurrentParseTree.FileName == "<Source>" ? null : Path.GetExtension(context.CurrentParseTree.FileName); string fullpath = curExt == null ? null : Utils.FindFile(string.Format("{0}.{1}", path, curExt), context.CurrentParseTree.FileName) ?? Utils.FindFile(path, context.CurrentParseTree.FileName) ?? Utils.FindFile(string.Format("{0}.uosl", path), context.CurrentParseTree.FileName) ?? Utils.FindFile(string.Format("{0}.uosl.q", path), context.CurrentParseTree.FileName); if (fullpath != null) { fullpath = (new FileInfo(fullpath)).FullName; FileInfo fi = new FileInfo(fullpath); ParseCache cache; if (!Inherits.TryGetValue(fullpath, out cache) || fi.LastWriteTime > cache.FileDate) { Inherits[fullpath] = cache = new ParseCache(fi.LastWriteTime); LanguageOption options = Utils.DetermineFileLanguage(fullpath, this.Options); ParsingContext subcontext = new ParsingContext(new Parser(options == this.Options ? this : GetGrammar(options))); using (StreamReader reader = new StreamReader(fullpath)) { Inherits[fullpath] = cache = new ParseCache(fi.LastWriteTime, subcontext.Parser.Parse(reader.ReadToEnd(), fullpath)); } } if (refDepends != null) { refDepends.Add(path, fullpath); } if (cache.Tree != null) { if (cache.Tree.HasErrors()) { foreach (ParserMessage error in cache.Tree.ParserMessages) { if (error is ExternalErrorMessage) { context.AddParserMessage(error); } else { context.AddParserMessage(new ExternalErrorMessage(fullpath, error.Level, error.Location, error.Message, error.ParserState)); } } } if (cache.Tree.Root != null && cache.Tree.Root.AstNode != null) { if (((ScopedNode)cache.Tree.Root.AstNode).ScopeVars != null) { foreach (Field field in ((ScopedNode)cache.Tree.Root.AstNode).ScopeVars) { node.AddVar(field, context); } } if (((ScopedNode)cache.Tree.Root.AstNode).TreeFuncs != null) { foreach (Method method in ((ScopedNode)cache.Tree.Root.AstNode).TreeFuncs) { AddFunc(method, context); } } if (refDepends != null && cache.Tree.Root.FirstChild.AstNode is DeclarationsNode) { foreach (var kvp in ((DeclarationsNode)cache.Tree.Root.FirstChild.AstNode).Depends) { if (refDepends.ContainsKey(kvp.Key)) { context.AddParserMessage(ParserErrorLevel.Error, node.Span, "A recursion of inheritance detected occurred when parsing {0}.", kvp.Key); } else { refDepends.Add(kvp.Key, kvp.Value); } } } } } } else { context.AddParserError("{0} Dependancy not found.", path); } }
void ICompletionSource.AugmentCompletionSession(ICompletionSession session, IList <CompletionSet> completionSets) { List <string> strList = new List <string>(); SnapshotPoint point = session.GetTriggerPoint(m_textBuffer).GetPoint(m_textBuffer.CurrentSnapshot); if (nodeprovider.isComment(point)) { return; } Node node = nodeprovider.GetMostSpecificNode(point); List <Completion> m_compList = null; if (node != null && node.astnode is ScopedNode) { var terms = ((ScopedNode)node.astnode).ExpectedTerms.Where(term => term.FlagIsSet(Irony.Parsing.TermFlags.IsKeyword) || term.FlagIsSet(Irony.Parsing.TermFlags.IsPunctuation)); if (terms.Count() > 0) { foreach (var term in terms) { if (!strList.Contains(term.Name)) { strList.Add(term.Name); } } } else { foreach (var term in UOSL.Service.UOSLBase.Keywords) { if (!strList.Contains(term.Key)) { strList.Add(term.Key); } } } if (strList.Count > 0) { foreach (string str in strList) { (m_compList ?? (m_compList = new List <Completion>())).Add(new Completion(str, str, str, null, null)); } strList.Clear(); } } if (node == null || node.astnode.AsString == "Declarations" || node.astnode.AsString == "Script") { int pos = point.Position; // move to beginning of previous word while ((pos >= point.Snapshot.Length || char.IsWhiteSpace(point.Snapshot[pos])) && pos >= 0) { pos--; } int end = pos; while (!char.IsWhiteSpace(point.Snapshot[pos]) && pos >= 0) { pos--; } string keyword = pos > 0 && pos < end?point.Snapshot.GetText(pos + 1, end - pos) : null; if (keyword == "trigger" && m_trigsNames != null) { completionSets.Add(new CompletionSet( "Triggers", //the non-localized title of the tab "Triggers", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_trigsNames, null)); } else if (m_types != null && keyword == "function" || keyword == "member" || keyword == "forward") { completionSets.Add(new CompletionSet( "Types", //the non-localized title of the tab "Types", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_types, null)); } else if (m_trigsFull != null) { if (m_compList != null) { completionSets.Add(new CompletionSet( "Keywords", //the non-localized title of the tab "Keywords", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); } completionSets.Add(new CompletionSet( "Triggers", //the non-localized title of the tab "Triggers", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_trigsFull, null)); } else if (m_compList != null) { completionSets.Add(new CompletionSet( "Keywords", //the non-localized title of the tab "Keywords", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); } } else { if (m_compList != null) { completionSets.Add(new CompletionSet( "Keywords", //the non-localized title of the tab "Keywords", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); } ScopedNode snode = node.astnode as ScopedNode; if (snode.ScopeVars != null) { foreach (var var in snode.ScopeVars) { strList.Add(var.Name); } if (strList.Count > 0) { strList.Sort(); m_compList = new List <Completion>(); foreach (string str in strList) { m_compList.Add(new Completion(str, str, str, null, null)); } completionSets.Add(new CompletionSet( "Vars", //the non-localized title of the tab "Vars", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); strList.Clear(); } } if (nodeprovider.Funcs != null) { foreach (var func in nodeprovider.Funcs) { if (!strList.Contains(func.Name)) { strList.Add(func.Name); } } if (strList.Count > 0) { strList.Sort(); m_compList = new List <Completion>(); foreach (string str in strList) { m_compList.Add(new Completion(str, str, str, null, null)); } completionSets.Add(new CompletionSet( "Funcs", //the non-localized title of the tab "Funcs", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); } strList.Clear(); } if (m_core != null) { completionSets.Add(new CompletionSet( "Core", //the non-localized title of the tab "Core", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_core, null)); } } }
// private OperationOutcome validateElement(ElementDefinitionNavigator definition, IElementNavigator instance) private OperationOutcome validateElement(ElementDefinitionNavigator definition, ScopedNode instance) { var outcome = new OperationOutcome(); Trace(outcome, $"Start validation of ElementDefinition at path '{definition.QualifiedDefinitionPath()}'", Issue.PROCESSING_PROGRESS, instance); // If navigator cannot be moved to content, there's really nothing to validate against. if (definition.AtRoot && !definition.MoveToFirstChild()) { outcome.AddIssue($"Snapshot component of profile '{definition.StructureDefinition?.Url}' has no content.", Issue.PROFILE_ELEMENTDEF_IS_EMPTY, instance); return(outcome); } // This does not work, since the children might still be empty, we need something better //// Any node must either have a value, or children, or both (e.g. extensions on primitives) if (instance.Value == null && !instance.Children().Any()) { outcome.AddIssue("Element must not be empty", Issue.CONTENT_ELEMENT_MUST_HAVE_VALUE_OR_CHILDREN, instance); return(outcome); } var elementConstraints = definition.Current; if (elementConstraints.IsPrimitiveValueConstraint()) { // The "value" property of a FHIR Primitive is the bottom of our recursion chain, it does not have a nameReference // nor a <type>, the only thing left to do to validate the content is to validate the string representation of the // primitive against the regex given in the core definition outcome.Add(VerifyPrimitiveContents(elementConstraints, instance)); } else { bool isInlineChildren = !definition.Current.IsRootElement(); // Now, validate the children if (definition.HasChildren) { // If we are at the root of an abstract type (e.g. is this instance a Resource)? // or we are at a nested resource, we may expect more children in the instance than // we know about bool allowAdditionalChildren = (isInlineChildren && elementConstraints.IsResourcePlaceholder()) || (!isInlineChildren && definition.StructureDefinition.Abstract == true); // Handle in-lined constraints on children. In a snapshot, these children should be exhaustive, // so there's no point in also validating the <type> or <nameReference> // TODO: Check whether this is even true when the <type> has a profile? // Note: the snapshot is *not* exhaustive if the declared type is a base FHIR type (like Resource), // in which case there may be additional children (verified in the next step) outcome.Add(this.ValidateChildConstraints(definition, instance, allowAdditionalChildren: allowAdditionalChildren)); // Special case: if we are located at a nested resource (i.e. contained or Bundle.entry.resource), // we need to validate based on the actual type of the instance if (isInlineChildren && elementConstraints.IsResourcePlaceholder()) { outcome.Add(this.ValidateType(elementConstraints, instance)); } } if (!definition.HasChildren) { // No inline-children, so validation depends on the presence of a <type> or <nameReference> if (elementConstraints.Type != null || elementConstraints.NameReference != null) { outcome.Add(this.ValidateType(elementConstraints, instance)); outcome.Add(ValidateNameReference(elementConstraints, definition, instance)); } else { Trace(outcome, "ElementDefinition has no child, nor does it specify a type or nameReference to validate the instance data against", Issue.PROFILE_ELEMENTDEF_CONTAINS_NO_TYPE_OR_NAMEREF, instance); } } } outcome.Add(this.ValidateFixed(elementConstraints, instance)); outcome.Add(this.ValidatePattern(elementConstraints, instance)); outcome.Add(this.ValidateMinMaxValue(elementConstraints, instance)); outcome.Add(ValidateMaxLength(elementConstraints, instance)); outcome.Add(this.ValidateFp(elementConstraints, instance)); outcome.Add(this.ValidateBinding(elementConstraints, instance)); outcome.Add(this.ValidateExtension(elementConstraints, instance, "http://hl7.org/fhir/StructureDefinition/regex")); // If the report only has partial information, no use to show the hierarchy, so flatten it. if (Settings.Trace == false) { outcome.Flatten(); } return(outcome); }
public static OperationOutcome ValidateFp(this Validator v, ElementDefinition definition, ScopedNode instance) { var outcome = new OperationOutcome(); if (!definition.Constraint.Any()) { return(outcome); } if (v.Settings.SkipConstraintValidation) { return(outcome); } var context = instance.ResourceContext; foreach (var constraintElement in definition.Constraint) { bool success = false; try { var compiled = getExecutableConstraint(v, outcome, instance, constraintElement); success = compiled.Predicate(instance, new FhirEvaluationContext(context) { ElementResolver = callExternalResolver }); } catch (Exception e) { v.Trace(outcome, $"Evaluation of FhirPath for constraint '{constraintElement.Key}' failed: {e.Message}", Issue.PROFILE_ELEMENTDEF_INVALID_FHIRPATH_EXPRESSION, instance); } if (!success) { var text = "Instance failed constraint " + constraintElement.ConstraintDescription(); var issue = constraintElement.Severity == ElementDefinition.ConstraintSeverity.Error ? Issue.CONTENT_ELEMENT_FAILS_ERROR_CONSTRAINT : Issue.CONTENT_ELEMENT_FAILS_WARNING_CONSTRAINT; v.Trace(outcome, text, issue, instance); } } return(outcome); ITypedElement callExternalResolver(string url) { OperationOutcome o = new OperationOutcome(); var result = v.ExternalReferenceResolutionNeeded(url, o, "dummy"); if (o.Success && result != null) { return(result); } return(null); } }
private static Func <OperationOutcome> createValidatorForTypeRef(Validator validator, ScopedNode instance, ElementDefinition.TypeRefComponent tr) { // In STU3, we need to do BOTH // First, call Validate() against the profile (which is then a profile on Reference) THEN validate the referenced resource if (tr.Code == FHIRDefinedType.Reference) { return(() => validator.ValidateResourceReference(instance, tr)); } else { return(() => validator.Validate(instance, tr.GetDeclaredProfiles(), statedCanonicals: null, statedProfiles: null)); } }
internal static OperationOutcome ValidateTypeReferences(this Validator validator, IEnumerable <ElementDefinition.TypeRefComponent> typeRefs, ScopedNode instance) { //TODO: It's more efficient to do the non-reference types FIRST, since ANY match would be ok, //and validating non-references is cheaper //TODO: For each choice, we will currently try to resolve the reference. If it fails, you'll get multiple errors and probably //better separate the fetching of the instance from the validation, so we do not run the rest of the validation (multiple times!) //when a reference cannot be resolved. (this happens in a choice type where there are multiple references with multiple profiles) IEnumerable <Func <OperationOutcome> > validations = typeRefs.Select(tr => createValidatorForTypeRef(validator, instance, tr)); return(validator.Combine(BatchValidationMode.Any, instance, validations)); }
internal static OperationOutcome ValidateType(this Validator validator, ElementDefinition definition, ScopedNode instance) { var outcome = new OperationOutcome(); validator.Trace(outcome, "Validating against constraints specified by the element's defined type", Issue.PROCESSING_PROGRESS, instance); if (definition.Type.Any(tr => tr.Code == null)) { validator.Trace(outcome, "ElementDefinition contains a type with an empty type code", Issue.PROFILE_ELEMENTDEF_CONTAINS_NULL_TYPE, instance); } // Check if this is a choice: there are multiple distinct Codes to choose from var typeRefs = definition.Type.Where(tr => tr.Code != null); var choices = typeRefs.Select(tr => tr.Code.Value).Distinct(); if (choices.Count() > 1) { if (instance.InstanceType != null) { // This is a choice type, find out what type is present in the instance data // (e.g. deceased[Boolean], or _resourceType in json). This is exposed by IElementNavigator.TypeName. var instanceType = ModelInfo.FhirTypeNameToFhirType(instance.InstanceType); if (instanceType != null) { // In fact, the next statements are just an optimalization, without them, we would do an ANY validation // against *all* choices, what we do here is pre-filtering for sensible choices, and report if there isn't // any. var applicableChoices = typeRefs.Where(tr => ModelInfo.IsInstanceTypeFor(tr.Code.Value, instanceType.Value)); // Instance typename must be one of the applicable types in the choice if (applicableChoices.Any()) { outcome.Include(validator.ValidateTypeReferences(applicableChoices, instance)); } else { var choiceList = String.Join(",", choices.Select(t => "'" + t.GetLiteral() + "'")); validator.Trace(outcome, $"Type specified in the instance ('{instance.InstanceType}') is not one of the allowed choices ({choiceList})", Issue.CONTENT_ELEMENT_HAS_INCORRECT_TYPE, instance); } } else { validator.Trace(outcome, $"Instance indicates the element is of type '{instance.InstanceType}', which is not a known FHIR core type.", Issue.CONTENT_ELEMENT_CHOICE_INVALID_INSTANCE_TYPE, instance); } } else { validator.Trace(outcome, "ElementDefinition is a choice or contains a polymorphic type constraint, but the instance does not indicate its actual type", Issue.CONTENT_ELEMENT_CANNOT_DETERMINE_TYPE, instance); } } else if (choices.Count() == 1) { // Only one type present in list of typerefs, all of the typerefs are candidates outcome.Include(validator.ValidateTypeReferences(typeRefs, instance)); } return(outcome); }
private static OperationOutcome ValidateMatch(this Validator validator, Match match, ScopedNode parent) { var outcome = new OperationOutcome(); var definition = match.Definition.Current; if (definition.Min == null) { validator.Trace(outcome, $"Element definition does not specify a 'min' value, which is required. Cardinality has not been validated", Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING, parent); } else if (definition.Max == null) { validator.Trace(outcome, $"Element definition does not specify a 'max' value, which is required. Cardinality has not been validated", Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING, parent); } var cardinality = Cardinality.FromElementDefinition(definition); IBucket bucket; try { bucket = BucketFactory.CreateRoot(match.Definition, validator); } catch (NotImplementedException ni) { // Will throw if a non-supported slice type is encountered validator.Trace(outcome, ni.Message, Issue.UNSUPPORTED_SLICING_NOT_SUPPORTED, parent); return(outcome); } foreach (var element in match.InstanceElements) { var success = bucket.Add(element); // For the "root" slice group (=the original core element that was sliced, not resliced) // any element that does not match is an error // Since the ChildNameMatcher currently does the matching, this will never go wrong } outcome.Add(bucket.Validate(validator, parent)); return(outcome); }
internal static OperationOutcome ValidateResourceReference(this Validator validator, ScopedNode instance, ElementDefinition.TypeRefComponent typeRef) { var outcome = new OperationOutcome(); var reference = instance.ParseResourceReference()?.Reference; if (reference == null) // No reference found -> this is always valid { return(outcome); } // Try to resolve the reference *within* the current instance (Bundle, resource with contained resources) first var referencedResource = validator.resolveReference(instance, reference, out ElementDefinition.AggregationMode? encounteredKind, outcome); // Validate the kind of aggregation. // If no aggregation is given, all kinds of aggregation are allowed, otherwise only allow // those aggregation types that are given in the Aggregation element bool hasAggregation = typeRef.Aggregation != null && typeRef.Aggregation.Count() != 0; if (hasAggregation && !typeRef.Aggregation.Any(a => a == encounteredKind)) { validator.Trace(outcome, $"Encountered a reference ({reference}) of kind '{encounteredKind}' which is not allowed", Issue.CONTENT_REFERENCE_OF_INVALID_KIND, instance); } // Bail out if we are asked to follow an *external reference* when this is disabled in the settings if (validator.Settings.ResolveExteralReferences == false && encounteredKind == ElementDefinition.AggregationMode.Referenced) { return(outcome); } // If we failed to find a referenced resource within the current instance, try to resolve it using an external method if (referencedResource == null && encounteredKind == ElementDefinition.AggregationMode.Referenced) { try { referencedResource = validator.ExternalReferenceResolutionNeeded(reference, outcome, instance.Location); } catch (Exception e) { validator.Trace(outcome, $"Resolution of external reference {reference} failed. Message: {e.Message}", Issue.UNAVAILABLE_REFERENCED_RESOURCE, instance); } } // If the reference was resolved (either internally or externally, validate it if (referencedResource != null) { validator.Trace(outcome, $"Starting validation of referenced resource {reference} ({encounteredKind})", Issue.PROCESSING_START_NESTED_VALIDATION, instance); // References within the instance are dealt with within the same validator, // references to external entities will operate within a new instance of a validator (and hence a new tracking context). // In both cases, the outcome is included in the result. OperationOutcome childResult; if (encounteredKind != ElementDefinition.AggregationMode.Referenced) { childResult = validator.Validate(referencedResource, typeRef.GetDeclaredProfiles(), statedProfiles: null, statedCanonicals: null); } else { var newValidator = validator.NewInstance(); childResult = newValidator.Validate(referencedResource, typeRef.GetDeclaredProfiles(), statedProfiles: null, statedCanonicals: null); } // Prefix each path with the referring resource's path to keep the locations // interpretable foreach (var issue in childResult.Issue) { issue.Location = issue.Location.Concat(new string[] { instance.Location }); } outcome.Include(childResult); } else { validator.Trace(outcome, $"Cannot resolve reference {reference}", Issue.UNAVAILABLE_REFERENCED_RESOURCE, instance); } return(outcome); }
internal OperationOutcome ValidateNameReference(ElementDefinition definition, ElementDefinitionNavigator allDefinitions, ScopedNode instance) { var outcome = new OperationOutcome(); if (definition.NameReference != null) { Trace(outcome, $"Start validation of constraints referred to by nameReference '{definition.NameReference}'", Issue.PROCESSING_PROGRESS, instance); var referencedPositionNav = allDefinitions.ShallowCopy(); if (referencedPositionNav.JumpToNameReference(definition.NameReference)) { outcome.Include(Validate(instance, referencedPositionNav)); } else { Trace(outcome, $"ElementDefinition uses a non-existing nameReference '{definition.NameReference}'", Issue.PROFILE_ELEMENTDEF_INVALID_NAMEREFERENCE, instance); } } return(outcome); }