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