fix problem with FHIRPath engine wrongly checking types in context of CDA

This commit is contained in:
Grahame Grieve 2023-11-10 16:39:05 +11:00
parent eb1940e9c0
commit 110d627e83
5 changed files with 49 additions and 8 deletions

View File

@ -435,5 +435,13 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
return null; return null;
} }
public boolean isAbstractType(String typeName) {
StructureDefinition sd = context.fetchTypeDefinition(typeName);
if (sd != null) {
return sd.getAbstract();
}
return false;
}
} }

View File

@ -43,5 +43,6 @@ public class Constants {
public final static String VERSION_MM = "5.0"; public final static String VERSION_MM = "5.0";
public final static String DATE = "Thu, Mar 23, 2023 19:59+1100"; public final static String DATE = "Thu, Mar 23, 2023 19:59+1100";
public final static String URI_REGEX = "((http|https):\\/\\/([A-Za-z0-9\\\\\\.\\:\\%\\$\\-]*\\/)*?)?(Account|ActivityDefinition|ActorDefinition|AdministrableProductDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|ArtifactAssessment|AuditEvent|Basic|Binary|BiologicallyDerivedProduct|BiologicallyDerivedProductDispense|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|ChargeItemDefinition|Citation|Claim|ClaimResponse|ClinicalImpression|ClinicalUseDefinition|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|ConditionDefinition|Consent|Contract|Coverage|CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceAssociation|DeviceDefinition|DeviceDispense|DeviceMetric|DeviceRequest|DeviceUsage|DiagnosticReport|DocumentReference|Encounter|EncounterHistory|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceReport|EvidenceVariable|ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|FormularyItem|GenomicStudy|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingSelection|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|ImplementationGuide|Ingredient|InsurancePlan|InventoryItem|InventoryReport|Invoice|Library|Linkage|List|Location|ManufacturedItemDefinition|Measure|MeasureReport|Medication|MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|MedicinalProductDefinition|MessageDefinition|MessageHeader|MolecularSequence|NamingSystem|NutritionIntake|NutritionOrder|NutritionProduct|Observation|ObservationDefinition|OperationDefinition|OperationOutcome|Organization|OrganizationAffiliation|PackagedProductDefinition|Parameters|Patient|PaymentNotice|PaymentReconciliation|Permission|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|RegulatedAuthorization|RelatedPerson|RequestOrchestration|Requirements|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|StructureDefinition|StructureMap|Subscription|SubscriptionStatus|SubscriptionTopic|Substance|SubstanceDefinition|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|SubstanceReferenceInformation|SubstanceSourceMaterial|SupplyDelivery|SupplyRequest|Task|TerminologyCapabilities|TestPlan|TestReport|TestScript|Transport|ValueSet|VerificationResult|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; public final static String URI_REGEX = "((http|https):\\/\\/([A-Za-z0-9\\\\\\.\\:\\%\\$\\-]*\\/)*?)?(Account|ActivityDefinition|ActorDefinition|AdministrableProductDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|ArtifactAssessment|AuditEvent|Basic|Binary|BiologicallyDerivedProduct|BiologicallyDerivedProductDispense|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|ChargeItemDefinition|Citation|Claim|ClaimResponse|ClinicalImpression|ClinicalUseDefinition|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|ConditionDefinition|Consent|Contract|Coverage|CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceAssociation|DeviceDefinition|DeviceDispense|DeviceMetric|DeviceRequest|DeviceUsage|DiagnosticReport|DocumentReference|Encounter|EncounterHistory|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceReport|EvidenceVariable|ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|FormularyItem|GenomicStudy|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingSelection|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|ImplementationGuide|Ingredient|InsurancePlan|InventoryItem|InventoryReport|Invoice|Library|Linkage|List|Location|ManufacturedItemDefinition|Measure|MeasureReport|Medication|MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|MedicinalProductDefinition|MessageDefinition|MessageHeader|MolecularSequence|NamingSystem|NutritionIntake|NutritionOrder|NutritionProduct|Observation|ObservationDefinition|OperationDefinition|OperationOutcome|Organization|OrganizationAffiliation|PackagedProductDefinition|Parameters|Patient|PaymentNotice|PaymentReconciliation|Permission|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|RegulatedAuthorization|RelatedPerson|RequestOrchestration|Requirements|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|StructureDefinition|StructureMap|Subscription|SubscriptionStatus|SubscriptionTopic|Substance|SubstanceDefinition|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|SubstanceReferenceInformation|SubstanceSourceMaterial|SupplyDelivery|SupplyRequest|Task|TerminologyCapabilities|TestPlan|TestReport|TestScript|Transport|ValueSet|VerificationResult|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?";
public static final String NS_FHIR_ROOT = "http://hl7.org/fhir";
public static final String NS_CDA_ROOT = "http://hl7.org/cda/stds/core"; public static final String NS_CDA_ROOT = "http://hl7.org/cda/stds/core";
} }

