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