
001package org.hl7.fhir.common.hapi.validation.support; 002 003import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 004import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 005import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 006import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; 007import ca.uhn.fhir.context.support.TranslateConceptResult; 008import ca.uhn.fhir.context.support.TranslateConceptResults; 009import ca.uhn.fhir.i18n.Msg; 010import ca.uhn.fhir.context.FhirContext; 011import ca.uhn.fhir.context.FhirVersionEnum; 012import ca.uhn.fhir.context.support.ConceptValidationOptions; 013import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; 014import ca.uhn.fhir.context.support.IValidationSupport; 015import ca.uhn.fhir.context.support.ValidationSupportContext; 016import ca.uhn.fhir.rest.client.api.IGenericClient; 017import ca.uhn.fhir.util.BundleUtil; 018import ca.uhn.fhir.util.JsonUtil; 019import ca.uhn.fhir.util.ParametersUtil; 020import ca.uhn.fhir.util.StringUtil; 021import org.apache.commons.lang3.StringUtils; 022import org.apache.commons.lang3.Validate; 023import org.hl7.fhir.instance.model.api.IBase; 024import org.hl7.fhir.instance.model.api.IBaseBundle; 025import org.hl7.fhir.instance.model.api.IBaseCoding; 026import org.hl7.fhir.instance.model.api.IBaseParameters; 027import org.hl7.fhir.instance.model.api.IBaseResource; 028import org.hl7.fhir.instance.model.api.IPrimitiveType; 029import org.hl7.fhir.r4.model.CodeSystem; 030import org.hl7.fhir.r4.model.Coding; 031import org.hl7.fhir.r4.model.Parameters; 032import org.hl7.fhir.r4.model.ValueSet; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import javax.annotation.Nonnull; 037import java.io.IOException; 038import java.sql.Array; 039import java.util.ArrayList; 040import java.util.List; 041import java.util.Objects; 042import java.util.Optional; 043 044import static org.apache.commons.lang3.StringUtils.isBlank; 045import static org.apache.commons.lang3.StringUtils.isNotBlank; 046 047/** 048 * This class is an implementation of {@link IValidationSupport} that fetches validation codes 049 * from a remote FHIR based terminology server. It will invoke the FHIR 050 * <a href="http://hl7.org/fhir/valueset-operation-validate-code.html">ValueSet/$validate-code</a> 051 * operation in order to validate codes. 052 */ 053public class RemoteTerminologyServiceValidationSupport extends BaseValidationSupport implements IValidationSupport { 054 private static final Logger ourLog = LoggerFactory.getLogger(RemoteTerminologyServiceValidationSupport.class); 055 056 private String myBaseUrl; 057 private List<Object> myClientInterceptors = new ArrayList<>(); 058 059 /** 060 * Constructor 061 * 062 * @param theFhirContext The FhirContext object to use 063 */ 064 public RemoteTerminologyServiceValidationSupport(FhirContext theFhirContext) { 065 super(theFhirContext); 066 } 067 068 public RemoteTerminologyServiceValidationSupport(FhirContext theFhirContext, String theBaseUrl) { 069 super(theFhirContext); 070 myBaseUrl = theBaseUrl; 071 } 072 073 @Override 074 public CodeValidationResult validateCode(@Nonnull ValidationSupportContext theValidationSupportContext, @Nonnull ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { 075 return invokeRemoteValidateCode(theCodeSystem, theCode, theDisplay, theValueSetUrl, null); 076 } 077 078 @Override 079 public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { 080 081 IBaseResource valueSet = theValueSet; 082 083 // some external validators require the system when the code is passed 084 // so let's try to get it from the VS if is is not present 085 String codeSystem = theCodeSystem; 086 if (isNotBlank(theCode) && isBlank(codeSystem)) { 087 codeSystem = extractCodeSystemForCode((ValueSet) theValueSet, theCode); 088 } 089 090 // Remote terminology services shouldn't be used to validate codes with an implied system 091 if (isBlank(codeSystem)) { return null; } 092 093 String valueSetUrl = DefaultProfileValidationSupport.getConformanceResourceUrl(myCtx, valueSet); 094 if (isNotBlank(valueSetUrl)) { 095 valueSet = null; 096 } else { 097 valueSetUrl = null; 098 } 099 return invokeRemoteValidateCode(codeSystem, theCode, theDisplay, valueSetUrl, valueSet); 100 } 101 102 /** 103 * Try to obtain the codeSystem of the received code from the received ValueSet 104 */ 105 private String extractCodeSystemForCode(ValueSet theValueSet, String theCode) { 106 if (theValueSet.getCompose() == null || theValueSet.getCompose().getInclude() == null 107 || theValueSet.getCompose().getInclude().isEmpty()) { 108 return null; 109 } 110 111 if (theValueSet.getCompose().getInclude().size() == 1) { 112 ValueSet.ConceptSetComponent include = theValueSet.getCompose().getInclude().iterator().next(); 113 return getVersionedCodeSystem(include); 114 } 115 116 // when component has more than one include, their codeSystem(s) could be different, so we need to make sure 117 // that we are picking up the system for the include to which the code corresponds 118 for (ValueSet.ConceptSetComponent include: theValueSet.getCompose().getInclude()) { 119 if (include.hasSystem()) { 120 for (ValueSet.ConceptReferenceComponent concept : include.getConcept()) { 121 if (concept.hasCodeElement() && concept.getCode().equals(theCode)) { 122 return getVersionedCodeSystem(include); 123 } 124 } 125 } 126 } 127 128 // at this point codeSystem couldn't be extracted for a multi-include ValueSet. Just on case it was 129 // because the format was not well handled, let's allow to watch the VS by an easy logging change 130 ourLog.trace("CodeSystem couldn't be extracted for code: {} for ValueSet: {}", theCode, theValueSet.getId()); 131 return null; 132 } 133 134 private String getVersionedCodeSystem(ValueSet.ConceptSetComponent theComponent) { 135 String codeSystem = theComponent.getSystem(); 136 if ( ! codeSystem.contains("|") && theComponent.hasVersion()) { 137 codeSystem += "|" + theComponent.getVersion(); 138 } 139 return codeSystem; 140 } 141 142 @Override 143 public IBaseResource fetchCodeSystem(String theSystem) { 144 IGenericClient client = provideClient(); 145 Class<? extends IBaseBundle> bundleType = myCtx.getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class); 146 IBaseBundle results = client 147 .search() 148 .forResource("CodeSystem") 149 .where(CodeSystem.URL.matches().value(theSystem)) 150 .returnBundle(bundleType) 151 .execute(); 152 List<IBaseResource> resultsList = BundleUtil.toListOfResources(myCtx, results); 153 if (resultsList.size() > 0) { 154 return resultsList.get(0); 155 } 156 157 return null; 158 } 159 160 @Override 161 public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { 162 Validate.notBlank(theCode, "theCode must be provided"); 163 164 IGenericClient client = provideClient(); 165 FhirContext fhirContext = client.getFhirContext(); 166 FhirVersionEnum fhirVersion = fhirContext.getVersion().getVersion(); 167 168 switch (fhirVersion) { 169 case DSTU3: 170 case R4: 171 IBaseParameters params = ParametersUtil.newInstance(fhirContext); 172 ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode); 173 if (!StringUtils.isEmpty(theSystem)) { 174 ParametersUtil.addParameterToParametersString(fhirContext, params, "system", theSystem); 175 } 176 if (!StringUtils.isEmpty(theDisplayLanguage)) { 177 ParametersUtil.addParameterToParametersString(fhirContext, params, "language", theDisplayLanguage); 178 } 179 Class<?> codeSystemClass = myCtx.getResourceDefinition("CodeSystem").getImplementingClass(); 180 IBaseParameters outcome = client 181 .operation() 182 .onType((Class<? extends IBaseResource>) codeSystemClass) 183 .named("$lookup") 184 .withParameters(params) 185 .useHttpGet() 186 .execute(); 187 if (outcome != null && !outcome.isEmpty()) { 188 switch (fhirVersion) { 189 case DSTU3: 190 return generateLookupCodeResultDSTU3(theCode, theSystem, (org.hl7.fhir.dstu3.model.Parameters)outcome); 191 case R4: 192 return generateLookupCodeResultR4(theCode, theSystem, (org.hl7.fhir.r4.model.Parameters)outcome); 193 } 194 } 195 break; 196 default: 197 throw new UnsupportedOperationException(Msg.code(710) + "Unsupported FHIR version '" + fhirVersion.getFhirVersionString() + 198 "'. Only DSTU3 and R4 are supported."); 199 } 200 return null; 201 } 202 203 private LookupCodeResult generateLookupCodeResultDSTU3(String theCode, String theSystem, org.hl7.fhir.dstu3.model.Parameters outcomeDSTU3) { 204 // NOTE: I wanted to put all of this logic into the IValidationSupport Class, but it would've required adding 205 // several new dependencies on version-specific libraries and that is explicitly forbidden (see comment in POM). 206 LookupCodeResult result = new LookupCodeResult(); 207 result.setSearchedForCode(theCode); 208 result.setSearchedForSystem(theSystem); 209 result.setFound(true); 210 for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent parameterComponent : outcomeDSTU3.getParameter()) { 211 switch (parameterComponent.getName()) { 212 case "property": 213 org.hl7.fhir.dstu3.model.Property part = parameterComponent.getChildByName("part"); 214 // The assumption here is that we may only have 2 elements in this part, and if so, these 2 will be saved 215 if (part != null && part.hasValues() && part.getValues().size() >= 2) { 216 String key = ((org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) part.getValues().get(0)).getValue().toString(); 217 String value = ((org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) part.getValues().get(1)).getValue().toString(); 218 if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) { 219 result.getProperties().add(new StringConceptProperty(key, value)); 220 } 221 } 222 break; 223 case "designation": 224 ConceptDesignation conceptDesignation = new ConceptDesignation(); 225 for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent : parameterComponent.getPart()) { 226 switch(designationComponent.getName()) { 227 case "language": 228 conceptDesignation.setLanguage(designationComponent.getValue().toString()); 229 break; 230 case "use": 231 org.hl7.fhir.dstu3.model.Coding coding = (org.hl7.fhir.dstu3.model.Coding)designationComponent.getValue(); 232 if (coding != null) { 233 conceptDesignation.setUseSystem(coding.getSystem()); 234 conceptDesignation.setUseCode(coding.getCode()); 235 conceptDesignation.setUseDisplay(coding.getDisplay()); 236 } 237 break; 238 case "value": 239 conceptDesignation.setValue(((designationComponent.getValue() == null)?null:designationComponent.getValue().toString())); 240 break; 241 } 242 } 243 result.getDesignations().add(conceptDesignation); 244 break; 245 case "name": 246 result.setCodeSystemDisplayName(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString())); 247 break; 248 case "version": 249 result.setCodeSystemVersion(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString())); 250 break; 251 case "display": 252 result.setCodeDisplay(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString())); 253 break; 254 case "abstract": 255 result.setCodeIsAbstract(((parameterComponent.getValue() == null)?false:Boolean.parseBoolean(parameterComponent.getValue().toString()))); 256 break; 257 } 258 } 259 return result; 260 } 261 262 private LookupCodeResult generateLookupCodeResultR4(String theCode, String theSystem, org.hl7.fhir.r4.model.Parameters outcomeR4) { 263 // NOTE: I wanted to put all of this logic into the IValidationSupport Class, but it would've required adding 264 // several new dependencies on version-specific libraries and that is explicitly forbidden (see comment in POM). 265 LookupCodeResult result = new LookupCodeResult(); 266 result.setSearchedForCode(theCode); 267 result.setSearchedForSystem(theSystem); 268 result.setFound(true); 269 for (org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent parameterComponent : outcomeR4.getParameter()) { 270 switch (parameterComponent.getName()) { 271 case "property": 272 org.hl7.fhir.r4.model.Property part = parameterComponent.getChildByName("part"); 273 // The assumption here is that we may only have 2 elements in this part, and if so, these 2 will be saved 274 if (part != null && part.hasValues() && part.getValues().size() >= 2) { 275 String key = ((org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent) part.getValues().get(0)).getValue().toString(); 276 String value = ((org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent) part.getValues().get(1)).getValue().toString(); 277 if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) { 278 result.getProperties().add(new StringConceptProperty(key, value)); 279 } 280 } 281 break; 282 case "designation": 283 ConceptDesignation conceptDesignation = new ConceptDesignation(); 284 for (org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent designationComponent : parameterComponent.getPart()) { 285 switch(designationComponent.getName()) { 286 case "language": 287 conceptDesignation.setLanguage(designationComponent.getValue().toString()); 288 break; 289 case "use": 290 org.hl7.fhir.r4.model.Coding coding = (org.hl7.fhir.r4.model.Coding)designationComponent.getValue(); 291 if (coding != null) { 292 conceptDesignation.setUseSystem(coding.getSystem()); 293 conceptDesignation.setUseCode(coding.getCode()); 294 conceptDesignation.setUseDisplay(coding.getDisplay()); 295 } 296 break; 297 case "value": 298 conceptDesignation.setValue(((designationComponent.getValue() == null)?null:designationComponent.getValue().toString())); 299 break; 300 } 301 } 302 result.getDesignations().add(conceptDesignation); 303 break; 304 case "name": 305 result.setCodeSystemDisplayName(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString())); 306 break; 307 case "version": 308 result.setCodeSystemVersion(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString())); 309 break; 310 case "display": 311 result.setCodeDisplay(((parameterComponent.getValue() == null)?null:parameterComponent.getValue().toString())); 312 break; 313 case "abstract": 314 result.setCodeIsAbstract(((parameterComponent.getValue() == null)?false:Boolean.parseBoolean(parameterComponent.getValue().toString()))); 315 break; 316 } 317 } 318 return result; 319 } 320 321 @Override 322 public IBaseResource fetchValueSet(String theValueSetUrl) { 323 IGenericClient client = provideClient(); 324 Class<? extends IBaseBundle> bundleType = myCtx.getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class); 325 IBaseBundle results = client 326 .search() 327 .forResource("ValueSet") 328 .where(CodeSystem.URL.matches().value(theValueSetUrl)) 329 .returnBundle(bundleType) 330 .execute(); 331 List<IBaseResource> resultsList = BundleUtil.toListOfResources(myCtx, results); 332 if (resultsList.size() > 0) { 333 return resultsList.get(0); 334 } 335 336 return null; 337 } 338 339 @Override 340 public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { 341 return fetchCodeSystem(theSystem) != null; 342 } 343 344 @Override 345 public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { 346 return fetchValueSet(theValueSetUrl) != null; 347 } 348 349 @Override 350 public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) { 351 IGenericClient client = provideClient(); 352 FhirContext fhirContext = client.getFhirContext(); 353 354 IBaseParameters params = buildTranslateInputParameters(fhirContext, theRequest); 355 356 IBaseParameters outcome = client 357 .operation() 358 .onType("ConceptMap") 359 .named("$translate") 360 .withParameters(params) 361 .execute(); 362 363 return translateOutcomeToResults(fhirContext, outcome); 364 } 365 366 private IGenericClient provideClient() { 367 IGenericClient retVal = myCtx.newRestfulGenericClient(myBaseUrl); 368 for (Object next : myClientInterceptors) { 369 retVal.registerInterceptor(next); 370 } 371 return retVal; 372 } 373 374 protected CodeValidationResult invokeRemoteValidateCode(String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, IBaseResource theValueSet) { 375 if (isBlank(theCode)) { 376 return null; 377 } 378 379 IGenericClient client = provideClient(); 380 381 IBaseParameters input = buildValidateCodeInputParameters(theCodeSystem, theCode, theDisplay, theValueSetUrl, theValueSet); 382 383 String resourceType = "ValueSet"; 384 if (theValueSet == null && theValueSetUrl == null) { 385 resourceType = "CodeSystem"; 386 } 387 388 IBaseParameters output = client 389 .operation() 390 .onType(resourceType) 391 .named("validate-code") 392 .withParameters(input) 393 .execute(); 394 395 List<String> resultValues = ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "result"); 396 if (resultValues.size() < 1 || isBlank(resultValues.get(0))) { 397 return null; 398 } 399 Validate.isTrue(resultValues.size() == 1, "Response contained %d 'result' values", resultValues.size()); 400 401 boolean success = "true".equalsIgnoreCase(resultValues.get(0)); 402 403 CodeValidationResult retVal = new CodeValidationResult(); 404 if (success) { 405 406 retVal.setCode(theCode); 407 List<String> displayValues = ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "display"); 408 if (displayValues.size() > 0) { 409 retVal.setDisplay(displayValues.get(0)); 410 } 411 412 } else { 413 414 retVal.setSeverity(IssueSeverity.ERROR); 415 List<String> messageValues = ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "message"); 416 if (messageValues.size() > 0) { 417 retVal.setMessage(messageValues.get(0)); 418 } 419 420 } 421 return retVal; 422 } 423 424 protected IBaseParameters buildValidateCodeInputParameters(String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, IBaseResource theValueSet) { 425 IBaseParameters params = ParametersUtil.newInstance(getFhirContext()); 426 427 if (theValueSet == null && theValueSetUrl == null) { 428 ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "url", theCodeSystem); 429 ParametersUtil.addParameterToParametersString(getFhirContext(), params, "code", theCode); 430 if (isNotBlank(theDisplay)) { 431 ParametersUtil.addParameterToParametersString(getFhirContext(), params, "display", theDisplay); 432 } 433 return params; 434 } 435 436 if (isNotBlank(theValueSetUrl)) { 437 ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "url", theValueSetUrl); 438 } 439 ParametersUtil.addParameterToParametersString(getFhirContext(), params, "code", theCode); 440 if (isNotBlank(theCodeSystem)) { 441 ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "system", theCodeSystem); 442 } 443 if (isNotBlank(theDisplay)) { 444 ParametersUtil.addParameterToParametersString(getFhirContext(), params, "display", theDisplay); 445 } 446 if (theValueSet != null) { 447 ParametersUtil.addParameterToParameters(getFhirContext(), params, "valueSet", theValueSet); 448 } 449 return params; 450 } 451 452 453 /** 454 * Sets the FHIR Terminology Server base URL 455 * 456 * @param theBaseUrl The base URL, e.g. "https://hapi.fhir.org/baseR4" 457 */ 458 public void setBaseUrl(String theBaseUrl) { 459 Validate.notBlank(theBaseUrl, "theBaseUrl must be provided"); 460 myBaseUrl = theBaseUrl; 461 } 462 463 /** 464 * Adds an interceptor that will be registered to all clients. 465 * <p> 466 * Note that this method is not thread-safe and should only be called prior to this module 467 * being used. 468 * </p> 469 * 470 * @param theClientInterceptor The interceptor (must not be null) 471 */ 472 public void addClientInterceptor(@Nonnull Object theClientInterceptor) { 473 Validate.notNull(theClientInterceptor, "theClientInterceptor must not be null"); 474 myClientInterceptors.add(theClientInterceptor); 475 } 476 477 private IBaseParameters buildTranslateInputParameters(FhirContext fhirContext, TranslateCodeRequest theRequest) { 478 IBaseParameters params = ParametersUtil.newInstance(fhirContext); 479 if (!StringUtils.isEmpty(theRequest.getConceptMapUrl())) { 480 ParametersUtil.addParameterToParametersUri(fhirContext, params, "url", theRequest.getConceptMapUrl()); 481 } 482 if (!StringUtils.isEmpty(theRequest.getConceptMapVersion())) { 483 ParametersUtil.addParameterToParametersString(fhirContext, params, "conceptMapVersion", theRequest.getConceptMapVersion()); 484 } 485 if (theRequest.getCodings() != null) { 486 addCodingsToTranslateParameters(fhirContext, theRequest.getCodings(), params); 487 } 488 if (!StringUtils.isEmpty(theRequest.getSourceValueSetUrl())) { 489 ParametersUtil.addParameterToParametersUri(fhirContext, params, "source", theRequest.getSourceValueSetUrl()); 490 } 491 if (!StringUtils.isEmpty(theRequest.getTargetValueSetUrl())) { 492 ParametersUtil.addParameterToParametersUri(fhirContext, params, "target", theRequest.getTargetValueSetUrl()); 493 } 494 if (!StringUtils.isEmpty(theRequest.getTargetSystemUrl())) { 495 ParametersUtil.addParameterToParametersUri(fhirContext, params, "targetsystem", theRequest.getTargetSystemUrl()); 496 } 497 if (theRequest.isReverse()) { 498 ParametersUtil.addParameterToParametersBoolean(fhirContext, params, "reverse", theRequest.isReverse()); 499 } 500 501 return params; 502 } 503 504 private void addCodingsToTranslateParameters(FhirContext fhirContext, List<IBaseCoding> theCodings, IBaseParameters theParams) { 505 BaseRuntimeElementCompositeDefinition<?> codeableConceptDef = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(fhirContext.getElementDefinition("CodeableConcept")); 506 BaseRuntimeChildDefinition codings = codeableConceptDef.getChildByName("coding"); 507 BaseRuntimeElementCompositeDefinition<?> codingDef = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(fhirContext.getElementDefinition("Coding")); 508 BaseRuntimeChildDefinition codingSystemChild = codingDef.getChildByName("system"); 509 BaseRuntimeChildDefinition codingCodeChild = codingDef.getChildByName("code"); 510 BaseRuntimeElementDefinition<IPrimitiveType<?>> systemDef = (RuntimePrimitiveDatatypeDefinition) fhirContext.getElementDefinition("uri"); 511 BaseRuntimeElementDefinition<IPrimitiveType<?>> codeDef = (RuntimePrimitiveDatatypeDefinition) fhirContext.getElementDefinition("code"); 512 513 IBase codeableConcept = codeableConceptDef.newInstance(); 514 515 for (IBaseCoding aCoding : theCodings) { 516 IBaseCoding newCoding = (IBaseCoding) codingDef.newInstance(); 517 518 IPrimitiveType<?> newSystem = systemDef.newInstance(aCoding.getSystem()); 519 codingSystemChild.getMutator().addValue(newCoding, newSystem); 520 IPrimitiveType<?> newCode = codeDef.newInstance(aCoding.getCode()); 521 codingCodeChild.getMutator().addValue(newCoding, newCode); 522 523 codings.getMutator().addValue(codeableConcept, newCoding); 524 } 525 526 ParametersUtil.addParameterToParameters(fhirContext, theParams, "codeableConcept", codeableConcept); 527 } 528 529 private TranslateConceptResults translateOutcomeToResults(FhirContext fhirContext, IBaseParameters outcome) { 530 Optional<String> result = ParametersUtil.getNamedParameterValueAsString(fhirContext, outcome, "result"); 531 Optional<String> message = ParametersUtil.getNamedParameterValueAsString(fhirContext, outcome, "message"); 532 List<IBase> matches = ParametersUtil.getNamedParameters(fhirContext, outcome, "match"); 533 534 TranslateConceptResults retVal = new TranslateConceptResults(); 535 if (result.isPresent()) { 536 retVal.setResult(Boolean.parseBoolean(result.get())); 537 } 538 if (message.isPresent()) { 539 retVal.setMessage(message.get()); 540 } 541 if (!matches.isEmpty()) { 542 retVal.setResults(matchesToTranslateConceptResults(fhirContext, matches)); 543 } 544 545 return retVal; 546 } 547 548 private List<TranslateConceptResult> matchesToTranslateConceptResults(FhirContext fhirContext, List<IBase> theMatches) { 549 List<TranslateConceptResult> resultList = new ArrayList(); 550 for (IBase m : theMatches) { 551 TranslateConceptResult match = new TranslateConceptResult(); 552 String equivalence = ParametersUtil.getParameterPartValueAsString(fhirContext, m, "equivalence"); 553 Optional<IBase> concept = ParametersUtil.getParameterPartValue(fhirContext, m, "concept"); 554 String source = ParametersUtil.getParameterPartValueAsString(fhirContext, m, "source"); 555 556 if (StringUtils.isNotBlank(equivalence)) { 557 match.setEquivalence(equivalence); 558 } 559 560 if (concept.isPresent()) { 561 IBaseCoding matchedCoding = (IBaseCoding) concept.get(); 562 match.setSystem(matchedCoding.getSystem()); 563 match.setCode(matchedCoding.getCode()); 564 match.setDisplay(matchedCoding.getDisplay()); 565 566 if (StringUtils.isNotBlank(source)) { 567 match.setConceptMapUrl(source); 568 } 569 570 resultList.add(match); 571 } 572 } 573 return resultList; 574 } 575 576}