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}