View File

@ -62,12 +62,20 @@ public class TypeDetails {
public static final Set<String> FP_NUMBERS = new HashSet<String>(Arrays.asList(FP_Integer, FP_Decimal)); public static final Set<String> FP_NUMBERS = new HashSet<String>(Arrays.asList(FP_Integer, FP_Decimal));
public static class ProfiledType { public static class ProfiledType {
@Override
public String toString() {
return uri;
}
private String uri; private String uri;
private List<String> profiles; // or, not and private List<String> profiles; // or, not and
private List<ElementDefinitionBindingComponent> bindings; private List<ElementDefinitionBindingComponent> bindings;
public ProfiledType(String n) { public ProfiledType(String n) {
uri = ns(n); uri = ns(n);
if (uri.equals("http://hl7.org/fhir/StructureDefinition/CDA")) {
System.out.println("!"); // #FIXME
}
} }
public String getUri() { public String getUri() {

View File

@ -3308,7 +3308,8 @@ public class FHIRPathEngine {
} }
case As : { case As : {
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); String tn = checkType(focus, exp);
TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, tn);
if (td.typesHaveTargets()) { if (td.typesHaveTargets()) {
td.addTargets(focus.getTargets()); td.addTargets(focus.getTargets());
} }
@ -3316,10 +3317,7 @@ public class FHIRPathEngine {
} }
case OfType : { case OfType : {
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
String tn = exp.getParameters().get(0).getName(); String tn = checkType(focus, exp);
if (typeCastIsImpossible(focus, tn)) {
typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE, focus.describeMin(), tn, exp.toString()), I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE));
}
TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, tn); TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON, tn);
if (td.typesHaveTargets()) { if (td.typesHaveTargets()) {
td.addTargets(focus.getTargets()); td.addTargets(focus.getTargets());
@ -3639,6 +3637,27 @@ public class FHIRPathEngine {
throw new Error("not Implemented yet"); throw new Error("not Implemented yet");
} }
private String checkType(TypeDetails focus, ExpressionNode exp) {
String tn;
if (exp.getParameters().get(0).getInner() != null) {
tn = exp.getParameters().get(0).getName()+"."+exp.getParameters().get(0).getInner().getName();
} else {
tn = "FHIR."+exp.getParameters().get(0).getName();
}
if (tn.startsWith("System.")) {
tn = tn.substring(7);
} else if (tn.startsWith("FHIR.")) {
tn = Utilities.pathURL(Constants.NS_FHIR_ROOT, "StructureDefinition", tn.substring(5));
} else if (tn.startsWith("CDA.")) {
tn = Utilities.pathURL(Constants.NS_CDA_ROOT, "StructureDefinition", tn.substring(4));
}
if (typeCastIsImpossible(focus, tn)) {
typeWarnings.add(new IssueMessage(worker.formatMessage(I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE, focus.describeMin(), tn, exp.toString()), I18nConstants.FHIRPATH_OFTYPE_IMPOSSIBLE));
}
return tn;
}
private boolean typeCastIsImpossible(TypeDetails focus, String tn) { private boolean typeCastIsImpossible(TypeDetails focus, String tn) {
return !focus.hasType(tn); return !focus.hasType(tn);
} }
@ -5988,7 +6007,7 @@ public class FHIRPathEngine {
for (TypeRefComponent t : ed.getType()) { for (TypeRefComponent t : ed.getType()) {
if (t.hasCode() && t.getCodeElement().hasValue()) { if (t.hasCode() && t.getCodeElement().hasValue()) {
String tn = null; String tn = null;
if (t.getCode().equals("Element") || t.getCode().equals("BackboneElement")) { if (Utilities.existsInList(t.getCode(), "Element", "BackboneElement", "Base") || cu.isAbstractType(t.getCode())) {
tn = sdi.getType()+"#"+ed.getPath(); tn = sdi.getType()+"#"+ed.getPath();
} else { } else {
tn = t.getCode(); tn = t.getCode();

View File

@ -560,7 +560,12 @@ public class StructureDefinitionValidator extends BaseValidator {
if (Utilities.existsInList(rootPath, context.getResourceNames())) { if (Utilities.existsInList(rootPath, context.getResourceNames())) {
fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings); fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings);
} else { } else {
fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings); StructureDefinition sd = context.fetchTypeDefinition(rootPath);
if (sd != null) {
fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings);
} else {
fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings);
}
} }
for (IssueMessage s : warnings) { for (IssueMessage s : warnings) {
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, s.getId(), false, key+": "+s.getMessage()); warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, s.getId(), false, key+": "+s.getMessage());