001package org.hl7.fhir.validation.instance.utils; 002 003public class FHIRPathExpressionFixer { 004 005 006 public static String fixExpr(String expr, String key) { 007 // this is a hack work around for past publication of wrong FHIRPath expressions 008 // R4 009 // waiting for 4.0.2 010 //TODO is this expression below correct? @grahamegrieve 011 if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) { 012 return "probability.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))"; 013 } 014 if ("enableWhen.count() > 2 implies enableBehavior.exists()".equals(expr)) { 015 return "enableWhen.count() >= 2 implies enableBehavior.exists()"; 016 } 017 if ("txt-2".equals(key)) { 018 return "htmlChecks2()"; 019 } 020 if ("generated='generated' implies source.empty()".equals(expr)) { 021 return "generation='generated' implies source.empty()"; 022 } 023 // fixes to string functions in FHIRPath 024 // ref-1 025 if (expr.equals("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids')) or (reference='#' and %rootResource!=%resource)")) { // R5 026 return "reference.exists() implies ("+expr+")"; 027 } 028 if (expr.equals("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))")) { // R4/R4B 029 return "reference.exists() implies (reference = '#' or ("+expr+"))"; 030 } 031 if (expr.equals("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))")) { // STU3 032 return "reference.exists() implies (reference = '#' or (reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))))"; 033 } 034 // bld-8 035 if (expr.equals("fullUrl.contains('/_history/').not()")) { // R4 036 return "fullUrl.exists() implies fullUrl.contains('/_history/').not()"; 037 } 038 if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) { 039 return "name.exists() implies name.matches('^[A-Z]([A-Za-z0-9_]){0,254}$')"; 040 } 041 // canonical 042 if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) { 043 return ("name.exists() implies name.matches('[A-Z]([A-Za-z0-9_]){0,254}')"); 044 } 045 046 // R5 ballot 047 if (expr.equals("url.matches('([^|#])*')")) { 048 return ("$this.matches('([^|#])*')"); 049 } 050 if (expr.equals("((kind in 'resource' | 'complex-type') and (specialization = 'derivation')) implies differential.element.where((min != 0 and min != 1) or (max != '1' and max != '*')).empty()")) { 051 return "((kind in 'resource' | 'complex-type') and (derivation = 'specialization')) implies differential.element.where((min.exists() and min != 0 and min != 1) or (max.exists() and max != '1' and max != '*')).empty()"; 052 } 053 054 // clarification in FHIRPath spec 055 if ("eld-19".equals(key)) { 056 return "path.matches('^[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\.[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\[x\\\\])?(\\\\:[^\\\\s\\\\.]+)?)*$')"; 057 } 058 if ("eld-20".equals(key)) { 059 return "path.matches('^[A-Za-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*(\\\\[x])?)*$')"; 060 } 061 062 // handled in 4.0.1 063 if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) { 064 return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())"; 065 } 066 if ("isModifier implies isModifierReason.exists()".equals(expr)) { 067 return "(isModifier.exists() and isModifier) implies isModifierReason.exists()"; 068 } 069 if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) { 070 return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))"; 071 } 072 if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) { 073 return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()"; 074 } 075 if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) { 076 return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()"; 077 } 078 079 // R3 080 if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) { 081 return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')"; 082 } 083 if ("value.empty() or code!=component.code".equals(expr)) { 084 return "value.empty() or (code in component.code).not()"; 085 } 086 if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) { 087 return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; 088 } 089 if ("element.all(definition and min and max)".equals(expr)) { 090 return "element.all(definition.exists() and min.exists() and max.exists())"; 091 } 092 if ("telecom or endpoint".equals(expr)) { 093 return "telecom.exists() or endpoint.exists()"; 094 } 095 if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) { 096 return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)"; 097 } 098 if ("searchType implies type = 'string'".equals(expr)) { 099 return "searchType.exists() implies type = 'string'"; 100 } 101 if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) { 102 return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')"; 103 } 104 if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) { 105 return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())"; 106 } 107 if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))".equals(expr)) { 108 return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))"; 109 } 110 if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))".equals(expr)) { 111 return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))"; 112 } 113 if ("probability is decimal implies probability.as(decimal) <= 100".equals(expr)) { 114 if (key.equals("ras-2")) { 115 return "probability.empty() or (probability is decimal implies probability.as(decimal) <= 100)"; 116 } 117 } 118 if ("".equals(expr)) { 119 return ""; 120 } 121 return expr; 122 } 123 124}