private void ButtonCheckExpression_Click(object sender, RoutedEventArgs e) { var inputNav = GetResourceNavigator(); if (inputNav == null) { return; } // Don't need to cache this, it is cached in the fhir-client Hl7.FhirPath.Expressions.Expression expr = null; try { expr = _compiler.Parse(textboxExpression.Text); } catch (Exception ex) { SetResults("Expression compilation error:\r\n" + ex.Message, true); return; } if (expr != null) { try { ExpressionElementContext context = new ExpressionElementContext(inputNav.Name); if (inputNav is dstu2::Hl7.Fhir.ElementModel.PocoNavigator pn2) { if (pn2.FhirValue is f2.Questionnaire q) { context._q2 = q; } } else if (inputNav is stu3::Hl7.Fhir.ElementModel.PocoNavigator pn3) { if (pn3.FhirValue is f3.Questionnaire q) { context._q3 = q; } } ResetResults(); CheckExpression(expr, "", context); } catch (Exception ex) { SetResults("Expression Check error:\r\n" + ex.Message, true); return; } } }
private ExpressionElementContext CheckExpression(Hl7.FhirPath.Expressions.Expression expr, string prefix, ExpressionElementContext context) { if (expr is ChildExpression) { var func = expr as ChildExpression; var focusContext = CheckExpression(func.Focus, prefix + "-- ", context); var childContext = focusContext.Child(func.ChildName); if (childContext != null) { if (focusContext._q2 != null) { if (func.ChildName == "group") { childContext._2gs = new List <f2.Questionnaire.GroupComponent>(); childContext._2gs.Add(focusContext._q2.Group); } } 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); } 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); } else { AppendResults($"{prefix}{func.FunctionName}"); } var focusContext = CheckExpression(func.Focus, prefix + "-- ", context); if (func.FunctionName == "binary.as") { if (func.Arguments.Count() != 2) { AppendResults($"{prefix}{func.FunctionName} INVALID AS Operation", true); return(focusContext); } var argContextResult = CheckExpression(func.Arguments.First(), prefix + " ", focusContext); 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); // Filter the values that are not in this set focusContext._2gs = argContextResult._2gs; focusContext._2qs = argContextResult._2qs; } else { foreach (var item in func.Arguments) { var argContextResult = CheckExpression(item, prefix + " ", focusContext); } } 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(); // filter out all of the other linkIds from the list 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); } } } } 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)"); 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()); return(context); }
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)) { return(null); } var newContext = new ExpressionElementContext(); 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.ElementType != typeof(string)) // (only occurs for extension.url and elementdefinition.id) { newContext._cm3.Add(stu3::Hl7.Fhir.Introspection.ClassMapping.Create(item.ElementType)); } } } 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); }