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