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