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