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