![](/hapi-fhir/images/logos/raccoon-forwards.png)
001package org.hl7.fhir.common.hapi.validation.validator; 002 003import ca.uhn.fhir.context.FhirVersionEnum; 004import ca.uhn.fhir.context.support.ConceptValidationOptions; 005import ca.uhn.fhir.context.support.IValidationSupport; 006import ca.uhn.fhir.context.support.ValidationSupportContext; 007import ca.uhn.fhir.i18n.Msg; 008import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 009import ca.uhn.fhir.sl.cache.CacheFactory; 010import ca.uhn.fhir.sl.cache.LoadingCache; 011import ca.uhn.fhir.system.HapiSystemProperties; 012import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; 013import jakarta.annotation.Nonnull; 014import jakarta.annotation.Nullable; 015import org.apache.commons.lang3.Validate; 016import org.apache.commons.lang3.builder.EqualsBuilder; 017import org.apache.commons.lang3.builder.HashCodeBuilder; 018import org.fhir.ucum.UcumService; 019import org.hl7.fhir.exceptions.FHIRException; 020import org.hl7.fhir.exceptions.TerminologyServiceException; 021import org.hl7.fhir.instance.model.api.IBaseResource; 022import org.hl7.fhir.r5.context.IContextResourceLoader; 023import org.hl7.fhir.r5.context.IWorkerContext; 024import org.hl7.fhir.r5.context.IWorkerContextManager; 025import org.hl7.fhir.r5.model.CodeSystem; 026import org.hl7.fhir.r5.model.CodeableConcept; 027import org.hl7.fhir.r5.model.Coding; 028import org.hl7.fhir.r5.model.NamingSystem; 029import org.hl7.fhir.r5.model.OperationOutcome; 030import org.hl7.fhir.r5.model.PackageInformation; 031import org.hl7.fhir.r5.model.Parameters; 032import org.hl7.fhir.r5.model.Resource; 033import org.hl7.fhir.r5.model.StructureDefinition; 034import org.hl7.fhir.r5.model.ValueSet; 035import org.hl7.fhir.r5.profilemodel.PEBuilder; 036import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; 037import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest; 038import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; 039import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; 040import org.hl7.fhir.r5.utils.validation.IResourceValidator; 041import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; 042import org.hl7.fhir.utilities.FhirPublication; 043import org.hl7.fhir.utilities.TimeTracker; 044import org.hl7.fhir.utilities.i18n.I18nBase; 045import org.hl7.fhir.utilities.npm.BasePackageCacheManager; 046import org.hl7.fhir.utilities.npm.NpmPackage; 047import org.hl7.fhir.utilities.validation.ValidationMessage; 048import org.hl7.fhir.utilities.validation.ValidationOptions; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import java.io.FileNotFoundException; 053import java.io.IOException; 054import java.util.ArrayList; 055import java.util.List; 056import java.util.Locale; 057import java.util.Map; 058import java.util.Set; 059 060import static org.apache.commons.lang3.StringUtils.isBlank; 061import static org.apache.commons.lang3.StringUtils.isNotBlank; 062 063public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWorkerContext { 064 private static final Logger ourLog = LoggerFactory.getLogger(VersionSpecificWorkerContextWrapper.class); 065 private final ValidationSupportContext myValidationSupportContext; 066 private final VersionCanonicalizer myVersionCanonicalizer; 067 private final LoadingCache<ResourceKey, IBaseResource> myFetchResourceCache; 068 private volatile List<StructureDefinition> myAllStructures; 069 private org.hl7.fhir.r5.model.Parameters myExpansionProfile; 070 071 public VersionSpecificWorkerContextWrapper( 072 ValidationSupportContext theValidationSupportContext, VersionCanonicalizer theVersionCanonicalizer) { 073 myValidationSupportContext = theValidationSupportContext; 074 myVersionCanonicalizer = theVersionCanonicalizer; 075 076 long timeoutMillis = HapiSystemProperties.getTestValidationResourceCachesMs(); 077 078 myFetchResourceCache = CacheFactory.build(timeoutMillis, 10000, key -> { 079 String fetchResourceName = key.getResourceName(); 080 if (myValidationSupportContext 081 .getRootValidationSupport() 082 .getFhirContext() 083 .getVersion() 084 .getVersion() 085 == FhirVersionEnum.DSTU2) { 086 if ("CodeSystem".equals(fetchResourceName)) { 087 fetchResourceName = "ValueSet"; 088 } 089 } 090 091 Class<? extends IBaseResource> fetchResourceType; 092 if (fetchResourceName.equals("Resource")) { 093 fetchResourceType = null; 094 } else { 095 fetchResourceType = myValidationSupportContext 096 .getRootValidationSupport() 097 .getFhirContext() 098 .getResourceDefinition(fetchResourceName) 099 .getImplementingClass(); 100 } 101 102 IBaseResource fetched = myValidationSupportContext 103 .getRootValidationSupport() 104 .fetchResource(fetchResourceType, key.getUri()); 105 106 Resource canonical = myVersionCanonicalizer.resourceToValidatorCanonical(fetched); 107 108 if (canonical instanceof StructureDefinition) { 109 StructureDefinition canonicalSd = (StructureDefinition) canonical; 110 if (canonicalSd.getSnapshot().isEmpty()) { 111 ourLog.info("Generating snapshot for StructureDefinition: {}", canonicalSd.getUrl()); 112 fetched = myValidationSupportContext 113 .getRootValidationSupport() 114 .generateSnapshot(theValidationSupportContext, fetched, "", null, ""); 115 Validate.isTrue( 116 fetched != null, 117 "StructureDefinition %s has no snapshot, and no snapshot generator is configured", 118 key.getUri()); 119 canonical = myVersionCanonicalizer.resourceToValidatorCanonical(fetched); 120 } 121 } 122 123 return canonical; 124 }); 125 126 setValidationMessageLanguage(getLocale()); 127 } 128 129 @Override 130 public Set<String> getBinaryKeysAsSet() { 131 throw new UnsupportedOperationException(Msg.code(2118)); 132 } 133 134 @Override 135 public boolean hasBinaryKey(String s) { 136 return myValidationSupportContext.getRootValidationSupport().fetchBinary(s) != null; 137 } 138 139 @Override 140 public byte[] getBinaryForKey(String s) { 141 return myValidationSupportContext.getRootValidationSupport().fetchBinary(s); 142 } 143 144 @Override 145 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader) throws FHIRException { 146 throw new UnsupportedOperationException(Msg.code(652)); 147 } 148 149 @Override 150 public int loadFromPackage(NpmPackage pi, IContextResourceLoader loader, List<String> types) 151 throws FileNotFoundException, IOException, FHIRException { 152 throw new UnsupportedOperationException(Msg.code(653)); 153 } 154 155 @Override 156 public int loadFromPackageAndDependencies(NpmPackage pi, IContextResourceLoader loader, BasePackageCacheManager pcm) 157 throws FHIRException { 158 throw new UnsupportedOperationException(Msg.code(654)); 159 } 160 161 @Override 162 public boolean hasPackage(String id, String ver) { 163 throw new UnsupportedOperationException(Msg.code(655)); 164 } 165 166 @Override 167 public boolean hasPackage(PackageInformation packageInformation) { 168 return false; 169 } 170 171 @Override 172 public PackageInformation getPackage(String id, String ver) { 173 return null; 174 } 175 176 @Override 177 public int getClientRetryCount() { 178 throw new UnsupportedOperationException(Msg.code(656)); 179 } 180 181 @Override 182 public IWorkerContext setClientRetryCount(int value) { 183 throw new UnsupportedOperationException(Msg.code(657)); 184 } 185 186 @Override 187 public TimeTracker clock() { 188 return null; 189 } 190 191 @Override 192 public IWorkerContextManager.IPackageLoadingTracker getPackageTracker() { 193 throw new UnsupportedOperationException(Msg.code(2235)); 194 } 195 196 @Override 197 public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker packageTracker) { 198 throw new UnsupportedOperationException(Msg.code(2266)); 199 } 200 201 @Override 202 public String getSpecUrl() { 203 return ""; 204 } 205 206 @Override 207 public PEBuilder getProfiledElementBuilder( 208 PEBuilder.PEElementPropertiesPolicy thePEElementPropertiesPolicy, boolean theB) { 209 throw new UnsupportedOperationException(Msg.code(2264)); 210 } 211 212 @Override 213 public PackageInformation getPackageForUrl(String s) { 214 throw new UnsupportedOperationException(Msg.code(2236)); 215 } 216 217 @Override 218 public org.hl7.fhir.r5.model.Parameters getExpansionParameters() { 219 return myExpansionProfile; 220 } 221 222 @Override 223 public void setExpansionParameters(Parameters expParameters) { 224 setExpansionProfile(expParameters); 225 } 226 227 public void setExpansionProfile(org.hl7.fhir.r5.model.Parameters expParameters) { 228 myExpansionProfile = expParameters; 229 } 230 231 private List<StructureDefinition> allStructures() { 232 233 List<StructureDefinition> retVal = myAllStructures; 234 if (retVal == null) { 235 retVal = new ArrayList<>(); 236 for (IBaseResource next : 237 myValidationSupportContext.getRootValidationSupport().fetchAllStructureDefinitions()) { 238 try { 239 StructureDefinition converted = myVersionCanonicalizer.structureDefinitionToCanonical(next); 240 retVal.add(converted); 241 } catch (FHIRException e) { 242 throw new InternalErrorException(Msg.code(659) + e); 243 } 244 } 245 myAllStructures = retVal; 246 } 247 248 return retVal; 249 } 250 251 @Override 252 public void cacheResource(Resource res) {} 253 254 @Override 255 public void cacheResourceFromPackage(Resource res, PackageInformation packageDetails) throws FHIRException {} 256 257 @Override 258 public void cachePackage(PackageInformation packageInformation) {} 259 260 @Nonnull 261 private ValidationResult convertValidationResult( 262 String theSystem, @Nullable IValidationSupport.CodeValidationResult theResult) { 263 ValidationResult retVal = null; 264 if (theResult != null) { 265 String code = theResult.getCode(); 266 String display = theResult.getDisplay(); 267 268 String issueSeverityCode = theResult.getSeverityCode(); 269 String message = theResult.getMessage(); 270 ValidationMessage.IssueSeverity issueSeverity = null; 271 if (issueSeverityCode != null) { 272 issueSeverity = ValidationMessage.IssueSeverity.fromCode(issueSeverityCode); 273 } else if (isNotBlank(message)) { 274 issueSeverity = ValidationMessage.IssueSeverity.INFORMATION; 275 } 276 277 CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent = null; 278 if (code != null) { 279 conceptDefinitionComponent = new CodeSystem.ConceptDefinitionComponent() 280 .setCode(code) 281 .setDisplay(display); 282 } 283 284 retVal = new ValidationResult( 285 issueSeverity, 286 message, 287 theSystem, 288 theResult.getCodeSystemVersion(), 289 conceptDefinitionComponent, 290 display, 291 getIssuesForCodeValidation(theResult.getCodeValidationIssues())); 292 } 293 294 if (retVal == null) { 295 retVal = new ValidationResult(ValidationMessage.IssueSeverity.ERROR, "Validation failed", null); 296 } 297 298 return retVal; 299 } 300 301 private List<OperationOutcome.OperationOutcomeIssueComponent> getIssuesForCodeValidation( 302 List<IValidationSupport.CodeValidationIssue> codeValidationIssues) { 303 List<OperationOutcome.OperationOutcomeIssueComponent> issues = new ArrayList<>(); 304 305 for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeValidationIssues) { 306 307 CodeableConcept codeableConcept = new CodeableConcept().setText(codeValidationIssue.getMessage()); 308 codeableConcept.addCoding( 309 "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", 310 getIssueCodingFromCodeValidationIssue(codeValidationIssue), 311 null); 312 313 OperationOutcome.OperationOutcomeIssueComponent issue = 314 new OperationOutcome.OperationOutcomeIssueComponent() 315 .setSeverity(getIssueSeverityFromCodeValidationIssue(codeValidationIssue)) 316 .setCode(getIssueTypeFromCodeValidationIssue(codeValidationIssue)) 317 .setDetails(codeableConcept); 318 issue.getDetails().setText(codeValidationIssue.getMessage()); 319 issue.addExtension() 320 .setUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id") 321 .setValue(new org.hl7.fhir.r5.model.StringType("Terminology_PassThrough_TX_Message")); 322 issues.add(issue); 323 } 324 return issues; 325 } 326 327 private String getIssueCodingFromCodeValidationIssue(IValidationSupport.CodeValidationIssue codeValidationIssue) { 328 switch (codeValidationIssue.getCoding()) { 329 case VS_INVALID: 330 return "vs-invalid"; 331 case NOT_FOUND: 332 return "not-found"; 333 case NOT_IN_VS: 334 return "not-in-vs"; 335 case INVALID_CODE: 336 return "invalid-code"; 337 case INVALID_DISPLAY: 338 return "invalid-display"; 339 } 340 return null; 341 } 342 343 private OperationOutcome.IssueType getIssueTypeFromCodeValidationIssue( 344 IValidationSupport.CodeValidationIssue codeValidationIssue) { 345 switch (codeValidationIssue.getCode()) { 346 case NOT_FOUND: 347 return OperationOutcome.IssueType.NOTFOUND; 348 case CODE_INVALID: 349 return OperationOutcome.IssueType.CODEINVALID; 350 case INVALID: 351 return OperationOutcome.IssueType.INVALID; 352 } 353 return null; 354 } 355 356 private OperationOutcome.IssueSeverity getIssueSeverityFromCodeValidationIssue( 357 IValidationSupport.CodeValidationIssue codeValidationIssue) { 358 switch (codeValidationIssue.getSeverity()) { 359 case FATAL: 360 return OperationOutcome.IssueSeverity.FATAL; 361 case ERROR: 362 return OperationOutcome.IssueSeverity.ERROR; 363 case WARNING: 364 return OperationOutcome.IssueSeverity.WARNING; 365 case INFORMATION: 366 return OperationOutcome.IssueSeverity.INFORMATION; 367 } 368 return null; 369 } 370 371 @Override 372 public ValueSetExpansionOutcome expandVS( 373 org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean Hierarchical) { 374 IBaseResource convertedSource; 375 try { 376 convertedSource = myVersionCanonicalizer.valueSetFromValidatorCanonical(source); 377 } catch (FHIRException e) { 378 throw new InternalErrorException(Msg.code(661) + e); 379 } 380 IValidationSupport.ValueSetExpansionOutcome expanded = myValidationSupportContext 381 .getRootValidationSupport() 382 .expandValueSet(myValidationSupportContext, null, convertedSource); 383 384 org.hl7.fhir.r5.model.ValueSet convertedResult = null; 385 if (expanded.getValueSet() != null) { 386 try { 387 convertedResult = myVersionCanonicalizer.valueSetToValidatorCanonical(expanded.getValueSet()); 388 } catch (FHIRException e) { 389 throw new InternalErrorException(Msg.code(662) + e); 390 } 391 } 392 393 String error = expanded.getError(); 394 TerminologyServiceErrorClass result = null; 395 396 return new ValueSetExpansionOutcome(convertedResult, error, result, expanded.getErrorIsFromServer()); 397 } 398 399 @Override 400 public ValueSetExpansionOutcome expandVS( 401 Resource src, 402 org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent binding, 403 boolean cacheOk, 404 boolean Hierarchical) { 405 ValueSet valueSet = fetchResource(ValueSet.class, binding.getValueSet(), src); 406 return expandVS(valueSet, cacheOk, Hierarchical); 407 } 408 409 @Override 410 public ValueSetExpansionOutcome expandVS(ValueSet.ConceptSetComponent inc, boolean hierarchical, boolean noInactive) 411 throws TerminologyServiceException { 412 throw new UnsupportedOperationException(Msg.code(664)); 413 } 414 415 @Override 416 public Locale getLocale() { 417 return myValidationSupportContext 418 .getRootValidationSupport() 419 .getFhirContext() 420 .getLocalizer() 421 .getLocale(); 422 } 423 424 @Override 425 public void setLocale(Locale locale) { 426 // ignore 427 } 428 429 @Override 430 public org.hl7.fhir.r5.model.CodeSystem fetchCodeSystem(String system) { 431 IBaseResource fetched = 432 myValidationSupportContext.getRootValidationSupport().fetchCodeSystem(system); 433 if (fetched == null) { 434 return null; 435 } 436 try { 437 return (org.hl7.fhir.r5.model.CodeSystem) myVersionCanonicalizer.codeSystemToValidatorCanonical(fetched); 438 } catch (FHIRException e) { 439 throw new InternalErrorException(Msg.code(665) + e); 440 } 441 } 442 443 @Override 444 public CodeSystem fetchCodeSystem(String system, String verison) { 445 IBaseResource fetched = 446 myValidationSupportContext.getRootValidationSupport().fetchCodeSystem(system); 447 if (fetched == null) { 448 return null; 449 } 450 try { 451 return (org.hl7.fhir.r5.model.CodeSystem) myVersionCanonicalizer.codeSystemToValidatorCanonical(fetched); 452 } catch (FHIRException e) { 453 throw new InternalErrorException(Msg.code(1992) + e); 454 } 455 } 456 457 @Override 458 public CodeSystem fetchCodeSystem(String system, FhirPublication fhirVersion) { 459 return null; 460 } 461 462 @Override 463 public CodeSystem fetchCodeSystem(String system, String version, FhirPublication fhirVersion) { 464 return null; 465 } 466 467 @Override 468 public CodeSystem fetchSupplementedCodeSystem(String system) { 469 return null; 470 } 471 472 @Override 473 public CodeSystem fetchSupplementedCodeSystem(String system, String version) { 474 return null; 475 } 476 477 @Override 478 public CodeSystem fetchSupplementedCodeSystem(String system, FhirPublication fhirVersion) { 479 return null; 480 } 481 482 @Override 483 public CodeSystem fetchSupplementedCodeSystem(String system, String version, FhirPublication fhirVersion) { 484 return null; 485 } 486 487 @Override 488 public <T extends Resource> T fetchResourceRaw(Class<T> class_, String uri) { 489 return fetchResource(class_, uri); 490 } 491 492 @Override 493 public <T extends Resource> T fetchResource(Class<T> class_, String theUri) { 494 if (isBlank(theUri)) { 495 return null; 496 } 497 498 String uri = theUri; 499 // handle profile version, if present 500 if (theUri.contains("|")) { 501 String[] parts = theUri.split("\\|"); 502 if (parts.length == 2) { 503 uri = parts[0]; 504 } else { 505 ourLog.warn("Unrecognized profile uri: {}", theUri); 506 } 507 } 508 509 ResourceKey key = new ResourceKey(class_.getSimpleName(), uri); 510 @SuppressWarnings("unchecked") 511 T retVal = (T) myFetchResourceCache.get(key); 512 513 return retVal; 514 } 515 516 @Override 517 public Resource fetchResourceById(String type, String uri) { 518 throw new UnsupportedOperationException(Msg.code(666)); 519 } 520 521 @Override 522 public Resource fetchResourceById(String type, String uri, FhirPublication fhirVersion) { 523 return null; 524 } 525 526 @Override 527 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException { 528 T retVal = fetchResource(class_, uri); 529 if (retVal == null) { 530 throw new FHIRException( 531 Msg.code(667) + "Can not find resource of type " + class_.getSimpleName() + " with uri " + uri); 532 } 533 return retVal; 534 } 535 536 @Override 537 public <T extends Resource> T fetchResource(Class<T> class_, String uri, String version) { 538 return fetchResource(class_, uri + "|" + version); 539 } 540 541 @Override 542 public <T extends Resource> T fetchResource(Class<T> class_, String uri, FhirPublication fhirVersion) { 543 return null; 544 } 545 546 @Override 547 public <T extends Resource> T fetchResource( 548 Class<T> class_, String uri, String version, FhirPublication fhirVersion) { 549 return null; 550 } 551 552 @Override 553 public <T extends Resource> T fetchResource(Class<T> class_, String uri, Resource canonicalForSource) { 554 return fetchResource(class_, uri); 555 } 556 557 @Override 558 public <T extends Resource> List<T> fetchResourcesByType(Class<T> class_, FhirPublication fhirVersion) { 559 return null; 560 } 561 562 @Override 563 public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri, Resource sourceOfReference) 564 throws FHIRException { 565 throw new UnsupportedOperationException(Msg.code(2214)); 566 } 567 568 @Override 569 public List<String> getResourceNames() { 570 return new ArrayList<>(myValidationSupportContext 571 .getRootValidationSupport() 572 .getFhirContext() 573 .getResourceTypes()); 574 } 575 576 @Override 577 public List<String> getResourceNames(FhirPublication fhirVersion) { 578 return null; 579 } 580 581 @Override 582 public Set<String> getResourceNamesAsSet() { 583 return myValidationSupportContext 584 .getRootValidationSupport() 585 .getFhirContext() 586 .getResourceTypes(); 587 } 588 589 @Override 590 public Set<String> getResourceNamesAsSet(FhirPublication theFhirVersion) { 591 return null; 592 } 593 594 @Override 595 public StructureDefinition fetchTypeDefinition(String theTypeName) { 596 return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + theTypeName); 597 } 598 599 @Override 600 public StructureDefinition fetchTypeDefinition(String theTypeName, FhirPublication theFhirVersion) { 601 return null; 602 } 603 604 @Override 605 public List<StructureDefinition> fetchTypeDefinitions(String theTypeName) { 606 List<StructureDefinition> allStructures = new ArrayList<>(allStructures()); 607 allStructures.removeIf(sd -> !sd.hasType() || !sd.getType().equals(theTypeName)); 608 return allStructures; 609 } 610 611 @Override 612 public List<StructureDefinition> fetchTypeDefinitions(String theTypeName, FhirPublication theFhirVersion) { 613 return null; 614 } 615 616 @Override 617 public boolean isPrimitiveType(String theType) { 618 List<StructureDefinition> allStructures = new ArrayList<>(allStructures()); 619 return allStructures.stream() 620 .filter(structureDefinition -> 621 structureDefinition.getKind() == StructureDefinition.StructureDefinitionKind.PRIMITIVETYPE) 622 .anyMatch(structureDefinition -> theType.equals(structureDefinition.getName())); 623 } 624 625 @Override 626 public boolean isDataType(String theType) { 627 return !isPrimitiveType(theType); 628 } 629 630 @Override 631 public UcumService getUcumService() { 632 throw new UnsupportedOperationException(Msg.code(676)); 633 } 634 635 @Override 636 public void setUcumService(UcumService ucumService) { 637 throw new UnsupportedOperationException(Msg.code(677)); 638 } 639 640 @Override 641 public String getVersion() { 642 return myValidationSupportContext 643 .getRootValidationSupport() 644 .getFhirContext() 645 .getVersion() 646 .getVersion() 647 .getFhirVersionString(); 648 } 649 650 @Override 651 public <T extends Resource> boolean hasResource(Class<T> class_, String uri) { 652 if (isBlank(uri)) { 653 return false; 654 } 655 656 ResourceKey key = new ResourceKey(class_.getSimpleName(), uri); 657 return myFetchResourceCache.get(key) != null; 658 } 659 660 @Override 661 public <T extends Resource> boolean hasResource(Class<T> class_, String uri, Resource sourceOfReference) { 662 return false; 663 } 664 665 @Override 666 public <T extends Resource> boolean hasResource(Class<T> class_, String uri, FhirPublication fhirVersion) { 667 return false; 668 } 669 670 @Override 671 public boolean isNoTerminologyServer() { 672 return false; 673 } 674 675 @Override 676 public Set<String> getCodeSystemsUsed() { 677 throw new UnsupportedOperationException(Msg.code(681)); 678 } 679 680 @Override 681 public IResourceValidator newValidator() { 682 throw new UnsupportedOperationException(Msg.code(684)); 683 } 684 685 @Override 686 public Map<String, NamingSystem> getNSUrlMap() { 687 throw new UnsupportedOperationException(Msg.code(2265)); 688 } 689 690 @Override 691 public org.hl7.fhir.r5.context.ILoggingService getLogger() { 692 return null; 693 } 694 695 @Override 696 public void setLogger(org.hl7.fhir.r5.context.ILoggingService logger) { 697 throw new UnsupportedOperationException(Msg.code(687)); 698 } 699 700 @Override 701 public boolean supportsSystem(String system) { 702 return myValidationSupportContext 703 .getRootValidationSupport() 704 .isCodeSystemSupported(myValidationSupportContext, system); 705 } 706 707 @Override 708 public boolean supportsSystem(String system, FhirPublication fhirVersion) throws TerminologyServiceException { 709 return supportsSystem(system); 710 } 711 712 @Override 713 public ValueSetExpansionOutcome expandVS( 714 ValueSet source, boolean cacheOk, boolean heiarchical, boolean incompleteOk) { 715 return null; 716 } 717 718 @Override 719 public ValidationResult validateCode( 720 ValidationOptions theOptions, String system, String version, String code, String display) { 721 ConceptValidationOptions validationOptions = convertConceptValidationOptions(theOptions); 722 return doValidation(null, validationOptions, system, code, display); 723 } 724 725 @Override 726 public ValidationResult validateCode( 727 ValidationOptions theOptions, 728 String theSystem, 729 String version, 730 String theCode, 731 String display, 732 ValueSet theValueSet) { 733 IBaseResource convertedVs = null; 734 735 try { 736 if (theValueSet != null) { 737 convertedVs = myVersionCanonicalizer.valueSetFromValidatorCanonical(theValueSet); 738 } 739 } catch (FHIRException e) { 740 throw new InternalErrorException(Msg.code(689) + e); 741 } 742 743 ConceptValidationOptions validationOptions = convertConceptValidationOptions(theOptions); 744 745 return doValidation(convertedVs, validationOptions, theSystem, theCode, display); 746 } 747 748 @Override 749 public ValidationResult validateCode( 750 ValidationOptions theOptions, String code, org.hl7.fhir.r5.model.ValueSet theValueSet) { 751 IBaseResource convertedVs = null; 752 try { 753 if (theValueSet != null) { 754 convertedVs = myVersionCanonicalizer.valueSetFromValidatorCanonical(theValueSet); 755 } 756 } catch (FHIRException e) { 757 throw new InternalErrorException(Msg.code(690) + e); 758 } 759 760 ConceptValidationOptions validationOptions = 761 convertConceptValidationOptions(theOptions).setInferSystem(true); 762 763 return doValidation(convertedVs, validationOptions, null, code, null); 764 } 765 766 @Override 767 public ValidationResult validateCode( 768 ValidationOptions theOptions, 769 org.hl7.fhir.r5.model.Coding theCoding, 770 org.hl7.fhir.r5.model.ValueSet theValueSet) { 771 IBaseResource convertedVs = null; 772 773 try { 774 if (theValueSet != null) { 775 convertedVs = myVersionCanonicalizer.valueSetFromValidatorCanonical(theValueSet); 776 } 777 } catch (FHIRException e) { 778 throw new InternalErrorException(Msg.code(691) + e); 779 } 780 781 ConceptValidationOptions validationOptions = convertConceptValidationOptions(theOptions); 782 String system = theCoding.getSystem(); 783 String code = theCoding.getCode(); 784 String display = theCoding.getDisplay(); 785 786 return doValidation(convertedVs, validationOptions, system, code, display); 787 } 788 789 @Override 790 public ValidationResult validateCode( 791 ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) { 792 return validateCode(options, code, vs); 793 } 794 795 @Override 796 public void validateCodeBatch( 797 ValidationOptions options, List<? extends CodingValidationRequest> codes, ValueSet vs) { 798 for (CodingValidationRequest next : codes) { 799 ValidationResult outcome = validateCode(options, next.getCoding(), vs); 800 next.setResult(outcome); 801 } 802 } 803 804 @Override 805 public void validateCodeBatchByRef( 806 ValidationOptions validationOptions, List<? extends CodingValidationRequest> list, String s) { 807 ValueSet valueSet = fetchResource(ValueSet.class, s); 808 validateCodeBatch(validationOptions, list, valueSet); 809 } 810 811 @Nonnull 812 private ValidationResult doValidation( 813 IBaseResource theValueSet, 814 ConceptValidationOptions theValidationOptions, 815 String theSystem, 816 String theCode, 817 String theDisplay) { 818 IValidationSupport.CodeValidationResult result; 819 if (theValueSet != null) { 820 result = validateCodeInValueSet(theValueSet, theValidationOptions, theSystem, theCode, theDisplay); 821 } else { 822 result = validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay); 823 } 824 return convertValidationResult(theSystem, result); 825 } 826 827 private IValidationSupport.CodeValidationResult validateCodeInValueSet( 828 IBaseResource theValueSet, 829 ConceptValidationOptions theValidationOptions, 830 String theSystem, 831 String theCode, 832 String theDisplay) { 833 IValidationSupport.CodeValidationResult result = myValidationSupportContext 834 .getRootValidationSupport() 835 .validateCodeInValueSet( 836 myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, theValueSet); 837 if (result != null) { 838 /* We got a value set result, which could be successful, or could contain errors/warnings. The code 839 might also be invalid in the code system, so we will check that as well and add those issues 840 to our result. 841 */ 842 IValidationSupport.CodeValidationResult codeSystemResult = 843 validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay); 844 final boolean valueSetResultContainsInvalidDisplay = result.getCodeValidationIssues().stream() 845 .anyMatch(codeValidationIssue -> codeValidationIssue.getCoding() 846 == IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY); 847 if (codeSystemResult != null) { 848 for (IValidationSupport.CodeValidationIssue codeValidationIssue : 849 codeSystemResult.getCodeValidationIssues()) { 850 /* Value set validation should already have checked the display name. If we get INVALID_DISPLAY 851 issues from code system validation, they will only repeat what was already caught. 852 */ 853 if (codeValidationIssue.getCoding() != IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY 854 || !valueSetResultContainsInvalidDisplay) { 855 result.addCodeValidationIssue(codeValidationIssue); 856 } 857 } 858 } 859 } 860 return result; 861 } 862 863 private IValidationSupport.CodeValidationResult validateCodeInCodeSystem( 864 ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) { 865 return myValidationSupportContext 866 .getRootValidationSupport() 867 .validateCode(myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, null); 868 } 869 870 @Override 871 public ValidationResult validateCode( 872 ValidationOptions theOptions, 873 org.hl7.fhir.r5.model.CodeableConcept code, 874 org.hl7.fhir.r5.model.ValueSet theVs) { 875 876 List<ValidationResult> validationResultsOk = new ArrayList<>(); 877 List<OperationOutcome.OperationOutcomeIssueComponent> issues = new ArrayList<>(); 878 for (Coding next : code.getCoding()) { 879 if (!next.hasSystem()) { 880 String message = 881 "Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided"; 882 OperationOutcome.OperationOutcomeIssueComponent issue = 883 new OperationOutcome.OperationOutcomeIssueComponent() 884 .setSeverity(OperationOutcome.IssueSeverity.WARNING) 885 .setCode(OperationOutcome.IssueType.NOTFOUND) 886 .setDiagnostics(message) 887 .setDetails(new CodeableConcept().setText(message)); 888 889 issues.add(issue); 890 } 891 ValidationResult retVal = validateCode(theOptions, next, theVs); 892 if (retVal.isOk()) { 893 validationResultsOk.add(retVal); 894 } else { 895 for (OperationOutcome.OperationOutcomeIssueComponent issue : retVal.getIssues()) { 896 issues.add(issue); 897 } 898 } 899 } 900 901 if (code.getCoding().size() > 0) { 902 if (!myValidationSupportContext.isEnabledValidationForCodingsLogicalAnd()) { 903 if (validationResultsOk.size() == code.getCoding().size()) { 904 return validationResultsOk.get(0); 905 } 906 } else { 907 if (validationResultsOk.size() > 0) { 908 return validationResultsOk.get(0); 909 } 910 } 911 } 912 913 return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null, issues); 914 } 915 916 private static OperationOutcome.OperationOutcomeIssueComponent getOperationOutcomeTxIssueComponent( 917 String message, OperationOutcome.IssueType issueCode, String txIssueTypeCode) { 918 OperationOutcome.OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent() 919 .setSeverity(OperationOutcome.IssueSeverity.ERROR) 920 .setDiagnostics(message); 921 issue.getDetails().setText(message); 922 923 issue.setCode(issueCode); 924 issue.getDetails().addCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", txIssueTypeCode, null); 925 return issue; 926 } 927 928 public void invalidateCaches() { 929 myFetchResourceCache.invalidateAll(); 930 } 931 932 @Override 933 public <T extends Resource> List<T> fetchResourcesByType(Class<T> theClass) { 934 if (theClass.equals(StructureDefinition.class)) { 935 return (List<T>) allStructures(); 936 } 937 throw new UnsupportedOperationException(Msg.code(650) + "Unable to fetch resources of type: " + theClass); 938 } 939 940 @Override 941 public <T extends Resource> List<T> fetchResourcesByUrl(Class<T> class_, String url) { 942 throw new UnsupportedOperationException(Msg.code(2509) + "Can't fetch all resources of url: " + url); 943 } 944 945 @Override 946 public boolean isForPublication() { 947 return false; 948 } 949 950 @Override 951 public void setForPublication(boolean b) { 952 throw new UnsupportedOperationException(Msg.code(2351)); 953 } 954 955 @Override 956 public OIDSummary urlsForOid(String oid, String resourceType) { 957 return null; 958 } 959 960 @Override 961 public <T extends Resource> T findTxResource(Class<T> class_, String canonical, Resource sourceOfReference) { 962 if (canonical == null) { 963 return null; 964 } 965 return fetchResource(class_, canonical, sourceOfReference); 966 } 967 968 @Override 969 public <T extends Resource> T findTxResource(Class<T> class_, String canonical) { 970 if (canonical == null) { 971 return null; 972 } 973 974 return fetchResource(class_, canonical); 975 } 976 977 @Override 978 public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version) { 979 if (canonical == null) { 980 return null; 981 } 982 983 return fetchResource(class_, canonical, version); 984 } 985 986 public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) { 987 ConceptValidationOptions retVal = new ConceptValidationOptions(); 988 if (theOptions.isGuessSystem()) { 989 retVal = retVal.setInferSystem(true); 990 } 991 return retVal; 992 } 993 994 @Nonnull 995 public static VersionSpecificWorkerContextWrapper newVersionSpecificWorkerContextWrapper( 996 IValidationSupport theValidationSupport) { 997 VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(theValidationSupport.getFhirContext()); 998 return new VersionSpecificWorkerContextWrapper( 999 new ValidationSupportContext(theValidationSupport), versionCanonicalizer); 1000 } 1001 1002 private static class ResourceKey { 1003 private final int myHashCode; 1004 private final String myResourceName; 1005 private final String myUri; 1006 1007 private ResourceKey(String theResourceName, String theUri) { 1008 myResourceName = theResourceName; 1009 myUri = theUri; 1010 myHashCode = new HashCodeBuilder(17, 37) 1011 .append(myResourceName) 1012 .append(myUri) 1013 .toHashCode(); 1014 } 1015 1016 @Override 1017 public boolean equals(Object theO) { 1018 if (this == theO) { 1019 return true; 1020 } 1021 1022 if (theO == null || getClass() != theO.getClass()) { 1023 return false; 1024 } 1025 1026 ResourceKey that = (ResourceKey) theO; 1027 1028 return new EqualsBuilder() 1029 .append(myResourceName, that.myResourceName) 1030 .append(myUri, that.myUri) 1031 .isEquals(); 1032 } 1033 1034 public String getResourceName() { 1035 return myResourceName; 1036 } 1037 1038 public String getUri() { 1039 return myUri; 1040 } 1041 1042 @Override 1043 public int hashCode() { 1044 return myHashCode; 1045 } 1046 } 1047 1048 @Override 1049 public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) { 1050 throw new UnsupportedOperationException(Msg.code(2489)); 1051 } 1052 1053 @Override 1054 public boolean isServerSideSystem(String url) { 1055 return false; 1056 } 1057}