
001package org.hl7.fhir.common.hapi.validation.support; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.context.FhirVersionEnum; 005import ca.uhn.fhir.context.support.ConceptValidationOptions; 006import ca.uhn.fhir.context.support.IValidationSupport; 007import ca.uhn.fhir.context.support.ValidationSupportContext; 008import ca.uhn.fhir.context.support.ValueSetExpansionOptions; 009import ca.uhn.fhir.i18n.Msg; 010import ca.uhn.fhir.parser.IParser; 011import ca.uhn.fhir.util.FhirVersionIndependentConcept; 012import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; 013import org.apache.commons.lang3.Validate; 014import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50; 015import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50; 016import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; 017import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50; 018import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; 019import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; 020import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; 021import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; 022import org.hl7.fhir.dstu2.model.ValueSet; 023import org.hl7.fhir.instance.model.api.IBaseResource; 024import org.hl7.fhir.instance.model.api.IPrimitiveType; 025import org.hl7.fhir.r5.model.CanonicalType; 026import org.hl7.fhir.r5.model.CodeSystem; 027import org.hl7.fhir.r5.model.Enumerations; 028import org.hl7.fhir.utilities.validation.ValidationMessage; 029 030import javax.annotation.Nonnull; 031import javax.annotation.Nullable; 032import java.util.ArrayList; 033import java.util.Collections; 034import java.util.HashSet; 035import java.util.List; 036import java.util.Objects; 037import java.util.Optional; 038import java.util.Set; 039import java.util.function.Consumer; 040import java.util.function.Function; 041import java.util.stream.Collectors; 042 043import static org.apache.commons.lang3.StringUtils.contains; 044import static org.apache.commons.lang3.StringUtils.defaultString; 045import static org.apache.commons.lang3.StringUtils.isBlank; 046import static org.apache.commons.lang3.StringUtils.isNotBlank; 047import static org.apache.commons.lang3.StringUtils.substringAfter; 048import static org.apache.commons.lang3.StringUtils.substringBefore; 049import static org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService.getFhirVersionEnum; 050 051/** 052 * This class is a basic in-memory terminology service, designed to expand ValueSets and validate codes 053 * completely in-memory. It is suitable for runtime validation purposes where no dedicated terminology 054 * service exists (either an internal one such as the HAPI FHIR JPA terminology service, or an 055 * external term service API) 056 */ 057@SuppressWarnings("EnhancedSwitchMigration") 058public class InMemoryTerminologyServerValidationSupport implements IValidationSupport { 059 private static final String OUR_PIPE_CHARACTER = "|"; 060 061 private final FhirContext myCtx; 062 private final VersionCanonicalizer myVersionCanonicalizer; 063 064 public InMemoryTerminologyServerValidationSupport(FhirContext theCtx) { 065 Validate.notNull(theCtx, "theCtx must not be null"); 066 myCtx = theCtx; 067 myVersionCanonicalizer = new VersionCanonicalizer(theCtx); 068 } 069 070 @Override 071 public FhirContext getFhirContext() { 072 return myCtx; 073 } 074 075 @Override 076 public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) { 077 return expandValueSet(theValidationSupportContext, theValueSetToExpand, null, null); 078 } 079 080 private ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, String theWantSystemAndVersion, String theWantCode) { 081 org.hl7.fhir.r5.model.ValueSet expansionR5; 082 try { 083 expansionR5 = expandValueSetToCanonical(theValidationSupportContext, theValueSetToExpand, theWantSystemAndVersion, theWantCode); 084 } catch (ExpansionCouldNotBeCompletedInternallyException e) { 085 return new ValueSetExpansionOutcome(e.getMessage()); 086 } 087 if (expansionR5 == null) { 088 return null; 089 } 090 091 IBaseResource expansion; 092 switch (myCtx.getVersion().getVersion()) { 093 case DSTU2: { 094 org.hl7.fhir.r4.model.ValueSet expansionR4 = (org.hl7.fhir.r4.model.ValueSet) VersionConvertorFactory_40_50.convertResource(expansionR5, new BaseAdvisor_40_50(false)); 095 expansion = myVersionCanonicalizer.valueSetFromCanonical(expansionR4); 096 break; 097 } 098 case DSTU2_HL7ORG: { 099 expansion = VersionConvertorFactory_10_50.convertResource(expansionR5, new BaseAdvisor_10_50(false)); 100 break; 101 } 102 case DSTU3: { 103 expansion = VersionConvertorFactory_30_50.convertResource(expansionR5, new BaseAdvisor_30_50(false)); 104 break; 105 } 106 case R4: { 107 expansion = VersionConvertorFactory_40_50.convertResource(expansionR5, new BaseAdvisor_40_50(false)); 108 break; 109 } 110 case R4B: { 111 expansion = VersionConvertorFactory_43_50.convertResource(expansionR5, new BaseAdvisor_43_50(false)); 112 break; 113 } 114 case R5: { 115 expansion = expansionR5; 116 break; 117 } 118 case DSTU2_1: 119 default: 120 throw new IllegalArgumentException(Msg.code(697) + "Can not handle version: " + myCtx.getVersion().getVersion()); 121 } 122 123 return new ValueSetExpansionOutcome(expansion); 124 } 125 126 private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 127 org.hl7.fhir.r5.model.ValueSet expansionR5; 128 switch (getFhirVersionEnum(theValidationSupportContext.getRootValidationSupport().getFhirContext(), theValueSetToExpand)) { 129 case DSTU2: { 130 expansionR5 = expandValueSetDstu2(theValidationSupportContext, (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); 131 break; 132 } 133 case DSTU2_HL7ORG: { 134 expansionR5 = expandValueSetDstu2Hl7Org(theValidationSupportContext, (ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); 135 break; 136 } 137 case DSTU3: { 138 expansionR5 = expandValueSetDstu3(theValidationSupportContext, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); 139 break; 140 } 141 case R4: { 142 expansionR5 = expandValueSetR4(theValidationSupportContext, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); 143 break; 144 } 145 case R4B: { 146 expansionR5 = expandValueSetR4B(theValidationSupportContext, (org.hl7.fhir.r4b.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); 147 break; 148 } 149 case R5: { 150 expansionR5 = expandValueSetR5(theValidationSupportContext, (org.hl7.fhir.r5.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); 151 break; 152 } 153 case DSTU2_1: 154 default: 155 throw new IllegalArgumentException(Msg.code(698) + "Can not handle version: " + myCtx.getVersion().getVersion()); 156 } 157 158 return expansionR5; 159 } 160 161 @Override 162 public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersion, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { 163 org.hl7.fhir.r5.model.ValueSet expansion; 164 String vsUrl = CommonCodeSystemsTerminologyService.getValueSetUrl(getFhirContext(), theValueSet); 165 try { 166 expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode); 167 } catch (ExpansionCouldNotBeCompletedInternallyException e) { 168 CodeValidationResult codeValidationResult = new CodeValidationResult(); 169 codeValidationResult.setSeverityCode("error"); 170 171 String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code " + theCodeSystemUrlAndVersion + "#" + theCode; 172 if (e.getMessage() != null) { 173 msg += ". Error was: " + e.getMessage(); 174 } 175 176 codeValidationResult.setMessage(msg); 177 return codeValidationResult; 178 } 179 180 if (expansion == null) { 181 return null; 182 } 183 184 return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemUrlAndVersion, theCode, theDisplay, expansion, vsUrl); 185 } 186 187 188 @Override 189 @Nullable 190 public CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { 191 IBaseResource vs; 192 if (isNotBlank(theValueSetUrl)) { 193 vs = theValidationSupportContext.getRootValidationSupport().fetchValueSet(theValueSetUrl); 194 if (vs == null) { 195 return null; 196 } 197 } else { 198 String codeSystemUrl; 199 String codeSystemVersion = null; 200 int codeSystemVersionIndex = theCodeSystem.indexOf("|"); 201 if (codeSystemVersionIndex > -1) { 202 codeSystemUrl = theCodeSystem.substring(0, codeSystemVersionIndex); 203 codeSystemVersion = theCodeSystem.substring(codeSystemVersionIndex + 1); 204 } else { 205 codeSystemUrl = theCodeSystem; 206 } 207 switch (myCtx.getVersion().getVersion()) { 208 case DSTU2: 209 case DSTU2_HL7ORG: 210 vs = new org.hl7.fhir.dstu2.model.ValueSet() 211 .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() 212 .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); 213 break; 214 case DSTU3: 215 if (codeSystemVersion != null) { 216 vs = new org.hl7.fhir.dstu3.model.ValueSet() 217 .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent() 218 .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); 219 } else { 220 vs = new org.hl7.fhir.dstu3.model.ValueSet() 221 .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent() 222 .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); 223 } 224 break; 225 case R4: 226 if (codeSystemVersion != null) { 227 vs = new org.hl7.fhir.r4.model.ValueSet() 228 .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent() 229 .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); 230 } else { 231 vs = new org.hl7.fhir.r4.model.ValueSet() 232 .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent() 233 .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); 234 } 235 break; 236 case R4B: 237 if (codeSystemVersion != null) { 238 vs = new org.hl7.fhir.r4b.model.ValueSet() 239 .setCompose(new org.hl7.fhir.r4b.model.ValueSet.ValueSetComposeComponent() 240 .addInclude(new org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); 241 } else { 242 vs = new org.hl7.fhir.r4b.model.ValueSet() 243 .setCompose(new org.hl7.fhir.r4b.model.ValueSet.ValueSetComposeComponent() 244 .addInclude(new org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); 245 } 246 break; 247 case R5: 248 if (codeSystemVersion != null) { 249 vs = new org.hl7.fhir.r5.model.ValueSet() 250 .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent() 251 .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); 252 } else { 253 vs = new org.hl7.fhir.r5.model.ValueSet() 254 .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent() 255 .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); 256 } 257 break; 258 case DSTU2_1: 259 default: 260 throw new IllegalArgumentException(Msg.code(699) + "Can not handle version: " + myCtx.getVersion().getVersion()); 261 } 262 } 263 264 ValueSetExpansionOutcome valueSetExpansionOutcome = expandValueSet(theValidationSupportContext, vs, theCodeSystem, theCode); 265 if (valueSetExpansionOutcome == null) { 266 return null; 267 } 268 269 if (valueSetExpansionOutcome.getError() != null) { 270 return new CodeValidationResult() 271 .setSeverity(IssueSeverity.ERROR) 272 .setMessage(valueSetExpansionOutcome.getError()); 273 } 274 275 IBaseResource expansion = valueSetExpansionOutcome.getValueSet(); 276 return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, expansion, theValueSetUrl); 277 } 278 279 private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersionToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion, String theValueSetUrl) { 280 assert theExpansion != null; 281 282 boolean caseSensitive = true; 283 IBaseResource codeSystemToValidateResource = null; 284 if (!theOptions.isInferSystem() && isNotBlank(theCodeSystemUrlAndVersionToValidate)) { 285 codeSystemToValidateResource = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystemUrlAndVersionToValidate); 286 } 287 288 List<FhirVersionIndependentConcept> codes = new ArrayList<>(); 289 switch (getFhirVersionEnum(theValidationSupportContext.getRootValidationSupport().getFhirContext(), theExpansion)) { 290 case DSTU2: { 291 ca.uhn.fhir.model.dstu2.resource.ValueSet expansionVs = (ca.uhn.fhir.model.dstu2.resource.ValueSet) theExpansion; 292 List<ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains> contains = expansionVs.getExpansion().getContains(); 293 flattenAndConvertCodesDstu2(contains, codes); 294 break; 295 } 296 case DSTU2_HL7ORG: { 297 ValueSet expansionVs = (ValueSet) theExpansion; 298 List<ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains(); 299 flattenAndConvertCodesDstu2Hl7Org(contains, codes); 300 break; 301 } 302 case DSTU3: { 303 org.hl7.fhir.dstu3.model.ValueSet expansionVs = (org.hl7.fhir.dstu3.model.ValueSet) theExpansion; 304 List<org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains(); 305 flattenAndConvertCodesDstu3(contains, codes); 306 break; 307 } 308 case R4: { 309 org.hl7.fhir.r4.model.ValueSet expansionVs = (org.hl7.fhir.r4.model.ValueSet) theExpansion; 310 List<org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains(); 311 flattenAndConvertCodesR4(contains, codes); 312 break; 313 } 314 case R4B: { 315 org.hl7.fhir.r4b.model.ValueSet expansionVs = (org.hl7.fhir.r4b.model.ValueSet) theExpansion; 316 List<org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains(); 317 flattenAndConvertCodesR4B(contains, codes); 318 break; 319 } 320 case R5: { 321 org.hl7.fhir.r5.model.ValueSet expansionVs = (org.hl7.fhir.r5.model.ValueSet) theExpansion; 322 List<org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent> contains = expansionVs.getExpansion().getContains(); 323 flattenAndConvertCodesR5(contains, codes); 324 break; 325 } 326 case DSTU2_1: 327 default: 328 throw new IllegalArgumentException(Msg.code(700) + "Can not handle version: " + myCtx.getVersion().getVersion()); 329 } 330 331 String codeSystemResourceName = null; 332 String codeSystemResourceVersion = null; 333 String codeSystemResourceContentMode = null; 334 if (codeSystemToValidateResource != null) { 335 switch (getFhirVersionEnum(theValidationSupportContext.getRootValidationSupport().getFhirContext(), codeSystemToValidateResource)) { 336 case DSTU2: 337 case DSTU2_HL7ORG: { 338 caseSensitive = true; 339 break; 340 } 341 case DSTU3: { 342 org.hl7.fhir.dstu3.model.CodeSystem systemDstu3 = (org.hl7.fhir.dstu3.model.CodeSystem) codeSystemToValidateResource; 343 caseSensitive = systemDstu3.getCaseSensitive(); 344 codeSystemResourceName = systemDstu3.getName(); 345 codeSystemResourceVersion = systemDstu3.getVersion(); 346 codeSystemResourceContentMode = systemDstu3.getContentElement().getValueAsString(); 347 break; 348 } 349 case R4: { 350 org.hl7.fhir.r4.model.CodeSystem systemR4 = (org.hl7.fhir.r4.model.CodeSystem) codeSystemToValidateResource; 351 caseSensitive = systemR4.getCaseSensitive(); 352 codeSystemResourceName = systemR4.getName(); 353 codeSystemResourceVersion = systemR4.getVersion(); 354 codeSystemResourceContentMode = systemR4.getContentElement().getValueAsString(); 355 break; 356 } 357 case R4B: { 358 org.hl7.fhir.r4b.model.CodeSystem systemR4B = (org.hl7.fhir.r4b.model.CodeSystem) codeSystemToValidateResource; 359 caseSensitive = systemR4B.getCaseSensitive(); 360 codeSystemResourceName = systemR4B.getName(); 361 codeSystemResourceVersion = systemR4B.getVersion(); 362 codeSystemResourceContentMode = systemR4B.getContentElement().getValueAsString(); 363 break; 364 } 365 case R5: { 366 CodeSystem systemR5 = (CodeSystem) codeSystemToValidateResource; 367 caseSensitive = systemR5.getCaseSensitive(); 368 codeSystemResourceName = systemR5.getName(); 369 codeSystemResourceVersion = systemR5.getVersion(); 370 codeSystemResourceContentMode = systemR5.getContentElement().getValueAsString(); 371 break; 372 } 373 case DSTU2_1: 374 default: 375 throw new IllegalArgumentException(Msg.code(701) + "Can not handle version: " + myCtx.getVersion().getVersion()); 376 } 377 } 378 379 String codeSystemUrlToValidate = null; 380 String codeSystemVersionToValidate = null; 381 if (theCodeSystemUrlAndVersionToValidate != null) { 382 int versionIndex = theCodeSystemUrlAndVersionToValidate.indexOf("|"); 383 if (versionIndex > -1) { 384 codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate.substring(0, versionIndex); 385 codeSystemVersionToValidate = theCodeSystemUrlAndVersionToValidate.substring(versionIndex + 1); 386 } else { 387 codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate; 388 } 389 } 390 for (FhirVersionIndependentConcept nextExpansionCode : codes) { 391 392 boolean codeMatches; 393 if (caseSensitive) { 394 codeMatches = defaultString(theCodeToValidate).equals(nextExpansionCode.getCode()); 395 } else { 396 codeMatches = defaultString(theCodeToValidate).equalsIgnoreCase(nextExpansionCode.getCode()); 397 } 398 if (codeMatches) { 399 if (theOptions.isInferSystem() || (nextExpansionCode.getSystem().equals(codeSystemUrlToValidate) && (codeSystemVersionToValidate == null || codeSystemVersionToValidate.equals(nextExpansionCode.getSystemVersion())))) { 400 String csVersion = codeSystemResourceVersion; 401 if (isNotBlank(nextExpansionCode.getSystemVersion())) { 402 csVersion = nextExpansionCode.getSystemVersion(); 403 } 404 if (!theOptions.isValidateDisplay() || (isBlank(nextExpansionCode.getDisplay()) || isBlank(theDisplayToValidate) || nextExpansionCode.getDisplay().equals(theDisplayToValidate))) { 405 CodeValidationResult codeValidationResult = new CodeValidationResult() 406 .setCode(theCodeToValidate) 407 .setDisplay(nextExpansionCode.getDisplay()) 408 .setCodeSystemName(codeSystemResourceName) 409 .setCodeSystemVersion(csVersion); 410 if (isNotBlank(theValueSetUrl)) { 411 codeValidationResult.setMessage("Code was validated against in-memory expansion of ValueSet: " + theValueSetUrl); 412 } 413 return codeValidationResult; 414 } else { 415 String message = "Concept Display \"" + theDisplayToValidate + "\" does not match expected \"" + nextExpansionCode.getDisplay() + "\""; 416 if (isNotBlank(theValueSetUrl)) { 417 message += " for in-memory expansion of ValueSet: " + theValueSetUrl; 418 } 419 return new CodeValidationResult() 420 .setSeverity(IssueSeverity.ERROR) 421 .setDisplay(nextExpansionCode.getDisplay()) 422 .setMessage(message) 423 .setCodeSystemName(codeSystemResourceName) 424 .setCodeSystemVersion(csVersion); 425 } 426 } 427 } 428 } 429 430 ValidationMessage.IssueSeverity severity; 431 String message; 432 if ("fragment".equals(codeSystemResourceContentMode)) { 433 severity = ValidationMessage.IssueSeverity.WARNING; 434 message = "Unknown code in fragment CodeSystem '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'"; 435 } else { 436 severity = ValidationMessage.IssueSeverity.ERROR; 437 message = "Unknown code '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'"; 438 } 439 if (isNotBlank(theValueSetUrl)) { 440 message += " for in-memory expansion of ValueSet '" + theValueSetUrl + "'"; 441 } 442 443 return new CodeValidationResult() 444 .setSeverityCode(severity.toCode()) 445 .setMessage(message); 446 } 447 448 @Override 449 public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { 450 CodeValidationResult codeValidationResult = validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null); 451 if (codeValidationResult == null) { 452 return null; 453 } 454 return codeValidationResult.asLookupCodeResult(theSystem, theCode); 455 } 456 457 @Nullable 458 private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 459 org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(theInput, new BaseAdvisor_10_50(false)); 460 return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode)); 461 } 462 463 @Nullable 464 private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 465 IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser(); 466 IParser parserHapi = FhirContext.forDstu2Cached().newJsonParser(); 467 468 org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(theInput)); 469 org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false)); 470 return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode)); 471 } 472 473 @Override 474 public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { 475 if (isBlank(theSystem)) { 476 return false; 477 } 478 479 IBaseResource cs = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theSystem); 480 481 if (!myCtx.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU2_1)) { 482 return cs != null; 483 } 484 485 if (cs != null) { 486 IPrimitiveType<?> content = getFhirContext().newTerser().getSingleValueOrNull(cs, "content", IPrimitiveType.class); 487 return !"not-present".equals(content.getValueAsString()); 488 } 489 490 return false; 491 } 492 493 @Override 494 public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { 495 return isNotBlank(theValueSetUrl) && theValidationSupportContext.getRootValidationSupport().fetchValueSet(theValueSetUrl) != null; 496 } 497 498 499 private void addCodesDstu2Hl7Org(List<ValueSet.ConceptDefinitionComponent> theSourceList, List<CodeSystem.ConceptDefinitionComponent> theTargetList) { 500 for (ValueSet.ConceptDefinitionComponent nextSource : theSourceList) { 501 CodeSystem.ConceptDefinitionComponent targetConcept = new CodeSystem.ConceptDefinitionComponent().setCode(nextSource.getCode()).setDisplay(nextSource.getDisplay()); 502 theTargetList.add(targetConcept); 503 addCodesDstu2Hl7Org(nextSource.getConcept(), targetConcept.getConcept()); 504 } 505 } 506 507 private void addCodesDstu2(List<ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept> theSourceList, List<CodeSystem.ConceptDefinitionComponent> theTargetList) { 508 for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextSource : theSourceList) { 509 CodeSystem.ConceptDefinitionComponent targetConcept = new CodeSystem.ConceptDefinitionComponent().setCode(nextSource.getCode()).setDisplay(nextSource.getDisplay()); 510 theTargetList.add(targetConcept); 511 addCodesDstu2(nextSource.getConcept(), targetConcept.getConcept()); 512 } 513 } 514 515 @Nullable 516 private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 517 org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(theInput, new BaseAdvisor_30_50(false)); 518 return (expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode)); 519 } 520 521 @Nullable 522 private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 523 org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(theInput, new BaseAdvisor_40_50(false)); 524 return expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode); 525 } 526 527 @Nullable 528 private org.hl7.fhir.r5.model.ValueSet expandValueSetR4B(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4b.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 529 org.hl7.fhir.r5.model.ValueSet input = (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(theInput, new BaseAdvisor_43_50(false)); 530 return expandValueSetR5(theValidationSupportContext, input, theWantSystemUrlAndVersion, theWantCode); 531 } 532 533 @Nullable 534 private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput) throws ExpansionCouldNotBeCompletedInternallyException { 535 return expandValueSetR5(theValidationSupportContext, theInput, null, null); 536 } 537 538 @Nullable 539 private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 540 Set<FhirVersionIndependentConcept> concepts = new HashSet<>(); 541 542 expandValueSetR5IncludeOrExcludes(theValidationSupportContext, concepts, theInput.getCompose().getInclude(), true, theWantSystemUrlAndVersion, theWantCode); 543 expandValueSetR5IncludeOrExcludes(theValidationSupportContext, concepts, theInput.getCompose().getExclude(), false, theWantSystemUrlAndVersion, theWantCode); 544 545 org.hl7.fhir.r5.model.ValueSet retVal = new org.hl7.fhir.r5.model.ValueSet(); 546 for (FhirVersionIndependentConcept next : concepts) { 547 org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent contains = retVal.getExpansion().addContains(); 548 contains.setSystem(next.getSystem()); 549 contains.setCode(next.getCode()); 550 contains.setDisplay(next.getDisplay()); 551 contains.setVersion(next.getSystemVersion()); 552 } 553 554 return retVal; 555 } 556 557 /** 558 * Use with caution - this is not a stable API 559 * 560 * @since 5.6.0 561 */ 562 public void expandValueSetIncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theIncludeOrExclude) throws ExpansionCouldNotBeCompletedInternallyException { 563 expandValueSetR5IncludeOrExclude(theValidationSupportContext, theConsumer, null, null, theIncludeOrExclude); 564 } 565 566 567 private void expandValueSetR5IncludeOrExcludes(ValidationSupportContext theValidationSupportContext, Set<FhirVersionIndependentConcept> theConcepts, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 568 Consumer<FhirVersionIndependentConcept> consumer = c -> { 569 if (theComposeListIsInclude) { 570 theConcepts.add(c); 571 } else { 572 theConcepts.remove(c); 573 } 574 }; 575 expandValueSetR5IncludeOrExcludes(theValidationSupportContext, consumer, theComposeList, theWantSystemUrlAndVersion, theWantCode); 576 } 577 578 579 private void expandValueSetR5IncludeOrExcludes(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, List<org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent> theComposeList, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { 580 ExpansionCouldNotBeCompletedInternallyException caughtException = null; 581 for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) { 582 try { 583 boolean outcome = expandValueSetR5IncludeOrExclude(theValidationSupportContext, theConsumer, theWantSystemUrlAndVersion, theWantCode, nextInclude); 584 if (isNotBlank(theWantCode)) { 585 if (outcome) { 586 return; 587 } 588 } 589 } catch (ExpansionCouldNotBeCompletedInternallyException e) { 590 if (isBlank(theWantCode)) { 591 throw e; 592 } else { 593 caughtException = e; 594 } 595 } 596 } 597 if (caughtException != null) { 598 throw caughtException; 599 } 600 } 601 602 /** 603 * Returns <code>true</code> if at least one code was added 604 */ 605 private boolean expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Consumer<FhirVersionIndependentConcept> theConsumer, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode, org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent theInclude) throws ExpansionCouldNotBeCompletedInternallyException { 606 607 String wantSystemUrl = null; 608 String wantSystemVersion = null; 609 610 if (theWantSystemUrlAndVersion != null) { 611 int versionIndex = theWantSystemUrlAndVersion.indexOf(OUR_PIPE_CHARACTER); 612 if (versionIndex > -1) { 613 wantSystemUrl = theWantSystemUrlAndVersion.substring(0, versionIndex); 614 wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex + 1); 615 } else { 616 wantSystemUrl = theWantSystemUrlAndVersion; 617 } 618 } 619 620 String includeOrExcludeConceptSystemUrl = theInclude.getSystem(); 621 String includeOrExcludeConceptSystemVersion = theInclude.getVersion(); 622 623 Function<String, CodeSystem> codeSystemLoader = newCodeSystemLoader(theValidationSupportContext); 624 Function<String, org.hl7.fhir.r5.model.ValueSet> valueSetLoader = newValueSetLoader(theValidationSupportContext); 625 626 List<FhirVersionIndependentConcept> nextCodeList = new ArrayList<>(); 627 CodeSystem includeOrExcludeSystemResource = null; 628 629 if (isNotBlank(includeOrExcludeConceptSystemUrl)) { 630 631 includeOrExcludeConceptSystemVersion = optionallyPopulateVersionFromUrl(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion); 632 includeOrExcludeConceptSystemUrl = substringBefore(includeOrExcludeConceptSystemUrl, OUR_PIPE_CHARACTER); 633 634 if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) { 635 return false; 636 } 637 638 if (wantSystemVersion != null && !wantSystemVersion.equals(includeOrExcludeConceptSystemVersion)) { 639 return false; 640 } 641 642 String loadedCodeSystemUrl; 643 if (includeOrExcludeConceptSystemVersion != null) { 644 loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl + OUR_PIPE_CHARACTER + includeOrExcludeConceptSystemVersion; 645 } else { 646 loadedCodeSystemUrl = includeOrExcludeConceptSystemUrl; 647 } 648 649 includeOrExcludeSystemResource = codeSystemLoader.apply(loadedCodeSystemUrl); 650 651 Set<String> wantCodes; 652 if (theInclude.getConcept().isEmpty()) { 653 wantCodes = null; 654 } else { 655 wantCodes = theInclude 656 .getConcept() 657 .stream().map(t -> t.getCode()).collect(Collectors.toSet()); 658 } 659 660 boolean ableToHandleCode = false; 661 String failureMessage = null; 662 FailureType failureType = FailureType.OTHER; 663 664 if (includeOrExcludeSystemResource == null || includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT) { 665 666 if (theWantCode != null) { 667 if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) { 668 LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, includeOrExcludeConceptSystemUrl, theWantCode, null); 669 if (lookup != null) { 670 ableToHandleCode = true; 671 if (lookup.isFound()) { 672 CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() 673 .addConcept() 674 .setCode(theWantCode) 675 .setDisplay(lookup.getCodeDisplay()); 676 List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition); 677 addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes); 678 } 679 } 680 } else { 681 682 /* 683 * If we're doing an expansion specifically looking for a single code, that means we're validating that code. 684 * In the case where we have a ValueSet that explicitly enumerates a collection of codes 685 * (via ValueSet.compose.include.code) in a code system that is unknown we'll assume the code is valid 686 * even if we can't find the CodeSystem. This is a compromise obviously, since it would be ideal for 687 * CodeSystems to always be known, but realistically there are always going to be CodeSystems that 688 * can't be supplied because of copyright issues, or because they are grammar based. Allowing a VS to 689 * enumerate a set of good codes for them is a nice compromise there. 690 */ 691 if (Objects.equals(theInclude.getSystem(), theWantSystemUrlAndVersion)) { 692 Optional<org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent> matchingEnumeratedConcept = theInclude.getConcept().stream().filter(t -> Objects.equals(t.getCode(), theWantCode)).findFirst(); 693 694 // If the ValueSet.compose.include has no individual concepts in it, and 695 // we can't find the actual referenced CodeSystem, we have no choice 696 // but to fail 697 if (!theInclude.getConcept().isEmpty()) { 698 ableToHandleCode = true; 699 } else { 700 failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl); 701 } 702 703 if (matchingEnumeratedConcept.isPresent()) { 704 CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() 705 .addConcept() 706 .setCode(theWantCode) 707 .setDisplay(matchingEnumeratedConcept.get().getDisplay()); 708 List<CodeSystem.ConceptDefinitionComponent> codesList = Collections.singletonList(conceptDefinition); 709 addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes); 710 } 711 } 712 713 } 714 } else { 715 if (isNotBlank(theInclude.getSystem()) && !theInclude.getConcept().isEmpty() && theInclude.getFilter().isEmpty() && theInclude.getValueSet().isEmpty()) { 716 theInclude 717 .getConcept() 718 .stream() 719 .map(t -> new FhirVersionIndependentConcept(theInclude.getSystem(), t.getCode(), t.getDisplay(), theInclude.getVersion())) 720 .forEach(t -> nextCodeList.add(t)); 721 ableToHandleCode = true; 722 } 723 724 if (!ableToHandleCode) { 725 failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl); 726 } 727 } 728 729 } else { 730 ableToHandleCode = true; 731 } 732 733 if (!ableToHandleCode) { 734 if (includeOrExcludeSystemResource == null && failureMessage == null) { 735 failureMessage = getFailureMessageForMissingOrUnusableCodeSystem(includeOrExcludeSystemResource, loadedCodeSystemUrl); 736 } 737 738 if (includeOrExcludeSystemResource == null) { 739 failureType = FailureType.UNKNOWN_CODE_SYSTEM; 740 } 741 742 throw new ExpansionCouldNotBeCompletedInternallyException(Msg.code(702) + failureMessage, failureType); 743 } 744 745 if (includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() != Enumerations.CodeSystemContentMode.NOTPRESENT) { 746 addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, includeOrExcludeSystemResource.getConcept(), nextCodeList, wantCodes); 747 } 748 749 } 750 751 for (CanonicalType nextValueSetInclude : theInclude.getValueSet()) { 752 org.hl7.fhir.r5.model.ValueSet vs = valueSetLoader.apply(nextValueSetInclude.getValueAsString()); 753 if (vs != null) { 754 org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theWantSystemUrlAndVersion, theWantCode); 755 if (subExpansion == null) { 756 throw new ExpansionCouldNotBeCompletedInternallyException(Msg.code(703) + "Failed to expand ValueSet: " + nextValueSetInclude.getValueAsString(), FailureType.OTHER); 757 } 758 for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) { 759 nextCodeList.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); 760 } 761 } 762 } 763 764 boolean retVal = false; 765 766 for (FhirVersionIndependentConcept next : nextCodeList) { 767 if (includeOrExcludeSystemResource != null && theWantCode != null) { 768 boolean matches; 769 if (includeOrExcludeSystemResource.getCaseSensitive()) { 770 matches = theWantCode.equals(next.getCode()); 771 } else { 772 matches = theWantCode.equalsIgnoreCase(next.getCode()); 773 } 774 if (!matches) { 775 continue; 776 } 777 } 778 779 theConsumer.accept(next); 780 retVal = true; 781 } 782 783 return retVal; 784 } 785 786 private Function<String, org.hl7.fhir.r5.model.ValueSet> newValueSetLoader(ValidationSupportContext theValidationSupportContext) { 787 switch (myCtx.getVersion().getVersion()) { 788 case DSTU2: 789 case DSTU2_HL7ORG: 790 return t -> { 791 IBaseResource vs = theValidationSupportContext.getRootValidationSupport().fetchValueSet(t); 792 if (vs instanceof ca.uhn.fhir.model.dstu2.resource.ValueSet) { 793 IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser(); 794 IParser parserHapi = FhirContext.forDstu2Cached().newJsonParser(); 795 ca.uhn.fhir.model.dstu2.resource.ValueSet valueSet = (ca.uhn.fhir.model.dstu2.resource.ValueSet) vs; 796 org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(valueSet)); 797 return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSetRi, new BaseAdvisor_10_50(false)); 798 } else { 799 org.hl7.fhir.dstu2.model.ValueSet valueSet = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t); 800 return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_10_50.convertResource(valueSet, new BaseAdvisor_10_50(false)); 801 } 802 }; 803 case DSTU3: 804 return t -> { 805 org.hl7.fhir.dstu3.model.ValueSet valueSet = (org.hl7.fhir.dstu3.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t); 806 return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_30_50.convertResource(valueSet, new BaseAdvisor_30_50(false)); 807 }; 808 case R4: 809 return t -> { 810 org.hl7.fhir.r4.model.ValueSet valueSet = (org.hl7.fhir.r4.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t); 811 return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_40_50.convertResource(valueSet, new BaseAdvisor_40_50(false)); 812 }; 813 case R4B: 814 return t -> { 815 org.hl7.fhir.r4b.model.ValueSet valueSet = (org.hl7.fhir.r4b.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t); 816 return (org.hl7.fhir.r5.model.ValueSet) VersionConvertorFactory_43_50.convertResource(valueSet, new BaseAdvisor_43_50(false)); 817 }; 818 default: 819 case DSTU2_1: 820 case R5: 821 return t -> (org.hl7.fhir.r5.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchValueSet(t); 822 } 823 } 824 825 private Function<String, CodeSystem> newCodeSystemLoader(ValidationSupportContext theValidationSupportContext) { 826 switch (myCtx.getVersion().getVersion()) { 827 case DSTU2: 828 case DSTU2_HL7ORG: 829 return t -> { 830 IBaseResource codeSystem = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); 831 CodeSystem retVal = null; 832 if (codeSystem != null) { 833 retVal = new CodeSystem(); 834 if (codeSystem instanceof ca.uhn.fhir.model.dstu2.resource.ValueSet) { 835 ca.uhn.fhir.model.dstu2.resource.ValueSet codeSystemCasted = (ca.uhn.fhir.model.dstu2.resource.ValueSet) codeSystem; 836 retVal.setUrl(codeSystemCasted.getUrl()); 837 addCodesDstu2(codeSystemCasted.getCodeSystem().getConcept(), retVal.getConcept()); 838 } else { 839 org.hl7.fhir.dstu2.model.ValueSet codeSystemCasted = (org.hl7.fhir.dstu2.model.ValueSet) codeSystem; 840 retVal.setUrl(codeSystemCasted.getUrl()); 841 addCodesDstu2Hl7Org(codeSystemCasted.getCodeSystem().getConcept(), retVal.getConcept()); 842 } 843 } 844 return retVal; 845 }; 846 case DSTU3: 847 return t -> { 848 org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); 849 return (CodeSystem) VersionConvertorFactory_30_50.convertResource(codeSystem, new BaseAdvisor_30_50(false)); 850 }; 851 case R4: 852 return t -> { 853 org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); 854 return (CodeSystem) VersionConvertorFactory_40_50.convertResource(codeSystem, new BaseAdvisor_40_50(false)); 855 }; 856 case R4B: 857 return t -> { 858 org.hl7.fhir.r4b.model.CodeSystem codeSystem = (org.hl7.fhir.r4b.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); 859 return (CodeSystem) VersionConvertorFactory_43_50.convertResource(codeSystem, new BaseAdvisor_43_50(false)); 860 }; 861 case DSTU2_1: 862 case R5: 863 default: 864 return t -> (org.hl7.fhir.r5.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); 865 866 } 867 } 868 869 private String getFailureMessageForMissingOrUnusableCodeSystem(CodeSystem includeOrExcludeSystemResource, String loadedCodeSystemUrl) { 870 String failureMessage; 871 if (includeOrExcludeSystemResource == null) { 872 failureMessage = "Unable to expand ValueSet because CodeSystem could not be found: " + loadedCodeSystemUrl; 873 } else { 874 assert includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT; 875 failureMessage = "Unable to expand ValueSet because CodeSystem has CodeSystem.content=not-present but contents were not found: " + loadedCodeSystemUrl; 876 } 877 return failureMessage; 878 } 879 880 private void addCodes(String theCodeSystemUrl, String theCodeSystemVersion, List<CodeSystem.ConceptDefinitionComponent> theSource, List<FhirVersionIndependentConcept> theTarget, Set<String> theCodeFilter) { 881 for (CodeSystem.ConceptDefinitionComponent next : theSource) { 882 if (isNotBlank(next.getCode())) { 883 if (theCodeFilter == null || theCodeFilter.contains(next.getCode())) { 884 theTarget.add(new FhirVersionIndependentConcept(theCodeSystemUrl, next.getCode(), next.getDisplay(), theCodeSystemVersion)); 885 } 886 } 887 addCodes(theCodeSystemUrl, theCodeSystemVersion, next.getConcept(), theTarget, theCodeFilter); 888 } 889 } 890 891 private String optionallyPopulateVersionFromUrl(String theSystemUrl, String theVersion) { 892 if(contains(theSystemUrl, OUR_PIPE_CHARACTER) && isBlank(theVersion)){ 893 theVersion = substringAfter(theSystemUrl, OUR_PIPE_CHARACTER); 894 } 895 return theVersion; 896 } 897 898 public enum FailureType { 899 900 UNKNOWN_CODE_SYSTEM, 901 OTHER 902 903 } 904 905 public static class ExpansionCouldNotBeCompletedInternallyException extends Exception { 906 907 private static final long serialVersionUID = -2226561628771483085L; 908 private final FailureType myFailureType; 909 910 public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) { 911 super(theMessage); 912 myFailureType = theFailureType; 913 } 914 915 public FailureType getFailureType() { 916 return myFailureType; 917 } 918 } 919 920 private static void flattenAndConvertCodesDstu2(List<ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) { 921 for (ca.uhn.fhir.model.dstu2.resource.ValueSet.ExpansionContains next : theInput) { 922 theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); 923 flattenAndConvertCodesDstu2(next.getContains(), theFhirVersionIndependentConcepts); 924 } 925 } 926 927 private static void flattenAndConvertCodesDstu2Hl7Org(List<org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) { 928 for (org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { 929 theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); 930 flattenAndConvertCodesDstu2Hl7Org(next.getContains(), theFhirVersionIndependentConcepts); 931 } 932 } 933 934 private static void flattenAndConvertCodesDstu3(List<org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) { 935 for (org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { 936 theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); 937 flattenAndConvertCodesDstu3(next.getContains(), theFhirVersionIndependentConcepts); 938 } 939 } 940 941 private static void flattenAndConvertCodesR4(List<org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) { 942 for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { 943 theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); 944 flattenAndConvertCodesR4(next.getContains(), theFhirVersionIndependentConcepts); 945 } 946 } 947 948 private static void flattenAndConvertCodesR4B(List<org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) { 949 for (org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { 950 theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); 951 flattenAndConvertCodesR4B(next.getContains(), theFhirVersionIndependentConcepts); 952 } 953 } 954 955 private static void flattenAndConvertCodesR5(List<org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent> theInput, List<FhirVersionIndependentConcept> theFhirVersionIndependentConcepts) { 956 for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { 957 theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); 958 flattenAndConvertCodesR5(next.getContains(), theFhirVersionIndependentConcepts); 959 } 960 } 961 962}