
001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.dao; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.context.FhirVersionEnum; 024import ca.uhn.fhir.context.RuntimeResourceDefinition; 025import ca.uhn.fhir.i18n.Msg; 026import ca.uhn.fhir.interceptor.model.RequestPartitionId; 027import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; 028import ca.uhn.fhir.jpa.api.dao.IDao; 029import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao; 030import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; 031import ca.uhn.fhir.jpa.entity.PartitionEntity; 032import ca.uhn.fhir.jpa.esr.ExternallyStoredResourceServiceRegistry; 033import ca.uhn.fhir.jpa.esr.IExternallyStoredResourceService; 034import ca.uhn.fhir.jpa.model.config.PartitionSettings; 035import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; 036import ca.uhn.fhir.jpa.model.dao.JpaPid; 037import ca.uhn.fhir.jpa.model.entity.BaseTag; 038import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity; 039import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; 040import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum; 041import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity; 042import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; 043import ca.uhn.fhir.jpa.model.entity.ResourceTable; 044import ca.uhn.fhir.jpa.model.entity.TagDefinition; 045import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; 046import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; 047import ca.uhn.fhir.model.api.IResource; 048import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; 049import ca.uhn.fhir.model.api.Tag; 050import ca.uhn.fhir.model.api.TagList; 051import ca.uhn.fhir.model.base.composite.BaseCodingDt; 052import ca.uhn.fhir.model.primitive.IdDt; 053import ca.uhn.fhir.model.primitive.InstantDt; 054import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; 055import ca.uhn.fhir.parser.DataFormatException; 056import ca.uhn.fhir.parser.IParser; 057import ca.uhn.fhir.parser.LenientErrorHandler; 058import ca.uhn.fhir.rest.api.Constants; 059import ca.uhn.fhir.util.IMetaTagSorter; 060import ca.uhn.fhir.util.MetaUtil; 061import jakarta.annotation.Nullable; 062import org.apache.commons.collections4.CollectionUtils; 063import org.apache.commons.lang3.Validate; 064import org.hl7.fhir.instance.model.api.IAnyResource; 065import org.hl7.fhir.instance.model.api.IBaseCoding; 066import org.hl7.fhir.instance.model.api.IBaseMetaType; 067import org.hl7.fhir.instance.model.api.IBaseResource; 068import org.hl7.fhir.instance.model.api.IIdType; 069import org.slf4j.Logger; 070import org.slf4j.LoggerFactory; 071import org.springframework.beans.factory.annotation.Autowired; 072 073import java.util.ArrayList; 074import java.util.Collection; 075import java.util.Date; 076import java.util.List; 077import java.util.Optional; 078 079import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.decodeResource; 080import static java.util.Objects.nonNull; 081import static org.apache.commons.lang3.StringUtils.isBlank; 082import static org.apache.commons.lang3.StringUtils.isNotBlank; 083 084public class JpaStorageResourceParser implements IJpaStorageResourceParser { 085 public static final LenientErrorHandler LENIENT_ERROR_HANDLER = new LenientErrorHandler(false).disableAllErrors(); 086 private static final Logger ourLog = LoggerFactory.getLogger(JpaStorageResourceParser.class); 087 088 @Autowired 089 private FhirContext myFhirContext; 090 091 @Autowired 092 private JpaStorageSettings myStorageSettings; 093 094 @Autowired 095 private IResourceHistoryTableDao myResourceHistoryTableDao; 096 097 @Autowired 098 private IResourceHistoryProvenanceDao myResourceHistoryProvenanceDao; 099 100 @Autowired 101 private PartitionSettings myPartitionSettings; 102 103 @Autowired 104 private IPartitionLookupSvc myPartitionLookupSvc; 105 106 @Autowired 107 private ExternallyStoredResourceServiceRegistry myExternallyStoredResourceServiceRegistry; 108 109 @Autowired 110 IMetaTagSorter myMetaTagSorter; 111 112 @Override 113 public IBaseResource toResource(IBasePersistedResource theEntity, boolean theForHistoryOperation) { 114 RuntimeResourceDefinition type = myFhirContext.getResourceDefinition(theEntity.getResourceType()); 115 Class<? extends IBaseResource> resourceType = type.getImplementingClass(); 116 return toResource(resourceType, (IBaseResourceEntity<JpaPid>) theEntity, null, theForHistoryOperation); 117 } 118 119 @Override 120 public <R extends IBaseResource> R toResource( 121 Class<R> theResourceType, 122 IBaseResourceEntity<?> theEntity, 123 Collection<BaseTag> theTagList, 124 boolean theForHistoryOperation) { 125 126 // 1. get resource, it's encoding and the tags if any 127 byte[] resourceBytes; 128 String resourceText; 129 ResourceEncodingEnum resourceEncoding; 130 @Nullable Collection<? extends BaseTag> tagList; 131 long version; 132 String provenanceSourceUri = null; 133 String provenanceRequestId = null; 134 135 if (theEntity instanceof ResourceHistoryTable) { 136 ResourceHistoryTable history = (ResourceHistoryTable) theEntity; 137 resourceBytes = history.getResource(); 138 resourceText = history.getResourceTextVc(); 139 resourceEncoding = history.getEncoding(); 140 141 // For search results we get the list of tags passed in because we load it 142 // in bulk for all resources we're going to return, but for read results 143 // we don't get the list passed in so we need to load it here. 144 tagList = theTagList; 145 if (tagList == null) { 146 switch (myStorageSettings.getTagStorageMode()) { 147 case VERSIONED: 148 default: 149 if (history.isHasTags()) { 150 tagList = history.getTags(); 151 } 152 break; 153 case NON_VERSIONED: 154 if (history.getResourceTable().isHasTags()) { 155 tagList = history.getResourceTable().getTags(); 156 } 157 break; 158 case INLINE: 159 tagList = null; 160 } 161 } 162 163 version = history.getVersion(); 164 provenanceSourceUri = history.getSourceUri(); 165 provenanceRequestId = history.getRequestId(); 166 if (isBlank(provenanceSourceUri) && isBlank(provenanceRequestId)) { 167 if (myStorageSettings.isAccessMetaSourceInformationFromProvenanceTable()) { 168 Optional<ResourceHistoryProvenanceEntity> provenanceOpt = myResourceHistoryProvenanceDao.findById( 169 history.getId().asIdAndPartitionId()); 170 if (provenanceOpt.isPresent()) { 171 ResourceHistoryProvenanceEntity provenance = provenanceOpt.get(); 172 provenanceRequestId = provenance.getRequestId(); 173 provenanceSourceUri = provenance.getSourceUri(); 174 } 175 } 176 } 177 } else if (theEntity instanceof ResourceTable) { 178 ResourceTable resource = (ResourceTable) theEntity; 179 ResourceHistoryTable history; 180 if (resource.getCurrentVersionEntity() != null) { 181 history = resource.getCurrentVersionEntity(); 182 } else { 183 version = theEntity.getVersion(); 184 history = myResourceHistoryTableDao.findForIdAndVersion( 185 theEntity.getResourceId().toFk(), version); 186 ((ResourceTable) theEntity).setCurrentVersionEntity(history); 187 188 while (history == null) { 189 if (version > 1L) { 190 version--; 191 history = myResourceHistoryTableDao.findForIdAndVersion( 192 theEntity.getResourceId().toFk(), version); 193 } else { 194 return null; 195 } 196 } 197 } 198 199 resourceBytes = history.getResource(); 200 resourceEncoding = history.getEncoding(); 201 resourceText = history.getResourceTextVc(); 202 switch (myStorageSettings.getTagStorageMode()) { 203 case VERSIONED: 204 case NON_VERSIONED: 205 if (resource.isHasTags()) { 206 tagList = resource.getTags(); 207 } else { 208 tagList = List.of(); 209 } 210 break; 211 case INLINE: 212 default: 213 tagList = null; 214 break; 215 } 216 version = history.getVersion(); 217 provenanceSourceUri = history.getSourceUri(); 218 provenanceRequestId = history.getRequestId(); 219 if (isBlank(provenanceSourceUri) && isBlank(provenanceRequestId)) { 220 if (myStorageSettings.isAccessMetaSourceInformationFromProvenanceTable()) { 221 Optional<ResourceHistoryProvenanceEntity> provenanceOpt = myResourceHistoryProvenanceDao.findById( 222 history.getId().asIdAndPartitionId()); 223 if (provenanceOpt.isPresent()) { 224 ResourceHistoryProvenanceEntity provenance = provenanceOpt.get(); 225 provenanceRequestId = provenance.getRequestId(); 226 provenanceSourceUri = provenance.getSourceUri(); 227 } 228 } 229 } 230 } else { 231 // something wrong 232 return null; 233 } 234 235 // 2. get The text 236 String decodedResourceText = decodedResourceText(resourceBytes, resourceText, resourceEncoding); 237 238 // 3. Use the appropriate custom type if one is specified in the context 239 Class<R> resourceType = determineTypeToParse(theResourceType, tagList); 240 241 // 4. parse the text to FHIR 242 R retVal = parseResource(theEntity, resourceEncoding, decodedResourceText, resourceType); 243 244 // 5. fill MetaData 245 retVal = populateResourceMetadata(theEntity, theForHistoryOperation, tagList, version, retVal); 246 247 // 6. Handle source (provenance) 248 MetaUtil.populateResourceSource(myFhirContext, provenanceSourceUri, provenanceRequestId, retVal); 249 250 // 7. Add partition information 251 populateResourcePartitionInformation(theEntity, retVal); 252 253 // 8. sort tags, security labels and profiles 254 myMetaTagSorter.sort(retVal.getMeta()); 255 256 return retVal; 257 } 258 259 private <R extends IBaseResource> void populateResourcePartitionInformation( 260 IBaseResourceEntity theEntity, R retVal) { 261 if (myPartitionSettings.isPartitioningEnabled()) { 262 PartitionablePartitionId partitionId = theEntity.getPartitionId(); 263 if (partitionId != null && partitionId.getPartitionId() != null) { 264 PartitionEntity persistedPartition = 265 myPartitionLookupSvc.getPartitionById(partitionId.getPartitionId()); 266 retVal.setUserData(Constants.RESOURCE_PARTITION_ID, persistedPartition.toRequestPartitionId()); 267 } else { 268 retVal.setUserData( 269 Constants.RESOURCE_PARTITION_ID, 270 RequestPartitionId.fromPartitionId(myPartitionSettings.getDefaultPartitionId())); 271 } 272 } 273 } 274 275 @SuppressWarnings("unchecked") 276 private <R extends IBaseResource> R parseResource( 277 IBaseResourceEntity<?> theEntity, 278 ResourceEncodingEnum theResourceEncoding, 279 String theDecodedResourceText, 280 Class<R> theResourceType) { 281 R retVal; 282 if (theResourceEncoding == ResourceEncodingEnum.ESR) { 283 284 int colonIndex = theDecodedResourceText.indexOf(':'); 285 Validate.isTrue(colonIndex > 0, "Invalid ESR address: %s", theDecodedResourceText); 286 String providerId = theDecodedResourceText.substring(0, colonIndex); 287 String address = theDecodedResourceText.substring(colonIndex + 1); 288 Validate.notBlank(providerId, "No provider ID in ESR address: %s", theDecodedResourceText); 289 Validate.notBlank(address, "No address in ESR address: %s", theDecodedResourceText); 290 IExternallyStoredResourceService provider = 291 myExternallyStoredResourceServiceRegistry.getProvider(providerId); 292 retVal = (R) provider.fetchResource(address); 293 294 } else if (theResourceEncoding != ResourceEncodingEnum.DEL) { 295 296 IParser parser = new TolerantJsonParser( 297 getContext(theEntity.getFhirVersion()), LENIENT_ERROR_HANDLER, theEntity.getResourceId()); 298 299 try { 300 retVal = parser.parseResource(theResourceType, theDecodedResourceText); 301 } catch (Exception e) { 302 StringBuilder b = new StringBuilder(); 303 b.append("Failed to parse database resource["); 304 b.append(myFhirContext.getResourceType(theResourceType)); 305 b.append("/"); 306 b.append(theEntity.getIdDt().getIdPart()); 307 b.append(" (pid "); 308 b.append(theEntity.getId()); 309 b.append(", version "); 310 b.append(theEntity.getFhirVersion().name()); 311 b.append("): "); 312 b.append(e.getMessage()); 313 String msg = b.toString(); 314 ourLog.error(msg, e); 315 throw new DataFormatException(Msg.code(928) + msg, e); 316 } 317 318 } else { 319 320 retVal = (R) myFhirContext 321 .getResourceDefinition(theEntity.getResourceType()) 322 .newInstance(); 323 } 324 return retVal; 325 } 326 327 @SuppressWarnings("unchecked") 328 private <R extends IBaseResource> Class<R> determineTypeToParse( 329 Class<R> theResourceType, @Nullable Collection<? extends BaseTag> tagList) { 330 Class<R> resourceType = theResourceType; 331 if (tagList != null) { 332 if (myFhirContext.hasDefaultTypeForProfile()) { 333 for (BaseTag nextTag : tagList) { 334 if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) { 335 String profile = nextTag.getTag().getCode(); 336 if (isNotBlank(profile)) { 337 Class<? extends IBaseResource> newType = myFhirContext.getDefaultTypeForProfile(profile); 338 if (newType != null && theResourceType.isAssignableFrom(newType)) { 339 ourLog.debug("Using custom type {} for profile: {}", newType.getName(), profile); 340 resourceType = (Class<R>) newType; 341 break; 342 } 343 } 344 } 345 } 346 } 347 } 348 return resourceType; 349 } 350 351 @SuppressWarnings("unchecked") 352 @Override 353 public <R extends IBaseResource> R populateResourceMetadata( 354 IBaseResourceEntity<?> theEntitySource, 355 boolean theForHistoryOperation, 356 @Nullable Collection<? extends BaseTag> tagList, 357 long theVersion, 358 R theResourceTarget) { 359 if (theResourceTarget instanceof IResource) { 360 IResource res = (IResource) theResourceTarget; 361 theResourceTarget = 362 (R) populateResourceMetadataHapi(theEntitySource, tagList, theForHistoryOperation, res, theVersion); 363 } else { 364 IAnyResource res = (IAnyResource) theResourceTarget; 365 theResourceTarget = 366 populateResourceMetadataRi(theEntitySource, tagList, theForHistoryOperation, res, theVersion); 367 } 368 return theResourceTarget; 369 } 370 371 @SuppressWarnings("unchecked") 372 private <R extends IResource> R populateResourceMetadataHapi( 373 IBaseResourceEntity<?> theEntity, 374 @Nullable Collection<? extends BaseTag> theTagList, 375 boolean theForHistoryOperation, 376 R res, 377 Long theVersion) { 378 R retVal = res; 379 if (theEntity.getDeleted() != null) { 380 res = (R) myFhirContext.getResourceDefinition(res).newInstance(); 381 retVal = res; 382 ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted())); 383 if (theForHistoryOperation) { 384 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE); 385 } 386 } else if (theForHistoryOperation) { 387 /* 388 * If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT. 389 */ 390 Date published = theEntity.getPublished().getValue(); 391 Date updated = theEntity.getUpdated().getValue(); 392 if (published.equals(updated)) { 393 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST); 394 } else { 395 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT); 396 } 397 } 398 399 res.setId(theEntity.getIdDt().withVersion(theVersion.toString())); 400 401 ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion())); 402 ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished()); 403 ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated()); 404 IDao.RESOURCE_PID.put(res, theEntity.getResourceId()); 405 406 if (theTagList != null) { 407 if (theEntity.isHasTags()) { 408 TagList tagList = new TagList(); 409 List<IBaseCoding> securityLabels = new ArrayList<>(); 410 List<IdDt> profiles = new ArrayList<>(); 411 for (BaseTag next : theTagList) { 412 TagDefinition nextTag = next.getTag(); 413 switch (nextTag.getTagType()) { 414 case PROFILE: 415 profiles.add(new IdDt(nextTag.getCode())); 416 break; 417 case SECURITY_LABEL: 418 IBaseCoding secLabel = 419 (IBaseCoding) myFhirContext.getVersion().newCodingDt(); 420 secLabel.setSystem(nextTag.getSystem()); 421 secLabel.setCode(nextTag.getCode()); 422 secLabel.setDisplay(nextTag.getDisplay()); 423 secLabel.setVersion(nextTag.getVersion()); 424 Boolean userSelected = nextTag.getUserSelected(); 425 if (userSelected != null) { 426 secLabel.setUserSelected(userSelected); 427 } 428 securityLabels.add(secLabel); 429 break; 430 case TAG: 431 Tag e = new Tag(nextTag.getSystem(), nextTag.getCode(), nextTag.getDisplay()); 432 e.setVersion(nextTag.getVersion()); 433 // careful! These are Boolean, not boolean. 434 e.setUserSelectedBoolean(nextTag.getUserSelected()); 435 tagList.add(e); 436 break; 437 } 438 } 439 if (tagList.size() > 0) { 440 ResourceMetadataKeyEnum.TAG_LIST.put(res, tagList); 441 } 442 if (securityLabels.size() > 0) { 443 ResourceMetadataKeyEnum.SECURITY_LABELS.put(res, toBaseCodingList(securityLabels)); 444 } 445 if (profiles.size() > 0) { 446 ResourceMetadataKeyEnum.PROFILES.put(res, profiles); 447 } 448 } 449 } 450 451 return retVal; 452 } 453 454 @SuppressWarnings("unchecked") 455 private <R extends IBaseResource> R populateResourceMetadataRi( 456 IBaseResourceEntity theEntity, 457 @Nullable Collection<? extends BaseTag> theTagList, 458 boolean theForHistoryOperation, 459 IAnyResource res, 460 Long theVersion) { 461 R retVal = (R) res; 462 if (theEntity.getDeleted() != null) { 463 res = (IAnyResource) myFhirContext.getResourceDefinition(res).newInstance(); 464 retVal = (R) res; 465 ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted())); 466 if (theForHistoryOperation) { 467 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE); 468 } 469 } else if (theForHistoryOperation) { 470 /* 471 * If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT. 472 */ 473 Date published = theEntity.getPublished().getValue(); 474 Date updated = theEntity.getUpdated().getValue(); 475 if (published.equals(updated)) { 476 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST); 477 } else { 478 ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT); 479 } 480 } 481 482 res.getMeta().setLastUpdated(null); 483 res.getMeta().setVersionId(null); 484 485 updateResourceMetadata(theEntity, res); 486 res.setId(res.getIdElement().withVersion(theVersion.toString())); 487 488 res.getMeta().setLastUpdated(theEntity.getUpdatedDate()); 489 IDao.RESOURCE_PID.put(res, theEntity.getResourceId()); 490 491 if (CollectionUtils.isNotEmpty(theTagList)) { 492 res.getMeta().getTag().clear(); 493 res.getMeta().getProfile().clear(); 494 res.getMeta().getSecurity().clear(); 495 496 boolean haveWarnedForMissingTag = false; 497 for (BaseTag next : theTagList) { 498 if (next.getTag() == null) { 499 if (!haveWarnedForMissingTag) { 500 ourLog.warn( 501 "Tag definition HFJ_TAG_DEF#{} is missing, returned Resource.meta may not be complete", 502 next.getTagId()); 503 haveWarnedForMissingTag = true; 504 } 505 continue; 506 } 507 508 switch (next.getTag().getTagType()) { 509 case PROFILE: 510 res.getMeta().addProfile(next.getTag().getCode()); 511 break; 512 case SECURITY_LABEL: 513 IBaseCoding sec = res.getMeta().addSecurity(); 514 sec.setSystem(next.getTag().getSystem()); 515 sec.setCode(next.getTag().getCode()); 516 sec.setDisplay(next.getTag().getDisplay()); 517 break; 518 case TAG: 519 IBaseCoding tag = res.getMeta().addTag(); 520 tag.setSystem(next.getTag().getSystem()); 521 tag.setCode(next.getTag().getCode()); 522 tag.setDisplay(next.getTag().getDisplay()); 523 tag.setVersion(next.getTag().getVersion()); 524 Boolean userSelected = next.getTag().getUserSelected(); 525 // the tag is created with a null userSelected, but the api is primitive boolean. 526 // Only update if we are non-null. 527 if (nonNull(userSelected)) { 528 tag.setUserSelected(userSelected); 529 } 530 break; 531 } 532 } 533 } 534 535 return retVal; 536 } 537 538 @Override 539 public void updateResourceMetadata(IBaseResourceEntity<?> theEntitySource, IBaseResource theResourceTarget) { 540 IIdType id = theEntitySource.getIdDt(); 541 if (myFhirContext.getVersion().getVersion().isRi()) { 542 id = myFhirContext.getVersion().newIdType().setValue(id.getValue()); 543 } 544 545 if (id.hasResourceType() == false) { 546 id = id.withResourceType(theEntitySource.getResourceType()); 547 } 548 549 theResourceTarget.setId(id); 550 if (theResourceTarget instanceof IResource) { 551 ResourceMetadataKeyEnum.VERSION.put(theResourceTarget, id.getVersionIdPart()); 552 ResourceMetadataKeyEnum.UPDATED.put(theResourceTarget, theEntitySource.getUpdated()); 553 } else { 554 IBaseMetaType meta = theResourceTarget.getMeta(); 555 meta.setVersionId(id.getVersionIdPart()); 556 meta.setLastUpdated(theEntitySource.getUpdatedDate()); 557 } 558 559 populateResourcePartitionInformation(theEntitySource, theResourceTarget); 560 } 561 562 private FhirContext getContext(FhirVersionEnum theVersion) { 563 Validate.notNull(theVersion, "theVersion must not be null"); 564 if (theVersion == myFhirContext.getVersion().getVersion()) { 565 return myFhirContext; 566 } 567 return FhirContext.forCached(theVersion); 568 } 569 570 private static String decodedResourceText( 571 byte[] resourceBytes, String resourceText, ResourceEncodingEnum resourceEncoding) { 572 String decodedResourceText; 573 if (resourceText != null) { 574 decodedResourceText = resourceText; 575 } else { 576 decodedResourceText = decodeResource(resourceBytes, resourceEncoding); 577 } 578 return decodedResourceText; 579 } 580 581 private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) { 582 ArrayList<BaseCodingDt> retVal = new ArrayList<>(theSecurityLabels.size()); 583 for (IBaseCoding next : theSecurityLabels) { 584 retVal.add((BaseCodingDt) next); 585 } 586 return retVal; 587 } 588}