001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 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_CODING = "Coding"; 778 String TYPE_GROUP = "group"; 779 780 class StringConceptProperty extends BaseConceptProperty { 781 private final String myValue; 782 783 /** 784 * Constructor 785 * 786 * @param theName The name 787 */ 788 public StringConceptProperty(String theName, String theValue) { 789 super(theName); 790 myValue = theValue; 791 } 792 793 public String getValue() { 794 return myValue; 795 } 796 797 @Override 798 public String getType() { 799 return TYPE_STRING; 800 } 801 } 802 803 class CodingConceptProperty extends BaseConceptProperty { 804 private final String myCode; 805 private final String myCodeSystem; 806 private final String myDisplay; 807 808 /** 809 * Constructor 810 * 811 * @param theName The name 812 */ 813 public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) { 814 super(theName); 815 myCodeSystem = theCodeSystem; 816 myCode = theCode; 817 myDisplay = theDisplay; 818 } 819 820 public String getCode() { 821 return myCode; 822 } 823 824 public String getCodeSystem() { 825 return myCodeSystem; 826 } 827 828 public String getDisplay() { 829 return myDisplay; 830 } 831 832 @Override 833 public String getType() { 834 return TYPE_CODING; 835 } 836 } 837 838 class GroupConceptProperty extends BaseConceptProperty { 839 public GroupConceptProperty(String thePropertyName) { 840 super(thePropertyName); 841 } 842 843 private List<BaseConceptProperty> subProperties; 844 845 public BaseConceptProperty addSubProperty(BaseConceptProperty theProperty) { 846 if (subProperties == null) { 847 subProperties = new ArrayList<>(); 848 } 849 subProperties.add(theProperty); 850 return this; 851 } 852 853 public List<BaseConceptProperty> getSubProperties() { 854 return subProperties != null ? subProperties : Collections.emptyList(); 855 } 856 857 @Override 858 public String getType() { 859 return TYPE_GROUP; 860 } 861 } 862 863 /** 864 * This is a hapi-fhir internal version agnostic object holding information about the validation result. 865 * An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult. 866 */ 867 class CodeValidationResult { 868 public static final String SOURCE_DETAILS = "sourceDetails"; 869 public static final String RESULT = "result"; 870 public static final String MESSAGE = "message"; 871 public static final String DISPLAY = "display"; 872 873 private String myCode; 874 private String myMessage; 875 private IssueSeverity mySeverity; 876 private String myCodeSystemName; 877 private String myCodeSystemVersion; 878 private List<BaseConceptProperty> myProperties; 879 private String myDisplay; 880 private String mySourceDetails; 881 882 private List<CodeValidationIssue> myIssues; 883 884 public CodeValidationResult() { 885 super(); 886 } 887 888 /** 889 * This field may contain information about what the source of the 890 * validation information was. 891 */ 892 public String getSourceDetails() { 893 return mySourceDetails; 894 } 895 896 /** 897 * This field may contain information about what the source of the 898 * validation information was. 899 */ 900 public CodeValidationResult setSourceDetails(String theSourceDetails) { 901 mySourceDetails = theSourceDetails; 902 return this; 903 } 904 905 public String getDisplay() { 906 return myDisplay; 907 } 908 909 public CodeValidationResult setDisplay(String theDisplay) { 910 myDisplay = theDisplay; 911 return this; 912 } 913 914 public String getCode() { 915 return myCode; 916 } 917 918 public CodeValidationResult setCode(String theCode) { 919 myCode = theCode; 920 return this; 921 } 922 923 public String getCodeSystemName() { 924 return myCodeSystemName; 925 } 926 927 public CodeValidationResult setCodeSystemName(String theCodeSystemName) { 928 myCodeSystemName = theCodeSystemName; 929 return this; 930 } 931 932 public String getCodeSystemVersion() { 933 return myCodeSystemVersion; 934 } 935 936 public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) { 937 myCodeSystemVersion = theCodeSystemVersion; 938 return this; 939 } 940 941 public String getMessage() { 942 return myMessage; 943 } 944 945 public CodeValidationResult setMessage(String theMessage) { 946 myMessage = theMessage; 947 return this; 948 } 949 950 public List<BaseConceptProperty> getProperties() { 951 return myProperties; 952 } 953 954 public void setProperties(List<BaseConceptProperty> theProperties) { 955 myProperties = theProperties; 956 } 957 958 public IssueSeverity getSeverity() { 959 return mySeverity; 960 } 961 962 public CodeValidationResult setSeverity(IssueSeverity theSeverity) { 963 mySeverity = theSeverity; 964 return this; 965 } 966 967 /** 968 * @deprecated Please use method {@link #getIssues()} instead. 969 */ 970 @Deprecated(since = "7.4.6") 971 public List<CodeValidationIssue> getCodeValidationIssues() { 972 return getIssues(); 973 } 974 975 /** 976 * @deprecated Please use method {@link #setIssues(List)} instead. 977 */ 978 @Deprecated(since = "7.4.6") 979 public CodeValidationResult setCodeValidationIssues(List<CodeValidationIssue> theCodeValidationIssues) { 980 return setIssues(theCodeValidationIssues); 981 } 982 983 /** 984 * @deprecated Please use method {@link #addIssue(CodeValidationIssue)} instead. 985 */ 986 @Deprecated(since = "7.4.6") 987 public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) { 988 getCodeValidationIssues().add(theCodeValidationIssue); 989 return this; 990 } 991 992 public List<CodeValidationIssue> getIssues() { 993 if (myIssues == null) { 994 myIssues = new ArrayList<>(); 995 } 996 return myIssues; 997 } 998 999 public CodeValidationResult setIssues(List<CodeValidationIssue> theIssues) { 1000 myIssues = new ArrayList<>(theIssues); 1001 return this; 1002 } 1003 1004 public CodeValidationResult addIssue(CodeValidationIssue theCodeValidationIssue) { 1005 getIssues().add(theCodeValidationIssue); 1006 return this; 1007 } 1008 1009 public boolean isOk() { 1010 return isNotBlank(myCode); 1011 } 1012 1013 public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) { 1014 LookupCodeResult retVal = new LookupCodeResult(); 1015 retVal.setSearchedForSystem(theSearchedForSystem); 1016 retVal.setSearchedForCode(theSearchedForCode); 1017 if (isOk()) { 1018 retVal.setFound(true); 1019 retVal.setCodeDisplay(myDisplay); 1020 retVal.setCodeSystemDisplayName(getCodeSystemName()); 1021 retVal.setCodeSystemVersion(getCodeSystemVersion()); 1022 } 1023 return retVal; 1024 } 1025 1026 /** 1027 * Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string 1028 */ 1029 public String getSeverityCode() { 1030 String retVal = null; 1031 if (getSeverity() != null) { 1032 retVal = getSeverity().getCode(); 1033 } 1034 return retVal; 1035 } 1036 1037 /** 1038 * Sets an issue severity using a severity code. Please use method {@link #setSeverity(IssueSeverity)} instead. 1039 * @param theSeverityCode the code 1040 * @return the current {@link CodeValidationResult} instance 1041 */ 1042 @Deprecated(since = "7.4.6") 1043 public CodeValidationResult setSeverityCode(@Nonnull String theSeverityCode) { 1044 setSeverity(IssueSeverity.fromCode(theSeverityCode)); 1045 return this; 1046 } 1047 1048 public IBaseParameters toParameters(FhirContext theContext) { 1049 IBaseParameters retVal = ParametersUtil.newInstance(theContext); 1050 1051 ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, isOk()); 1052 if (isNotBlank(getMessage())) { 1053 ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, getMessage()); 1054 } 1055 if (isNotBlank(getDisplay())) { 1056 ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, getDisplay()); 1057 } 1058 if (isNotBlank(getSourceDetails())) { 1059 ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails()); 1060 } 1061 /* 1062 should translate issues as well, except that is version specific code, so it requires more refactoring 1063 or replace the current class with org.hl7.fhir.r5.terminologies.utilities.ValidationResult 1064 @see VersionSpecificWorkerContextWrapper#getIssuesForCodeValidation 1065 */ 1066 1067 return retVal; 1068 } 1069 } 1070 1071 class ValueSetExpansionOutcome { 1072 1073 private final IBaseResource myValueSet; 1074 private final String myError; 1075 1076 private boolean myErrorIsFromServer; 1077 1078 public ValueSetExpansionOutcome(String theError, boolean theErrorIsFromServer) { 1079 myValueSet = null; 1080 myError = theError; 1081 myErrorIsFromServer = theErrorIsFromServer; 1082 } 1083 1084 public ValueSetExpansionOutcome(IBaseResource theValueSet) { 1085 myValueSet = theValueSet; 1086 myError = null; 1087 myErrorIsFromServer = false; 1088 } 1089 1090 public String getError() { 1091 return myError; 1092 } 1093 1094 public IBaseResource getValueSet() { 1095 return myValueSet; 1096 } 1097 1098 public boolean getErrorIsFromServer() { 1099 return myErrorIsFromServer; 1100 } 1101 } 1102 1103 class LookupCodeResult { 1104 1105 private String myCodeDisplay; 1106 private boolean myCodeIsAbstract; 1107 private String myCodeSystemDisplayName; 1108 private String myCodeSystemVersion; 1109 private boolean myFound; 1110 private String mySearchedForCode; 1111 private String mySearchedForSystem; 1112 private List<BaseConceptProperty> myProperties; 1113 private List<ConceptDesignation> myDesignations; 1114 private String myErrorMessage; 1115 1116 /** 1117 * Constructor 1118 */ 1119 public LookupCodeResult() { 1120 super(); 1121 } 1122 1123 public List<BaseConceptProperty> getProperties() { 1124 if (myProperties == null) { 1125 myProperties = new ArrayList<>(); 1126 } 1127 return myProperties; 1128 } 1129 1130 public void setProperties(List<BaseConceptProperty> theProperties) { 1131 myProperties = theProperties; 1132 } 1133 1134 @Nonnull 1135 public List<ConceptDesignation> getDesignations() { 1136 if (myDesignations == null) { 1137 myDesignations = new ArrayList<>(); 1138 } 1139 return myDesignations; 1140 } 1141 1142 public String getCodeDisplay() { 1143 return myCodeDisplay; 1144 } 1145 1146 public void setCodeDisplay(String theCodeDisplay) { 1147 myCodeDisplay = theCodeDisplay; 1148 } 1149 1150 public String getCodeSystemDisplayName() { 1151 return myCodeSystemDisplayName; 1152 } 1153 1154 public void setCodeSystemDisplayName(String theCodeSystemDisplayName) { 1155 myCodeSystemDisplayName = theCodeSystemDisplayName; 1156 } 1157 1158 public String getCodeSystemVersion() { 1159 return myCodeSystemVersion; 1160 } 1161 1162 public void setCodeSystemVersion(String theCodeSystemVersion) { 1163 myCodeSystemVersion = theCodeSystemVersion; 1164 } 1165 1166 public String getSearchedForCode() { 1167 return mySearchedForCode; 1168 } 1169 1170 public LookupCodeResult setSearchedForCode(String theSearchedForCode) { 1171 mySearchedForCode = theSearchedForCode; 1172 return this; 1173 } 1174 1175 public String getSearchedForSystem() { 1176 return mySearchedForSystem; 1177 } 1178 1179 public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) { 1180 mySearchedForSystem = theSearchedForSystem; 1181 return this; 1182 } 1183 1184 public boolean isCodeIsAbstract() { 1185 return myCodeIsAbstract; 1186 } 1187 1188 public void setCodeIsAbstract(boolean theCodeIsAbstract) { 1189 myCodeIsAbstract = theCodeIsAbstract; 1190 } 1191 1192 public boolean isFound() { 1193 return myFound; 1194 } 1195 1196 public LookupCodeResult setFound(boolean theFound) { 1197 myFound = theFound; 1198 return this; 1199 } 1200 1201 public void throwNotFoundIfAppropriate() { 1202 if (isFound() == false) { 1203 throw new ResourceNotFoundException(Msg.code(1738) + "Unable to find code[" + getSearchedForCode() 1204 + "] in system[" + getSearchedForSystem() + "]"); 1205 } 1206 } 1207 1208 /** 1209 * Converts the current LookupCodeResult instance into a IBaseParameters instance which is returned 1210 * to the client of the $lookup operation. 1211 * @param theContext the FHIR context used for running the operation 1212 * @param thePropertyNamesToFilter the properties which are passed as parameter to filter the result. 1213 * @return the output for the lookup operation. 1214 */ 1215 public IBaseParameters toParameters( 1216 FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNamesToFilter) { 1217 1218 IBaseParameters retVal = ParametersUtil.newInstance(theContext); 1219 if (isNotBlank(getCodeSystemDisplayName())) { 1220 ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName()); 1221 } 1222 if (isNotBlank(getCodeSystemVersion())) { 1223 ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion()); 1224 } 1225 ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay()); 1226 ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract()); 1227 1228 if (myProperties != null) { 1229 1230 final List<BaseConceptProperty> propertiesToReturn; 1231 if (thePropertyNamesToFilter != null && !thePropertyNamesToFilter.isEmpty()) { 1232 // TODO MM: The logic to filter of properties could actually be moved to the lookupCode provider. 1233 // That is where the rest of the lookupCode input parameter handling is done. 1234 // This was left as is for now but can be done with next opportunity. 1235 Set<String> propertyNameList = thePropertyNamesToFilter.stream() 1236 .map(IPrimitiveType::getValueAsString) 1237 .collect(Collectors.toSet()); 1238 propertiesToReturn = myProperties.stream() 1239 .filter(p -> propertyNameList.contains(p.getPropertyName())) 1240 .collect(Collectors.toList()); 1241 } else { 1242 propertiesToReturn = myProperties; 1243 } 1244 1245 for (BaseConceptProperty next : propertiesToReturn) { 1246 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property"); 1247 populateProperty(theContext, property, next); 1248 } 1249 } 1250 1251 if (myDesignations != null) { 1252 for (ConceptDesignation next : myDesignations) { 1253 IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation"); 1254 ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage()); 1255 ParametersUtil.addPartCoding( 1256 theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay()); 1257 ParametersUtil.addPartString(theContext, property, "value", next.getValue()); 1258 } 1259 } 1260 1261 return retVal; 1262 } 1263 1264 private void populateProperty( 1265 FhirContext theContext, IBase theProperty, BaseConceptProperty theConceptProperty) { 1266 ParametersUtil.addPartCode(theContext, theProperty, "code", theConceptProperty.getPropertyName()); 1267 String propertyType = theConceptProperty.getType(); 1268 switch (propertyType) { 1269 case TYPE_STRING: 1270 StringConceptProperty stringConceptProperty = (StringConceptProperty) theConceptProperty; 1271 ParametersUtil.addPartString(theContext, theProperty, "value", stringConceptProperty.getValue()); 1272 break; 1273 case TYPE_CODING: 1274 CodingConceptProperty codingConceptProperty = (CodingConceptProperty) theConceptProperty; 1275 ParametersUtil.addPartCoding( 1276 theContext, 1277 theProperty, 1278 "value", 1279 codingConceptProperty.getCodeSystem(), 1280 codingConceptProperty.getCode(), 1281 codingConceptProperty.getDisplay()); 1282 break; 1283 case TYPE_GROUP: 1284 GroupConceptProperty groupConceptProperty = (GroupConceptProperty) theConceptProperty; 1285 if (groupConceptProperty.getSubProperties().isEmpty()) { 1286 break; 1287 } 1288 groupConceptProperty.getSubProperties().forEach(p -> { 1289 IBase subProperty = ParametersUtil.addPart(theContext, theProperty, "subproperty", null); 1290 populateProperty(theContext, subProperty, p); 1291 }); 1292 break; 1293 default: 1294 throw new IllegalStateException( 1295 Msg.code(1739) + "Don't know how to handle " + theConceptProperty.getClass()); 1296 } 1297 } 1298 1299 public LookupCodeResult setErrorMessage(String theErrorMessage) { 1300 myErrorMessage = theErrorMessage; 1301 return this; 1302 } 1303 1304 public String getErrorMessage() { 1305 return myErrorMessage; 1306 } 1307 1308 public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) { 1309 return new LookupCodeResult() 1310 .setFound(false) 1311 .setSearchedForSystem(theSearchedForSystem) 1312 .setSearchedForCode(theSearchedForCode); 1313 } 1314 } 1315 1316 class TranslateCodeRequest { 1317 private final String myTargetSystemUrl; 1318 private final String myConceptMapUrl; 1319 private final String myConceptMapVersion; 1320 private final String mySourceValueSetUrl; 1321 private final String myTargetValueSetUrl; 1322 private final IIdType myResourceId; 1323 private final boolean myReverse; 1324 private List<IBaseCoding> myCodings; 1325 1326 public TranslateCodeRequest(List<IBaseCoding> theCodings, String theTargetSystemUrl) { 1327 myCodings = theCodings; 1328 myTargetSystemUrl = theTargetSystemUrl; 1329 myConceptMapUrl = null; 1330 myConceptMapVersion = null; 1331 mySourceValueSetUrl = null; 1332 myTargetValueSetUrl = null; 1333 myResourceId = null; 1334 myReverse = false; 1335 } 1336 1337 public TranslateCodeRequest( 1338 List<IBaseCoding> theCodings, 1339 String theTargetSystemUrl, 1340 String theConceptMapUrl, 1341 String theConceptMapVersion, 1342 String theSourceValueSetUrl, 1343 String theTargetValueSetUrl, 1344 IIdType theResourceId, 1345 boolean theReverse) { 1346 myCodings = theCodings; 1347 myTargetSystemUrl = theTargetSystemUrl; 1348 myConceptMapUrl = theConceptMapUrl; 1349 myConceptMapVersion = theConceptMapVersion; 1350 mySourceValueSetUrl = theSourceValueSetUrl; 1351 myTargetValueSetUrl = theTargetValueSetUrl; 1352 myResourceId = theResourceId; 1353 myReverse = theReverse; 1354 } 1355 1356 @Override 1357 public boolean equals(Object theO) { 1358 if (this == theO) { 1359 return true; 1360 } 1361 1362 if (theO == null || getClass() != theO.getClass()) { 1363 return false; 1364 } 1365 1366 TranslateCodeRequest that = (TranslateCodeRequest) theO; 1367 1368 return new EqualsBuilder() 1369 .append(myCodings, that.myCodings) 1370 .append(myTargetSystemUrl, that.myTargetSystemUrl) 1371 .append(myConceptMapUrl, that.myConceptMapUrl) 1372 .append(myConceptMapVersion, that.myConceptMapVersion) 1373 .append(mySourceValueSetUrl, that.mySourceValueSetUrl) 1374 .append(myTargetValueSetUrl, that.myTargetValueSetUrl) 1375 .append(myResourceId, that.myResourceId) 1376 .append(myReverse, that.myReverse) 1377 .isEquals(); 1378 } 1379 1380 @Override 1381 public int hashCode() { 1382 return new HashCodeBuilder(17, 37) 1383 .append(myCodings) 1384 .append(myTargetSystemUrl) 1385 .append(myConceptMapUrl) 1386 .append(myConceptMapVersion) 1387 .append(mySourceValueSetUrl) 1388 .append(myTargetValueSetUrl) 1389 .append(myResourceId) 1390 .append(myReverse) 1391 .toHashCode(); 1392 } 1393 1394 public List<IBaseCoding> getCodings() { 1395 return myCodings; 1396 } 1397 1398 public String getTargetSystemUrl() { 1399 return myTargetSystemUrl; 1400 } 1401 1402 public String getConceptMapUrl() { 1403 return myConceptMapUrl; 1404 } 1405 1406 public String getConceptMapVersion() { 1407 return myConceptMapVersion; 1408 } 1409 1410 public String getSourceValueSetUrl() { 1411 return mySourceValueSetUrl; 1412 } 1413 1414 public String getTargetValueSetUrl() { 1415 return myTargetValueSetUrl; 1416 } 1417 1418 public IIdType getResourceId() { 1419 return myResourceId; 1420 } 1421 1422 public boolean isReverse() { 1423 return myReverse; 1424 } 1425 1426 @Override 1427 public String toString() { 1428 return new ToStringBuilder(this) 1429 .append("sourceValueSetUrl", mySourceValueSetUrl) 1430 .append("targetSystemUrl", myTargetSystemUrl) 1431 .append("targetValueSetUrl", myTargetValueSetUrl) 1432 .append("reverse", myReverse) 1433 .toString(); 1434 } 1435 } 1436 1437 /** 1438 * When validating a CodeableConcept containing multiple codings, this method can be used to control whether 1439 * the validator requires all codings in the CodeableConcept to be valid in order to consider the 1440 * CodeableConcept valid. 1441 * <p> 1442 * See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation, and the refer to the values below 1443 * for the behaviour associated with each value. 1444 * </p> 1445 * <p> 1446 * <ul> 1447 * <li>If <code>false</code> (default setting) the validation for codings will return a positive result only if 1448 * ALL codings are valid.</li> 1449 * <li>If <code>true</code> the validation for codings will return a positive result if ANY codings are valid. 1450 * </li> 1451 * </ul> 1452 * </p> 1453 * @return true or false depending on the desired coding validation behaviour. 1454 */ 1455 default boolean isCodeableConceptValidationSuccessfulIfNotAllCodingsAreValid() { 1456 return false; 1457 } 1458}