001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.context.support; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 025import ca.uhn.fhir.util.ParametersUtil; 026import ca.uhn.fhir.util.UrlUtil; 027import org.apache.commons.lang3.Validate; 028import org.apache.commons.lang3.builder.EqualsBuilder; 029import org.apache.commons.lang3.builder.HashCodeBuilder; 030import org.hl7.fhir.instance.model.api.IBase; 031import org.hl7.fhir.instance.model.api.IBaseCoding; 032import org.hl7.fhir.instance.model.api.IBaseParameters; 033import org.hl7.fhir.instance.model.api.IBaseResource; 034import org.hl7.fhir.instance.model.api.IIdType; 035import org.hl7.fhir.instance.model.api.IPrimitiveType; 036 037import javax.annotation.Nonnull; 038import javax.annotation.Nullable; 039import java.util.ArrayList; 040import java.util.Arrays; 041import java.util.Collections; 042import java.util.List; 043import java.util.Set; 044import java.util.function.Supplier; 045import java.util.stream.Collectors; 046 047import static org.apache.commons.lang3.StringUtils.defaultString; 048import static org.apache.commons.lang3.StringUtils.isNotBlank; 049 050/** 051 * This interface is a version-independent representation of the 052 * various functions that can be provided by validation and terminology 053 * services. 054 * <p> 055 * This interface is invoked directly by internal parts of the HAPI FHIR API, including the 056 * Validator and the FHIRPath evaluator. It is used to supply artifacts required for validation 057 * (e.g. StructureDefinition resources, ValueSet resources, etc.) and also to provide 058 * terminology functions such as code validation, ValueSet expansion, etc. 059 * </p> 060 * <p> 061 * Implementations are not required to implement all of the functions 062 * in this interface; in fact it is expected that most won't. Any 063 * methods which are not implemented may simply return <code>null</code> 064 * and calling code is expected to be able to handle this. Generally, a 065 * series of implementations of this interface will be joined together using 066 * the 067 * <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/ValidationSupportChain2.html">ValidationSupportChain</a> 068 * class. 069 * </p> 070 * <p> 071 * See <a href="https://hapifhir.io/hapi-fhir/docs/validation/validation_support_modules.html">Validation Support Modules</a> 072 * for information on how to assemble and configure implementations of this interface. See also 073 * the <code>org.hl7.fhir.common.hapi.validation.support</code> 074 * <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/package-summary.html">package summary</a> 075 * in the <code>hapi-fhir-validation</code> module for many implementations of this interface. 076 * </p> 077 * 078 * @since 5.0.0 079 */ 080public interface IValidationSupport { 081 String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/"; 082 083 084 /** 085 * Expands the given portion of a ValueSet 086 * 087 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 088 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 089 * @param theExpansionOptions If provided (can be <code>null</code>), contains options controlling the expansion 090 * @param theValueSetToExpand The valueset that should be expanded 091 * @return The expansion, or null 092 */ 093 @Nullable 094 default ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) { 095 return null; 096 } 097 098 /** 099 * Expands the given portion of a ValueSet by canonical URL. 100 * 101 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 102 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 103 * @param theExpansionOptions If provided (can be <code>null</code>), contains options controlling the expansion 104 * @param theValueSetUrlToExpand The valueset that should be expanded 105 * @return The expansion, or null 106 * @throws ResourceNotFoundException If no ValueSet can be found with the given URL 107 * @since 6.0.0 108 */ 109 @Nullable 110 default ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull String theValueSetUrlToExpand) throws ResourceNotFoundException { 111 Validate.notBlank(theValueSetUrlToExpand, "theValueSetUrlToExpand must not be null or blank"); 112 IBaseResource valueSet = fetchValueSet(theValueSetUrlToExpand); 113 if (valueSet == null) { 114 throw new ResourceNotFoundException(Msg.code(2024) + "Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSetUrlToExpand)); 115 } 116 return expandValueSet(theValidationSupportContext, theExpansionOptions, valueSet); 117 } 118 119 /** 120 * Load and return all conformance resources associated with this 121 * validation support module. This method may return null if it doesn't 122 * make sense for a given module. 123 */ 124 @Nullable 125 default List<IBaseResource> fetchAllConformanceResources() { 126 return null; 127 } 128 129 /** 130 * Load and return all possible search parameters 131 * 132 * @since 6.6.0 133 */ 134 @Nullable 135 default <T extends IBaseResource> List<T> fetchAllSearchParameters() { 136 return null; 137 } 138 139 /** 140 * Load and return all possible structure definitions 141 */ 142 @Nullable 143 default <T extends IBaseResource> List<T> fetchAllStructureDefinitions() { 144 return null; 145 } 146 147 /** 148 * Load and return all possible structure definitions aside from resource definitions themselves 149 */ 150 @Nullable 151 default <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() { 152 List<T> retVal = fetchAllStructureDefinitions(); 153 if (retVal != null) { 154 List<T> newList = new ArrayList<>(retVal.size()); 155 for (T next : retVal) { 156 String url = defaultString(getFhirContext().newTerser().getSinglePrimitiveValueOrNull(next, "url")); 157 if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) { 158 String lastPart = url.substring("http://hl7.org/fhir/StructureDefinition/".length()); 159 if (getFhirContext().getResourceTypes().contains(lastPart)) { 160 continue; 161 } 162 } 163 164 newList.add(next); 165 } 166 167 retVal = newList; 168 } 169 170 return retVal; 171 } 172 173 /** 174 * Fetch a code system by ID 175 * 176 * @param theSystem The code system 177 * @return The valueset (must not be null, but can be an empty ValueSet) 178 */ 179 @Nullable 180 default IBaseResource fetchCodeSystem(String theSystem) { 181 return null; 182 } 183 184 /** 185 * Loads a resource needed by the validation (a StructureDefinition, or a 186 * ValueSet) 187 * 188 * <p> 189 * Note: Since 5.3.0, {@literal theClass} can be {@literal null} 190 * </p> 191 * 192 * @param theClass The type of the resource to load, or <code>null</code> to return any resource with the given canonical URI 193 * @param theUri The resource URI 194 * @return Returns the resource, or <code>null</code> if no resource with the 195 * given URI can be found 196 */ 197 @SuppressWarnings("unchecked") 198 @Nullable 199 default <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) { 200 Validate.notBlank(theUri, "theUri must not be null or blank"); 201 202 if (theClass == null) { 203 Supplier<IBaseResource>[] sources = new Supplier[]{ 204 () -> fetchStructureDefinition(theUri), 205 () -> fetchValueSet(theUri), 206 () -> fetchCodeSystem(theUri) 207 }; 208 return (T) Arrays 209 .stream(sources) 210 .map(t -> t.get()) 211 .filter(t -> t != null) 212 .findFirst() 213 .orElse(null); 214 } 215 216 switch (getFhirContext().getResourceType(theClass)) { 217 case "StructureDefinition": 218 return theClass.cast(fetchStructureDefinition(theUri)); 219 case "ValueSet": 220 return theClass.cast(fetchValueSet(theUri)); 221 case "CodeSystem": 222 return theClass.cast(fetchCodeSystem(theUri)); 223 } 224 225 if (theUri.startsWith(URL_PREFIX_VALUE_SET)) { 226 return theClass.cast(fetchValueSet(theUri)); 227 } 228 229 return null; 230 } 231 232 @Nullable 233 default IBaseResource fetchStructureDefinition(String theUrl) { 234 return null; 235 } 236 237 /** 238 * Returns <code>true</code> if codes in the given code system can be expanded 239 * or validated 240 * 241 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 242 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 243 * @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code> 244 * @return Returns <code>true</code> if codes in the given code system can be 245 * validated 246 */ 247 default boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { 248 return false; 249 } 250 251 /** 252 * Returns <code>true</code> if a Remote Terminology Service is currently configured 253 * 254 * @return Returns <code>true</code> if a Remote Terminology Service is currently configured 255 */ 256 default boolean isRemoteTerminologyServiceConfigured() { 257 return false; 258 } 259 260 /** 261 * Fetch the given ValueSet by URL, or returns null if one can't be found for the given URL 262 */ 263 @Nullable 264 default IBaseResource fetchValueSet(String theValueSetUrl) { 265 return null; 266 } 267 268 /** 269 * Fetch the given binary data by key. 270 * 271 * @param binaryKey 272 * @return 273 */ 274 default byte[] fetchBinary(String binaryKey) { 275 return null; 276 } 277 278 /** 279 * Validates that the given code exists and if possible returns a display 280 * name. This method is called to check codes which are found in "example" 281 * binding fields (e.g. <code>Observation.code</code>) in the default profile. 282 * 283 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 284 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 285 * @param theOptions Provides options controlling the validation 286 * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" 287 * @param theCode The code, e.g. "<code>1234-5</code>" 288 * @param theDisplay The display name, if it should also be validated 289 * @return Returns a validation result object 290 */ 291 @Nullable 292 default CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { 293 return null; 294 } 295 296 /** 297 * Validates that the given code exists and if possible returns a display 298 * name. This method is called to check codes which are found in "example" 299 * binding fields (e.g. <code>Observation.code</code>) in the default profile. 300 * 301 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 302 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 303 * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" 304 * @param theCode The code, e.g. "<code>1234-5</code>" 305 * @param theDisplay The display name, if it should also be validated 306 * @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource. 307 * @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request 308 */ 309 @Nullable 310 default CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { 311 return null; 312 } 313 314 /** 315 * Look up a code using the system and code value 316 * 317 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 318 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 319 * @param theSystem The CodeSystem URL 320 * @param theCode The code 321 * @param theDisplayLanguage to filter out the designation by the display language. To return all designation, set this value to <code>null</code>. 322 */ 323 @Nullable 324 default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { 325 return null; 326 } 327 328 /** 329 * Look up a code using the system and code value 330 * 331 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 332 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 333 * @param theSystem The CodeSystem URL 334 * @param theCode The code 335 */ 336 @Nullable 337 default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { 338 return lookupCode(theValidationSupportContext, theSystem, theCode, null); 339 } 340 341 /** 342 * Returns <code>true</code> if the given valueset can be validated by the given 343 * validation support module 344 * 345 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 346 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 347 * @param theValueSetUrl The ValueSet canonical URL 348 */ 349 default boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { 350 return false; 351 } 352 353 /** 354 * Generate a snapshot from the given differential profile. 355 * 356 * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to 357 * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. 358 * @return Returns null if this module does not know how to handle this request 359 */ 360 @Nullable 361 default IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) { 362 return null; 363 } 364 365 /** 366 * Returns the FHIR Context associated with this module 367 */ 368 FhirContext getFhirContext(); 369 370 /** 371 * This method clears any temporary caches within the validation support. It is mainly intended for unit tests, 372 * but could be used in non-test scenarios as well. 373 */ 374 default void invalidateCaches() { 375 // nothing 376 } 377 378 /** 379 * Attempt to translate the given concept from one code system to another 380 */ 381 @Nullable 382 default TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) { 383 return null; 384 } 385 386 enum IssueSeverity { 387 /** 388 * The issue caused the action to fail, and no further checking could be performed. 389 */ 390 FATAL, 391 /** 392 * The issue is sufficiently important to cause the action to fail. 393 */ 394 ERROR, 395 /** 396 * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired. 397 */ 398 WARNING, 399 /** 400 * The issue has no relation to the degree of success of the action. 401 */ 402 INFORMATION 403 } 404 405 class ConceptDesignation { 406 407 private String myLanguage; 408 private String myUseSystem; 409 private String myUseCode; 410 private String myUseDisplay; 411 private String myValue; 412 413 public String getLanguage() { 414 return myLanguage; 415 } 416 417 public ConceptDesignation setLanguage(String theLanguage) { 418 myLanguage = theLanguage; 419 return this; 420 } 421 422 public String getUseSystem() { 423 return myUseSystem; 424 } 425 426 public ConceptDesignation setUseSystem(String theUseSystem) { 427 myUseSystem = theUseSystem; 428 return this; 429 } 430 431 public String getUseCode() { 432 return myUseCode; 433 } 434 435 public ConceptDesignation setUseCode(String theUseCode) { 436 myUseCode = theUseCode; 437 return this; 438 } 439 440 public String getUseDisplay() { 441 return myUseDisplay; 442 } 443 444 public ConceptDesignation setUseDisplay(String theUseDisplay) { 445 myUseDisplay = theUseDisplay; 446 return this; 447 } 448 449 public String getValue() { 450 return myValue; 451 } 452 453 public ConceptDesignation setValue(String theValue) { 454 myValue = theValue; 455 return this; 456 } 457 } 458 459 abstract class BaseConceptProperty { 460 private final String myPropertyName; 461 462 /** 463 * Constructor 464 */ 465 protected BaseConceptProperty(String thePropertyName) { 466 myPropertyName = thePropertyName; 467 } 468 469 public String getPropertyName() { 470 return myPropertyName; 471 } 472 } 473 474 class StringConceptProperty extends BaseConceptProperty { 475 private final String myValue; 476 477 /** 478 * Constructor 479 * 480 * @param theName The name 481 */ 482 public StringConceptProperty(String theName, String theValue) { 483 super(theName); 484 myValue = theValue; 485 } 486 487 public String getValue() { 488 return myValue; 489 } 490 } 491 492 class CodingConceptProperty extends BaseConceptProperty { 493 private final String myCode; 494 private final String myCodeSystem; 495 private final String myDisplay; 496 497 /** 498 * Constructor 499 * 500 * @param theName The name 501 */ 502 public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) { 503 super(theName); 504 myCodeSystem = theCodeSystem; 505 myCode = theCode; 506 myDisplay = theDisplay; 507 } 508 509 public String getCode() { 510 return myCode; 511 } 512 513 public String getCodeSystem() { 514 return myCodeSystem; 515 } 516 517 public String getDisplay() { 518 return myDisplay; 519 } 520 } 521 522 class CodeValidationResult { 523 private String myCode; 524 private String myMessage; 525 private IssueSeverity mySeverity; 526 private String myCodeSystemName; 527 private String myCodeSystemVersion; 528 private List<BaseConceptProperty> myProperties; 529 private String myDisplay; 530 531 public CodeValidationResult() { 532 super(); 533 } 534 535 public String getDisplay() { 536 return myDisplay; 537 } 538 539 public CodeValidationResult setDisplay(String theDisplay) { 540 myDisplay = theDisplay; 541 return this; 542 } 543 544 public String getCode() { 545 return myCode; 546 } 547 548 public CodeValidationResult setCode(String theCode) { 549 myCode = theCode; 550 return this; 551 } 552 553 String getCodeSystemName() { 554 return myCodeSystemName; 555 } 556 557 public CodeValidationResult setCodeSystemName(String theCodeSystemName) { 558 myCodeSystemName = theCodeSystemName; 559 return this; 560 } 561 562 public String getCodeSystemVersion() { 563 return myCodeSystemVersion; 564 } 565 566 public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) { 567 myCodeSystemVersion = theCodeSystemVersion; 568 return this; 569 } 570 571 public String getMessage() { 572 return myMessage; 573 } 574 575 public CodeValidationResult setMessage(String theMessage) { 576 myMessage = theMessage; 577 return this; 578 } 579 580 public List<BaseConceptProperty> getProperties() { 581 return myProperties; 582 } 583 584 public void setProperties(List<BaseConceptProperty> theProperties) { 585 myProperties = theProperties; 586 } 587 588 public IssueSeverity getSeverity() { 589 return mySeverity; 590 } 591 592 public CodeValidationResult setSeverity(IssueSeverity theSeverity) { 593 mySeverity = theSeverity; 594 return this; 595 } 596 597 public boolean isOk() { 598 return isNotBlank(myCode); 599 } 600 601 public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) { 602 LookupCodeResult retVal = new LookupCodeResult(); 603 retVal.setSearchedForSystem(theSearchedForSystem); 604 retVal.setSearchedForCode(theSearchedForCode); 605 if (isOk()) { 606 retVal.setFound(true); 607 retVal.setCodeDisplay(myDisplay); 608 retVal.setCodeSystemDisplayName(getCodeSystemName()); 609 retVal.setCodeSystemVersion(getCodeSystemVersion()); 610 } 611 return retVal; 612 } 613 614 /** 615 * Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string 616 */ 617 public String getSeverityCode() { 618 String retVal = null; 619 if (getSeverity() != null) { 620 retVal = getSeverity().name().toLowerCase(); 621 } 622 return retVal; 623 } 624 625 /** 626 * Sets an issue severity as a string code. Value must be the name of 627 * one of the enum values in {@link IssueSeverity}. Value is case-insensitive. 628 */ 629 public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) { 630 setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase())); 631 return this; 632 } 633 } 634 635 class ValueSetExpansionOutcome { 636 637 private final IBaseResource myValueSet; 638 private final String myError; 639 640 public ValueSetExpansionOutcome(String theError) { 641 myValueSet = null; 642 myError = theError; 643 } 644 645 public ValueSetExpansionOutcome(IBaseResource theValueSet) { 646 myValueSet = theValueSet; 647 myError = null; 648 } 649 650 public String getError() { 651 return myError; 652 } 653 654 public IBaseResource getValueSet() { 655 return myValueSet; 656 } 657 } 658 659 class LookupCodeResult { 660 661 private String myCodeDisplay; 662 private boolean myCodeIsAbstract; 663 private String myCodeSystemDisplayName; 664 private String myCodeSystemVersion; 665 private boolean myFound; 666 private String mySearchedForCode; 667 private String mySearchedForSystem; 668 private List<IValidationSupport.BaseConceptProperty> myProperties; 669 private List<ConceptDesignation> myDesignations; 670 671 /** 672 * Constructor 673 */ 674 public LookupCodeResult() { 675 super(); 676 } 677 678 public List<BaseConceptProperty> getProperties() { 679 if (myProperties == null) { 680 myProperties = new ArrayList<>(); 681 } 682 return myProperties; 683 } 684 685 public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) { 686 myProperties = theProperties; 687 } 688 689 @Nonnull 690 public List<ConceptDesignation> getDesignations() { 691 if (myDesignations == null) { 692 myDesignations = new ArrayList<>(); 693 } 694 return myDesignations; 695 } 696 697 public String getCodeDisplay() { 698 return myCodeDisplay; 699 } 700 701 public void setCodeDisplay(String theCodeDisplay) { 702 myCodeDisplay = theCodeDisplay; 703 } 704 705 public String getCodeSystemDisplayName() { 706 return myCodeSystemDisplayName; 707 } 708 709 public void setCodeSystemDisplayName(String theCodeSystemDisplayName) { 710 myCodeSystemDisplayName = theCodeSystemDisplayName; 711 } 712 713 public String getCodeSystemVersion() { 714 return myCodeSystemVersion; 715 } 716 717 public void setCodeSystemVersion(String theCodeSystemVersion) { 718 myCodeSystemVersion = theCodeSystemVersion; 719 } 720 721 public String getSearchedForCode() { 722 return mySearchedForCode; 723 } 724 725 public LookupCodeResult setSearchedForCode(String theSearchedForCode) { 726 mySearchedForCode = theSearchedForCode; 727 return this; 728 } 729 730 public String getSearchedForSystem() { 731 return mySearchedForSystem; 732 } 733 734 public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) { 735 mySearchedForSystem = theSearchedForSystem; 736 return this; 737 } 738 739 public boolean isCodeIsAbstract() { 740 return myCodeIsAbstract; 741 } 742 743 public void setCodeIsAbstract(boolean theCodeIsAbstract) { 744 myCodeIsAbstract = theCodeIsAbstract; 745 } 746 747 public boolean isFound() { 748 return myFound; 749 } 750 751 public LookupCodeResult setFound(boolean theFound) { 752 myFound = theFound; 753 return this; 754 } 755 756 public void throwNotFoundIfAppropriate() { 757 if (isFound() == false) { 758 throw new ResourceNotFoundException(Msg.code(1738) + "Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]"); 759 } 760 } 761 762 public IBaseParameters toParameters(FhirContext theContext, List<? extends IPrimitiveType<String>> theProperties) { 763 764 IBaseParameters retVal = ParametersUtil.newInstance(theContext); 765 if (isNotBlank(getCodeSystemDisplayName())) { 766 ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName()); 767 } 768 if (isNotBlank(getCodeSystemVersion())) { 769 ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion()); 770 } 771 ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay()); 772 ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract()); 773 774 if (myProperties != null) { 775 776 Set<String> properties = Collections.emptySet(); 777 if (theProperties != null) { 778 properties = theProperties 779 .stream() 780 .map(IPrimitiveType::getValueAsString) 781 .collect(Collectors.toSet()); 782 } 783 784 for (IValidationSupport.BaseConceptProperty next : myProperties) { 785 786 if (!properties.isEmpty()) { 787 if (!properties.contains(next.getPropertyName())) { 788 continue; 789 } 790 } 791 792 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property"); 793 ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName()); 794 795 if (next instanceof IValidationSupport.StringConceptProperty) { 796 IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next; 797 ParametersUtil.addPartString(theContext, property, "value", prop.getValue()); 798 } else if (next instanceof IValidationSupport.CodingConceptProperty) { 799 IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next; 800 ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay()); 801 } else { 802 throw new IllegalStateException(Msg.code(1739) + "Don't know how to handle " + next.getClass()); 803 } 804 } 805 } 806 807 if (myDesignations != null) { 808 for (ConceptDesignation next : myDesignations) { 809 810 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation"); 811 ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage()); 812 ParametersUtil.addPartCoding(theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay()); 813 ParametersUtil.addPartString(theContext, property, "value", next.getValue()); 814 } 815 } 816 817 return retVal; 818 } 819 820 public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) { 821 return new LookupCodeResult() 822 .setFound(false) 823 .setSearchedForSystem(theSearchedForSystem) 824 .setSearchedForCode(theSearchedForCode); 825 } 826 } 827 828 829 class TranslateCodeRequest { 830 private final String myTargetSystemUrl; 831 private final String myConceptMapUrl; 832 private final String myConceptMapVersion; 833 private final String mySourceValueSetUrl; 834 private final String myTargetValueSetUrl; 835 private final IIdType myResourceId; 836 private final boolean myReverse; 837 private List<IBaseCoding> myCodings; 838 839 public TranslateCodeRequest(List<IBaseCoding> theCodings, String theTargetSystemUrl) { 840 myCodings = theCodings; 841 myTargetSystemUrl = theTargetSystemUrl; 842 myConceptMapUrl = null; 843 myConceptMapVersion = null; 844 mySourceValueSetUrl = null; 845 myTargetValueSetUrl = null; 846 myResourceId = null; 847 myReverse = false; 848 } 849 850 public TranslateCodeRequest( 851 List<IBaseCoding> theCodings, 852 String theTargetSystemUrl, 853 String theConceptMapUrl, 854 String theConceptMapVersion, 855 String theSourceValueSetUrl, 856 String theTargetValueSetUrl, 857 IIdType theResourceId, 858 boolean theReverse) { 859 myCodings = theCodings; 860 myTargetSystemUrl = theTargetSystemUrl; 861 myConceptMapUrl = theConceptMapUrl; 862 myConceptMapVersion = theConceptMapVersion; 863 mySourceValueSetUrl = theSourceValueSetUrl; 864 myTargetValueSetUrl = theTargetValueSetUrl; 865 myResourceId = theResourceId; 866 myReverse = theReverse; 867 } 868 869 @Override 870 public boolean equals(Object theO) { 871 if (this == theO) { 872 return true; 873 } 874 875 if (theO == null || getClass() != theO.getClass()) { 876 return false; 877 } 878 879 TranslateCodeRequest that = (TranslateCodeRequest) theO; 880 881 return new EqualsBuilder() 882 .append(myCodings, that.myCodings) 883 .append(myTargetSystemUrl, that.myTargetSystemUrl) 884 .append(myConceptMapUrl, that.myConceptMapUrl) 885 .append(myConceptMapVersion, that.myConceptMapVersion) 886 .append(mySourceValueSetUrl, that.mySourceValueSetUrl) 887 .append(myTargetValueSetUrl, that.myTargetValueSetUrl) 888 .append(myResourceId, that.myResourceId) 889 .append(myReverse, that.myReverse) 890 .isEquals(); 891 } 892 893 @Override 894 public int hashCode() { 895 return new HashCodeBuilder(17, 37) 896 .append(myCodings) 897 .append(myTargetSystemUrl) 898 .append(myConceptMapUrl) 899 .append(myConceptMapVersion) 900 .append(mySourceValueSetUrl) 901 .append(myTargetValueSetUrl) 902 .append(myResourceId) 903 .append(myReverse) 904 .toHashCode(); 905 } 906 907 public List<IBaseCoding> getCodings() { 908 return myCodings; 909 } 910 911 public String getTargetSystemUrl() { 912 return myTargetSystemUrl; 913 } 914 915 public String getConceptMapUrl() { 916 return myConceptMapUrl; 917 } 918 919 public String getConceptMapVersion() { 920 return myConceptMapVersion; 921 } 922 923 public String getSourceValueSetUrl() { 924 return mySourceValueSetUrl; 925 } 926 927 public String getTargetValueSetUrl() { 928 return myTargetValueSetUrl; 929 } 930 931 public IIdType getResourceId() { 932 return myResourceId; 933 } 934 935 public boolean isReverse() { 936 return myReverse; 937 } 938 } 939 940 /** 941 * See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation. 942 * <p> 943 * If true, validation for codings will return a positive result if all codings are valid. 944 * If false, validation for codings will return a positive result if there is any coding that is valid. 945 * 946 * @return if the application has configured validation to use logical AND, as opposed to logical OR, which is the default 947 */ 948 default boolean isEnabledValidationForCodingsLogicalAnd() { 949 return false; 950 } 951}