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