public static void CheckExpression(ITypedElement inputNav, Hl7.FhirPath.Expressions.Expression expr, Action <string, bool, string> AppendResults, Action ResetResults) { ExpressionElementContext context = new ExpressionElementContext(inputNav.Name); if (inputNav is dstu2::Hl7.Fhir.ElementModel.IFhirValueProvider pn2) { if (pn2.FhirValue is f2.Questionnaire q) { context._q2 = q; } } else if (inputNav is stu3::Hl7.Fhir.ElementModel.IFhirValueProvider pn3) { if (pn3.FhirValue is f3.Questionnaire q) { context._q3 = q; } } else if (inputNav is r4::Hl7.Fhir.ElementModel.IFhirValueProvider pn4) { if (pn4.FhirValue is f4.Questionnaire q) { context._q4 = q; } } ResetResults(); CheckExpression(expr, "", context, AppendResults); }
public ExpressionElementContext Child(string propertyName) { // Special case for the top level node if (propertyName == _typeName) { return(this); } if (!HasProperty(propertyName, out var dstu2, out var stu3, out var r4)) { return(null); } var newContext = new ExpressionElementContext(); if (r4?.Any() == true) { foreach (var item in r4) { try { if (item.Choice == r4::Hl7.Fhir.Introspection.ChoiceType.DatatypeChoice) { if (item.Name != propertyName && item.MatchesSuffixedName(propertyName)) { string ExpectedType = propertyName.Substring(item.Name.Length); if (Enum.TryParse <f4.FHIRAllTypes>(ExpectedType, out var result)) { if (f4.ModelInfo.IsDataType(result) || f4.ModelInfo.IsPrimitive(result)) { // may need to recheck that a typename of value23 won't work here string name = f4.ModelInfo.FhirTypeToFhirTypeName(result); var t = f4.ModelInfo.GetTypeForFhirType(name); newContext._cm4.Add(r4::Hl7.Fhir.Introspection.ClassMapping.Create(t)); } } } else { // would be great to be able to filter through only the viable types // but we don't have that, so just enumerate the available datatypes foreach (f4.FHIRAllTypes ev in Enum.GetValues(typeof(f4.FHIRAllTypes))) { if (f4.ModelInfo.IsDataType(ev) && !f4.ModelInfo.IsCoreSuperType(ev)) { string name = f4.ModelInfo.FhirTypeToFhirTypeName(ev); var t = f4.ModelInfo.GetTypeForFhirType(name); newContext._cm4.Add(r4::Hl7.Fhir.Introspection.ClassMapping.Create(t)); } } } } else { if (item.ElementType != typeof(string)) // (only occurs for extension.url and elementdefinition.id) { newContext._cm4.Add(r4::Hl7.Fhir.Introspection.ClassMapping.Create(item.ElementType)); } } } catch { } } } if (stu3?.Any() == true) { foreach (var item in stu3) { try { if (item.Choice == stu3::Hl7.Fhir.Introspection.ChoiceType.DatatypeChoice) { if (item.Name != propertyName && item.MatchesSuffixedName(propertyName)) { string ExpectedType = propertyName.Substring(item.Name.Length); if (Enum.TryParse <f3.FHIRAllTypes>(ExpectedType, out var result)) { if (f3.ModelInfo.IsDataType(result) || f3.ModelInfo.IsPrimitive(result)) { // may need to recheck that a typename of value23 won't work here string name = f3.ModelInfo.FhirTypeToFhirTypeName(result); var t = f3.ModelInfo.GetTypeForFhirType(name); newContext._cm3.Add(stu3::Hl7.Fhir.Introspection.ClassMapping.Create(t)); } } } else { // would be great to be able to filter through only the viable types // but we don't have that, so just enumerate the available datatypes foreach (f3.FHIRAllTypes ev in Enum.GetValues(typeof(f3.FHIRAllTypes))) { if (f3.ModelInfo.IsDataType(ev) && !f3.ModelInfo.IsCoreSuperType(ev)) { string name = f3.ModelInfo.FhirTypeToFhirTypeName(ev); var t = f3.ModelInfo.GetTypeForFhirType(name); newContext._cm3.Add(stu3::Hl7.Fhir.Introspection.ClassMapping.Create(t)); } } } } else { if (item.ImplementingType != typeof(string)) // (only occurs for extension.url and elementdefinition.id) { newContext._cm3.Add(stu3::Hl7.Fhir.Introspection.ClassMapping.Create(item.ImplementingType)); } } } catch { } } } if (dstu2?.Any() == true) { foreach (var item in dstu2) { try { if (item.Choice == dstu2::Hl7.Fhir.Introspection.ChoiceType.DatatypeChoice) { if (item.Name != propertyName && item.MatchesSuffixedName(propertyName)) { string ExpectedType = propertyName.Substring(item.Name.Length); if (Enum.TryParse <f2.FHIRDefinedType>(ExpectedType, out var result)) { if (f2.ModelInfo.IsDataType(result) || f2.ModelInfo.IsPrimitive(result)) { // may need to recheck that a typename of value23 won't work here string name = f2.ModelInfo.FhirTypeToFhirTypeName(result); var t = f2.ModelInfo.GetTypeForFhirType(name); newContext._cm2.Add(dstu2::Hl7.Fhir.Introspection.ClassMapping.Create(t)); } } } else { // would be great to be able to filter through only the viable types // but we don't have that, so just enumerate the available datatypes foreach (f2.FHIRDefinedType ev in Enum.GetValues(typeof(f2.FHIRDefinedType))) { if (f2.ModelInfo.IsDataType(ev) && !f2.ModelInfo.IsCoreSuperType(ev)) { string name = f2.ModelInfo.FhirTypeToFhirTypeName(ev); var t = f2.ModelInfo.GetTypeForFhirType(name); newContext._cm2.Add(dstu2::Hl7.Fhir.Introspection.ClassMapping.Create(t)); } } } } else { if (item.ElementType != typeof(string)) // (only occurs for extension.url and elementdefinition.id) { newContext._cm2.Add(dstu2::Hl7.Fhir.Introspection.ClassMapping.Create(item.ElementType)); } } } catch { } } } return(newContext); }
private static ExpressionElementContext CheckExpression(Hl7.FhirPath.Expressions.Expression expr, string prefix, ExpressionElementContext context, Action <string, bool, string> AppendResults) { if (expr is ChildExpression) { var func = expr as ChildExpression; var focusContext = CheckExpression(func.Focus, prefix + "-- ", context, AppendResults); var childContext = focusContext.Child(func.ChildName); if (childContext != null) { if (focusContext._q4 != null) { if (func.ChildName == "item") { childContext._4is = new List <f4.Questionnaire.ItemComponent>(); childContext._4is.AddRange(focusContext._q4.Item); } } if (focusContext._q3 != null) { if (func.ChildName == "item") { childContext._3is = new List <f3.Questionnaire.ItemComponent>(); childContext._3is.AddRange(focusContext._q3.Item); } } if (focusContext._q2 != null) { if (func.ChildName == "group") { childContext._2gs = new List <f2.Questionnaire.GroupComponent>(); childContext._2gs.Add(focusContext._q2.Group); } } if (focusContext._4is != null) { if (func.ChildName == "item") { childContext._4is = new List <f4.Questionnaire.ItemComponent>(); foreach (var item in focusContext._4is) { if (item.Item != null) { childContext._4is.AddRange(item.Item); } } } } if (focusContext._3is != null) { if (func.ChildName == "item") { childContext._3is = new List <f3.Questionnaire.ItemComponent>(); foreach (var item in focusContext._3is) { if (item.Item != null) { childContext._3is.AddRange(item.Item); } } } } if (focusContext._2gs != null) { if (func.ChildName == "group") { childContext._2gs = new List <f2.Questionnaire.GroupComponent>(); foreach (var item in focusContext._2gs) { if (item.Group != null) { childContext._2gs.AddRange(item.Group); } } } else if (func.ChildName == "question") { childContext._2qs = new List <f2.Questionnaire.QuestionComponent>(); foreach (var item in focusContext._2gs) { if (item.Question != null) { childContext._2qs.AddRange(item.Question); } } } } if (focusContext._2qs != null) { if (func.ChildName == "group") { childContext._2gs = new List <f2.Questionnaire.GroupComponent>(); foreach (var item in focusContext._2qs) { if (item.Group != null) { childContext._2gs.AddRange(item.Group); } } } } AppendResults($"{prefix}{func.ChildName}", false, childContext.Tooltip()); return(childContext); } else { AppendResults($"{prefix}{func.ChildName} *invalid property name*", true, null); } return(context); } if (expr is FunctionCallExpression) { var func = expr as FunctionCallExpression; var funcs = _compiler.Symbols.Filter(func.FunctionName, func.Arguments.Count() + 1); if (funcs.Count() == 0 && !(expr is BinaryExpression)) { AppendResults($"{prefix}{func.FunctionName} *invalid function name*", true, null); } else { AppendResults($"{prefix}{func.FunctionName}", false, null); } var focusContext = CheckExpression(func.Focus, prefix + "-- ", context, AppendResults); if (func.FunctionName == "binary.as") { if (func.Arguments.Count() != 2) { AppendResults($"{prefix}{func.FunctionName} INVALID AS Operation", true, null); return(focusContext); } var argContextResult = CheckExpression(func.Arguments.First(), prefix + " ", focusContext, AppendResults); var typeArg = func.Arguments.Skip(1).FirstOrDefault() as ConstantExpression; string typeCast = typeArg?.Value as string; argContextResult.RestrictToType(typeCast); return(argContextResult); } else if (func.FunctionName == "resolve") { // need to check what the available outcomes of resolving this are, and switch types to this } else { // if this is a where operation and the context inside is a linkId = , then check that the linkId is in context if (func.FunctionName == "where" && func.Arguments.Count() == 1 && (func.Arguments.First() as BinaryExpression)?.Op == "=") { var op = func.Arguments.First() as BinaryExpression; var argContextResult = CheckExpression(op, prefix + " ", focusContext, AppendResults); // Filter the values that are not in this set focusContext._4is = argContextResult._4is; focusContext._3is = argContextResult._3is; focusContext._2gs = argContextResult._2gs; focusContext._2qs = argContextResult._2qs; } else { foreach (var item in func.Arguments) { var argContextResult = CheckExpression(item, prefix + " ", focusContext, AppendResults); } } if (func.FunctionName == "binary.=") { ChildExpression prop = (ChildExpression)func.Arguments.Where(a => a is ChildExpression).FirstOrDefault(); ConstantExpression value = (ConstantExpression)func.Arguments.Where(a => a is ConstantExpression).FirstOrDefault(); if (prop?.ChildName == "linkId" && value != null) { var groupLinkIds = focusContext._2gs?.Select(i => i.LinkId).ToArray(); var questionLinkIds = focusContext._2qs?.Select(i => i.LinkId).ToArray(); var item4Ids = focusContext._4is?.Select(i => i.LinkId).ToArray(); var item3Ids = focusContext._3is?.Select(i => i.LinkId).ToArray(); // filter out all of the other linkIds from the list focusContext._4is?.RemoveAll(i => i.LinkId != value.Value as string); focusContext._3is?.RemoveAll(i => i.LinkId != value.Value as string); focusContext._2gs?.RemoveAll(g => g.LinkId != value.Value as string); focusContext._2qs?.RemoveAll(q => q.LinkId != value.Value as string); // Validate that there is an item with this value that is reachable if (focusContext._2gs?.Count() == 0 || focusContext._2qs?.Count() == 0) { // this linkId didn't exist in this context! string toolTip = "Available LinkIds:"; if (groupLinkIds != null) { toolTip += $"\r\nGroup: {String.Join(", ", groupLinkIds)}"; } if (questionLinkIds != null) { toolTip += $"\r\nQuestion: {String.Join(", ", questionLinkIds)}"; } AppendResults($"{prefix}{func.FunctionName} LinkId is not valid in this context", true, toolTip); } if (focusContext._3is?.Count() == 0) { // this linkId didn't exist in this context! string toolTip = "Available LinkIds:"; if (item3Ids != null) { toolTip += $"\r\nItems: {String.Join(", ", item3Ids)}"; } AppendResults($"{prefix}{func.FunctionName} LinkId is not valid in this context", true, toolTip); } if (focusContext._4is?.Count() == 0) { // this linkId didn't exist in this context! string toolTip = "Available LinkIds:"; if (item4Ids != null) { toolTip += $"\r\nItems: {String.Join(", ", item4Ids)}"; } AppendResults($"{prefix}{func.FunctionName} LinkId is not valid in this context", true, toolTip); } } } } return(focusContext); } //else if (expr is BinaryExpression) //{ // var func = expr as BinaryExpression; // sb.AppendLine(func.FunctionName); // CheckExpression(func.Left, sb); // sb.AppendLine(func.Op); // CheckExpression(func.Right, sb); // return; //} else if (expr is ConstantExpression) { var func = expr as ConstantExpression; AppendResults($"{prefix}{func.Value.ToString()} (constant)", false, null); return(null); // context doesn't propogate from this } else if (expr is VariableRefExpression) { var func = expr as VariableRefExpression; // sb.AppendFormat("{0}{1} (variable ref)\r\n", prefix, func.Name); return(context); } AppendResults(expr.GetType().ToString(), false, null); return(context); }