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