
001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2025 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 jakarta.annotation.Nonnull; 028import jakarta.annotation.Nullable; 029import org.apache.commons.lang3.Validate; 030import org.apache.commons.lang3.builder.EqualsBuilder; 031import org.apache.commons.lang3.builder.HashCodeBuilder; 032import org.apache.commons.lang3.builder.ToStringBuilder; 033import org.hl7.fhir.instance.model.api.IBase; 034import org.hl7.fhir.instance.model.api.IBaseCoding; 035import org.hl7.fhir.instance.model.api.IBaseParameters; 036import org.hl7.fhir.instance.model.api.IBaseResource; 037import org.hl7.fhir.instance.model.api.IIdType; 038import org.hl7.fhir.instance.model.api.IPrimitiveType; 039 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collections; 043import java.util.List; 044import java.util.Objects; 045import java.util.Set; 046import java.util.function.Supplier; 047import java.util.stream.Collectors; 048 049import static org.apache.commons.lang3.StringUtils.defaultString; 050import static org.apache.commons.lang3.StringUtils.isNotBlank; 051 052/** 053 * This interface is a version-independent representation of the 054 * various functions that can be provided by validation and terminology 055 * services. 056 * <p> 057 * This interface is invoked directly by internal parts of the HAPI FHIR API, including the 058 * Validator and the FHIRPath evaluator. It is used to supply artifacts required for validation 059 * (e.g. StructureDefinition resources, ValueSet resources, etc.) and also to provide 060 * terminology functions such as code validation, ValueSet expansion, etc. 061 * </p> 062 * <p> 063 * Implementations are not required to implement all of the functions 064 * in this interface; in fact it is expected that most won't. Any 065 * methods which are not implemented may simply return <code>null</code> 066 * and calling code is expected to be able to handle this. Generally, a 067 * series of implementations of this interface will be joined together using 068 * the 069 * <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.html">ValidationSupportChain</a> 070 * class. 071 * </p> 072 * <p> 073 * See <a href="https://hapifhir.io/hapi-fhir/docs/validation/validation_support_modules.html">Validation Support Modules</a> 074 * for information on how to assemble and configure implementations of this interface. See also 075 * the <code>org.hl7.fhir.common.hapi.validation.support</code> 076 * <a href="./package-summary.html">package summary</a> 077 * in the <code>hapi-fhir-validation</code> module for many implementations of this interface. 078 * </p> 079 * 080 * @since 5.0.0 081 */ 082public interface IValidationSupport { 083 String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/"; 084 String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/"; 085 086 /** 087 * Expands the given portion of a ValueSet 088 * 089 * @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 090 * 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. 091 * @param theExpansionOptions If provided (can be <code>null</code>), contains options controlling the expansion 092 * @param theValueSetToExpand The valueset that should be expanded 093 * @return The expansion, or null 094 */ 095 @Nullable 096 default ValueSetExpansionOutcome expandValueSet( 097 ValidationSupportContext theValidationSupportContext, 098 @Nullable ValueSetExpansionOptions theExpansionOptions, 099 @Nonnull IBaseResource theValueSetToExpand) { 100 return null; 101 } 102 103 /** 104 * Expands the given portion of a ValueSet by canonical URL. 105 * 106 * @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 107 * 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. 108 * @param theExpansionOptions If provided (can be <code>null</code>), contains options controlling the expansion 109 * @param theValueSetUrlToExpand The valueset that should be expanded 110 * @return The expansion, or null 111 * @throws ResourceNotFoundException If no ValueSet can be found with the given URL 112 * @since 6.0.0 113 */ 114 @Nullable 115 default ValueSetExpansionOutcome expandValueSet( 116 ValidationSupportContext theValidationSupportContext, 117 @Nullable ValueSetExpansionOptions theExpansionOptions, 118 @Nonnull String theValueSetUrlToExpand) 119 throws ResourceNotFoundException { 120 Validate.notBlank(theValueSetUrlToExpand, "theValueSetUrlToExpand must not be null or blank"); 121 IBaseResource valueSet = 122 theValidationSupportContext.getRootValidationSupport().fetchValueSet(theValueSetUrlToExpand); 123 if (valueSet == null) { 124 throw new ResourceNotFoundException( 125 Msg.code(2024) + "Unknown ValueSet: " + UrlUtil.escapeUrlParam(theValueSetUrlToExpand)); 126 } 127 return expandValueSet(theValidationSupportContext, theExpansionOptions, valueSet); 128 } 129 130 /** 131 * Load and return all conformance resources associated with this 132 * validation support module. This method may return null if it doesn't 133 * make sense for a given module. 134 */ 135 @Nullable 136 default List<IBaseResource> fetchAllConformanceResources() { 137 return null; 138 } 139 140 /** 141 * Load and return all possible search parameters 142 * 143 * @since 6.6.0 144 */ 145 @Nullable 146 default <T extends IBaseResource> List<T> fetchAllSearchParameters() { 147 return null; 148 } 149 150 /** 151 * Load and return all possible structure definitions 152 */ 153 @Nullable 154 default <T extends IBaseResource> List<T> fetchAllStructureDefinitions() { 155 return null; 156 } 157 158 /** 159 * Load and return all possible structure definitions aside from resource definitions themselves 160 */ 161 @Nullable 162 default <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() { 163 List<T> retVal = fetchAllStructureDefinitions(); 164 if (retVal != null) { 165 List<T> newList = new ArrayList<>(retVal.size()); 166 for (T next : retVal) { 167 String url = defaultString(getFhirContext().newTerser().getSinglePrimitiveValueOrNull(next, "url")); 168 if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) { 169 String lastPart = url.substring(URL_PREFIX_STRUCTURE_DEFINITION.length()); 170 if (getFhirContext().getResourceTypes().contains(lastPart)) { 171 continue; 172 } 173 } 174 175 newList.add(next); 176 } 177 178 retVal = newList; 179 } 180 181 return retVal; 182 } 183 184 /** 185 * Fetch a code system by ID 186 * 187 * @param theSystem The code system 188 * @return The valueset (must not be null, but can be an empty ValueSet) 189 */ 190 @Nullable 191 default IBaseResource fetchCodeSystem(String theSystem) { 192 return null; 193 } 194 195 /** 196 * Loads a resource needed by the validation (a StructureDefinition, or a 197 * ValueSet) 198 * 199 * <p> 200 * Note: Since 5.3.0, {@literal theClass} can be {@literal null} 201 * </p> 202 * 203 * @param theClass The type of the resource to load, or <code>null</code> to return any resource with the given canonical URI 204 * @param theUri The resource URI 205 * @return Returns the resource, or <code>null</code> if no resource with the 206 * given URI can be found 207 */ 208 @SuppressWarnings("unchecked") 209 @Nullable 210 default <T extends IBaseResource> T fetchResource(@Nullable Class<T> theClass, String theUri) { 211 Validate.notBlank(theUri, "theUri must not be null or blank"); 212 213 if (theClass == null) { 214 Supplier<IBaseResource>[] sources = new Supplier[] { 215 () -> fetchStructureDefinition(theUri), () -> fetchValueSet(theUri), () -> fetchCodeSystem(theUri) 216 }; 217 return (T) Arrays.stream(sources) 218 .map(Supplier::get) 219 .filter(Objects::nonNull) 220 .findFirst() 221 .orElse(null); 222 } 223 224 switch (getFhirContext().getResourceType(theClass)) { 225 case "StructureDefinition": 226 return theClass.cast(fetchStructureDefinition(theUri)); 227 case "ValueSet": 228 return theClass.cast(fetchValueSet(theUri)); 229 case "CodeSystem": 230 return theClass.cast(fetchCodeSystem(theUri)); 231 } 232 233 if (theUri.startsWith(URL_PREFIX_VALUE_SET)) { 234 return theClass.cast(fetchValueSet(theUri)); 235 } 236 237 return null; 238 } 239 240 @Nullable 241 default IBaseResource fetchStructureDefinition(String theUrl) { 242 return null; 243 } 244 245 /** 246 * Returns <code>true</code> if codes in the given code system can be expanded 247 * or validated 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 theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code> 252 * @return Returns <code>true</code> if codes in the given code system can be 253 * validated 254 */ 255 default boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { 256 return false; 257 } 258 259 /** 260 * Returns <code>true</code> if a Remote Terminology Service is currently configured 261 * 262 * @return Returns <code>true</code> if a Remote Terminology Service is currently configured 263 */ 264 default boolean isRemoteTerminologyServiceConfigured() { 265 return false; 266 } 267 268 /** 269 * Fetch the given ValueSet by URL, or returns null if one can't be found for the given URL 270 */ 271 @Nullable 272 default IBaseResource fetchValueSet(String theValueSetUrl) { 273 return null; 274 } 275 276 /** 277 * Fetch the given binary data by key. 278 * 279 * @param binaryKey 280 * @return 281 */ 282 default byte[] fetchBinary(String binaryKey) { 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 theOptions Provides options controlling the validation 294 * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" 295 * @param theCode The code, e.g. "<code>1234-5</code>" 296 * @param theDisplay The display name, if it should also be validated 297 * @return Returns a validation result object 298 */ 299 @Nullable 300 default CodeValidationResult validateCode( 301 ValidationSupportContext theValidationSupportContext, 302 ConceptValidationOptions theOptions, 303 String theCodeSystem, 304 String theCode, 305 String theDisplay, 306 String theValueSetUrl) { 307 return null; 308 } 309 310 /** 311 * Validates that the given code exists and if possible returns a display 312 * name. This method is called to check codes which are found in "example" 313 * binding fields (e.g. <code>Observation.code</code>) in the default profile. 314 * 315 * @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 316 * 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. 317 * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>" 318 * @param theCode The code, e.g. "<code>1234-5</code>" 319 * @param theDisplay The display name, if it should also be validated 320 * @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource. 321 * @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request 322 */ 323 @Nullable 324 default CodeValidationResult validateCodeInValueSet( 325 ValidationSupportContext theValidationSupportContext, 326 ConceptValidationOptions theOptions, 327 String theCodeSystem, 328 String theCode, 329 String theDisplay, 330 @Nonnull IBaseResource theValueSet) { 331 return null; 332 } 333 334 /** 335 * Look up a code using the system and code value. 336 * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead. 337 * 338 * @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 339 * 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. 340 * @param theSystem The CodeSystem URL 341 * @param theCode The code 342 * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to <code>null</code>. 343 */ 344 @Deprecated 345 @Nullable 346 default LookupCodeResult lookupCode( 347 ValidationSupportContext theValidationSupportContext, 348 String theSystem, 349 String theCode, 350 String theDisplayLanguage) { 351 return null; 352 } 353 354 /** 355 * Look up a code using the system and code value 356 * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead. 357 * 358 * @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 359 * 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. 360 * @param theSystem The CodeSystem URL 361 * @param theCode The code 362 */ 363 @Deprecated 364 @Nullable 365 default LookupCodeResult lookupCode( 366 ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { 367 return lookupCode(theValidationSupportContext, theSystem, theCode, null); 368 } 369 370 /** 371 * Look up a code using the system, code and other parameters captured in {@link LookupCodeRequest}. 372 * @since 7.0.0 373 * 374 * @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 375 * 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. 376 * @param theLookupCodeRequest The parameters used to perform the lookup, including system and code. 377 */ 378 @Nullable 379 default LookupCodeResult lookupCode( 380 ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { 381 // TODO: can change to return null once the deprecated methods are removed 382 return lookupCode( 383 theValidationSupportContext, 384 theLookupCodeRequest.getSystem(), 385 theLookupCodeRequest.getCode(), 386 theLookupCodeRequest.getDisplayLanguage()); 387 } 388 389 /** 390 * Returns <code>true</code> if the given ValueSet can be validated by the given 391 * validation support module 392 * 393 * @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 394 * 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. 395 * @param theValueSetUrl The ValueSet canonical URL 396 */ 397 default boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { 398 return false; 399 } 400 401 /** 402 * Generate a snapshot from the given differential profile. 403 * 404 * @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 405 * 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. 406 * @return Returns null if this module does not know how to handle this request 407 */ 408 @Nullable 409 default IBaseResource generateSnapshot( 410 ValidationSupportContext theValidationSupportContext, 411 IBaseResource theInput, 412 String theUrl, 413 String theWebUrl, 414 String theProfileName) { 415 return null; 416 } 417 418 /** 419 * Returns the FHIR Context associated with this module 420 */ 421 FhirContext getFhirContext(); 422 423 /** 424 * This method clears any temporary caches within the validation support. It is mainly intended for unit tests, 425 * but could be used in non-test scenarios as well. 426 */ 427 default void invalidateCaches() { 428 // nothing 429 } 430 431 /** 432 * Attempt to translate the given concept from one code system to another 433 */ 434 @Nullable 435 default TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) { 436 return null; 437 } 438 439 /** 440 * This field is used by the Terminology Troubleshooting Log to log which validation support module was used for the operation being logged. 441 */ 442 default String getName() { 443 return "Unknown " + getFhirContext().getVersion().getVersion() + " Validation Support"; 444 } 445 446 /** 447 * Defines codes in system <a href="http://hl7.org/fhir/issue-severity">http://hl7.org/fhir/issue-severity</a>. 448 */ 449 /* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */ 450 enum IssueSeverity { 451 /** 452 * The issue caused the action to fail, and no further checking could be performed. 453 */ 454 FATAL("fatal"), 455 /** 456 * The issue is sufficiently important to cause the action to fail. 457 */ 458 ERROR("error"), 459 /** 460 * 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. 461 */ 462 WARNING("warning"), 463 /** 464 * The issue has no relation to the degree of success of the action. 465 */ 466 INFORMATION("information"), 467 /** 468 * The operation was successful. 469 */ 470 SUCCESS("success"); 471 // the spec for OperationOutcome mentions that a code from http://hl7.org/fhir/issue-severity is required 472 473 private final String myCode; 474 475 IssueSeverity(String theCode) { 476 myCode = theCode; 477 } 478 /** 479 * Provide mapping to a code in system <a href="http://hl7.org/fhir/issue-severity">http://hl7.org/fhir/issue-severity</a>. 480 * @return the code 481 */ 482 public String getCode() { 483 return myCode; 484 } 485 /** 486 * Creates a {@link IssueSeverity} object from the given code. 487 * @return the {@link IssueSeverity} 488 */ 489 public static IssueSeverity fromCode(String theCode) { 490 switch (theCode) { 491 case "fatal": 492 return FATAL; 493 case "error": 494 return ERROR; 495 case "warning": 496 return WARNING; 497 case "information": 498 return INFORMATION; 499 case "success": 500 return SUCCESS; 501 default: 502 return null; 503 } 504 } 505 } 506 507 /** 508 * Defines codes in system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>. 509 * The binding is enforced as a part of validation logic in the FHIR Core Validation library where an exception is thrown. 510 * Only a sub-set of these codes are defined as constants because they relate to validation, 511 * If there are additional ones that come up, for Remote Terminology they are currently supported via 512 * {@link IValidationSupport.CodeValidationIssue#CodeValidationIssue(String, IssueSeverity, String)} 513 * while for internal validators, more constants can be added to make things easier and consistent. 514 * This maps to resource OperationOutcome.issue.code. 515 */ 516 /* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */ 517 class CodeValidationIssueCode { 518 public static final CodeValidationIssueCode NOT_FOUND = new CodeValidationIssueCode("not-found"); 519 public static final CodeValidationIssueCode CODE_INVALID = new CodeValidationIssueCode("code-invalid"); 520 public static final CodeValidationIssueCode INVALID = new CodeValidationIssueCode("invalid"); 521 522 private final String myCode; 523 524 // this is intentionally not exposed 525 CodeValidationIssueCode(String theCode) { 526 myCode = theCode; 527 } 528 529 /** 530 * Retrieve the corresponding code from system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>. 531 * @return the code 532 */ 533 public String getCode() { 534 return myCode; 535 } 536 } 537 538 /** 539 * Holds information about the details of a {@link CodeValidationIssue}. 540 * This maps to resource OperationOutcome.issue.details. 541 */ 542 /* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */ 543 class CodeValidationIssueDetails { 544 private final String myText; 545 private List<CodeValidationIssueCoding> myCodings; 546 547 public CodeValidationIssueDetails(String theText) { 548 myText = theText; 549 } 550 551 // intentionally not exposed 552 void addCoding(CodeValidationIssueCoding theCoding) { 553 getCodings().add(theCoding); 554 } 555 556 public CodeValidationIssueDetails addCoding(String theSystem, String theCode) { 557 if (myCodings == null) { 558 myCodings = new ArrayList<>(); 559 } 560 myCodings.add(new CodeValidationIssueCoding(theSystem, theCode)); 561 return this; 562 } 563 564 public String getText() { 565 return myText; 566 } 567 568 public List<CodeValidationIssueCoding> getCodings() { 569 if (myCodings == null) { 570 myCodings = new ArrayList<>(); 571 } 572 return myCodings; 573 } 574 } 575 576 /** 577 * Defines codes that can be part of the details of an issue. 578 * There are some constants available (pre-defined) for codes for system <a href="http://hl7.org/fhir/tools/CodeSystem/tx-issue-type">http://hl7.org/fhir/tools/CodeSystem/tx-issue-type</a>. 579 * This maps to resource OperationOutcome.issue.details.coding[0].code. 580 */ 581 class CodeValidationIssueCoding { 582 public static String TX_ISSUE_SYSTEM = "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type"; 583 public static CodeValidationIssueCoding VS_INVALID = 584 new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "vs-invalid"); 585 public static final CodeValidationIssueCoding NOT_FOUND = 586 new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "not-found"); 587 public static final CodeValidationIssueCoding NOT_IN_VS = 588 new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "not-in-vs"); 589 public static final CodeValidationIssueCoding INVALID_CODE = 590 new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "invalid-code"); 591 public static final CodeValidationIssueCoding INVALID_DISPLAY = 592 new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "vs-display"); 593 private final String mySystem, myCode; 594 595 // this is intentionally not exposed 596 CodeValidationIssueCoding(String theSystem, String theCode) { 597 mySystem = theSystem; 598 myCode = theCode; 599 } 600 601 /** 602 * Retrieve the corresponding code for the details of a validation issue. 603 * @return the code 604 */ 605 public String getCode() { 606 return myCode; 607 } 608 609 /** 610 * Retrieve the system for the details of a validation issue. 611 * @return the system 612 */ 613 public String getSystem() { 614 return mySystem; 615 } 616 } 617 618 /** 619 * This is a hapi-fhir internal version agnostic object holding information about a validation issue. 620 * An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult instead. 621 */ 622 class CodeValidationIssue { 623 private final String myDiagnostics; 624 private final IssueSeverity mySeverity; 625 private final CodeValidationIssueCode myCode; 626 private CodeValidationIssueDetails myDetails; 627 628 public CodeValidationIssue( 629 String theDiagnostics, IssueSeverity theSeverity, CodeValidationIssueCode theTypeCode) { 630 this(theDiagnostics, theSeverity, theTypeCode, null); 631 } 632 633 public CodeValidationIssue(String theDiagnostics, IssueSeverity theSeverity, String theTypeCode) { 634 this(theDiagnostics, theSeverity, new CodeValidationIssueCode(theTypeCode), null); 635 } 636 637 public CodeValidationIssue( 638 String theDiagnostics, 639 IssueSeverity theSeverity, 640 CodeValidationIssueCode theType, 641 CodeValidationIssueCoding theDetailsCoding) { 642 myDiagnostics = theDiagnostics; 643 mySeverity = theSeverity; 644 myCode = theType; 645 // reuse the diagnostics message as a detail text message 646 myDetails = new CodeValidationIssueDetails(theDiagnostics); 647 myDetails.addCoding(theDetailsCoding); 648 } 649 650 /** 651 * @deprecated Please use {@link #getDiagnostics()} instead. 652 */ 653 @Deprecated(since = "7.4.6") 654 public String getMessage() { 655 return getDiagnostics(); 656 } 657 658 public String getDiagnostics() { 659 return myDiagnostics; 660 } 661 662 public IssueSeverity getSeverity() { 663 return mySeverity; 664 } 665 666 /** 667 * @deprecated Please use {@link #getType()} instead. 668 */ 669 @Deprecated(since = "7.4.6") 670 public CodeValidationIssueCode getCode() { 671 return getType(); 672 } 673 674 public CodeValidationIssueCode getType() { 675 return myCode; 676 } 677 678 /** 679 * @deprecated Please use {@link #getDetails()} instead. That has support for multiple codings. 680 */ 681 @Deprecated(since = "7.4.6") 682 public CodeValidationIssueCoding getCoding() { 683 return myDetails != null 684 ? myDetails.getCodings().stream().findFirst().orElse(null) 685 : null; 686 } 687 688 public void setDetails(CodeValidationIssueDetails theDetails) { 689 this.myDetails = theDetails; 690 } 691 692 public CodeValidationIssueDetails getDetails() { 693 return myDetails; 694 } 695 696 public boolean hasIssueDetailCode(@Nonnull String theCode) { 697 // this method is system agnostic at the moment but it can be restricted if needed 698 return myDetails.getCodings().stream().anyMatch(coding -> theCode.equals(coding.getCode())); 699 } 700 } 701 702 class ConceptDesignation { 703 704 private String myLanguage; 705 private String myUseSystem; 706 private String myUseCode; 707 private String myUseDisplay; 708 private String myValue; 709 710 public String getLanguage() { 711 return myLanguage; 712 } 713 714 public ConceptDesignation setLanguage(String theLanguage) { 715 myLanguage = theLanguage; 716 return this; 717 } 718 719 public String getUseSystem() { 720 return myUseSystem; 721 } 722 723 public ConceptDesignation setUseSystem(String theUseSystem) { 724 myUseSystem = theUseSystem; 725 return this; 726 } 727 728 public String getUseCode() { 729 return myUseCode; 730 } 731 732 public ConceptDesignation setUseCode(String theUseCode) { 733 myUseCode = theUseCode; 734 return this; 735 } 736 737 public String getUseDisplay() { 738 return myUseDisplay; 739 } 740 741 public ConceptDesignation setUseDisplay(String theUseDisplay) { 742 myUseDisplay = theUseDisplay; 743 return this; 744 } 745 746 public String getValue() { 747 return myValue; 748 } 749 750 public ConceptDesignation setValue(String theValue) { 751 myValue = theValue; 752 return this; 753 } 754 } 755 756 abstract class BaseConceptProperty { 757 private final String myPropertyName; 758 759 /** 760 * Constructor 761 */ 762 protected BaseConceptProperty(String thePropertyName) { 763 myPropertyName = thePropertyName; 764 } 765 766 public String getPropertyName() { 767 return myPropertyName; 768 } 769 770 public abstract String getType(); 771 } 772 773 // The reason these cannot be declared within an enum is because a Remote Terminology Service 774 // can support arbitrary types. We do not restrict against the types in the spec. 775 // Some of the types in the spec are not yet implemented as well. 776 // @see https://github.com/hapifhir/hapi-fhir/issues/5700 777 String TYPE_STRING = "string"; 778 String TYPE_BOOLEAN = "boolean"; 779 String TYPE_CODING = "Coding"; 780 String TYPE_GROUP = "group"; 781 782 class StringConceptProperty extends BaseConceptProperty { 783 private final String myValue; 784 785 /** 786 * Constructor 787 * 788 * @param theName The name 789 */ 790 public StringConceptProperty(String theName, String theValue) { 791 super(theName); 792 myValue = theValue; 793 } 794 795 public String getValue() { 796 return myValue; 797 } 798 799 @Override 800 public String getType() { 801 return TYPE_STRING; 802 } 803 } 804 805 class BooleanConceptProperty extends BaseConceptProperty { 806 private final boolean myValue; 807 808 /** 809 * Constructor 810 * 811 * @param theName The name 812 */ 813 public BooleanConceptProperty(String theName, boolean theValue) { 814 super(theName); 815 myValue = theValue; 816 } 817 818 public boolean getValue() { 819 return myValue; 820 } 821 822 @Override 823 public String getType() { 824 return TYPE_BOOLEAN; 825 } 826 } 827 828 class CodingConceptProperty extends BaseConceptProperty { 829 private final String myCode; 830 private final String myCodeSystem; 831 private final String myDisplay; 832 833 /** 834 * Constructor 835 * 836 * @param theName The name 837 */ 838 public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) { 839 super(theName); 840 myCodeSystem = theCodeSystem; 841 myCode = theCode; 842 myDisplay = theDisplay; 843 } 844 845 public String getCode() { 846 return myCode; 847 } 848 849 public String getCodeSystem() { 850 return myCodeSystem; 851 } 852 853 public String getDisplay() { 854 return myDisplay; 855 } 856 857 @Override 858 public String getType() { 859 return TYPE_CODING; 860 } 861 } 862 863 class GroupConceptProperty extends BaseConceptProperty { 864 public GroupConceptProperty(String thePropertyName) { 865 super(thePropertyName); 866 } 867 868 private List<BaseConceptProperty> subProperties; 869 870 public BaseConceptProperty addSubProperty(BaseConceptProperty theProperty) { 871 if (subProperties == null) { 872 subProperties = new ArrayList<>(); 873 } 874 subProperties.add(theProperty); 875 return this; 876 } 877 878 public List<BaseConceptProperty> getSubProperties() { 879 return subProperties != null ? subProperties : Collections.emptyList(); 880 } 881 882 @Override 883 public String getType() { 884 return TYPE_GROUP; 885 } 886 } 887 888 /** 889 * This is a hapi-fhir internal version agnostic object holding information about the validation result. 890 * An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult. 891 */ 892 class CodeValidationResult { 893 public static final String SOURCE_DETAILS = "sourceDetails"; 894 public static final String RESULT = "result"; 895 public static final String MESSAGE = "message"; 896 public static final String DISPLAY = "display"; 897 898 private String myCode; 899 private String myMessage; 900 private IssueSeverity mySeverity; 901 private String myCodeSystemName; 902 private String myCodeSystemVersion; 903 private List<BaseConceptProperty> myProperties; 904 private String myDisplay; 905 private String mySourceDetails; 906 907 private List<CodeValidationIssue> myIssues; 908 909 public CodeValidationResult() { 910 super(); 911 } 912 913 /** 914 * This field may contain information about what the source of the 915 * validation information was. 916 */ 917 public String getSourceDetails() { 918 return mySourceDetails; 919 } 920 921 /** 922 * This field may contain information about what the source of the 923 * validation information was. 924 */ 925 public CodeValidationResult setSourceDetails(String theSourceDetails) { 926 mySourceDetails = theSourceDetails; 927 return this; 928 } 929 930 public String getDisplay() { 931 return myDisplay; 932 } 933 934 public CodeValidationResult setDisplay(String theDisplay) { 935 myDisplay = theDisplay; 936 return this; 937 } 938 939 public String getCode() { 940 return myCode; 941 } 942 943 public CodeValidationResult setCode(String theCode) { 944 myCode = theCode; 945 return this; 946 } 947 948 public String getCodeSystemName() { 949 return myCodeSystemName; 950 } 951 952 public CodeValidationResult setCodeSystemName(String theCodeSystemName) { 953 myCodeSystemName = theCodeSystemName; 954 return this; 955 } 956 957 public String getCodeSystemVersion() { 958 return myCodeSystemVersion; 959 } 960 961 public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) { 962 myCodeSystemVersion = theCodeSystemVersion; 963 return this; 964 } 965 966 public String getMessage() { 967 return myMessage; 968 } 969 970 public CodeValidationResult setMessage(String theMessage) { 971 myMessage = theMessage; 972 return this; 973 } 974 975 public List<BaseConceptProperty> getProperties() { 976 return myProperties; 977 } 978 979 public void setProperties(List<BaseConceptProperty> theProperties) { 980 myProperties = theProperties; 981 } 982 983 public IssueSeverity getSeverity() { 984 return mySeverity; 985 } 986 987 public CodeValidationResult setSeverity(IssueSeverity theSeverity) { 988 mySeverity = theSeverity; 989 return this; 990 } 991 992 /** 993 * @deprecated Please use method {@link #getIssues()} instead. 994 */ 995 @Deprecated(since = "7.4.6") 996 public List<CodeValidationIssue> getCodeValidationIssues() { 997 return getIssues(); 998 } 999 1000 /** 1001 * @deprecated Please use method {@link #setIssues(List)} instead. 1002 */ 1003 @Deprecated(since = "7.4.6") 1004 public CodeValidationResult setCodeValidationIssues(List<CodeValidationIssue> theCodeValidationIssues) { 1005 return setIssues(theCodeValidationIssues); 1006 } 1007 1008 /** 1009 * @deprecated Please use method {@link #addIssue(CodeValidationIssue)} instead. 1010 */ 1011 @Deprecated(since = "7.4.6") 1012 public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) { 1013 getCodeValidationIssues().add(theCodeValidationIssue); 1014 return this; 1015 } 1016 1017 public List<CodeValidationIssue> getIssues() { 1018 if (myIssues == null) { 1019 myIssues = new ArrayList<>(); 1020 } 1021 return myIssues; 1022 } 1023 1024 public CodeValidationResult setIssues(List<CodeValidationIssue> theIssues) { 1025 myIssues = new ArrayList<>(theIssues); 1026 return this; 1027 } 1028 1029 public CodeValidationResult addIssue(CodeValidationIssue theCodeValidationIssue) { 1030 getIssues().add(theCodeValidationIssue); 1031 return this; 1032 } 1033 1034 public boolean isOk() { 1035 return isNotBlank(myCode); 1036 } 1037 1038 public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) { 1039 LookupCodeResult retVal = new LookupCodeResult(); 1040 retVal.setSearchedForSystem(theSearchedForSystem); 1041 retVal.setSearchedForCode(theSearchedForCode); 1042 if (isOk()) { 1043 retVal.setFound(true); 1044 retVal.setCodeDisplay(myDisplay); 1045 retVal.setCodeSystemDisplayName(getCodeSystemName()); 1046 retVal.setCodeSystemVersion(getCodeSystemVersion()); 1047 } 1048 return retVal; 1049 } 1050 1051 /** 1052 * Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string 1053 */ 1054 public String getSeverityCode() { 1055 String retVal = null; 1056 if (getSeverity() != null) { 1057 retVal = getSeverity().getCode(); 1058 } 1059 return retVal; 1060 } 1061 1062 /** 1063 * Sets an issue severity using a severity code. Please use method {@link #setSeverity(IssueSeverity)} instead. 1064 * @param theSeverityCode the code 1065 * @return the current {@link CodeValidationResult} instance 1066 */ 1067 @Deprecated(since = "7.4.6") 1068 public CodeValidationResult setSeverityCode(@Nonnull String theSeverityCode) { 1069 setSeverity(IssueSeverity.fromCode(theSeverityCode)); 1070 return this; 1071 } 1072 1073 public IBaseParameters toParameters(FhirContext theContext) { 1074 IBaseParameters retVal = ParametersUtil.newInstance(theContext); 1075 1076 ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, isOk()); 1077 if (isNotBlank(getMessage())) { 1078 ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, getMessage()); 1079 } 1080 if (isNotBlank(getDisplay())) { 1081 ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, getDisplay()); 1082 } 1083 if (isNotBlank(getSourceDetails())) { 1084 ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails()); 1085 } 1086 /* 1087 should translate issues as well, except that is version specific code, so it requires more refactoring 1088 or replace the current class with org.hl7.fhir.r5.terminologies.utilities.ValidationResult 1089 @see VersionSpecificWorkerContextWrapper#getIssuesForCodeValidation 1090 */ 1091 1092 return retVal; 1093 } 1094 } 1095 1096 class ValueSetExpansionOutcome { 1097 1098 private final IBaseResource myValueSet; 1099 private final String myError; 1100 1101 private final boolean myErrorIsFromServer; 1102 1103 public ValueSetExpansionOutcome(String theError, boolean theErrorIsFromServer) { 1104 myValueSet = null; 1105 myError = theError; 1106 myErrorIsFromServer = theErrorIsFromServer; 1107 } 1108 1109 public ValueSetExpansionOutcome(IBaseResource theValueSet) { 1110 myValueSet = theValueSet; 1111 myError = null; 1112 myErrorIsFromServer = false; 1113 } 1114 1115 public String getError() { 1116 return myError; 1117 } 1118 1119 public IBaseResource getValueSet() { 1120 return myValueSet; 1121 } 1122 1123 public boolean getErrorIsFromServer() { 1124 return myErrorIsFromServer; 1125 } 1126 } 1127 1128 class LookupCodeResult { 1129 1130 private String myCodeDisplay; 1131 private boolean myCodeIsAbstract; 1132 private String myCodeSystemDisplayName; 1133 private String myCodeSystemVersion; 1134 private boolean myFound; 1135 private String mySearchedForCode; 1136 private String mySearchedForSystem; 1137 private List<BaseConceptProperty> myProperties; 1138 private List<ConceptDesignation> myDesignations; 1139 private String myErrorMessage; 1140 1141 /** 1142 * Constructor 1143 */ 1144 public LookupCodeResult() { 1145 super(); 1146 } 1147 1148 public List<BaseConceptProperty> getProperties() { 1149 if (myProperties == null) { 1150 myProperties = new ArrayList<>(); 1151 } 1152 return myProperties; 1153 } 1154 1155 public void setProperties(List<BaseConceptProperty> theProperties) { 1156 myProperties = theProperties; 1157 } 1158 1159 @Nonnull 1160 public List<ConceptDesignation> getDesignations() { 1161 if (myDesignations == null) { 1162 myDesignations = new ArrayList<>(); 1163 } 1164 return myDesignations; 1165 } 1166 1167 public String getCodeDisplay() { 1168 return myCodeDisplay; 1169 } 1170 1171 public void setCodeDisplay(String theCodeDisplay) { 1172 myCodeDisplay = theCodeDisplay; 1173 } 1174 1175 public String getCodeSystemDisplayName() { 1176 return myCodeSystemDisplayName; 1177 } 1178 1179 public void setCodeSystemDisplayName(String theCodeSystemDisplayName) { 1180 myCodeSystemDisplayName = theCodeSystemDisplayName; 1181 } 1182 1183 public String getCodeSystemVersion() { 1184 return myCodeSystemVersion; 1185 } 1186 1187 public void setCodeSystemVersion(String theCodeSystemVersion) { 1188 myCodeSystemVersion = theCodeSystemVersion; 1189 } 1190 1191 public String getSearchedForCode() { 1192 return mySearchedForCode; 1193 } 1194 1195 public LookupCodeResult setSearchedForCode(String theSearchedForCode) { 1196 mySearchedForCode = theSearchedForCode; 1197 return this; 1198 } 1199 1200 public String getSearchedForSystem() { 1201 return mySearchedForSystem; 1202 } 1203 1204 public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) { 1205 mySearchedForSystem = theSearchedForSystem; 1206 return this; 1207 } 1208 1209 public boolean isCodeIsAbstract() { 1210 return myCodeIsAbstract; 1211 } 1212 1213 public void setCodeIsAbstract(boolean theCodeIsAbstract) { 1214 myCodeIsAbstract = theCodeIsAbstract; 1215 } 1216 1217 public boolean isFound() { 1218 return myFound; 1219 } 1220 1221 public LookupCodeResult setFound(boolean theFound) { 1222 myFound = theFound; 1223 return this; 1224 } 1225 1226 public void throwNotFoundIfAppropriate() { 1227 if (!isFound()) { 1228 throw new ResourceNotFoundException(Msg.code(1738) + "Unable to find code[" + getSearchedForCode() 1229 + "] in system[" + getSearchedForSystem() + "]"); 1230 } 1231 } 1232 1233 /** 1234 * Converts the current LookupCodeResult instance into a IBaseParameters instance which is returned 1235 * to the client of the $lookup operation. 1236 * @param theContext the FHIR context used for running the operation 1237 * @param thePropertyNamesToFilter the properties which are passed as parameter to filter the result. 1238 * @return the output for the lookup operation. 1239 */ 1240 public IBaseParameters toParameters( 1241 FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNamesToFilter) { 1242 1243 IBaseParameters retVal = ParametersUtil.newInstance(theContext); 1244 if (isNotBlank(getCodeSystemDisplayName())) { 1245 ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName()); 1246 } 1247 if (isNotBlank(getCodeSystemVersion())) { 1248 ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion()); 1249 } 1250 ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay()); 1251 ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract()); 1252 1253 if (myProperties != null) { 1254 1255 final List<BaseConceptProperty> propertiesToReturn; 1256 if (thePropertyNamesToFilter != null && !thePropertyNamesToFilter.isEmpty()) { 1257 // TODO MM: The logic to filter of properties could actually be moved to the lookupCode provider. 1258 // That is where the rest of the lookupCode input parameter handling is done. 1259 // This was left as is for now but can be done with next opportunity. 1260 Set<String> propertyNameList = thePropertyNamesToFilter.stream() 1261 .map(IPrimitiveType::getValueAsString) 1262 .collect(Collectors.toSet()); 1263 propertiesToReturn = myProperties.stream() 1264 .filter(p -> propertyNameList.contains(p.getPropertyName())) 1265 .collect(Collectors.toList()); 1266 } else { 1267 propertiesToReturn = myProperties; 1268 } 1269 1270 for (BaseConceptProperty next : propertiesToReturn) { 1271 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property"); 1272 populateProperty(theContext, property, next); 1273 } 1274 } 1275 1276 if (myDesignations != null) { 1277 for (ConceptDesignation next : myDesignations) { 1278 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation"); 1279 ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage()); 1280 ParametersUtil.addPartCoding( 1281 theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay()); 1282 ParametersUtil.addPartString(theContext, property, "value", next.getValue()); 1283 } 1284 } 1285 1286 return retVal; 1287 } 1288 1289 private void populateProperty( 1290 FhirContext theContext, IBase theProperty, BaseConceptProperty theConceptProperty) { 1291 ParametersUtil.addPartCode(theContext, theProperty, "code", theConceptProperty.getPropertyName()); 1292 String propertyType = theConceptProperty.getType(); 1293 switch (propertyType) { 1294 case TYPE_STRING: 1295 StringConceptProperty stringConceptProperty = (StringConceptProperty) theConceptProperty; 1296 ParametersUtil.addPartString(theContext, theProperty, "value", stringConceptProperty.getValue()); 1297 break; 1298 case TYPE_BOOLEAN: 1299 BooleanConceptProperty booleanConceptProperty = (BooleanConceptProperty) theConceptProperty; 1300 ParametersUtil.addPartBoolean(theContext, theProperty, "value", booleanConceptProperty.getValue()); 1301 break; 1302 case TYPE_CODING: 1303 CodingConceptProperty codingConceptProperty = (CodingConceptProperty) theConceptProperty; 1304 ParametersUtil.addPartCoding( 1305 theContext, 1306 theProperty, 1307 "value", 1308 codingConceptProperty.getCodeSystem(), 1309 codingConceptProperty.getCode(), 1310 codingConceptProperty.getDisplay()); 1311 break; 1312 case TYPE_GROUP: 1313 GroupConceptProperty groupConceptProperty = (GroupConceptProperty) theConceptProperty; 1314 if (groupConceptProperty.getSubProperties().isEmpty()) { 1315 break; 1316 } 1317 groupConceptProperty.getSubProperties().forEach(p -> { 1318 IBase subProperty = ParametersUtil.addPart(theContext, theProperty, "subproperty", null); 1319 populateProperty(theContext, subProperty, p); 1320 }); 1321 break; 1322 default: 1323 throw new IllegalStateException( 1324 Msg.code(1739) + "Don't know how to handle " + theConceptProperty.getClass()); 1325 } 1326 } 1327 1328 public LookupCodeResult setErrorMessage(String theErrorMessage) { 1329 myErrorMessage = theErrorMessage; 1330 return this; 1331 } 1332 1333 public String getErrorMessage() { 1334 return myErrorMessage; 1335 } 1336 1337 public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) { 1338 return new LookupCodeResult() 1339 .setFound(false) 1340 .setSearchedForSystem(theSearchedForSystem) 1341 .setSearchedForCode(theSearchedForCode); 1342 } 1343 } 1344 1345 class TranslateCodeRequest { 1346 private final String myTargetSystemUrl; 1347 private final String myConceptMapUrl; 1348 private final String myConceptMapVersion; 1349 private final String mySourceValueSetUrl; 1350 private final String myTargetValueSetUrl; 1351 private final IIdType myResourceId; 1352 private final boolean myReverse; 1353 private final List<IBaseCoding> myCodings; 1354 1355 public TranslateCodeRequest(List<IBaseCoding> theCodings, String theTargetSystemUrl) { 1356 myCodings = theCodings; 1357 myTargetSystemUrl = theTargetSystemUrl; 1358 myConceptMapUrl = null; 1359 myConceptMapVersion = null; 1360 mySourceValueSetUrl = null; 1361 myTargetValueSetUrl = null; 1362 myResourceId = null; 1363 myReverse = false; 1364 } 1365 1366 public TranslateCodeRequest( 1367 List<IBaseCoding> theCodings, 1368 String theTargetSystemUrl, 1369 String theConceptMapUrl, 1370 String theConceptMapVersion, 1371 String theSourceValueSetUrl, 1372 String theTargetValueSetUrl, 1373 IIdType theResourceId, 1374 boolean theReverse) { 1375 myCodings = theCodings; 1376 myTargetSystemUrl = theTargetSystemUrl; 1377 myConceptMapUrl = theConceptMapUrl; 1378 myConceptMapVersion = theConceptMapVersion; 1379 mySourceValueSetUrl = theSourceValueSetUrl; 1380 myTargetValueSetUrl = theTargetValueSetUrl; 1381 myResourceId = theResourceId; 1382 myReverse = theReverse; 1383 } 1384 1385 @Override 1386 public boolean equals(Object theO) { 1387 if (this == theO) { 1388 return true; 1389 } 1390 1391 if (theO == null || getClass() != theO.getClass()) { 1392 return false; 1393 } 1394 1395 TranslateCodeRequest that = (TranslateCodeRequest) theO; 1396 1397 return new EqualsBuilder() 1398 .append(myCodings, that.myCodings) 1399 .append(myTargetSystemUrl, that.myTargetSystemUrl) 1400 .append(myConceptMapUrl, that.myConceptMapUrl) 1401 .append(myConceptMapVersion, that.myConceptMapVersion) 1402 .append(mySourceValueSetUrl, that.mySourceValueSetUrl) 1403 .append(myTargetValueSetUrl, that.myTargetValueSetUrl) 1404 .append(myResourceId, that.myResourceId) 1405 .append(myReverse, that.myReverse) 1406 .isEquals(); 1407 } 1408 1409 @Override 1410 public int hashCode() { 1411 return new HashCodeBuilder(17, 37) 1412 .append(myCodings) 1413 .append(myTargetSystemUrl) 1414 .append(myConceptMapUrl) 1415 .append(myConceptMapVersion) 1416 .append(mySourceValueSetUrl) 1417 .append(myTargetValueSetUrl) 1418 .append(myResourceId) 1419 .append(myReverse) 1420 .toHashCode(); 1421 } 1422 1423 public List<IBaseCoding> getCodings() { 1424 return myCodings; 1425 } 1426 1427 public String getTargetSystemUrl() { 1428 return myTargetSystemUrl; 1429 } 1430 1431 public String getConceptMapUrl() { 1432 return myConceptMapUrl; 1433 } 1434 1435 public String getConceptMapVersion() { 1436 return myConceptMapVersion; 1437 } 1438 1439 public String getSourceValueSetUrl() { 1440 return mySourceValueSetUrl; 1441 } 1442 1443 public String getTargetValueSetUrl() { 1444 return myTargetValueSetUrl; 1445 } 1446 1447 public IIdType getResourceId() { 1448 return myResourceId; 1449 } 1450 1451 public boolean isReverse() { 1452 return myReverse; 1453 } 1454 1455 @Override 1456 public String toString() { 1457 return new ToStringBuilder(this) 1458 .append("sourceValueSetUrl", mySourceValueSetUrl) 1459 .append("targetSystemUrl", myTargetSystemUrl) 1460 .append("targetValueSetUrl", myTargetValueSetUrl) 1461 .append("reverse", myReverse) 1462 .toString(); 1463 } 1464 } 1465 1466 /** 1467 * When validating a CodeableConcept containing multiple codings, this method can be used to control whether 1468 * the validator requires all codings in the CodeableConcept to be valid in order to consider the 1469 * CodeableConcept valid. 1470 * <p> 1471 * See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation, and the refer to the values below 1472 * for the behaviour associated with each value. 1473 * </p> 1474 * <p> 1475 * <ul> 1476 * <li>If <code>false</code> (default setting) the validation for codings will return a positive result only if 1477 * ALL codings are valid.</li> 1478 * <li>If <code>true</code> the validation for codings will return a positive result if ANY codings are valid. 1479 * </li> 1480 * </ul> 1481 * </p> 1482 * @return true or false depending on the desired coding validation behaviour. 1483 */ 1484 default boolean isCodeableConceptValidationSuccessfulIfNotAllCodingsAreValid() { 1485 return false; 1486 } 1487}