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