
001package ca.uhn.fhir.context.support; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2022 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.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 * Validates that the given code exists and if possible returns a display 260 * name. This method is called to check codes which are found in "example" 261 * binding fields (e.g. <code>Observation.code</code>) in the default profile. 262 * 263 * @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 264 * 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. 265 * @param theOptions Provides options controlling the validation 266 * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" 267 * @param theCode The code, e.g. "<code>1234-5</code>" 268 * @param theDisplay The display name, if it should also be validated 269 * @return Returns a validation result object 270 */ 271 @Nullable 272 default CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { 273 return null; 274 } 275 276 /** 277 * Validates that the given code exists and if possible returns a display 278 * name. This method is called to check codes which are found in "example" 279 * binding fields (e.g. <code>Observation.code</code>) in the default profile. 280 * 281 * @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 282 * 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. 283 * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" 284 * @param theCode The code, e.g. "<code>1234-5</code>" 285 * @param theDisplay The display name, if it should also be validated 286 * @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource. 287 * @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request 288 */ 289 @Nullable 290 default CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { 291 return null; 292 } 293 294 /** 295 * Look up a code using the system and code value 296 * 297 * @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 298 * 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. 299 * @param theSystem The CodeSystem URL 300 * @param theCode The code 301 * @param theDisplayLanguage to filter out the designation by the display language. To return all designation, set this value to <code>null</code>. 302 */ 303 @Nullable 304 default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { 305 return null; 306 } 307 308 /** 309 * Look up a code using the system and code value 310 * 311 * @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 312 * 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. 313 * @param theSystem The CodeSystem URL 314 * @param theCode The code 315 */ 316 @Nullable 317 default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { 318 return lookupCode(theValidationSupportContext, theSystem, theCode, null); 319 } 320 321 /** 322 * Returns <code>true</code> if the given valueset can be validated by the given 323 * validation support module 324 * 325 * @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 326 * 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. 327 * @param theValueSetUrl The ValueSet canonical URL 328 */ 329 default boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { 330 return false; 331 } 332 333 /** 334 * Generate a snapshot from the given differential profile. 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 * @return Returns null if this module does not know how to handle this request 339 */ 340 @Nullable 341 default IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) { 342 return null; 343 } 344 345 /** 346 * Returns the FHIR Context associated with this module 347 */ 348 FhirContext getFhirContext(); 349 350 /** 351 * This method clears any temporary caches within the validation support. It is mainly intended for unit tests, 352 * but could be used in non-test scenarios as well. 353 */ 354 default void invalidateCaches() { 355 // nothing 356 } 357 358 /** 359 * Attempt to translate the given concept from one code system to another 360 */ 361 @Nullable 362 default TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) { 363 return null; 364 } 365 366 enum IssueSeverity { 367 /** 368 * The issue caused the action to fail, and no further checking could be performed. 369 */ 370 FATAL, 371 /** 372 * The issue is sufficiently important to cause the action to fail. 373 */ 374 ERROR, 375 /** 376 * 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. 377 */ 378 WARNING, 379 /** 380 * The issue has no relation to the degree of success of the action. 381 */ 382 INFORMATION 383 } 384 385 class ConceptDesignation { 386 387 private String myLanguage; 388 private String myUseSystem; 389 private String myUseCode; 390 private String myUseDisplay; 391 private String myValue; 392 393 public String getLanguage() { 394 return myLanguage; 395 } 396 397 public ConceptDesignation setLanguage(String theLanguage) { 398 myLanguage = theLanguage; 399 return this; 400 } 401 402 public String getUseSystem() { 403 return myUseSystem; 404 } 405 406 public ConceptDesignation setUseSystem(String theUseSystem) { 407 myUseSystem = theUseSystem; 408 return this; 409 } 410 411 public String getUseCode() { 412 return myUseCode; 413 } 414 415 public ConceptDesignation setUseCode(String theUseCode) { 416 myUseCode = theUseCode; 417 return this; 418 } 419 420 public String getUseDisplay() { 421 return myUseDisplay; 422 } 423 424 public ConceptDesignation setUseDisplay(String theUseDisplay) { 425 myUseDisplay = theUseDisplay; 426 return this; 427 } 428 429 public String getValue() { 430 return myValue; 431 } 432 433 public ConceptDesignation setValue(String theValue) { 434 myValue = theValue; 435 return this; 436 } 437 } 438 439 abstract class BaseConceptProperty { 440 private final String myPropertyName; 441 442 /** 443 * Constructor 444 */ 445 protected BaseConceptProperty(String thePropertyName) { 446 myPropertyName = thePropertyName; 447 } 448 449 public String getPropertyName() { 450 return myPropertyName; 451 } 452 } 453 454 class StringConceptProperty extends BaseConceptProperty { 455 private final String myValue; 456 457 /** 458 * Constructor 459 * 460 * @param theName The name 461 */ 462 public StringConceptProperty(String theName, String theValue) { 463 super(theName); 464 myValue = theValue; 465 } 466 467 public String getValue() { 468 return myValue; 469 } 470 } 471 472 class CodingConceptProperty extends BaseConceptProperty { 473 private final String myCode; 474 private final String myCodeSystem; 475 private final String myDisplay; 476 477 /** 478 * Constructor 479 * 480 * @param theName The name 481 */ 482 public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) { 483 super(theName); 484 myCodeSystem = theCodeSystem; 485 myCode = theCode; 486 myDisplay = theDisplay; 487 } 488 489 public String getCode() { 490 return myCode; 491 } 492 493 public String getCodeSystem() { 494 return myCodeSystem; 495 } 496 497 public String getDisplay() { 498 return myDisplay; 499 } 500 } 501 502 class CodeValidationResult { 503 private String myCode; 504 private String myMessage; 505 private IssueSeverity mySeverity; 506 private String myCodeSystemName; 507 private String myCodeSystemVersion; 508 private List<BaseConceptProperty> myProperties; 509 private String myDisplay; 510 511 public CodeValidationResult() { 512 super(); 513 } 514 515 public String getDisplay() { 516 return myDisplay; 517 } 518 519 public CodeValidationResult setDisplay(String theDisplay) { 520 myDisplay = theDisplay; 521 return this; 522 } 523 524 public String getCode() { 525 return myCode; 526 } 527 528 public CodeValidationResult setCode(String theCode) { 529 myCode = theCode; 530 return this; 531 } 532 533 String getCodeSystemName() { 534 return myCodeSystemName; 535 } 536 537 public CodeValidationResult setCodeSystemName(String theCodeSystemName) { 538 myCodeSystemName = theCodeSystemName; 539 return this; 540 } 541 542 public String getCodeSystemVersion() { 543 return myCodeSystemVersion; 544 } 545 546 public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) { 547 myCodeSystemVersion = theCodeSystemVersion; 548 return this; 549 } 550 551 public String getMessage() { 552 return myMessage; 553 } 554 555 public CodeValidationResult setMessage(String theMessage) { 556 myMessage = theMessage; 557 return this; 558 } 559 560 public List<BaseConceptProperty> getProperties() { 561 return myProperties; 562 } 563 564 public void setProperties(List<BaseConceptProperty> theProperties) { 565 myProperties = theProperties; 566 } 567 568 public IssueSeverity getSeverity() { 569 return mySeverity; 570 } 571 572 public CodeValidationResult setSeverity(IssueSeverity theSeverity) { 573 mySeverity = theSeverity; 574 return this; 575 } 576 577 public boolean isOk() { 578 return isNotBlank(myCode); 579 } 580 581 public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) { 582 LookupCodeResult retVal = new LookupCodeResult(); 583 retVal.setSearchedForSystem(theSearchedForSystem); 584 retVal.setSearchedForCode(theSearchedForCode); 585 if (isOk()) { 586 retVal.setFound(true); 587 retVal.setCodeDisplay(myDisplay); 588 retVal.setCodeSystemDisplayName(getCodeSystemName()); 589 retVal.setCodeSystemVersion(getCodeSystemVersion()); 590 } 591 return retVal; 592 } 593 594 /** 595 * Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string 596 */ 597 public String getSeverityCode() { 598 String retVal = null; 599 if (getSeverity() != null) { 600 retVal = getSeverity().name().toLowerCase(); 601 } 602 return retVal; 603 } 604 605 /** 606 * Sets an issue severity as a string code. Value must be the name of 607 * one of the enum values in {@link IssueSeverity}. Value is case-insensitive. 608 */ 609 public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) { 610 setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase())); 611 return this; 612 } 613 } 614 615 class ValueSetExpansionOutcome { 616 617 private final IBaseResource myValueSet; 618 private final String myError; 619 620 public ValueSetExpansionOutcome(String theError) { 621 myValueSet = null; 622 myError = theError; 623 } 624 625 public ValueSetExpansionOutcome(IBaseResource theValueSet) { 626 myValueSet = theValueSet; 627 myError = null; 628 } 629 630 public String getError() { 631 return myError; 632 } 633 634 public IBaseResource getValueSet() { 635 return myValueSet; 636 } 637 } 638 639 class LookupCodeResult { 640 641 private String myCodeDisplay; 642 private boolean myCodeIsAbstract; 643 private String myCodeSystemDisplayName; 644 private String myCodeSystemVersion; 645 private boolean myFound; 646 private String mySearchedForCode; 647 private String mySearchedForSystem; 648 private List<IValidationSupport.BaseConceptProperty> myProperties; 649 private List<ConceptDesignation> myDesignations; 650 651 /** 652 * Constructor 653 */ 654 public LookupCodeResult() { 655 super(); 656 } 657 658 public List<BaseConceptProperty> getProperties() { 659 if (myProperties == null) { 660 myProperties = new ArrayList<>(); 661 } 662 return myProperties; 663 } 664 665 public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) { 666 myProperties = theProperties; 667 } 668 669 @Nonnull 670 public List<ConceptDesignation> getDesignations() { 671 if (myDesignations == null) { 672 myDesignations = new ArrayList<>(); 673 } 674 return myDesignations; 675 } 676 677 public String getCodeDisplay() { 678 return myCodeDisplay; 679 } 680 681 public void setCodeDisplay(String theCodeDisplay) { 682 myCodeDisplay = theCodeDisplay; 683 } 684 685 public String getCodeSystemDisplayName() { 686 return myCodeSystemDisplayName; 687 } 688 689 public void setCodeSystemDisplayName(String theCodeSystemDisplayName) { 690 myCodeSystemDisplayName = theCodeSystemDisplayName; 691 } 692 693 public String getCodeSystemVersion() { 694 return myCodeSystemVersion; 695 } 696 697 public void setCodeSystemVersion(String theCodeSystemVersion) { 698 myCodeSystemVersion = theCodeSystemVersion; 699 } 700 701 public String getSearchedForCode() { 702 return mySearchedForCode; 703 } 704 705 public LookupCodeResult setSearchedForCode(String theSearchedForCode) { 706 mySearchedForCode = theSearchedForCode; 707 return this; 708 } 709 710 public String getSearchedForSystem() { 711 return mySearchedForSystem; 712 } 713 714 public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) { 715 mySearchedForSystem = theSearchedForSystem; 716 return this; 717 } 718 719 public boolean isCodeIsAbstract() { 720 return myCodeIsAbstract; 721 } 722 723 public void setCodeIsAbstract(boolean theCodeIsAbstract) { 724 myCodeIsAbstract = theCodeIsAbstract; 725 } 726 727 public boolean isFound() { 728 return myFound; 729 } 730 731 public LookupCodeResult setFound(boolean theFound) { 732 myFound = theFound; 733 return this; 734 } 735 736 public void throwNotFoundIfAppropriate() { 737 if (isFound() == false) { 738 throw new ResourceNotFoundException(Msg.code(1738) + "Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]"); 739 } 740 } 741 742 public IBaseParameters toParameters(FhirContext theContext, List<? extends IPrimitiveType<String>> theProperties) { 743 744 IBaseParameters retVal = ParametersUtil.newInstance(theContext); 745 if (isNotBlank(getCodeSystemDisplayName())) { 746 ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName()); 747 } 748 if (isNotBlank(getCodeSystemVersion())) { 749 ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion()); 750 } 751 ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay()); 752 ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract()); 753 754 if (myProperties != null) { 755 756 Set<String> properties = Collections.emptySet(); 757 if (theProperties != null) { 758 properties = theProperties 759 .stream() 760 .map(IPrimitiveType::getValueAsString) 761 .collect(Collectors.toSet()); 762 } 763 764 for (IValidationSupport.BaseConceptProperty next : myProperties) { 765 766 if (!properties.isEmpty()) { 767 if (!properties.contains(next.getPropertyName())) { 768 continue; 769 } 770 } 771 772 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property"); 773 ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName()); 774 775 if (next instanceof IValidationSupport.StringConceptProperty) { 776 IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next; 777 ParametersUtil.addPartString(theContext, property, "value", prop.getValue()); 778 } else if (next instanceof IValidationSupport.CodingConceptProperty) { 779 IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next; 780 ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay()); 781 } else { 782 throw new IllegalStateException(Msg.code(1739) + "Don't know how to handle " + next.getClass()); 783 } 784 } 785 } 786 787 if (myDesignations != null) { 788 for (ConceptDesignation next : myDesignations) { 789 790 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation"); 791 ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage()); 792 ParametersUtil.addPartCoding(theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay()); 793 ParametersUtil.addPartString(theContext, property, "value", next.getValue()); 794 } 795 } 796 797 return retVal; 798 } 799 800 public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) { 801 return new LookupCodeResult() 802 .setFound(false) 803 .setSearchedForSystem(theSearchedForSystem) 804 .setSearchedForCode(theSearchedForCode); 805 } 806 } 807 808 809 class TranslateCodeRequest { 810 private List<IBaseCoding> myCodings; 811 private final String myTargetSystemUrl; 812 private final String myConceptMapUrl; 813 private final String myConceptMapVersion; 814 private final String mySourceValueSetUrl; 815 private final String myTargetValueSetUrl; 816 private final Long myResourcePid; 817 private final boolean myReverse; 818 819 public TranslateCodeRequest(List<IBaseCoding> theCodings, String theTargetSystemUrl) { 820 myCodings = theCodings; 821 myTargetSystemUrl = theTargetSystemUrl; 822 myConceptMapUrl = null; 823 myConceptMapVersion = null; 824 mySourceValueSetUrl = null; 825 myTargetValueSetUrl = null; 826 myResourcePid = null; 827 myReverse = false; 828 } 829 830 public TranslateCodeRequest( 831 List<IBaseCoding> theCodings, 832 String theTargetSystemUrl, 833 String theConceptMapUrl, 834 String theConceptMapVersion, 835 String theSourceValueSetUrl, 836 String theTargetValueSetUrl, 837 Long theResourcePid, 838 boolean theReverse) { 839 myCodings = theCodings; 840 myTargetSystemUrl = theTargetSystemUrl; 841 myConceptMapUrl = theConceptMapUrl; 842 myConceptMapVersion = theConceptMapVersion; 843 mySourceValueSetUrl = theSourceValueSetUrl; 844 myTargetValueSetUrl = theTargetValueSetUrl; 845 myResourcePid = theResourcePid; 846 myReverse = theReverse; 847 } 848 849 @Override 850 public boolean equals(Object theO) { 851 if (this == theO) { 852 return true; 853 } 854 855 if (theO == null || getClass() != theO.getClass()) { 856 return false; 857 } 858 859 TranslateCodeRequest that = (TranslateCodeRequest) theO; 860 861 return new EqualsBuilder() 862 .append(myCodings, that.myCodings) 863 .append(myTargetSystemUrl, that.myTargetSystemUrl) 864 .append(myConceptMapUrl, that.myConceptMapUrl) 865 .append(myConceptMapVersion, that.myConceptMapVersion) 866 .append(mySourceValueSetUrl, that.mySourceValueSetUrl) 867 .append(myTargetValueSetUrl, that.myTargetValueSetUrl) 868 .append(myResourcePid, that.myResourcePid) 869 .append(myReverse, that.myReverse) 870 .isEquals(); 871 } 872 873 @Override 874 public int hashCode() { 875 return new HashCodeBuilder(17, 37) 876 .append(myCodings) 877 .append(myTargetSystemUrl) 878 .append(myConceptMapUrl) 879 .append(myConceptMapVersion) 880 .append(mySourceValueSetUrl) 881 .append(myTargetValueSetUrl) 882 .append(myResourcePid) 883 .append(myReverse) 884 .toHashCode(); 885 } 886 887 public List<IBaseCoding> getCodings() { 888 return myCodings; 889 } 890 891 public String getTargetSystemUrl() { 892 return myTargetSystemUrl; 893 } 894 895 public String getConceptMapUrl() { 896 return myConceptMapUrl; 897 } 898 899 public String getConceptMapVersion() { 900 return myConceptMapVersion; 901 } 902 903 public String getSourceValueSetUrl() { 904 return mySourceValueSetUrl; 905 } 906 907 public String getTargetValueSetUrl() { 908 return myTargetValueSetUrl; 909 } 910 911 public Long getResourcePid() { 912 return myResourcePid; 913 } 914 915 public boolean isReverse() { 916 return myReverse; 917 } 918 } 919 920 921}